Pārlūkot izejas kodu

feat: 优化主页显示内容
1. 增添展示快的增删改逻辑
2. 轮播库扩展
3. 首页样式调整

kindring 1 mēnesi atpakaļ
vecāks
revīzija
be49216337

+ 1 - 1
components/productCenter.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="w-screen pad:w-full">
-    <big-title>{{lang===langType.cn?"产品中心":getAbbrText("产品中心")}}</big-title>
+    <big-title>{{lang===langType.cn?"核心产品":getAbbrText("核心产品")}}</big-title>
     <div class="container mx-auto productCenter">
         <a class="product"
            v-for="(product,i) in products"

+ 10 - 8
components/showingStand.vue

@@ -1,24 +1,23 @@
 <template>
   <div class="w-screen pad:w-full">
-    <big-title>{{lang===langType.cn?"关于我们":getAbbrText("关于我们")}}</big-title>
+    <big-title>{{lang===langType.cn?"解决方案":getAbbrText("解决方案")}}</big-title>
     <div class="container mx-auto showing">
-      <a
-        v-for="(item,i) in chunkItems"
+      <div
+        v-for="(item,i) in showBlocks"
         :class="`chunk imgBox chunk${(i+1)}`"
         :key="item.id"
-        :href="item.href"
       >
 <!--         实时管理(物联网) -->
-        <img :src="item.img" alt="imgNotFound">
+        <img :src="item.filePath" alt="item.title">
         <p class="chunkText">
           <span class="title">{{lang===langType.cn?item.title:getAbbrText(item.title)}}</span>
           <span class="subTitle">{{lang===langType.cn?item.subTitle:getAbbrText(item.subTitle)}}</span>
           <span class="chunkMore">
-          {{lang===langType.cn?"了解更多":getAbbrText("了解更多")}}
+          {{lang===langType.cn?"解决方案":getAbbrText("解决方案")}}
         </span>
         </p>
 
-      </a>
+      </div>
     </div>
   </div>
 </template>
@@ -36,12 +35,15 @@ export default {
     lang:{
       default: langMap.lang.cn
     },
+    showBlocks:{
+      default: []
+    }
   },
   data(){
     return {
       langType: langMap.lang,
       // chunk 1-7
-      chunkItems:showingStandData,
+      chunkItems: showingStandData,
       baseWidth: 500,
       baseHeight: 350,
     }

+ 13 - 0
map/apiMap.js

@@ -108,6 +108,19 @@ export const apiMap = {
   baseUpdateCarousel: {
     path: `/api/base/carousel`
   },
+  // 展示块相关接口
+  showBlocks: {
+    path: `/api/base/showblocks`
+  },
+  showBlocksList: {
+    path: `/api/base/showblocks/list`
+  },
+  baseAddShowBlock: {
+    path: `/api/base/showblocks`
+  },
+  baseUpdateShowBlock: {
+    path: `/api/base/showblocks`
+  },
   baseInfo: {
     path: `/api/base/info`
   },

+ 6 - 5
pages/index.vue

@@ -3,14 +3,12 @@
 <!--    header 部分布局-->
     <lucency-header :lang="lang" page-key="index" :is-phone="isPhone" />
     <banner :banners="carousel" :wait-time="2500"/>
-    <new-center v-if="isPhone" :lang="lang"/>
-    <product-center v-if="isPhone" :lang="lang"/>
+    <new-center  :lang="lang"/>
+    <product-center  :lang="lang"/>
 <!--    优势项目部分展示,参考海康-->
-    <showing-stand :lang="lang"/>
+    <showing-stand :show-blocks="showBlocks" :lang="lang"/>
 <!--    产品中心,参考大华,动态加载这部分内容-->
     <product-center v-if="!isPhone" :lang="lang"/>
-<!--    新闻中心-->
-    <new-center v-if="!isPhone" :lang="lang"/>
 <!--    页脚 -->
     <default-footer :lang="lang"/>
 
@@ -46,6 +44,9 @@ export default {
       // url转换
       return this.$store.state.index.carousel;
     },
+    showBlocks(){
+      return this.$store.state.index.showBlocks;
+    },
   },
   methods:{
     switchLang(nextLang){

+ 976 - 8
pages/manger/index/showing.vue

@@ -1,19 +1,987 @@
 <script>
-  export default {
-    name: "showing",
-    data() {
-      return {
+import axios from "axios";
+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 ImageViewer from "../../../components/public/imageViewer.vue";
+import ImageTable from "../../../components/public/imageTable.vue";
+import Pop from "../../../components/public/pop.vue";
+import PopCard from "../../../components/public/popCard.vue";
+import InputRow from "../../../components/public/form/inputRow.vue";
+import dbField_esm from "../../../map/dbField_esm";
+import {apiMap} from "../../../map/apiMap";
+import SearchBox from "../../../components/search/searchBox.vue";
+import {pTypes} from "../../../map/productMap";
+import {newsType} from "../../../map/newMap";
+import {toNumber,isEmpty} from "../../../until/typeTool";
+
+export default defineComponent({
+  name: 'showing',
+  computed: {
+    dbField_esm() {
+      return dbField_esm
+    },
+    productTypes(){
+      let arr = this.$store.getters.productTypes;
+      // 添加 all
+      arr.unshift({text: '全部', key: 'all' });
+      return arr;
+    },
+    newsTypes(){// 添加 all
+      let arr = this.$store.getters.allNewsTypes;
+      arr.unshift({text: '全部', key: 'all' });
+      return arr;
+    },
+  },
+  components: {SearchBox, InputRow, PopCard, Pop, ImageTable, ImageViewer, RoundedTitle},
+  async asyncData(ctx){
+    // 加载展示块数据
+    let [err,res] = await handle(axios.get(apiMap.showBlocksList.path));
+    if(err){
+      return {};
+    }
+    let result = res.data;
+    if(result.code === rCode.OK){
+      return {showBlockList: result.data}
+    }else{
+      this.$message.error(result.msg);
+      return {}
+    }
+    return {}
+  },
+  data(){
+    return {
+      limit: 10,
+      loading: false,
+      showBlockList: [],
+      popShow: false,
+      popLoading: false,
+      showBlockPopShow: false,
+      showBlockPopLoading: false,
+      isEditShowBlock: false,
+      showBlockData: {},
+      form: {
+        // 排序
+        sort: {
+          val:0,
+          init: 0,
+          msg: '',
+          state: 0,
+        },
+        // 状态 0:禁用,1:启用
+        state: {
+          val: dbField_esm.db_base.carouselState.disable,
+          init: dbField_esm.db_base.carouselState.disable,
+          msg: '',
+          state: 0,
+          options: [
+            {label: '禁用', value: dbField_esm.db_base.carouselState.disable},
+            {label: '启用', value: dbField_esm.db_base.carouselState.enable},
+          ]
+        },
+        // 展示块类型 '0:product','1:news','2:page','3:href'
+        type: {
+          val: 0,
+          init: 0,
+          msg: '',
+          state: 0,
+          options: [
+            {label: '直接链接', value: 0,checkField: 'href'},
+            {label: '内部产品', value: 1,checkField: 'productId'},
+            {label: '指向新闻', value: 2,checkField: 'newsId'},
+          ],
+        },
+        // 具体值
+        value: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+          // 依赖字段
+          depend: 'type',
+          showText: '',// 展示用字段
+          oldShowText: '',
+        },
+        // 标题
+        title: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+        },
+        // 副标题
+        subTitle: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+        },
+        // file
+        fileData: {
+          val: '',
+          init: '',
+          msg: '',
+          state: 0,
+          showText: '',// 展示用字段
+          oldShowText: '',
+        },
+      },
+      productSelectVisible: false,
+      productSearch: {
+        type: {
+          val: '',
+          oldVal: '',
+          init: '',
+          msg: '',
+          options: [],
+        }
+      },
+      newsSelectVisible: false,
+      newsVisible: false,
+      newsSearch: {
+        type: {
+          val: '',
+          oldVal: '',
+          init: '',
+          msg: '',
+          options: [],
+        }
+      },
+      imageSelectVisible: false,
+
+    }
+  },
+  mounted() {
+    if(this.showBlockList.length === 0){
+      this.getShowBlockList();
+    }
+  },
+  methods: {
+    async getShowBlockList(){
+      this.loading = true;
+      let [err,res] = await handle(this.$axios.get(apiMap.showBlocksList.path));
+      this.loading = false;
+      if(err){
+        if(this.NotificationKey){
+          this.$notification.close(this.NotificationKey);
+        }
+        this.NotificationKey = `open${Date.now()}`;
+        return this.$notification.error({
+          message: '展示块数据加载失败',
+          description:`异常: ${err.message}`,
+          duration: 0,
+          btn: h => {
+            return h(
+              'a-button',
+              {
+                props: {
+                  type: 'primary',
+                  size: 'small',
+                },
+                on: {
+                  click: () => {
+                    this.$notification.close(this.NotificationKey);
+                    this.getShowBlockList();
+                  },
+                },
+              },
+              '重试',
+            );
+          },
+          key:this.NotificationKey,
+          onClose: close,
+        });
+      }
+      let result = res.data;
+      if(result.code === rCode.OK){
+        this.showBlockList = result.data;
+        return {showBlockList: result.data}
+      }else{
+        this.$message.error(result.msg);
+        return {}
+      }
+    },
+    // 搜索产品,只需要产品名等信息
+    async getProductSearch(searchParam){
+      console.log(searchParam)
+      if(this.productSearch.type.val !== this.productSearch.type.oldVal){
+        searchParam.p = 1;
+      }
+      searchParam.type = this.productSearch.type.val;
+      searchParam.l = this.limit;
+      searchParam.p = searchParam.page;
+      let [err,res] = await handle(
+        this.$axios.get(
+          apiMap.searchProductMini.path,
+          {params:searchParam})
+      );
+      if(err){
+        console.log(err);
+        return [{message:'请求数据失败'},null];
+      }
+
+      let result = res.data;
+      if(result.code === rCode.OK){
+        this.productSearch.type.oldVal = this.productSearch.type.val;
+        // data 转换
+        result.data = result.data.map(item=>{
+          item.showText = item.name;
+          return item;
+        });
+        return [null,result];
+      }else{
+        // 可捕获的服务器错误
+        return [{message:result.msg},null];
+      }
+    },
+    // 加载展示块默认数据
+    async getNewsSearch(searchParam){
+      console.log(searchParam)
+      if(this.newsSearch.type.val !== this.newsSearch.type.oldVal){
+        searchParam.p = 1;
+      }
+      searchParam.type = this.newsSearch.type.val;
+      searchParam.l = this.limit;
+      searchParam.p = searchParam.page;
+      let [err,res] = await handle(
+        this.$axios.get(
+          apiMap.searchNewsMini.path,
+          {params:searchParam})
+      );
+      if(err){
+        console.log(err);
+        return [{message:'请求数据失败'},null];
+      }
+
+      let result = res.data;
+      if(result.code === rCode.OK){
+        this.newsSearch.type.oldVal = this.newsSearch.type.val;
+        // data 转换
+        result.data = result.data.map(item=>{
+          item.showText = item.name;
+          return item;
+        });
+        return [null,result];
+      }else{
+        // 可捕获的服务器错误
+        return [{message:result.msg},null];
+      }
+    },
+
+    checkFormItem(field,enumOptions,reCheckField){
+      let formItem = this.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 this.form) {
+          formItem = this.form[fieldKey];
+          let depend = this.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
+      }
+    },
+    initShowBlockForm(){
+      this.showBlockData = {};
+      let keys = Object.keys(this.form);
+      for(let i = 0; i < keys.length; i++){
+        let key = keys[i];
+        this.form[key].val = this.form[key].init;
+        this.form[key].msg = '';
+        this.form[key].state = 0;
+        this.form.value.showText = '';
+      }
+    },
+    openAddShowBlockModal(){
+      // 初始化表单
+      this.initShowBlockForm();
+      this.showBlockPopShow = true;
+      this.isEditShowBlock = false;
+      this.$nextTick(()=>{
+        // 打开弹窗. 选择图片,填写链接地址,排序
+        this.productSearch.type.options = this.productTypes;
+        this.newsSearch.type.options = this.newsTypes;
+        // 默认值设置
+        this.productSearch.type.val = this.productTypes[0].key;
+        this.productSearch.type.oldVal = this.productTypes[0].key;
+        this.productSearch.type.init = this.productTypes[0].key;
+
+        this.newsSearch.type.val = this.newsTypes[0].key;
+        this.newsSearch.type.oldVal = this.newsTypes[0].key;
+        this.newsSearch.type.init = this.newsTypes[0].key;
+      });
+    },
+    onPopOkClickHandle(){
+      if(this.isEditShowBlock){
+        console.log('保存修改后的展示块数据');
+        this.updateShowBlockExecute();
+      }else{
+        console.log('新增展示块');
+        this.addShowBlockExecute();
+      }
+    },
+    async addShowBlockExecute(){
+      // 生成新数据表单
+      let showBlockData = {};
+      let isPass = this.checkFormItem();
+      if(!isPass){
+        return console.log('数据验证不通过');
+      }
+      console.log('开始生成新数据表单');
+      showBlockData.sort = this.form.sort.val;
+      showBlockData.type = this.form.type.val;
+      showBlockData.value = this.form.value.val;
+      showBlockData.title = this.form.title.val;
+      showBlockData.subTitle = this.form.subTitle.val;
+      showBlockData.fileId = this.form.fileData.val;
+      showBlockData.state = this.form.state.val;
+      // 生成新数据表单完毕
+      console.log('生成新数据表单完毕');
+      this.showBlockPopLoading = true;
+      let [err,res] = await handle(this.$axios.put(apiMap.baseAddShowBlock.path,showBlockData));
+      this.showBlockPopLoading = false;
+      if(err){
+        console.log(err);
+        return this.$message.error('新增展示块失败');
+      }
+      this.$message.success('新增展示块成功');
+      this.showBlockPopShow = false;
+      await this.getShowBlockList();
+
+    },
+    async updateShowBlockExecute(){
+      let showBlockData = {};
+      let isPass = this.checkFormItem();
+      if(!isPass){
+        return console.log('数据验证不通过');
+      }
+      // 获取更新项
+      console.log(`获取更新项`);
+      let updateItems = {};
+      console.log(this.showBlockData);
+      let blockId = this.showBlockData.id;
+      if(this.form.sort.val != toNumber(this.showBlockData.sort)){
+        updateItems.sort = this.form.sort.val;
+      }
+      if(this.form.type.val != toNumber(this.showBlockData.type)){
+        updateItems.type = this.form.type.val;
       }
+      if(this.form.value.val != this.showBlockData.value){
+        updateItems.value = this.form.value.val;
+      }
+      if(this.form.title.val != this.showBlockData.title){
+        updateItems.title = this.form.title.val;
+      }
+      if(this.form.subTitle.val != this.showBlockData.subTitle){
+        updateItems.subTitle = this.form.subTitle.val;
+      }
+      if(toNumber(this.form.fileData.val) != this.showBlockData.fileId){
+        updateItems.fileId = this.form.fileData.val;
+      }
+      if(this.form.state.val != this.showBlockData.state){
+        updateItems.state = this.form.state.val;
+      }
+      if(isEmpty(updateItems)){
+        return this.$message.warn('未修改任何数据');
+      }
+      console.log(`更新展示块数据,更新数量: ${Object.keys(updateItems).length} 更新字段: [${Object.keys(updateItems).join(',')}]`);
+      this.showBlockPopLoading = true;
+      let [err,res] = await handle(this.$axios.post(apiMap.baseUpdateShowBlock.path,{
+        blockId,
+        updateItems
+      }));
+      this.showBlockPopLoading = false;
+      if(err){
+        console.log(err);
+        return this.$message.error('更新展示块失败');
+      }
+      let  result = res.data;
+      if (result.code === rCode.OK){
+        this.$message.success('更新展示块成功');
+        this.showBlockPopShow = false;
+        await this.getShowBlockList();
+      }else{
+        this.$message.error(`更新展示块失败,${result.msg}`);
+      }
+
+    },
+
+    getShowBlockTypeText(type){
+      type = toNumber(type);
+      let typeText = '';
+      if(type === dbField_esm.db_base.carouselType.production){
+        typeText = '产品';
+      }else if(type === dbField_esm.db_base.carouselType.news){
+        typeText = '文章';
+      }else if(type === dbField_esm.db_base.carouselType.href){
+        typeText = '直接链接';
+      }else{
+        typeText = '暂未支持类型';
+      }
+      return typeText
+    },
+    getShowBlockStateText(state){
+      // state = toNumber(state);
+      let typeText = '';
+      if(state === dbField_esm.db_base.carouselState.enable){
+        typeText = '启用';
+      }else if(state === dbField_esm.db_base.carouselState.disable){
+        typeText = '禁用';
+      }else{
+        typeText = '未知状态';
+      }
+      return typeText
+    },
+    cancelPop(){
+      this.imageSelectVisible = false;
+    },
+    okHandle(fileItem){
+      console.log('文件列表');
+      console.log(fileItem);
+      this.cancelPop();
+      this.$nextTick(()=>{
+        this.form.fileData.val = fileItem.fileId;
+        this.form.fileData.state = 1;
+        this.form.fileData.msg = '';
+        this.form.fileData.showText = fileItem.filePath;
+      })
+    },
+    onProductSearchHandle(e){
+      console.log(`onProductSearchHandle ${e}`);
+      console.log(e);
+      console.log(this.productSearch.type.val);
+    },
+    onSelectedItemHandle(item){
+      console.log(`selected item ${item}`);
+      console.log(item);
+      this.form.value.val = item.id;
+      this.form.value.showText = item.showText;
+      this.form.value.msg = '';
+    },
+    onTypeChangeHandle(e){
+      console.log(`type change ${e}`);
+      // 清除其他值
+      this.form.value.val = '';
+      this.form.value.msg = '';
+      this.form.value.showText = '';
+    },
+
+    onNewsSearchHandle(e){
+      console.log(`onProductSearchHandle ${e}`);
+      console.log(e);
+      console.log(this.productSearch.type.val);
+    },
+    onClickEditHandle(item){
+      console.log(`点击编辑展示块`);
+      console.log(item);
+      if(!item || !item.id){
+        return this.$message.warn('展示块数据获取异常,已经取消编辑');
+      }
+      this.initShowBlockForm();
+
+      this.isEditShowBlock = true;
+      this.showBlockData = item;
+      this.showBlockPopShow = true;
+      this.showBlockPopLoading = true;
+      this.form.sort.val = toNumber(item.sort);
+      this.form.sort.init = toNumber(item.sort);
+      this.form.type.val = toNumber(item.type);
+      this.form.type.init = toNumber(item.type);
+      this.form.value.val = item.value;
+      this.form.value.init = item.value;
+      this.form.value.showText = item.valueShowText;
+      this.form.value.oldShowText = item.oldShowText;
+      this.form.title.val = item.title;
+      this.form.title.init = item.title;
+      this.form.subTitle.val = item.subTitle;
+      this.form.subTitle.init = item.subTitle;
+      this.form.fileData.val = item.fileId;
+      this.form.fileData.init = item.fileId;
+      this.form.fileData.showText = item.filePath;
+      this.form.fileData.oldShowText = item.filePath;
+      this.form.state.val = item.state;
+      this.showBlockPopLoading = false;
+
     }
-  }
+
+  },
+
+})
 </script>
 
 <template>
-  <div class="w-full p-2">
-    该板块正在调整中
-  </div>
+<div class="w-full p-2">
+  <rounded-title>展示块管理</rounded-title>
+  <div class="mt-2 rounded bg-white p-2">
+    <div class="mt-2 rounded bg-white p-2">
+      <div class="py-1 border-b border-cyan-300 flex justify-between">
+        点击下方块进行管理展示块数据
+        <div class="px-2 flex"><!--      刷新按钮-->
+          <a-button type="primary" class="ant-icon-btn " icon="reload" @click="getShowBlockList" :loading="loading"></a-button>
+        </div>
+      </div>
+
+      <div class="w-full h-auto transition">
+        <div v-show="loading" class="w-full h-64 flex justify-center items-center ">
+          <a-spin size="large" />
+        </div>
+
+        <!-- 展示块预览区域,保持与 showingStand.vue 一致的布局 -->
+        <div class="container mx-auto showing">
+          <div
+            v-for="(item,i) in showBlockList"
+            :class="`chunk imgBox chunk${(i+1)}`"
+            :key="item.id"
+          >
+            <!--         实时管理(物联网) -->
+            <img :src="item.filePath" alt="item.title">
+            <p class="chunkText">
+              <span class="title">{{item.title}}</span>
+              <span class="subTitle">{{item.subTitle}}</span>
+            </p>
+            <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity bg-black bg-opacity-30">
+              <a-button @click="onClickEditHandle(item)" type="primary">编辑</a-button>
+            </div>
+
+          </div>
+        </div>
+        </div>
+        </div>
+      </div>
+
+  <pop :show="showBlockPopShow" :loading="showBlockPopLoading">
+    <pop-card>
+<!--      <template slot="header" class="w-full">-->
+<!--        编辑展示块-->
+<!--      </template>-->
+      <template slot="close-group">
+        <a-button icon="close"  @click="showBlockPopShow = false"></a-button>
+      </template>
+      <div class="w-full">
+         <input-row :msg="form.sort.msg"
+                    label="排序">
+           <a-input-number v-model="form.sort.val"
+                           @focus="form.sort.msg=''"
+                           :min="0"
+                           @blur="checkFormItem('sort')"
+           />
+         </input-row>
+
+        <input-row :msg="form.state.msg"
+                    label="状态">
+            <a-radio-group v-model="form.state.val">
+              <a-radio-button v-for="opt in form.state.options"
+                              :key="'cState'+opt.value"
+                              :value="opt.value">
+                {{ opt.label }}
+              </a-radio-button>
+            </a-radio-group>
+        </input-row>
+
+        <input-row :msg="form.title.msg"
+                    label="标题">
+          <a-input v-model="form.title.val"
+                   placeholder="请输入标题"
+                   @focus="form.title.msg=''"
+                   @blur="checkFormItem('title')"
+          />
+        </input-row>
+
+        <input-row :msg="form.subTitle.msg"
+                    label="副标题">
+          <a-textarea v-model="form.subTitle.val"
+                   placeholder="请输入副标题"
+                   :rows="2"
+                   @focus="form.subTitle.msg=''"
+                   @blur="checkFormItem('subTitle')"
+          />
+        </input-row>
+
+<!--          展示块类型选择 -->
+          <input-row :msg="form.type.msg"
+                      label="展示块类型">
+            <a-radio-group v-model="form.type.val" @change="onTypeChangeHandle">
+              <a-radio-button v-for="opt in form.type.options"
+                              :key="'cType'+opt.value"
+                              :value="opt.value">
+                {{ opt.label }}
+              </a-radio-button>
+            </a-radio-group>
+          </input-row>
+
+<!--        展示块具体值 -->
+<!--        链接-->
+        <input-row
+          v-show="form.type.val === dbField_esm.db_base.carouselType.href"
+          :msg="form.value.msg"
+                    label="输入链接">
+          <a-input v-model="form.value.val"
+                   placeholder="输入要指向的链接地址"
+                   @focus="form.value.msg=''"
+                   @blur="checkFormItem('value', null,'href')"
+          />
+        </input-row>
+<!--        产品选择-->
+        <input-row
+          v-show="form.type.val === dbField_esm.db_base.carouselType.production"
+          :msg="form.value.msg"
+          label="选择产品">
+          {{ form.value.showText }}
+          <a-popover  v-model="productSelectVisible" title="选择产品" trigger="click">
+            <div slot="content" class="searchBox" >
+              <search-box
+                class="h-72"
+                :loadDataApi="getProductSearch"
+                :limit="limit"
+                search-placeholder="请输入产品名称关键字"
+                loadTip="正在搜索产品中..."
+                @onSelectedItem="onSelectedItemHandle"
+              >
+                <div class="w-full" slot="otherSearchItem">
+                  <a-radio-group v-model="productSearch.type.val"
+                                 @change="onProductSearchHandle">
+                    <a-radio-button v-for="opt in productSearch.type.options"
+                                    :key="'cType'+opt.key"
+                                    :value="opt.key">
+                      {{ opt.text }}
+                    </a-radio-button>
+                  </a-radio-group>
+                </div>
+              </search-box>
+            </div>
+            <a-button type="primary" >
+              选择产品
+            </a-button>
+          </a-popover>
+        </input-row>
+<!--        新闻选择-->
+        <input-row
+          v-show="form.type.val === dbField_esm.db_base.carouselType.news"
+          :msg="form.value.msg"
+          label="文章选择">
+          {{ form.value.showText }}
+          <a-popover  v-model="newsSelectVisible" title="选择你需要的文章" trigger="click">
+            <div slot="content" class="searchBox" >
+              <search-box
+                class="h-72"
+                :loadDataApi="getNewsSearch"
+                :limit="limit"
+                search-placeholder="请输入文章名称关键字"
+                loadTip="正在搜索文章中..."
+                ref="productSearch"
+                @onSelectedItem="onSelectedItemHandle"
+              >
+                <div class="w-full" slot="otherSearchItem">
+                  <a-radio-group v-model="newsSearch.type.val"
+                                 @change="onNewsSearchHandle">
+                    <a-radio-button v-for="opt in newsSearch.type.options"
+                                    :key="'cType'+opt.key"
+                                    :value="opt.key">
+                      {{ opt.text }}
+                    </a-radio-button>
+                  </a-radio-group>
+                </div>
+              </search-box>
+            </div>
+            <a-button type="primary">
+              选择文章
+            </a-button>
+          </a-popover>
+        </input-row>
+
+<!--        选择图片 -->
+        <input-row label="展示图片"
+                   :msg="form.fileData.msg">
+
+          <div class="w-full h-60 rounded relative" @click="imageSelectVisible = true">
+            <image-viewer class="" :src="form.fileData.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">
+              点击选择图片
+            </div>
+          </div>
+
+        </input-row>
+      </div>
+      <template class="w-full" slot="footer">
+        <a-button @click="onPopOkClickHandle">{{isEditShowBlock? '保存': '新增'}}</a-button>
+      </template>
+    </pop-card>
+  </pop>
+
+  <pop :show="imageSelectVisible">
+
+    <image-table class="w-5/12 h-1/2"
+                 @cancel="imageSelectVisible = false"
+                 @ok="okHandle"></image-table>
+  </pop>
+</div>
 </template>
 
 <style scoped>
+.searchBox{
+  width: 420px;
+  height: 520px;
+}
+
+.showing{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.showing .chunk {
+  box-shadow: 1px 1px 3px black;
+  /*box-sizing: border-box;*/
+  /*padding:5px;*/
+  position: relative;
+  overflow: hidden;
+  cursor: pointer;
+  width: 96%;
+  aspect-ratio:500/350;
+  margin-bottom: 10px;
+}
+
+
+
+.showing .chunk:hover > img{
+  left: -20px;
+  top: -20px;
+  max-width: calc(100% + 40px);
+  width: calc(100% + 40px);
+  height: calc(100% + 40px);
+}
+.showing .chunk .chunkText{
+  display: block;
+  position: absolute;
+  width: 100%;
+  height: 150px;
+  bottom: -50px;
+  /*background: #000;*/
+  color: white;
+  transition: all .5s;
+  padding: 0 15px;
+  background-image: linear-gradient(rgba(0,0,0,0) 0,rgba(0,0,0,0.5) 75% );
+}
+.showing .chunk:hover .chunkText{
+  bottom: 0;
+  background-image: linear-gradient(rgba(0,0,0,0) 0,rgba(0,0,0,0.66) 66% 15% 80% );
+}
+.showing .chunk .chunkText .title{
+  height: 40px;
+  font-size: 1.5em;
+  display: flex;
+  /*justify-content: center;*/
+  align-items: center;
+}
+.showing .chunk .chunkText .subTitle{
+  padding: 5px 0;
+  min-height: 35px;
+  max-height: 80px;
+  font-size: 1.2em;
+}
+.showing .chunk .chunkText .chunkMore{
+  margin-top: 20px;
+  height: 30px;
+  font-size: 0.9em;
+  color: #ff6e3f;
+}
+.showing .chunk .chunkText .chunkMore:hover{
+  color: orangered;
+}
+
+.showing .chunk .chunkText > *{
+  display: block;
+}
+.showing .chunk1 {
+  grid-area: a;
+}
+.showing .chunk2{
+  grid-area: b;
+}
+.showing .chunk3{
+  grid-area: c;
+  aspect-ratio:500/810;
+}
+.showing .chunk4{
+  grid-area: d;
+  aspect-ratio:870/400;
+}
+.showing .chunk5{
+  grid-area: e;
+}
+.showing .chunk6{
+  grid-area: f;
+}
+.showing .chunk7{
+  grid-area: g;
+}
+@media screen and (min-width: 1024px) {
+  /* 其他针对移动设备的样式 */
+  .showing{
+    display: grid;
+    grid-template-columns: repeat(3, 500px);
+    grid-template-rows: repeat(3, 410px);
+    grid-template-areas:
+    'a b c'
+    'd d c'
+    'e f g';
+    grid-gap: 20px;
+  }
+  .showing .chunk {
+    box-shadow: 1px 1px 3px black;
+    box-sizing: border-box;
+    /*padding:5px;*/
+    position: relative;
+    overflow: hidden;
+    cursor: pointer;
+    aspect-ratio:500/410;
+    margin-bottom: 0;
+    width: 100%;
+  }
 
+  .showing .chunk:hover{
+    box-shadow: 1px 1px 3px deepskyblue;
+  }
+  .showing .chunk3{
+    grid-area: c;
+    aspect-ratio:500/840;
+  }
+  .showing .chunk4{
+    grid-area: d;
+    aspect-ratio:870/350;
+  }
+}
+.showing .chunk .chunkText .title{
+  height: 40px;
+  font-size: 1.5em;
+  display: flex;
+  align-items: center;
+}
+.showing .chunk .chunkText .subTitle{
+  padding: 5px 0;
+  min-height: 35px;
+  max-height: 80px;
+  font-size: 1.2em;
+}
+.showing .chunk1 {
+  grid-area: a;
+}
+.showing .chunk2{
+  grid-area: b;
+}
+.showing .chunk3{
+  grid-area: c;
+  aspect-ratio:500/810;
+}
+.showing .chunk4{
+  grid-area: d;
+  aspect-ratio:870/400;
+}
+.showing .chunk5{
+  grid-area: e;
+}
+.showing .chunk6{
+  grid-area: f;
+}
+.showing .chunk7{
+  grid-area: g;
+}
+@media screen and (min-width: 1024px) {
+  .showing{
+    display: grid;
+    grid-template-columns: repeat(3, 500px);
+    grid-template-rows: repeat(3, 410px);
+    grid-template-areas:
+    'a b c'
+    'd d c'
+    'e f g';
+    grid-gap: 20px;
+  }
+  .showing .chunk {
+    box-shadow: 1px 1px 3px black;
+    position: relative;
+    overflow: hidden;
+    cursor: pointer;
+    aspect-ratio:500/410;
+    margin-bottom: 0;
+    width: 100%;
+  }
+
+  .showing .chunk:hover{
+    box-shadow: 1px 1px 3px deepskyblue;
+  }
+  .showing .chunk3{
+    grid-area: c;
+    aspect-ratio:500/840;
+  }
+  .showing .chunk4{
+    grid-area: d;
+    aspect-ratio:870/350;
+  }
+}
 </style>

+ 226 - 22
server/control/c_base.js

@@ -3,11 +3,10 @@ const d_base = require("../database/d_base");
 const d_product = require("../database/d_product");
 const d_news = require("../database/d_news");
 const codeMap = require("../map/rcodeMap");
-const dbField = require("../map/dbField");
+const {db_base} = require("../map/dbField");
 const {searchHandle} = require("../tools/searchSql");
 const {isEmpty, toNumber} = require("../tools/typeTool_cjs");
 const config_path = require("../../configs/path.json");
-const {isArray} = require("ant-design-vue/lib/_util/vue-types/utils");
 const {mvFile, rmFile} = require("../tools/saveFiles_cjs");
 const {getUnixTimeStamp} = require("../tools/time_cjs");
 const {filePathToUrl} = require("../tools/filePathTool");
@@ -17,7 +16,7 @@ const log = require("../logger").logger("c_base","info");
 async function getEnableCarousel(){
   let err,res;
   [err,res] = await handle(d_base.getCarousel({
-    state: dbField.db_base.carouselState.enable
+    state: db_base.carouselState.enable
   }));
   // console.log(res);
   if(err){
@@ -52,10 +51,10 @@ async function addCarousel(sort,fileId,type,value){
   let valueShowText = '';
   let updateTime = getUnixTimeStamp();
   // 根据type判断value
-  if(type !== dbField.db_base.carouselType.href){
-    if(type === dbField.db_base.carouselType.production){
+  if(type !== db_base.carouselType.href){
+    if(type === db_base.carouselType.production){
       [err,res] = await handle(d_product.getProductById(value));
-    }else if(type === dbField.db_base.carouselType.news){
+    }else if(type === db_base.carouselType.news){
       // 获取新闻信息
       [err,res] = await handle(d_news.getNewsById(value));
     }
@@ -96,10 +95,10 @@ async function updateCarousel(id,updateItems){
   let rawData = {};
   let unixTime = getUnixTimeStamp();
   let updateData = {};
-  let typeKeys = Object.keys(dbField.db_base.carouselType);
+  let typeKeys = Object.keys(db_base.carouselType);
   let typeString = '';
   let typeShowText = '';
-  let carouselType = dbField.db_base.carouselType.href;
+  let carouselType = db_base.carouselType.href;
   // 判断是否需要更新type
   if (updateItems.value){
     // 获取基础type值
@@ -121,12 +120,12 @@ async function updateCarousel(id,updateItems){
     }
     console.log(toNumber(typeString))
     // 需要更新具体值
-    if(toNumber(typeString) !== dbField.db_base.carouselType.href){
-      if(toNumber(typeString) === dbField.db_base.carouselType.production){
+    if(toNumber(typeString) !== db_base.carouselType.href){
+      if(toNumber(typeString) === db_base.carouselType.production){
         typeShowText = '产品';
         // 获取产品信息
         [err,res] = await handle(d_product.getProductById(updateItems.value));
-      }else if(toNumber(typeString) === dbField.db_base.carouselType.news){
+      }else if(toNumber(typeString) === db_base.carouselType.news){
         typeShowText = '文章';
         // 获取新闻信息
         [err,res] = await handle(d_news.getNewsById(updateItems.value));
@@ -174,6 +173,190 @@ async function updateCarousel(id,updateItems){
   return [null,res];
 }
 
+// 展示块控制函数
+
+/**
+ * 获取启用的展示块列表
+ * @returns {Promise<*>}
+ */
+async function getEnableShowBlocks() {
+  let err, res;
+  [err, res] = await handle(d_base.getShowBlocks());
+  if (err) {
+    return [err, null];
+  }
+  // 路径转换
+  res = res.map(item => {
+    item.filePath = filePathToUrl(item.fileType, item.filePath);
+    return item;
+  });
+  return [null, res];
+}
+
+/**
+ * 获取所有展示块
+ * @returns {Promise<*>}
+ */
+async function getAllShowBlocks() {
+  let err, res;
+  [err, res] = await handle(d_base.getShowBlocks());
+  if (err) {
+    return [err, null];
+  }
+  // 路径转换
+  res = res.map(item => {
+    item.filePath = filePathToUrl(item.fileType, item.filePath);
+    return item;
+  });
+  return [null, res];
+}
+
+/**
+ * 添加展示块
+ * @param sort 排序
+ * @param fileId 文件ID
+ * @param type 类型
+ * @param value 值
+ * @param title 标题
+ * @param subTitle 副标题
+ * @returns {Promise<*>}
+ */
+async function addShowBlock(sort, fileId, type, value, title, subTitle) {
+  let err, res;
+  let valueData = {};
+  let valueShowText = '';
+  let updateTime = getUnixTimeStamp();
+
+  // 根据type判断value
+  if (type !== db_base.carouselType.href) {
+    if (type === db_base.carouselType.production) {
+      [err, res] = await handle(d_product.getProductById(value));
+    } else if (type === db_base.carouselType.news) {
+      // 获取新闻信息
+      [err, res] = await handle(d_news.getNewsById(value));
+    }
+    if (err) {
+      console.log(err);
+      log.info(`[新增展示块] 检索产品信息失败`);
+      return [{ eCode: codeMap.ServerError, eMsg: `检索数据异常` }, null];
+    }
+    if (isEmpty(res)) {
+      log.info(`[新增展示块] 无法找到对应的产品或者文章信息`);
+      return [{ eCode: codeMap.NotFound, eMsg: `无法找到对应的产品或者文章信息` }, null];
+    }
+    valueData = res[0];
+  }
+  valueShowText = valueData.title;
+
+  [err, res] = await handle(d_base.addShowBlock(
+    sort, fileId, type, value, valueShowText, title, subTitle, updateTime
+  ));
+  if (err) {
+    log.info(`[新增展示块] 导入数据异常`)
+    return [err, null];
+  }
+  return [null, res];
+}
+
+/**
+ * 更新展示块
+ * @param id 展示块ID
+ * @param updateItems 更新项
+ * @returns {Promise<*>}
+ */
+async function updateShowBlock(id, updateItems) {
+  let err, res;
+  let rawData = {};
+  let unixTime = getUnixTimeStamp();
+  let updateData = {};
+  let typeKeys = Object.keys(db_base.carouselType);
+  let typeString = '';
+  let typeShowText = '';
+  let carouselType = db_base.carouselType.href;
+
+  // 判断是否需要更新type
+  if (updateItems.value) {
+    // 获取基础type值
+    if (!isEmpty(updateItems.type)) {
+      typeString = updateItems.type;
+      updateData.type = updateItems.type;
+    } else {
+      [err, res] = await handle(d_base.getShowBlockById(id));
+      if (err) {
+        log.error(`[修改展示块] 获取展示块基础数据时异常 ${err.message}`);
+        return [{ eCode: codeMap.ServerError, eMsg: `获取展示块基础数据异常` }, null];
+      }
+      if (isEmpty(res)) {
+        log.info(`[修改展示块] 无法通过id找到展示块数据`);
+        return [{ eCode: codeMap.NotFound, eMsg: `请检测id是否正确` }, null];
+      }
+      rawData = res[0];
+      typeString = rawData.type;
+    }
+    console.log(toNumber(typeString))
+    // 需要更新具体值
+    if (toNumber(typeString) !== db_base.carouselType.href) {
+      if (toNumber(typeString) === db_base.carouselType.production) {
+        typeShowText = '产品';
+        // 获取产品信息
+        [err, res] = await handle(d_product.getProductById(updateItems.value));
+      } else if (toNumber(typeString) === db_base.carouselType.news) {
+        typeShowText = '文章';
+        // 获取新闻信息
+        [err, res] = await handle(d_news.getNewsById(updateItems.value));
+      } else {
+        return [{ eCode: codeMap.NotParam, eMsg: `展示块类型异常! carouseType:${typeString} typeof:${typeof typeString}` }, null];
+      }
+      if (err) {
+        console.log(err);
+        log.error(`[修改展示块] 检索${typeShowText}信息失败 ${err.message}}`);
+        return [{ eCode: codeMap.ServerError, eMsg: `检索${typeShowText}数据异常` }, null];
+      }
+      if (isEmpty(res)) {
+        log.info(`[修改展示块] 无法找到对应的${typeShowText}数据`);
+        return [{ eCode: codeMap.NotFound, eMsg: `无法找到对应的产品或者文章信息` }, null];
+      }
+      updateData.valueShowText = res[0].title;
+    } else {
+      updateData.valueShowText = updateItems.value;
+    }
+
+    updateData.value = updateItems.value;
+  }
+
+  if (!isEmpty(updateItems.sort)) {
+    updateData.sort = toNumber(updateItems.sort);
+  }
+
+  if (!isEmpty(updateItems.state)) {
+    updateData.state = updateItems.state;
+  }
+
+  if (updateItems.fileId) {
+    updateData.fileId = updateItems.fileId;
+    // todo 检测文件id是否存在
+  }
+
+  if (updateItems.title) {
+    updateData.title = updateItems.title;
+  }
+
+  if (updateItems.subTitle) {
+    updateData.subTitle = updateItems.subTitle;
+  }
+
+  console.log(updateData);
+  [err, res] = await handle(d_base.updateShowBlock(id, updateData, unixTime));
+  if (err) {
+    console.log(err);
+    log.error(`[修改展示块] 数据更新失败,${err.message}`);
+    return [{ eCode: codeMap.SaveError, eMsg: `数据更新失败,请稍后重试` }, null];
+  }
+  return [null, res];
+}
+
+
+
 /**
  * 文件上传
  * @param type
@@ -186,9 +369,9 @@ async function uploadFile(type, files){
   let uploadPath = config_path.files;
   let fileNameArr = [];// 文件存储路径数组
   // 文件转存,使用newFileName作为路径
-  if(type === dbField.db_base.fileType.image){
+  if(type === db_base.fileType.image){
     uploadPath = config_path.images;
-  }else if(type === dbField.db_base.fileType.video){
+  }else if(type === db_base.fileType.video){
     uploadPath = config_path.videos;
   }
   let keys = Object.keys(files);
@@ -251,7 +434,7 @@ async function searchFiles(type = 0, key, l, p){
   }
   // console.log(type);
   // type为 数据库值加一,因为数据库中存储值从0开始,而前端显示从1开始
-  if((type - 1) !== dbField.db_base.fileType.all){
+  if((type - 1) !== db_base.fileType.all){
     _params.type = type;
   }
   // console.log(_params);
@@ -297,15 +480,16 @@ async function deleteFile(fileId){
 
 async function getBaseData(){
   // 同时获取所有需要获取的基础数据
-  let err,pType,nType,carousel,baseInfo,products,articles;
+  let err,pType,nType,carouselAndShowBlocks,baseInfo,products,articles;
+  let carousel = [], showBlocks = [];
   // todo 同时获取所有需要获取的基础数据
-  [err, products, articles, baseInfo, pType, nType, carousel] = await handleAll(
+  [err, products, articles, baseInfo, pType, nType, carouselAndShowBlocks] = await handleAll(
     d_product.searchProducts('array', {}, '', 1, 4),
     d_news.searchAllNewsMini('array', {}, '', 1, 5),
     d_base.getBaseInfo(),
     d_product.loadTypes(),
     d_news.loadTypes(),
-    d_base.getCarousel({state: dbField.db_base.carouselState.enable}),
+    d_base.getEnableCarouselAndShowBlocks(),
   );
   if (err) {
     log.error(`[基础数据] 获取基础数据类型分类失败 ${err.message}`);
@@ -315,18 +499,33 @@ async function getBaseData(){
   baseInfo.products = products;
   baseInfo.news = articles;
 
+  if (carouselAndShowBlocks && carouselAndShowBlocks.length) {
+    // log.info(`[基础数据] 获取轮播数据成功 ${carouselAndShowBlocks.length}`);
+    console.log(carouselAndShowBlocks)
+    carouselAndShowBlocks.forEach(item=>{
+      item.filePath = filePathToUrl(item.fileType, item.filePath);
+      // log.info(`carousel: ${db_base.showType.carousel} showBlock: ${db_base.showType.showBlock}`)
+      // log.info(`showType: ${item.showType}  ${item.showType == db_base.carouselType.carousel}`)
+      if(item.showType == db_base.showType.carousel){
+        log.info('添加轮播图')
+        carousel.push(item);
+      }
+      if(item.showType == db_base.showType.showBlock){
+        showBlocks.push(item);
+      }
+    })
+  }
+
+
   // 合并数据
   let baseData = {
     baseInfo,
     pType,
     nType,
     carousel,
+    showBlocks,
   };
-  // 轮播数据转换
-  carousel = carousel.map(item=>{
-    item.filePath = filePathToUrl(item.fileType,item.filePath);
-    return item;
-  });
+
   // todo 缓存接口数据
   log.info(`[基础数据] 类型分类获取成功
                 展示程序数据数量:${products.length}
@@ -334,6 +533,7 @@ async function getBaseData(){
                 程序类型数量:${pType.length}
                 文章类型数量:${nType.length}
                 轮播数据数量:${carousel.length}
+                展示快数量:${showBlocks.length}
                 `);
   return [null,baseData];
 }
@@ -391,6 +591,10 @@ module.exports = {
   addCarousel,
   deleteCarousel,
   updateCarousel,
+  getEnableShowBlocks,
+  getAllShowBlocks,
+  addShowBlock,
+  updateShowBlock,
   uploadFile,
   searchFiles,
   deleteFile,

+ 140 - 6
server/database/d_base.js

@@ -1,15 +1,21 @@
 const mysql = require('./mysql');
 const {searchSql,limitSql} = require("../tools/searchSql");
 const {isEmpty} = require("../tools/typeTool_cjs");
+const {db_base} = require("../map/dbField");
 const log = require("../logger").logger("d_base","info");
 
 /**
  * table carousel
  * id 主键
  * sort 排序
- * image 图片地址
+ * fileId 图片对应的id
  * type 1: 文章 2: 产品 3: 直接链接
  * link 文章id 产品id 链接
+ * value 根据不同的类型的直接值, 会再前端进行转换
+ * valueShowText 值用于提示的文字
+ * title 标题, 用于更自定义显示
+ * subTitle 副标题
+ * showType 显示位置, 用于区分轮播或者展示快的区域类型, 轮播可以多个, 展示快只允许7个
  */
 
 /**
@@ -23,14 +29,30 @@ function getCarousel(searchParam = {}){
                 hfy_files as f
             WHERE c.fileId = f.fileId`;
   let values = [];
+  sql += ` and c.showType = ${db_base.showType.carousel}`;
   if(!isEmpty(searchParam.state)){
     sql += ` and c.state = ?`;
     values.push(searchParam.state);
   }
+
   sql += ` order by c.sort asc`
   return mysql.pq(sql,values);
 }
 
+// 获取启用的轮播与展示块
+async function getEnableCarouselAndShowBlocks(){
+  let sql = `SELECT c.*,f.filePath,f.fileType,f.fileId
+            FROM
+                hfy_carousel as c,
+                hfy_files as f
+            WHERE c.fileId = f.fileId`;
+  let values = [];
+  sql += ` and c.state = ?`;
+  values.push(db_base.carouselState.enable);
+  sql += ` order by c.sort asc`
+  return mysql.pq(sql,values)
+}
+
 
 function getCarouselById(id){
   let sql = `SELECT c.*,f.filePath,f.fileType,f.fileId
@@ -41,17 +63,19 @@ function getCarouselById(id){
                 c.fileId = f.fileId
                 and c.id = ?
             `;
+  sql += ` and c.showType = ${db_base.showType.carousel}`;
+
   let values = [id];
   return mysql.pq(sql,values);
 }
 function addCarousel(sort,fileId,type,value,valueShowText,updateTime){
-  let sql = `INSERT INTO hfy_carousel (sort, fileId, type, value, valueShowText, updateTime)
+  let sql = `INSERT INTO hfy_carousel (sort, fileId, type, value, valueShowText, updateTime, showType)
                     VALUES (?,?,?,?,?,?)`;
-  return mysql.pq(sql,[sort,fileId,type,value,valueShowText,updateTime]);
+  return mysql.pq(sql,[sort,fileId,type,value,valueShowText,updateTime, db_base.showType.carousel]);
 }
 function deleteCarousel(id){
-  let sql = `DELETE FROM hfy_carousel WHERE id = ?`;
-  return mysql.pq(sql,[id]);
+  let sql = `DELETE FROM hfy_carousel WHERE id = ? and showType = ?`;
+  return mysql.pq(sql,[id, db_base.showType.carousel]);
 }
 
 function updateCarousel(id,updateParam,time){
@@ -81,12 +105,117 @@ function updateCarousel(id,updateParam,time){
   }
   sql += ` updateTime = ?`;
   values.push(time);
-  sql += ` WHERE id = ? limit 1`;
+  sql += ` WHERE id = ? and showType = ${db_base.showType.carousel} limit 1`;
   values.push(id);
   console.log(sql);
   console.log(values);
   return mysql.pq(sql,values);
 }
+
+// 展示块数据库操作函数
+/**
+ * 获取展示块数据
+ * @returns {Promise | Promise<unknown>}
+ */
+function getShowBlocks() {
+  let sql = `SELECT c.*,f.filePath,f.fileType,f.fileId
+            FROM
+                hfy_carousel as c,
+                hfy_files as f
+            WHERE c.fileId = f.fileId`;
+  let values = [];
+  sql += ` and c.showType = ${db_base.showType.showBlock}`;
+  sql += ` order by c.sort asc`
+  return mysql.pq(sql, values);
+}
+
+/**
+ * 根据ID获取展示块
+ * @param id 展示块ID
+ * @returns {Promise | Promise<unknown>}
+ */
+function getShowBlockById(id) {
+  let sql = `SELECT c.*,f.filePath,f.fileType,f.fileId
+            FROM
+                hfy_carousel as c,
+                hfy_files as f
+            WHERE
+                c.fileId = f.fileId
+                and c.id = ?
+            `;
+  sql += ` and c.showType = ${db_base.showType.showBlock}`;
+
+  let values = [id];
+  return mysql.pq(sql, values);
+}
+
+/**
+ * 添加展示块
+ * @param sort 排序
+ * @param fileId 文件ID
+ * @param type 类型
+ * @param value 值
+ * @param valueShowText 显示文本
+ * @param title 标题
+ * @param subTitle 副标题
+ * @param updateTime 更新时间
+ * @returns {Promise | Promise<unknown>}
+ */
+function addShowBlock(sort, fileId, type, value, valueShowText, title, subTitle, updateTime) {
+  let sql = `INSERT INTO hfy_carousel (sort, fileId, type, value, valueShowText, title, subTitle updateTime, showType)
+                    VALUES (?,?,?,?,?,?,?,?,?)`;
+  return mysql.pq(sql, [sort, fileId, type, value, valueShowText, updateTime, title, subTitle, db_base.showType.showBlock]);
+}
+
+/**
+ * 更新展示块
+ * @param id 展示块ID
+ * @param updateParam 更新参数
+ * @param time 更新时间
+ * @returns {Promise | Promise<unknown>}
+ */
+function updateShowBlock(id, updateParam, time) {
+  let sql = `UPDATE hfy_carousel SET `;
+  let values = [];
+  if (!isEmpty(updateParam.sort)) {
+    sql += ` sort = ?,`;
+    values.push(updateParam.sort);
+  }
+  if (updateParam.fileId) {
+    sql += ` fileId = ?,`;
+    values.push(updateParam.fileId);
+  }
+  if (!isEmpty(updateParam.type)) {
+    sql += ` type = ?,`;
+    values.push(updateParam.type);
+  }
+  if (updateParam.value && updateParam.valueShowText) {
+    sql += ` value = ?,`;
+    values.push(updateParam.value);
+    sql += ` valueShowText = ?,`;
+    values.push(updateParam.valueShowText);
+  }
+  if (!isEmpty(updateParam.state)) {
+    sql += ` state = ?,`;
+    values.push(updateParam.state);
+  }
+  if (updateParam.title) {
+    sql += ` title = ?,`;
+    values.push(updateParam.title);
+  }
+  if (updateParam.subTitle) {
+    sql += ` subTitle = ?,`;
+    values.push(updateParam.subTitle);
+  }
+  sql += ` updateTime = ?`;
+  values.push(time);
+  sql += ` WHERE id = ? and showType = ${db_base.showType.showBlock} limit 1`;
+  values.push(id);
+  return mysql.pq(sql, values);
+}
+
+
+
 function uploadFiles(type,fileNameArr,uploadTime){
   let sql = `INSERT INTO hfy_files (fileType, filePath, uploadTime)
                     VALUES ${fileNameArr.map(f=>`(?,?,?)`).join(',')}`;
@@ -211,6 +340,11 @@ module.exports = {
   addCarousel,
   deleteCarousel,
   updateCarousel,
+  getShowBlocks,
+  getShowBlockById,
+  addShowBlock,
+  updateShowBlock,
+  getEnableCarouselAndShowBlocks,
   uploadFiles,
   loadFiles,
   getFileById,

+ 7 - 0
server/map/dbField.js

@@ -33,6 +33,13 @@ const db_base = {
     disable: '0',
     enable: '1',
   },
+  // 展示类型
+  showType: {
+    // 轮播
+    carousel: '0',
+    // 展示快
+    showBlock: '1'
+  },
   // 文章类型分类
   newsType: {
     all: -1,

+ 76 - 0
server/router/r_base.js

@@ -104,6 +104,82 @@ router.delete('/carousel/:carouselId', checkLogin(progressField.session_hfy), as
   }
 });
 
+// 获取启用的展示块列表
+router.get('/showblocks', async (req, res) => {
+  try {
+    let [err, data] = await c.getEnableShowBlocks();
+    if (err) {
+      controlError(res, err, `获取启用的展示块失败 ${err.eMsg || err.message}`);
+    } else {
+      success(res, data);
+    }
+  } catch (e) {
+    ServerError(res, e, `获取启用展示块失败 ${e.message}`);
+  }
+});
+
+// 获取所有展示块
+router.get('/showblocks/list', async (req, res) => {
+  try {
+    let [err, data] = await c.getAllShowBlocks();
+    if (err) {
+      controlError(res, err, `获取所有展示块失败 ${err.eMsg || err.message}`);
+    } else {
+      success(res, data);
+    }
+  } catch (e) {
+    ServerError(res, e, `获取所有展示块失败 ${e.message}`);
+  }
+});
+
+// 添加展示块
+router.put('/showblocks', checkLogin(progressField.session_hfy), async (req, res) => {
+  try {
+    log.info(`[展示块新增] 开始新增展示块`);
+    // 获取展示块数据
+    let { sort, fileId, type, value, title, subTitle } = req.body;
+    let err, data;
+    // 检测参数
+    if (isEmpty(sort) || isEmpty(fileId) || isEmpty(type) || isEmpty(value) || isEmpty(title) || isEmpty(subTitle)) {
+      return paramFail(res, '缺少必要参数');
+    }
+    [err, data] = await c.addShowBlock(sort, fileId, type, value, title, subTitle);
+    if (err) {
+      controlError(res, err, `新增展示块失败 ${err.eMsg || err.message}`);
+    } else {
+      success(res, data);
+    }
+  } catch (e) {
+    ServerError(res, e, `新增展示块接口异常 ${e.message}`);
+  }
+});
+
+// 更新展示块
+router.post('/showblocks', checkLogin(progressField.session_hfy), async (req, res) => {
+  try {
+    log.info(`[展示块修改] 开始修改展示块数据`);
+    // 获取展示块数据
+    let { blockId, updateItems } = req.body;
+    let err, data;
+    console.log(blockId);
+    console.log(updateItems);
+    // 检测参数
+    if (isEmpty(blockId) || isEmpty(updateItems)) {
+      return paramFail(res, '缺少必要参数');
+    }
+    [err, data] = await c.updateShowBlock(blockId, updateItems);
+    if (err) {
+      controlError(res, err, `展示块修改 ${err.eMsg || err.message}`);
+    } else {
+      success(res, data);
+    }
+  } catch (e) {
+    ServerError(res, e, `展示块修改接口异常 ${e.message}`);
+  }
+});
+
+
+
 router.get('/files', checkLogin(progressField.session_hfy), async (req, res) => {
   try{
     let err, result;

+ 8 - 1
store/index.js

@@ -24,6 +24,7 @@ export const indexTypes = {
     setProductTypes: 'setProductTypes',
     setNewsTypes: 'setNewsTypes',
     setCarousel: 'setCarousel',
+    setShowBlocks: 'setShowBlocks',
     setPlatform: 'setPlatform'
   }
 }
@@ -39,7 +40,8 @@ export const modules = {
       nTypes: [],
       // 轮播数据
       carousel: [],
-
+      // 显示区块数据
+      showBlocks: []
     },
     mutations: {
       [indexTypes.mutations.setProductTypes](state, pTypes){
@@ -53,6 +55,9 @@ export const modules = {
       },
       [indexTypes.mutations.setPlatform](state, platform){
         state.platform = platform;
+      },
+      [indexTypes.mutations.setShowBlocks](state, showBlocks){
+        state.showBlocks = showBlocks;
       }
     },
     actions : {
@@ -73,6 +78,7 @@ export const modules = {
           commit(indexTypes.mutations.setProductTypes, data.pType);
           commit(indexTypes.mutations.setNewsTypes, data.nType);
           commit(indexTypes.mutations.setCarousel, data.carousel);
+          commit(indexTypes.mutations.setShowBlocks, data.showBlocks);
         }
       },
     },
@@ -81,6 +87,7 @@ export const modules = {
       pTypes: state => state.pTypes,
       nTypes: state => state.nTypes,
       carousel: state => state.carousel,
+      showBlocks: state => state.showBlocks,
       productTypes: state => {
         let arr = state.pTypes;
         let res = _transform(arr);