Преглед изворни кода

fix:
修复不同环境下的请求api功能.
接口访问移除问题

kindring пре 2 година
родитељ
комит
9e6a173c3d
71 измењених фајлова са 2289 додато и 235 уклоњено
  1. 6 5
      README.md
  2. 9 0
      assets/tailwindCom.css
  3. 5 5
      components/banner/banner.vue
  4. 5 2
      components/footer/defaultFooter.vue
  5. 24 7
      components/layout/adminLayout.vue
  6. 1 1
      components/productList.vue
  7. 339 0
      components/public/imageTable.vue
  8. 71 0
      components/public/imageViewer.vue
  9. 49 0
      components/public/loading.vue
  10. 50 0
      components/public/pop.vue
  11. 37 0
      components/public/popCard.vue
  12. 13 0
      components/public/roundedTitle.vue
  13. 109 0
      components/public/uploadFile.vue
  14. 2 2
      components/quickTab.vue
  15. 11 0
      ecosystem.config.js
  16. 2 2
      map/adminSideBar.js
  17. 14 1
      map/apiMap.js
  18. 29 0
      map/dbField_esm.js
  19. 0 39
      map/rcodeMap.js
  20. 31 0
      map/rcodeMap_esm.js
  21. 7 3
      nuxt.config.js
  22. 4 3
      package.json
  23. 19 21
      pages/manger/Login.vue
  24. 54 4
      pages/manger/index.vue
  25. 173 0
      pages/manger/index/carousel.vue
  26. 21 0
      pages/manger/index/index.vue
  27. 15 0
      pages/manger/index/test.vue
  28. 1 1
      pages/news/index.vue
  29. 1 1
      pages/news/item/_type.vue
  30. 1 1
      pages/product/item/_type.vue
  31. 2 1
      pages/product/item/index.vue
  32. 1 1
      pages/solution/item/_type.vue
  33. 4 0
      server/configs/database.json
  34. 9 0
      server/configs/path.json
  35. 155 0
      server/control/c_base.js
  36. 1 1
      server/control/c_news.js
  37. 1 1
      server/control/c_solution.js
  38. 66 4
      server/control/c_user.js
  39. 2 2
      server/control/product.js
  40. 90 0
      server/database/d_base.js
  41. 72 2
      server/database/d_user.js
  42. 1 0
      server/database/mysql.js
  43. 2 1
      server/database/pool.js
  44. 11 1
      server/index.js
  45. 29 0
      server/map/dbField.js
  46. 8 0
      server/map/progressField.js
  47. 49 0
      server/middleware/checkSession.js
  48. 80 0
      server/middleware/upload.js
  49. 3 3
      server/router/captcha.js
  50. 1 0
      server/router/index.js
  51. 99 0
      server/router/r_base.js
  52. 1 1
      server/router/r_news.js
  53. 3 1
      server/router/r_product.js
  54. 1 1
      server/router/r_solution.js
  55. 69 24
      server/router/r_user.js
  56. 54 0
      server/tools/decode_cjs.js
  57. 20 0
      server/tools/filePathTool.js
  58. 13 10
      server/tools/handle_cjs.js
  59. 20 1
      server/tools/result.js
  60. 74 0
      server/tools/saveFiles_cjs.js
  61. 3 5
      server/tools/searchSql.js
  62. 77 0
      server/tools/time_cjs.js
  63. 12 1
      server/tools/typeTool_cjs.js
  64. BIN
      static/ghs.png
  65. 3 3
      store/index.js
  66. 1 0
      tailwind.config.js
  67. 0 2
      until/FcCrypto/FcCrypto.js
  68. 63 63
      until/business.js
  69. 81 0
      until/domTool.js
  70. 0 8
      until/time.js
  71. 5 0
      yarn.lock

+ 6 - 5
README.md

@@ -43,14 +43,15 @@ pm2 start npm --name "hfy" -- run start
 4. 静态打包动态路由页面需要在`nuxt.config.js`中配置`generate.routes`'
 
 ## 功能新增设计
-### 图片库功能 image_lab
-> 图片id , 图片名称 , 图片路径 , 图片tag , 上传日期
+### 文件库功能 file_lab
+> id , 图片名称 , 图片路径 , 图片tag , 上传日期
 | 字段 | 类型 | 可选值 | 默认值 | 备注 |
 | --- | --- | --- | --- | --- |
-| imgId | int | pk | pk | 图片id |
+| id | int | pk | pk | 文件id |
 | imgName | varchar | '' | '' | 图片名称 |
-| tags | varchar | '' | '' | 图片tag,用,隔开 |
-| imgPath | varchar | '' | '' | 图片路径 |
+| fileType | char | '0:other','1:video','2:img' | 0 | 文件类型 |
+| tags | varchar | '' | '' | 图片tag,用,隔开,用于查询关键字 |
+| filePath | varchar | '' | '' | 资源路径 |
 | uploadTime | varchar | '' | '' | 上传时间 |
 
 

+ 9 - 0
assets/tailwindCom.css

@@ -0,0 +1,9 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer components {
+  .ant-icon-btn {
+    @apply !flex !items-center !justify-center;
+  }
+}

+ 5 - 5
components/banner/banner.vue

@@ -30,10 +30,10 @@
     <div class="banner-control bottom-14">
       <div
           v-for="(item,i) in banners"
-          :key="'handle-'+item.id"
+          :key="'handle_cjs-'+item.id"
           :class="{
-               'banner-handle':true,
-               'banner-handle-now':i===bannerIndex
+               'banner-handle_cjs':true,
+               'banner-handle_cjs-now':i===bannerIndex
                }"
           @click="changeBanner(i)"
       >
@@ -186,7 +186,7 @@ export default {
   opacity: 0.8;
 }
 
-.banner .banner-control .banner-handle {
+.banner .banner-control .banner-handle_cjs {
   width: 10px;
   height: 5px;
   border-radius: 2px;
@@ -200,7 +200,7 @@ export default {
   background-color: rgba(196, 167, 167, 0.79);
 }
 
-.banner .banner-control .banner-handle-now {
+.banner .banner-control .banner-handle_cjs-now {
   width: 15px;
   background-color: #f49b00;
 }

+ 5 - 2
components/footer/defaultFooter.vue

@@ -20,9 +20,12 @@
       </div>
     <!--        备案信息-->
       <div class="flex justify-center items-center">
-          <a class="record-item" >
+            <a class="record-item flex items-center" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=44030702001959" >
+                <img class="flex w-5 h-5 flex-shrink" src="/ghs.png" >粤公网安备 44030702001959号
+            </a>
+          <a class="record-item ml-2" href="https://beian.miit.gov.cn" >
             <span>备案号:</span>
-            <span>粤ICP备19062901号</span>
+            <span>粤ICP备18098745号</span>
           </a>
       </div>
 

+ 24 - 7
components/layout/adminLayout.vue

@@ -3,6 +3,9 @@
 export default {
   name: "adminLayout",
   props: {
+    indexPath: {
+      default: '/'
+    },
     headerHeight: {
       default: '35px'
     },
@@ -128,7 +131,13 @@ export default {
   <div class="lay-header"  >
     <div class="lay-logo">
       <slot name="logo">
-        <h1>{{logoTitle}}</h1>
+        <h1>
+          <a :href="indexPath">
+            {{logoTitle}}
+          </a>
+        </h1>
+
+
       </slot>
     </div>
     <div class="lay-menus">
@@ -176,7 +185,7 @@ export default {
                :key="sub.key"
                :class="`menu-item ${activeKey===sub.key?'menu-items-show':''}`"
           >
-            <span>{{sub.title}}</span>
+            <a :href="sub.path">{{sub.title}}</a>
           </div>
         </div>
 
@@ -263,15 +272,18 @@ export default {
 }
 .lay-content{
   width: calc(100% - var(--sideBarWidth));
-  overflow: hidden;
+  height: 100%;
+  overflow: auto;
   background-color: #d5d5d5;
 }
 .lay-sideBar-control{
-  position: absolute;
+  position: relative;
   width: 25px;
   height: 25px;
-  left: 100%;
+  left: 50%;
   top: 0px;
+  margin-top: 5px;
+  transform: translate(-50%, 0);
   border-radius: 3px;
   border: 1px solid black;
   z-index: 999;
@@ -341,12 +353,17 @@ export default {
 .bar-menu-item .sub-menu-box .menu-item{
   width: 100%;
   height: calc(var(--sideBarHeight) - 5px);
-  display: flex;
-  align-items: center;
   box-sizing: border-box;
   padding-left: calc(var(--sideBarHeight) + 5px);
   cursor: pointer;
 }
+.bar-menu-item .sub-menu-box .menu-item > a{
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
 .bar-menu-item .sub-menu-box .menu-item:hover{
   color: var(--btn-text-color-hover);
   background-color: var(--btn-color-hover);

+ 1 - 1
components/productList.vue

@@ -1,6 +1,6 @@
 <template>
 <div class="content productList">
-  <div class="conBox productCenter">
+  <div class="conBox container productCenter">
     <a class="product"
        v-for="(product,i) in productList"
        :key="'pro-'+i"

+ 339 - 0
components/public/imageTable.vue

@@ -0,0 +1,339 @@
+<template>
+  <div class="w-8/12 h-5/6 p-2 rounded flex flex-col justify-center border bg-white">
+    <div class="p-header mx-1.5 h-16  text-2xl flex border-b">
+<!--      刷新按钮-->
+      <a-button class="ant-icon-btn" type="primary" icon="reload" :loading="loadingState === 0" @click="loadImage" ></a-button>
+
+      <a-button class="ml-2" :type="tabKey===1?'primary':''" @click="callback(1)">选择图片</a-button>
+      <a-button class="ml-1" :type="tabKey===2?'primary':''" @click="callback(2)">上传图片</a-button>
+    </div>
+    <div class="p-con w-full px-1">
+
+      <loading v-if="tabKey===1" class="w-full h-full " :loading-state="loadingState" tip="获取图片中">
+        <div class="img-viewBox" :style="imgBoxStyle" ref="imgViewBox">
+          <div
+              v-for="item in images"
+              :key="item.path"
+              :class="`img-viewItem ${item.filePath === imgUrl?'img-viewItem-select bg-red-300':'bg-gray-400'}`"
+              :style="imgItemStyle"
+              @click="selectImg(item)"
+          >
+            <img class="w-auto h-full" :src="item.filePath" alt="item.fileName">
+          </div>
+        </div>
+        <template v-slot:loadFail>
+          <div class="w-full h-full flex justify-center items-center flex-col" >
+            <h2 class="text-2xl text-red-700">{{loadingMessage}}</h2>
+          </div>
+        </template>
+      </loading>
+
+<!--      文件上传部分 -->
+      <div
+        v-if="tabKey===2"
+        class="w-full h-full flex flex-col">
+        <div class="w-full h-32 rounded
+        border overflow-hidden flex-shrink">
+          <upload-file
+            :type="1"
+            :multiple="true"
+            @change="uploadChangeHandle"
+          > </upload-file>
+        </div>
+        <div class="w-full h-full overflow-auto">
+          <div
+            v-for="file in fileList"
+            class="w-full h-64 rounded mt-2
+            border flex relative px-2">
+            <div class="w-full absolute bottom-0 left-0"
+                 v-if="file.status !== fileState.waiting">
+              <a-progress :show-info="file.showInfo" :percent="file.percent" />
+            </div>
+
+            <div class="w-1/3 h-full p-4 relative rounded overflow-hidden">
+              <image-viewer :src="file.url"/>
+            </div>
+
+            <div class="w-2/3 h-full flex items-center relative justify-between">
+              <div>
+<!--                状态提示 -->
+                <div class="w-full">
+                  <a-alert :message="file.showInfo_message"
+                           :type="file.showInfo_type"
+                           show-icon />
+                </div>
+                <span>
+                  {{file.name}}
+                </span>
+
+              </div>
+              <a-button-group>
+                <a-button :disabled="file.status === fileState.uploading || file.status === fileState.success"
+                          :loading="file.status === fileState.uploading" >删除</a-button>
+                <a-button :disabled="file.status === fileState.uploading || file.status === fileState.success"
+                          :loading="file.status === fileState.uploading"
+                          class="ml-2" @click="uploadFileItem(file)">上传</a-button>
+              </a-button-group>
+
+            </div>
+          </div>
+        </div>
+
+      </div>
+
+    </div>
+    <div class="p-header w-full mt-2">
+      <a-button @click="close" type="danger">X</a-button>
+      <a-button class="ml-2" type="primary" @click="selectNowImg" :disabled="!imgUrl">选择当前照片</a-button>
+<!--      移除选择的照片 -->
+      <a-button class="ml-2" v-show="imgUrl" type="danger" @click="removeFileHandle">移除选择的照片</a-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import handle from "~/until/handle";
+import Loading from "~/components/public/loading";
+import {db_base} from "../../map/dbField_esm";
+import {rCode} from "../../map/rcodeMap_esm";
+import UploadFile from "./uploadFile.vue";
+import ImageViewer from "./imageViewer.vue";
+import domTool from "~/until/domTool";
+
+const fileState = {
+  waiting: 'waiting',
+  uploading: 'uploading',
+  success: 'success',
+  fail: 'fail'
+}
+export default {
+  name: "imageTable",
+  components: {ImageViewer, UploadFile, Loading},
+  props: {
+    imgHeight: {
+      type: Number,
+      default: 150
+    },
+    lineItem: {
+      type: Number,
+      default: 5
+    },
+    // 间隔,单位px
+    gap: {
+      type: Number,
+      default: 5
+    },
+  },
+  data(){
+    return {
+      uploadUrl: `/api/base/fileUp?type=${db_base.fileType.image}`,
+      tabKey: 1,
+      imgUrl:'',
+      fileData: {},
+      loadingState: 0,
+      loadingMessage: '',
+      searchKey: '',
+      fileType: db_base.fileType.image,
+      fileState: fileState,
+      fileList: [],
+      images: [],
+      imgBoxStyle: '',
+      imgItemStyle: '',
+
+    }
+  },
+  async mounted() {
+    // this.comStyle();
+    await this.loadImage();
+    // 监听resize
+  },
+  methods:{
+    close(){
+      this.$emit('cancel')
+    },
+    // 选择图片
+    selectNowImg(){
+      if(!this.imgUrl){
+        return this.$message.warn('请选择图片')
+      }
+      this.$emit('ok',this.fileData);
+    },
+    comStyle(){
+      this.$nextTick(()=>{
+        let el_imgViewBox = this.$refs.imgViewBox;
+        let res = domTool.comDomStyle(el_imgViewBox,this.lineItem,this.gap);
+        if(res === -1){
+          return this.$message.error('计算样式失败,元素不存在');
+        }
+        let {boxPadding,itemStyle} = res;
+        this.imgBoxStyle = boxPadding;
+        this.imgItemStyle = itemStyle + `;height:${this.imgHeight}px`;
+      });
+    },
+
+    async loadImage(){
+      this.loadingState = 0;
+      let url = '/api/base/files';
+      let params = {
+        type: this.fileType + 1,
+        key: this.searchKey
+      };
+      let [err,res] = await handle(this.$axios.get(url,{params}));
+      if(err){
+        this.loadingState = 2;
+        this.loadingMessage = '获取图片失败';
+        return console.log(err);
+      }
+      let result = res.data;
+      if(result.code === rCode.OK){
+        this.loadingState = 1;
+        this.comStyle();
+        this.images = result.data;
+      }else{
+        this.loadingState = 2;
+        this.loadingMessage = `获取图片失败${result.msg}`;
+        this.$message.error(result.msg);
+        return {}
+      }
+
+    },
+    async uploadChangeHandle(files){
+      console.log(files);
+      console.log(`files is ${files.length}`);
+      // 文件预览
+      for(let i = 0;i<files.length;i++){
+        let file = files[i];
+        let [err,fileUrl] = await handle(this.fileToPreview(file));
+        if(err){
+          this.$message.warn(`文件${file.name}预览失败`);
+          // 文件转换失败
+          return console.log(err);
+        }
+        this.fileList.push({
+          file: file,
+          uid: file.uid,
+          name: file.name,
+          status: fileState.waiting,
+          percent: 0,
+          showInfo: false,
+          showInfo_message: '点击按钮进行上传',
+          showInfo_type: 'info',
+          url: fileUrl
+        });
+      }
+    },
+    fileToPreview(file){
+      return new Promise((resolve, reject) => {
+        let reader = new FileReader();
+        reader.readAsDataURL(file);
+        reader.onload = function (e) {
+          resolve(e.target.result);
+        };
+        reader.onerror = function (e) {
+          reject(e);
+        };
+      });
+    },
+    callback(key) {
+      console.log(key);
+      this.tabKey = key;
+    },
+    // 选择图片
+    selectImg(fileData){
+      console.log(`selectImg ${fileData.filePath} fileId is ${fileData.fileId}`);
+      this.imgUrl = fileData.filePath;
+      this.fileData = fileData;
+    },
+    // 编辑图片信息
+    editImg(fileData){
+      console.log(`editImg ${fileData.filePath} fileId is ${fileData.fileId}`);
+    },
+    async uploadFileItem(file){
+      if(!file || !file.file || file.status === fileState.fail){
+        return this.$message.error('调用异常,已经阻止上传');
+      }
+      file.showInfo = true;
+      file.status = fileState.uploading;
+      file.percent = 0;
+      file.showInfo_message = '上传中';
+      file.showInfo_type = 'info';
+      let form = new FormData();
+      form.append('file',file.file);
+      let [err,res] = await handle(this.$axios.post(this.uploadUrl,form,{
+        onUploadProgress: (progressEvent) => {
+          file.percent = Math.round((progressEvent.loaded * 100) / progressEvent.total) || 0;
+        }
+      }));
+      if(err){
+        file.status = fileState.fail;
+        file.showInfo_message = `上传失败,${err.message}`;
+        file.showInfo_type = 'error';
+        return console.log(err);
+      }
+      let result = res.data;
+      if(result.code === rCode.OK) {
+        file.status = fileState.success;
+        file.showInfo_message = `文件上传成功`;
+        file.showInfo_type = 'success';
+        this.$message.success('上传成功');
+        await this.loadImage();
+      }else{
+        file.status = fileState.fail;
+        file.showInfo_message = `上传失败,${err.message}`;
+        file.showInfo_type = 'error';
+        this.$message.error(result.msg);
+      }
+    },
+    async updateFileItem(updateData){
+
+    },
+    async removeFileHandle(){
+      let fileData = this.fileData;
+      if(!fileData || !fileData.fileId){
+        return this.$message.warn('暂未选择图片');
+      }
+      let [err,res] = await handle(this.$axios.delete(`/api/base/file/${fileData.fileId}`));
+      if(err){
+        this.$message.error('删除图像资源失败');
+        return console.log(err);
+      }
+      let result = res.data;
+      if(result.code === rCode.OK) {
+        this.$message.success('删除图像资源成功');
+        this.imgUrl = '';
+        this.fileData = {};
+        await this.loadImage();
+      }else{
+        this.$message.error(result.msg);
+      }
+    }
+  },
+}
+</script>
+
+<style scoped>
+.p-header{
+  height:35px;
+}
+.p-con {
+  height: calc(100% - 80px);
+}
+.img-viewBox{
+  width: 100%;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: flex-start;
+  overflow: auto;
+}
+.img-viewItem{
+  cursor: pointer;
+  flex-shrink: 0;
+  transition: all 0.5s;
+  display: flex;
+  justify-content: center;
+}
+.img-viewItem-select{
+  border: 2px solid #01eef5;
+  box-shadow: 0 0 2px #fff;
+}
+</style>

+ 71 - 0
components/public/imageViewer.vue

@@ -0,0 +1,71 @@
+<script>
+export default {
+  name: 'imageViewer',
+  props: {
+    src: {
+      type: String,
+      default: ''
+    }
+  },
+  data(){
+    return {
+      isFull: false,
+    }
+  },
+
+}
+</script>
+
+<template>
+<div class="img">
+  <img :src="src"/>
+  <div v-show="isFull" class="img-con">
+    <div class="img-btn">+</div>
+    <div class="img-btn">-</div>
+  </div>
+</div>
+</template>
+
+<style scoped>
+.img{
+  width: 100%;
+  height: 100%;
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: #494949;
+  transition: width 0.5s,height 0.5s;
+}
+.img img{
+  display: block;
+  height: 100%;
+  width: auto;
+}
+.img .img-con{
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  height: 45px;
+}
+.img .img-con .img-btn{
+  display: inline-block;
+  width: 45px;
+  height: 45px;
+  line-height: 45px;
+  text-align: center;
+  background-color: rgba(0,0,0,0.5);
+  color: #fff;
+  cursor: pointer;
+  border-radius: 50%;
+}
+.img-full{
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 999;
+}
+</style>

+ 49 - 0
components/public/loading.vue

@@ -0,0 +1,49 @@
+<template>
+  <a-spin
+      :class="{
+        'my-loading-default':true,
+        'my-loading-css':(loadingState===0)}"
+      :spinning="loadingState===0"
+      :tip="tip">
+    <!--      加载中-->
+    <slot v-if="loadingState===1" ></slot>
+    <!--      加载成功插槽-->
+    <slot v-else-if="loadingState===2" name="loadFail"></slot>
+    <!--      加载失败插槽 -->
+  </a-spin>
+</template>
+
+<script>
+export default {
+  name: "loading",
+  props: {
+    tip: {
+      default: 'Loading...'
+    },
+    size: {
+      default: 'default',
+    },
+  //  加载状态 0加载中 1加载失败 2加载成功
+    loadingState: {
+      default: 0
+    }
+  },
+}
+</script>
+
+<style >
+.my-loading-default{
+  width: 100%;
+  height: 100%;
+}
+.my-loading-css{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+}
+.ant-spin-container{
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 50 - 0
components/public/pop.vue

@@ -0,0 +1,50 @@
+<template>
+<div
+    class=" w-screen h-screen fixed left-0 top-0 flex overflow-hidden justify-center items-center"
+    v-if="show"
+>
+  <div
+      class="mask absolute left-0 top-0 w-full h-full"
+      v-if="mask"
+  ></div>
+
+  <a-spin
+      :spinning="loading"
+      size="large"
+      class="w-full h-full flex justify-center items-center"
+  >
+    <div class="w-full h-full flex justify-center items-center">
+      <slot/>
+    </div>
+  </a-spin>
+
+</div>
+</template>
+
+<script>
+export default {
+  name: "pop",
+  props:{
+    mask:{
+      default: true
+    },
+    loading:{
+      default: false
+    },
+    show:{
+      default: false
+    }
+  },
+  data(){
+    return {
+    }
+  }
+}
+</script>
+
+<style scoped>
+  .mask{
+    background-color: #000;
+    opacity: 0.3;
+  }
+</style>

+ 37 - 0
components/public/popCard.vue

@@ -0,0 +1,37 @@
+<script setup>
+
+</script>
+
+<template>
+  <div class="w-8/12 h-5/6 p-2 rounded flex flex-col justify-center border bg-white">
+    <div class="p-header mx-1.5 h-16  text-2xl flex border-b ">
+      <div class="icon-btn-group">
+        <slot name="close-group"></slot>
+      </div>
+      <slot name="header"></slot>
+    </div>
+    <div class="p-con w-full px-1">
+      <slot></slot>
+    </div>
+    <div class="p-header w-full mt-2 h-4">
+      <slot name="footer"></slot>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.p-header{
+  height:35px;
+  position: relative;
+  cursor: default;
+}
+.p-con {
+  height: calc(100% - 80px);
+}
+.icon-btn-group{
+  position: absolute;
+  right: 0;
+  top: 0;
+  height: 100%;
+}
+</style>

+ 13 - 0
components/public/roundedTitle.vue

@@ -0,0 +1,13 @@
+<script setup>
+
+</script>
+
+<template>
+<div class="w-full py-4 bg-white text-3xl rounded px-2 cursor-default">
+  <slot></slot>
+</div>
+</template>
+
+<style scoped>
+
+</style>

+ 109 - 0
components/public/uploadFile.vue

@@ -0,0 +1,109 @@
+<script>
+const acceptMap = {
+  0: '*/*',
+  1: 'image/*',
+  2: 'video/*',
+  3: 'audio/*',
+};
+export default {
+  name: 'uploadFile',
+  props: {
+    //   多选
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    // 类型限制 0: all 1: image 2: video 3: audio
+    type: {
+      type: Number,
+      default: 0
+    },
+    customAccept : {
+      type: String,
+      default: ''
+    },
+    // 是否预览选择的资源
+    preview: {
+      type: Boolean,
+      default: false
+    },
+
+  },
+  data(){
+    return {
+    }
+  },
+  computed:{
+    acceptComputed(){
+      if(this.customAccept){
+        return this.customAccept;
+      }
+      return acceptMap[this.type]?acceptMap[this.type]:acceptMap["0"];
+    },
+  },
+  methods: {
+    changeHandle(e) {
+      console.log(e)
+      console.log(e.target.files)
+      let files = e.target.files;
+      if(files.length === 0){
+        return;
+      }
+      this.$emit('change',files);
+    }
+  }
+
+}
+</script>
+
+<template>
+<div class="uploadFile">
+  <div class="showTips">
+    <slot>
+        <div class="showTips-con-text">点击上传</div>
+    </slot>
+  </div>
+
+  <input type="file"
+         :multiple="multiple"
+         :accept="acceptComputed"
+          @change="changeHandle"
+  />
+</div>
+</template>
+
+<style scoped>
+.uploadFile{
+  width: 100%;
+  height: 100%;
+  position: relative;
+}
+.uploadFile input{
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  left: 0;
+  top: 0;
+  opacity: 0;
+}
+.uploadFile .showTips{
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  left: 0;
+  top: 0;
+  cursor: pointer;
+}
+.showTips .showTips-con-text{
+  color: #fff;
+  font-size: 24px;
+  font-weight: bold;
+  background-color: #6c6c6c;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+</style>

+ 2 - 2
components/quickTab.vue

@@ -8,7 +8,7 @@
           :key="item.com"
           @click="changeBanner(i)"
           :class="`
-          tab-handle
+          tab-handle_cjs
           ${(tabNow-1)===i?'table-left':''}
           ${tabNow===i?'table-now':''}
           ${(tabNow+1)===i?'table-right':''}
@@ -63,7 +63,7 @@ export default {
   background-color: skyblue;
 }
 
-.tab-handle:hover{
+.tab-handle_cjs:hover{
   background-color: #ffffff;
   color: black;
 }

+ 11 - 0
ecosystem.config.js

@@ -0,0 +1,11 @@
+module.exports = {
+  apps: [
+    {
+      name: 'hfyWebsite',
+      exec_mode: 'cluster',
+      instances: '10', // Or a number of instances
+      script: './node_modules/nuxt/bin/nuxt.js',
+      args: 'start'
+    }
+  ]
+}

+ 2 - 2
map/adminSideBar.js

@@ -6,12 +6,12 @@ export const adminMenus = [
       {
         key: 'carousel',
         title: '轮播图管理',
-        path: '/manger/index/carousel'
+        path: '/manger/carousel'
       },
       {
         key: 'showing',
         title: '展示块',
-        path: '/manger/index/showing'
+        path: '/manger/showing'
       },
       {
         key: 'indexProduct',

+ 14 - 1
map/apiMap.js

@@ -1,4 +1,17 @@
-export const baseUrl = 'http://127.0.0.1:3000';
+// const env = {
+//     dev: {
+//       MODE: 'development',
+//       ENV_API: `http://127.0.0.1:${devPort}`   //测试服务器地址
+//     },
+//     pro: {
+//       MODE: 'production',
+//       ENV_API: `http://127.0.0.1:${serverPort}`  // 正式服务器地址
+//     }
+// }
+const devPort = 3000;
+const serverPort = 4201;
+export const baseUrl = process.env.ENV_API;
+console.log('baseUrl', baseUrl);
 export const apiMap = {
   searchProduct: {
     path: `/api/product/search`,

+ 29 - 0
map/dbField_esm.js

@@ -0,0 +1,29 @@
+export const db_user = {
+  // 是否启用新密码存储方式
+  enableNewPasswd: {
+    true: 1,
+    false: 0,
+  },
+  // 用户状态
+  status: {
+    // 正常
+    normal: 1,
+    // 禁用
+    disable: 0,
+  }
+}
+
+export const db_base = {
+  // 文件类型
+  fileType: {
+    all: -1,
+    other: 0,
+    image: 1,
+    video: 2,
+  }
+}
+
+export default {
+  db_base,
+  db_user
+}

+ 0 - 39
map/rcodeMap.js

@@ -1,39 +0,0 @@
-export default {
-    // 无法匹配
-    notMatch: 0,
-    // 请求正常
-    ok: 1,
-    // 缺少参数
-    notParam: 2,
-    // 没有登录
-    notLogin: 3,
-    // 鉴权失败,没有权限, 不同场景不同提示内容 在登录页面账号显示封禁
-    permissionDenied: 4,
-    // 自定义错误信息,用于前端直接显示的信息
-    customError: 5,
-    // 服务器内部逻辑错误
-    serverError: 6,
-    // 服务器操作超时
-    timeout: 7,
-    // 无法找到记录,可用于账号密码错误等场景的自动化提示
-    notFound: 8,
-    // 二次访问接口异常
-    apiError: 9,
-    // 存储异常
-    saveError: 10,
-    // 数据重复,在某些新增的场景表示该数据已经重复
-    dataRepeat: 11,
-    0: {msg:'无法匹配',ok:false,type:'error'},
-    1:{msg:'ok',ok:true,type:'success'},
-    2:{msg:'缺少必要参数',ok:false,type:'error'},
-    3:{msg:'未登陆,请先登陆',ok:false,type:'error'},
-    4:{msg:'无权限操作',ok:false,type:'error'},
-    5:{msg:'自定义错误',ok:false,type:'error'},
-    6:{msg:'服务器错误',ok:false,type:'error'},
-    7:{msg:'服务器操作超时',ok:false,type:'error'},
-    8:{msg:'无法找到记录',ok:false,type:'error'},
-    9:{msg:'二次访问接口异常',ok:false,type:'error'},
-    10:{msg:'存储异常',ok:false,type:'error'},
-    11:{msg:'数据重复',ok:false,type:'error'},
-
-}

+ 31 - 0
map/rcodeMap_esm.js

@@ -0,0 +1,31 @@
+export const rCode = {
+  NotMATCH: 0,
+  OK: 1,
+  NotParam: 2,
+  NotLogin: 3,
+  NotPermission: 4,
+  CustomError: 5,
+  ServerError: 6,
+  Timeout: 7,
+  NotFound: 8,
+  ApiError: 9,
+  SaveError: 10,
+  DataRepeat: 11,
+  0: {msg:'无法匹配',ok:false,type:'error'},
+  1: {msg:'ok',ok:true,type:'success'},
+  2: {msg:'缺少必要参数',ok:false,type:'error'},
+  3: {msg:'未登陆,请先登陆',ok:false,type:'error'},
+  4: {msg:'无权限操作',ok:false,type:'error'},
+  5: {msg:'自定义错误',ok:false,type:'error'},
+  6: {msg:'服务器错误',ok:false,type:'error'},
+  7: {msg:'服务器操作超时',ok:false,type:'error'},
+  8: {msg:'无法找到记录',ok:false,type:'error'},
+  9: {msg:'二次访问接口异常',ok:false,type:'error'},
+  10:{msg:'存储异常',ok:false,type:'error'},
+  11:{msg:'数据重复',ok:false,type:'error'},
+}
+
+
+export default {
+  rCode
+}

+ 7 - 3
nuxt.config.js

@@ -1,6 +1,6 @@
 import * as path from "path";
 const devPort = 3000;
-const serverPort = 3000;
+const serverPort = 4201;
 const env = {
     dev: {
       MODE: 'development',
@@ -39,7 +39,8 @@ export default {
         ]
     },
     css: [
-      '@/assets/public.css'
+      '@/assets/public.css',
+      '@/assets/tailwindCom.css',
     ],
 
     // Auto import components (https://go.nuxtjs.dev/config-components)
@@ -65,6 +66,7 @@ export default {
     serverMiddleware: [
         '~/server/index.js'
     ],
+
     // styleResources: {
     //    scss: [
     //        '~/assets/_var.scss'
@@ -102,8 +104,10 @@ export default {
         // }
       },
     },
+    env : process.env.NODE_ENV === 'production' ? env.pro : env.dev,
     server: {
-      port: devPort, // default: 3000
+      // 根据环境变量判断当前运行环境 nuxt start 使用production环境
+      port: process.env.NODE_ENV === 'production' ? serverPort : devPort, // default: 3000
       host: '0.0.0.0', // default: localhost
     },
     generate: {

+ 4 - 3
package.json

@@ -3,9 +3,9 @@
   "version": "1.0.0",
   "private": true,
   "scripts": {
-    "dev": "nuxt",
-    "build": "nuxt build",
-    "start": "nuxt start",
+    "dev": "nuxt --NODE_ENV=development",
+    "build": "nuxt build --NODE_ENV=production",
+    "start": "nuxt start --PORT=production ",
     "generate": "nuxt generate"
   },
   "dependencies": {
@@ -13,6 +13,7 @@
     "ant-design-vue": "^1.6.5",
     "body-parser": "^1.20.2",
     "core-js": "^3.6.5",
+    "crypto-js": "^4.1.1",
     "express": "^4.17.1",
     "express-session": "^1.17.3",
     "formidable": "^3.5.0",

+ 19 - 21
pages/manger/Login.vue

@@ -91,20 +91,23 @@ import Vue from 'vue'
 // 引用ant-design-vue的input组件
 import antInput from 'ant-design-vue/lib/input';
 import 'ant-design-vue/dist/antd.css'
-
+import { message } from 'ant-design-vue';
+import { Modal } from 'ant-design-vue';
 
 // import apis from '@/apis/apis'
 import handle from "~/until/handle";
-import business from "~/until/business";
 import fieldIsAllow from "~/until/fieldIsAllow"
-import userData from  "~/until/saves/users";
 
 import Captcha from "~/components/public/captcha";
 import LightButton from "~/components/public/lightButton";
 import {login_types} from "../../store/login";
+import {rCode} from "../../map/rcodeMap_esm";
 
 Vue.use(antInput);
-
+// 引用message组件
+Vue.prototype.$message = message;
+// 引用modal组件
+Vue.prototype.$success = Modal.success;
 export default {
   name: 'App',
   components: {
@@ -143,7 +146,7 @@ export default {
         },
       },
       // 要切换的url地址
-      jumpUrl: '/',
+      jumpUrl: '/manger',
     }
   },
   computed:{
@@ -171,9 +174,7 @@ export default {
       this.form.captcha = '';
     },
     checkFormItem(field){
-      console.log(field);
-
-      this.setCaptcha(this.form[field].val);
+      // console.log(field);
       if (field){
         this.form[field].msg = fieldIsAllow({
           [field]:this.form[field].val,
@@ -199,25 +200,22 @@ export default {
       let passwd = this.form.password.val;
       let captcha = this.form.captcha.val;
       // apis.user.login(owner,passwd,captcha)
-      let [err,response] = await handle(this.$axios.post('/api/user/login',{
+      let [err,res] = await handle(this.$axios.post('/api/user/login',{
         owner,
         passwd,
         captcha,
       }));
-      let rcodeMean = business.checkResponseRcode(response,err);
-      if(rcodeMean.ok){
-        let res = rcodeMean.res;
-        // 登陆成功
-        this.$message.success(`登陆成功,稍后进行页面跳转`);
-        console.log(res);
-        userData.setUser(res.data)
+      this.$refs.captchaImg.refreshCaptcha();
+      if(err){
+        console.log(err);
+        return {};
+      }
+      let result = res.data;
+      if(result.code === rCode.OK){
         this.countDown();
       }else{
-        // 登陆失败
-        this.$message[rcodeMean.type](rcodeMean.msg);
-        // 更新验证码
-        this.$refs.captchaImg.refreshCaptcha();
-        this.form.captcha.val = '';
+        this.$message.error(result.msg);
+        return {solutions:[]}
       }
     },
     // 登陆成功

+ 54 - 4
pages/manger/index.vue

@@ -3,10 +3,26 @@
 
 import AdminLayout from "~/components/layout/adminLayout.vue";
 import {adminMenus} from "~/map/adminSideBar";
+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";
+Vue.use(antd);
 
 export default {
   name: "mangerIndex",
   components: {AdminLayout},
+  async asyncData(ctx) {
+    // 判断 store 中的 isLogin 是否为 true $store.state.login.captcha
+    console.log(ctx?.store?.state?.login)
+    if (!ctx?.store?.state?.login?.isLogin) {
+      // 如果未登录,重定向到登录页
+      // ctx.redirect('/manger/login')
+      ctx.redirect('/manger/login')
+    }
+    return {}
+  },
   data(){
     return {
       headerMenus:[
@@ -19,6 +35,26 @@ export default {
       sidebarMenus: adminMenus
 
     }
+  },
+  computed: {
+    userInfo(){
+      return this.$store.state.login.userInfo
+    }
+  },
+  methods: {
+    dataLogOut(){
+      this.$store.commit('login/'+login_types.mutations.userLogout);
+    },
+    async logout(){
+      let [err,res] = await handle(this.$axios.get('/api/user/logout'));
+      this.dataLogOut();
+      if(err){
+        return {};
+      }
+      this.$message.success('退出成功');
+      // 切换url至 /login
+      this.$router.push('/manger/login');
+    }
   }
 }
 </script>
@@ -29,11 +65,25 @@ export default {
   :logoTitle="'深圳合方圆站点管理'"
   :header-menus="headerMenus"
   :sidebar-menus="sidebarMenus"
+  index-path="/manger"
 >
-  test
-  <div class="abc">
-    bhh
-  </div>
+<!--  下拉菜单,对应slot user-->
+  <a-dropdown slot="user">
+    <a class="ant-dropdown-link" href="#">
+      {{ userInfo.name }} <a-icon type="down" />
+    </a>
+    <a-menu slot="overlay">
+<!--      <a-menu-item key="1">-->
+<!--        <a href="#">1st menu item</a>-->
+<!--      </a-menu-item>-->
+      <a-menu-item key="3">
+        <span @click="logout">退出登录</span>
+      </a-menu-item>
+    </a-menu>
+  </a-dropdown>
+  <nuxt-child>
+
+  </nuxt-child>
 </admin-layout>
 </div>
 </template>

+ 173 - 0
pages/manger/index/carousel.vue

@@ -0,0 +1,173 @@
+<script>
+import axios from "axios";
+import {defineComponent} from "vue";
+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";
+export default defineComponent({
+  name: 'carousel',
+  components: {PopCard, Pop, ImageTable, ImageViewer, RoundedTitle},
+  async asyncData(ctx){
+    // 加载轮播图数据
+    let [err,res] = await handle(axios.get('/api/base/carousel'));
+    if(err){
+      return {};
+    }
+    let result = res.data;
+    if(result.code === rCode.OK){
+      return {carouselList: result.data}
+    }else{
+      this.$message.error(result.msg);
+      return {}
+    }
+    return {}
+  },
+  data(){
+    return {
+      loading: false,
+      carouselList: [],
+      popShow: false,
+      popLoading: false,
+      carouselPopShow: true,
+      carouselPopLoading: false,
+      isEditCarousel: false,
+    }
+  },
+  mounted() {
+    if(this.carouselList.length === 0){
+      this.getCarouselList();
+    }
+  },
+  methods: {
+    async getCarouselList(){
+      this.loading = true;
+      let [err,res] = await handle(this.$axios.get('/api/base/carousel'));
+      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.getCarouselList();
+                  },
+                },
+              },
+              '重试',
+            );
+          },
+          key:this.NotificationKey,
+          onClose: close,
+        });
+      }
+      let result = res.data;
+      if(result.code === rCode.OK){
+        this.carouselList = result.data;
+        return {carouselList: result.data}
+      }else{
+        this.$message.error(result.msg);
+        return {}
+      }
+    },
+    async addCarouselItem(){
+      // 打开弹窗. 选择图片,填写链接地址,排序
+
+    },
+    showPop(){
+      this.popShow = true;
+      this.popLoading = false;
+    },
+    cancelPop(){
+      this.popShow = false;
+      this.popLoading = false;
+    },
+    okHandle(fileItem){
+      console.log(fileItem);
+      this.cancelPop();
+    }
+  },
+
+})
+</script>
+
+<template>
+<div class="w-full p-2">
+  <rounded-title>轮播图管理</rounded-title>
+  <div class="mt-2 rounded bg-white p-2">
+<!--    轮播图list , 左侧轮播图片, 右侧 轮播信息 -->
+  <div class="mt-2 rounded bg-white p-2">
+    <div class="py-1 border-b border-cyan-300 flex justify-between">
+        点击下方快进行管理轮播图数据,一次性不要添加过多轮播图
+<!--      新增按钮-->
+      <a-button type="primary" class="ant-icon-btn" icon="plus" @click="addCarouselItem" :loading="loading"></a-button>
+<!--      刷新按钮-->
+      <a-button type="primary" class="ant-icon-btn" icon="reload" @click="getCarouselList" :loading="loading"></a-button>
+    </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>
+
+      <div
+        v-for="(item,index) in carouselList"
+        :key="'carouse-'+index"
+        class="mt-2 rounded border flex h-72 overflow-hidden"
+      >
+        <div class="media w-1/2 h-full">
+          <image-viewer :src="item.filePath"></image-viewer>
+        </div>
+        <div class="w-1/2 h-full box-border pl-2">
+          <a-button @click="showPop">编辑</a-button>
+          排序{{item.sort}},数字越小越靠前
+          链接地址: {{item.href}}
+        </div>
+
+      </div>
+    </div>
+
+  </div>
+  </div>
+
+  <pop :show="popShow" :loading="popLoading">
+    <image-table @cancel="cancelPop" @ok="okHandle"></image-table>
+    <!--      <choose-to-sit></choose-to-sit>-->
+  </pop>
+  <pop :show="carouselPopShow" :loading="carouselPopLoading">
+    <pop-card>
+      <template slot="header" class="w-full">
+        {{isEditCarousel ? '编辑轮播图' : '新增轮播图'}}
+      </template>
+      <template slot="close-group">取消</template>
+      <div class="w-full">
+        内容
+      </div>
+      <template class="w-full" slot="footer">
+        <a-button>{{isEditCarousel? '保存': '新增'}}</a-button>
+      </template>
+    </pop-card>
+  </pop>
+</div>
+</template>
+
+<style scoped>
+
+</style>

+ 21 - 0
pages/manger/index/index.vue

@@ -0,0 +1,21 @@
+<script>
+export default {
+  name: 'indexView'
+}
+</script>
+
+<template>
+<div class="w-full h-auto p-2">
+  <h1>欢迎使用合方圆页面管理平台</h1>
+  <p>
+    本平台为合方圆公司内部使用,请勿外传
+  </p>
+  <p>
+    更多功能正在开发中,敬请期待
+  </p>
+</div>
+</template>
+
+<style scoped>
+
+</style>

+ 15 - 0
pages/manger/index/test.vue

@@ -0,0 +1,15 @@
+<script>
+export default {
+  name: 'Test',
+}
+</script>
+
+<template>
+<div class="das">
+  test page 1
+</div>
+</template>
+
+<style scoped>
+
+</style>

+ 1 - 1
pages/news/index.vue

@@ -111,7 +111,7 @@ export default {
     },
     async searchNews(){
       // 获取数据
-      let url = apiMap.searchProduct.path;
+      let url = apiMap.searchNews.path;
       url += `?key=${this.key}&type=${this.type}&p=${this.page}`
       let [err,res] = await handle(axios.get(url));
       if(err){ console.log(err); return null; }

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

@@ -6,7 +6,7 @@
 <script>
 import ItemIndex from "~/pages/news/item/index";
 import {apiMap, baseUrl} from "~/map/apiMap";
-import {toNumber} from "~/server/tools/typeTool";
+import {toNumber} from "~/until/typeTool";
 import handle from "~/until/handle";
 import axios from "axios";
 

+ 1 - 1
pages/product/item/_type.vue

@@ -7,7 +7,7 @@ import ItemIndex from "~/pages/product/item/index";
 import {apiMap, baseUrl} from "~/map/apiMap";
 import handle from "~/until/handle";
 import axios from "axios";
-import {toNumber} from "~/server/tools/typeTool";
+import {toNumber} from "~/until/typeTool";
 export default {
   name: "itemType",
   components: {ItemIndex},

+ 2 - 1
pages/product/item/index.vue

@@ -6,7 +6,7 @@
     <div class="conBox product-view">
       <div class="left">
         <div class="imgView">
-          <img :src="'/public/'+productDetail.image" alt=""/>
+          <img v-show="productDetail.image" :src="`/public/${productDetail.image}`" alt=""/>
         </div>
         <div class="concatUs">
 <!--          联系销售 -->
@@ -72,6 +72,7 @@ export default {
     console.log(this.pInfo);
     this.productDetail = this.pInfo;
     console.log(this.productDetail.detail)
+    console.log(this.productDetail.image)
     this.productDetail.detail = unescape(this.productDetail.detail);
   },
   mounted() {

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

@@ -5,7 +5,7 @@
 <script>
 import ItemIndex from "~/pages/solution/item/index";
 import {apiMap, baseUrl} from "~/map/apiMap";
-import {toNumber} from "~/server/tools/typeTool";
+import {toNumber} from "~/until/typeTool";
 import handle from "~/until/handle";
 import axios from "axios";
 export default {

+ 4 - 0
server/configs/database.json

@@ -1,8 +1,12 @@
 {
+  "host-": "127.0.0.1",
   "host": "154.23.140.251",
   "port": "3306",
+  "user-": "root",
   "user": "mysql85931094",
+  "password0": "12345678",
   "password": "zA564uyabd",
   "connectionLimit": "100",
+  "database2": "site",
   "database": "mysql85931094_db"
 }

+ 9 - 0
server/configs/path.json

@@ -0,0 +1,9 @@
+{
+  "tmp": "./tmp",
+  "images": "./upload/images",
+  "baseImages": "/upload/images",
+  "videos": "./upload/videos",
+  "baseVideos": "/upload/videos",
+  "files": "./upload/files",
+  "baseFiles": "/upload/files"
+}

+ 155 - 0
server/control/c_base.js

@@ -0,0 +1,155 @@
+const {handle, handleAll} = require('../tools/handle_cjs');
+const d_base = require("../database/d_base");
+const codeMap = require("../map/rcodeMap");
+const dbField = require("../map/dbField");
+const {searchHandle} = require("../tools/searchSql");
+const {isEmpty} = require("../tools/typeTool_cjs");
+const config_path = require("../configs/path");
+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");
+const log = require("../logger").logger("c_base","info");
+
+async function getCarousel(){
+  let err,res;
+  [err,res] = await handle(d_base.getCarousel());
+  // console.log(res);
+  if(err){
+    return [err,null];
+  }
+  // 路径转换
+  res = res.map(item=>{
+    item.filePath = filePathToUrl(item.fileType,item.filePath);
+    return item;
+  });
+  return [null,res];
+}
+
+/**
+ * 文件上传
+ * @param type
+ * @param files
+ * @returns {Promise<[{eMsg: string, eCode: number},null]|*[][]|[{eMsg: string, eCode: *},null]|*[]>}
+ */
+async function uploadFile(type, files){
+  // 文件类型一般不会被改变,只移动文件至对应的存储目录,并将路径存储到数据库中.只存储文件名
+  let err,res,newFileName;
+  let uploadPath = config_path.files;
+  let fileNameArr = [];// 文件存储路径数组
+  // 文件转存,使用newFileName作为路径
+  if(type === dbField.db_base.fileType.image){
+    uploadPath = config_path.images;
+  }else if(type === dbField.db_base.fileType.video){
+    uploadPath = config_path.videos;
+  }
+  let keys = Object.keys(files);
+  // 遍历转移文件
+  for(let fileKey of keys){
+    let file = files[fileKey];
+    // 判断当前文件是否为数组
+    if(!file.newFilename && file.length){
+      for(let f of file){
+        // console.log(f);
+        // console.log(f.newFilename);
+        log.info(`[文件上传] 开始移动文件 ${f.newFilename} to ${uploadPath}`);
+        [err,newFileName] = await mvFile(f,uploadPath, f.newFilename);
+        if(err){
+          return [err,null];
+        }
+        // console.log('移动文件成功' + newFileName);
+        fileNameArr.push(newFileName);
+      }
+    }else if(file.newFilename){
+     [err,newFileName] = await mvFile(file,uploadPath,file.newFilename);
+      if(err){
+        return [err,null];
+      }
+      fileNameArr.push(newFileName);
+    }else{
+      // 文件调用异常
+      log.error(`[文件上传] 文件调用异常,fileObj\n: ${JSON.stringify(file)}`);
+      return [
+        {
+          eCode:codeMap.ServerError,
+          eMsg:`文件调用异常`
+        },null];
+    }
+
+  }
+  // 存储至数据库
+  // console.log(type);
+  [err,res] = await handle(d_base.uploadFiles(type,fileNameArr,getUnixTimeStamp()));
+  if(err){
+    log.error(`[文件上传] 文件入库失败 ${err.message}`);
+    return [{eCode: codeMap.SaveError,eMsg: `文件存储失败!!!`},null];
+  }
+  log.info(`[文件上传] 文件上传成功 ${res.affectedRows}`);
+  return [null,fileNameArr];
+}
+
+/**
+ * 搜索文件
+ * @param type
+ * @param key
+ * @param l
+ * @param p
+ * @returns {Promise<err[]|{arr, total: number, limit, page}[]>}
+ */
+async function searchFiles(type = 0, key, l, p){
+  let err,res;
+  let _params = {
+    key: key,
+  }
+  // console.log(type);
+  // type为 数据库值加一,因为数据库中存储值从0开始,而前端显示从1开始
+  if((type - 1) !== dbField.db_base.fileType.all){
+    _params.type = type;
+  }
+  // console.log(_params);
+  [err,res] = await searchHandle('搜索文件失败', d_base.loadFiles, _params, l, p);
+  if(err){
+    log.info(`[文件资源] 加载失败 ${err.eDetail||err.message}`)
+    return [err,null];
+  }
+  // console.log(res);
+  res.arr = res.arr.map(item=>{
+    console.log(item);
+    item.filePath = filePathToUrl(item.fileType,item.filePath);
+    return item;
+  });
+  return [null,res];
+}
+
+
+async function deleteFile(fileId){
+  let err,res,rmRes;
+
+  // 获取文件信息
+  [err,res] = await handle(d_base.getFileById(fileId));
+  if(err){
+    log.info(`[文件资源] 获取文件信息失败 ${err.message}`);
+    return [{eCode:codeMap.ServerError,eMsg:`删除文件失败,无法查找文件`},null];
+  }
+  if(!res.length){
+    log.info(`[文件资源] 获取文件信息失败,无法找到文件`);
+    return [{eCode:codeMap.NotFound,eMsg:`删除文件失败,无法查找文件`},null];
+  }
+  let fileData = res[0];
+  let filePath = filePathToUrl(fileData.fileType,fileData.filePath);
+  [err,res,rmRes] = await handleAll(d_base.deleteFile(fileId),rmFile(filePath));
+  if(err){
+    console.log(err);
+    log.info(`[文件资源] 删除文件${fileId}失败 ${err.message}`);
+    return [{eCode:codeMap.SaveError,eMsg:`删除文件失败`},null];
+  }
+  return [null,true];
+}
+module.exports = {
+  getCarousel,
+  uploadFile,
+  searchFiles,
+  deleteFile
+}
+
+

+ 1 - 1
server/control/c_news.js

@@ -1,5 +1,5 @@
 const {searchHandle} = require('../tools/searchSql');
-const {handle} = require('../tools/handle');
+const {handle} = require('../tools/handle_cjs');
 const codeMap = require("../map/rcodeMap");
 const log = require("../logger").logger("c_solution","info");
 

+ 1 - 1
server/control/c_solution.js

@@ -1,5 +1,5 @@
 const {searchHandle} = require('../tools/searchSql');
-const {handle} = require('../tools/handle');
+const {handle} = require('../tools/handle_cjs');
 const d_solution = require("../database/d_solution");
 const codeMap = require("../map/rcodeMap");
 const log = require("../logger").logger("c_solution","info")

+ 66 - 4
server/control/c_user.js

@@ -1,12 +1,74 @@
-const {handle} = require('../tools/handle');
+const {handle} = require('../tools/handle_cjs');
 const log = require("../logger").logger("c_user","info");
 const d_user = require("../database/d_user");
+const codeMap = require("../map/rcodeMap");
+const dbField = require("../map/dbField");
+const {searchHandle} = require("../tools/searchSql");
 
-function login(owner,passwd){
-  return d_user.login(owner,passwd);
+/**
+ * 检测账号是否合法
+ * @param err
+ * @param res
+ * @returns {[{eMsg: string, eCode: number},null]|*[]}
+ * @private
+ */
+function _checkAccount(err,res){
+  let userData;
+  if(err){
+    log.warn(`[用户登录] 登陆失败 ${err.message}`);
+    return [err,null];
+  }
+  if(!res.length){
+    return [
+      {
+        eCode:codeMap.NotFound,
+        eMsg:`账号或者密码错误`
+      },null];
+  }
+  userData = res[0];
+  if(userData.status === dbField.db_user.status.disable){
+    log.info(`[账号检测] 账号已被禁用`);
+    return [
+      {
+        eCode:codeMap.NotPermission,
+        eMsg:`账号已被禁用`
+      },null];
+  }
+  return [null,userData];
+}
+async function login(owner,passwd){
+  let err,res,userData;
+  [err,res] = await handle(d_user.login(owner,passwd));
+  [err,userData] = _checkAccount(err,res);
+  if(err){
+    return [err,null];
+  }
+  log.info(`[用户登录] 登陆成功 ${userData.name}`)
+  return [null,userData];
+}
+
+async function loadAccount(accountId,p, l){
+  let err,res;
+  let userData;
+  let _params = {};
+  p = p || 1;
+  l = l || 10;
+  // 判断账号是否存在
+  [err,res] = await handle(d_user.checkAccount(accountId));
+  [err,userData] = _checkAccount(err,res);
+  if(err){
+    return [err,null];
+  }
+  return await searchHandle(
+    '加载账号失败',
+    d_user.loadAccounts,
+    _params,
+    p,
+    l);
 }
 
 
 module.exports = {
-    login
+    login,
+    loadAccount
 }

+ 2 - 2
server/control/product.js

@@ -1,6 +1,6 @@
 const d_product = require('../database/d_product');
 const {searchHandle} = require('../tools/searchSql');
-const {handle} = require('../tools/handle');
+const {handle} = require('../tools/handle_cjs');
 const codeMap = require("../map/rcodeMap");
 const log = require("../logger").logger("c_product","info")
 /**
@@ -43,7 +43,7 @@ async function getProductInfo(id)
         eMsg:`无法找到对应产品`
       },null]
   }
-
+  console.log(data);
   return [null,data];
 }
 

+ 90 - 0
server/database/d_base.js

@@ -0,0 +1,90 @@
+const mysql = require('./mysql');
+const {searchSql,limitSql} = require("../tools/searchSql");
+const {isEmpty} = require("../tools/typeTool_cjs");
+const log = require("../logger").logger("d_base","info");
+
+/**
+ * table carousel
+ * id 主键
+ * sort 排序
+ * image 图片地址
+ * type 1: 文章 2: 产品 3: 直接链接
+ * link 文章id 产品id 链接
+ */
+
+/**
+ * 获取轮播图
+ * @returns {Promise | Promise<unknown>}
+ */
+function getCarousel(){
+  let sql = `SELECT c.*,f.filePath,f.fileType,f.fileId
+            FROM
+                hfy_carousel as c,
+                hfy_files as f
+            WHERE c.fileId = f.fileId`;
+  return mysql.pq(sql);
+}
+
+function uploadFiles(type,fileNameArr,uploadTime){
+  let sql = `INSERT INTO hfy_files (fileType, filePath, uploadTime)
+                    VALUES ${fileNameArr.map(f=>`(?,?,?)`).join(',')}`;
+  let values = [];
+  for(let fileName of fileNameArr){
+    values.push(type);
+    values.push(fileName);
+    values.push(`${uploadTime}`);
+  }
+  console.log(sql);
+  console.log(values);
+  return mysql.pq(sql,values);
+}
+
+/**
+ * 搜索文件数据
+ * @param type
+ * @param _params
+ * @param p
+ * @param l
+ * @returns {*}
+ */
+function loadFiles(type = 'array',_params,p,l){
+  let sql = ``;
+  let values = [];
+  if(isEmpty(_params)){
+    _params = {};
+  }
+  if(type === 'count'){
+    sql = `select count(*) as total `;
+  }else {
+    sql = `select * `;
+  }
+  sql += `from hfy_files as f
+    where 1 = 1
+  `
+  if(_params.key){
+    sql += ` and ( f.name like '%${_params.key}% or f.tags like '%${_params.key}%' )`
+  }
+  if(_params.type){
+    sql += ` and f.fileType = ? `;
+    values.push(_params.type - 1);
+  }
+  sql += ` order by f.uploadTime desc `;
+  return searchSql(mysql.pq,type,sql,values,l,p);
+}
+
+function getFileById(fileId){
+  let sql = `select * from hfy_files where fileId = ?`;
+  return mysql.pq(sql,[fileId]);
+}
+
+function deleteFile(fileId){
+  let sql = `delete from hfy_files where fileId = ? limit 1`;
+  return mysql.pq(sql,[fileId]);
+}
+module.exports = {
+  getCarousel,
+  uploadFiles,
+  loadFiles,
+  getFileById,
+  deleteFile,
+}

+ 72 - 2
server/database/d_user.js

@@ -1,11 +1,81 @@
 const mysql = require('./mysql');
 const {searchSql,limitSql} = require("../tools/searchSql");
+const {isEmpty} = require("../tools/typeTool_cjs");
 const log = require("../logger").logger("d_user","info");
-
+const dbField = require("../map/dbField");
 function login(account,passwd){
+  let sql,values;
+  sql = `SELECT m.id,m.name,m.type,m.status,m.enableNewPasswd
+           FROM hfy_manager as m
+           WHERE name = ? AND password = ?
+           AND enableNewPasswd = ? `;
+  log.debug(`[用户登录] 登陆账号:${account} 密码:${passwd}`);
+  log.debug(sql);
+  values = [account,passwd,dbField.db_user.enableNewPasswd.true];
+  return mysql.pq(sql,values);
+}
+
+function addAccount(account,passwd){
+  let sql,values;
+  sql = `INSERT INTO hfy_manage (name,passwd) VALUES (?,?)`;
+  values = [account,passwd];
+  return mysql.pq(sql,values);
+}
+
+function checkAccount(id){
+  let sql,values;
+  sql = `SELECT
+            m.id,
+            m.name,
+            m.type,
+            m.status,
+          FROM hfy_manager as m
+          WHERE id = ?
+          limit 1`;
+  values = [id];
+  return mysql.pq(sql,values);
+}
 
+/**
+ * 加载账号
+ * @param type array|count
+ * @param _params key
+ * @param p page
+ * @param l limit
+ * @returns {*}
+ */
+function loadAccounts(type='array',_params,p,l){
+  let sql = ``;
+  let values = [];
+  if(isEmpty(_params)){
+    _params = {};
+  }
+  if(type === 'count'){
+    sql = `select count(*) as total `;
+  }else {
+    sql = `select
+    m.id,
+    m.name,
+    m.type,
+    m.status,
+    m.createTime,
+    m.updateTime
+    `;
+  }
+  sql += `
+    from
+    hfy_manager as m
+    where 1 = 1
+  `
+  if(_params.key){
+    sql += ` and m.name like '%${_params.key}%'`
+  }
+  return searchSql(mysql.pq,type,sql,values,l,p);
 }
 
 module.exports = {
-  login
+  login,
+  checkAccount,
+  addAccount,
+  loadAccounts,
 }

+ 1 - 0
server/database/mysql.js

@@ -20,6 +20,7 @@ function query(sql, values, cb) {
         // log.debug(`querySQL:${sql}  QueryValues:[${values.join(',')}]`)
         conn.query(sql, values, cb);
         conn.release();
+        conn.end();
     })
 } //简写部分代码
 

+ 2 - 1
server/database/pool.js

@@ -16,7 +16,8 @@ const pool = mysql.createPool({
     host: databseConfig.host, //地址
     user: databseConfig.user, //用户
     password: databseConfig.password, // 密码
-    database: databseConfig.database // 数据库名称
+    database: databseConfig.database, // 数据库名称
+    multipleStatements: true,// 允许多条sql同时执行
 }); // 创建连接池对象
 
 

+ 11 - 1
server/index.js

@@ -3,9 +3,17 @@ const session = require('express-session');
 const bodyParser = require('body-parser');
 
 const router = require('./router/index');
+const config_path = require('./configs/path');
 const app = express();
 const log = require('./logger').logger('app', 'info');
 
+// app.static(config_path.files);
+// app.static(config_path.images);
+// app.static(config_path.videos);
+app.use(config_path.baseFiles, express.static(config_path.files));
+app.use(config_path.baseImages, express.static(config_path.images));
+app.use(config_path.baseVideos, express.static(config_path.videos));
+
 app.use(
   session({
     secret: 'hfy',
@@ -23,13 +31,15 @@ app.use(bodyParser.json({ limit: '10mb' }));
 
 app.use((req, res, next)=>{
   log.info(`${req.method} To ${req.url}`);
+  // 添加时间,用于记录日志
+  res.queryTime = new Date();
   next();
 })
 
 
 app.use('/api',router)
 app.get('/hello', (req, res) => {
-    res.send('Hello World!');
+    res.send('合方圆! 成熟可靠!');
 });
 
 

+ 29 - 0
server/map/dbField.js

@@ -0,0 +1,29 @@
+const db_user = {
+  // 是否启用新密码存储方式
+  enableNewPasswd: {
+    true: 1,
+    false: 0,
+  },
+  // 用户状态
+  status: {
+    // 正常
+    normal: 1,
+    // 禁用
+    disable: 0,
+  }
+}
+
+const db_base = {
+  // 文件类型
+  fileType: {
+    all: -1,
+    other: 0,
+    image: 1,
+    video: 2,
+  }
+}
+
+module.exports = {
+  db_base,
+  db_user
+}

+ 8 - 0
server/map/progressField.js

@@ -0,0 +1,8 @@
+
+module.exports = {
+    // session_hfy:'hfySession',// 合方圆管理人员账户
+    session_hfy:'owner',//
+    loginUrl:{
+      owner:'/manage/login',
+    }
+}

+ 49 - 0
server/middleware/checkSession.js

@@ -0,0 +1,49 @@
+/*
+ * @Description: 检查用户是否已经登陆
+ * @Autor: kindring
+ * @Date: 2021-12-22 16:24:37
+ * @LastEditors: kindring
+ * @LastEditTime: 2022-01-26 14:07:50
+ * @LastDescript:
+ */
+const progressField = require('../map/progressField');
+const {notLogin} = require('../tools/result');
+const log = require('../logger').logger('checkLogin', 'info');
+/**
+ * 检查是否登录
+ * @param sessionField
+ * @param {'json'|'view'} type 返回的数据类型
+ * @returns
+ */
+function checkLogin(
+  sessionField = progressField.session_hfy,
+  type = 'json') {
+  return function(req, res, next) {
+    let session = req.session[sessionField]
+    if (!session) {
+      log.warn(`未登陆,请求接口,该接口验证类型${type}`)
+      // 类型
+      let resAction;
+      switch (type) {
+        case 'view':
+          resAction = {
+            action: 'redirect',
+            params: [302, progressField.loginUrl[sessionField]]
+          }
+          break;
+        case 'json':
+        default:
+          return notLogin(res, '请登录后在进行尝试');
+          break;
+      }
+      return res[resAction.action](...resAction.params)
+    }
+
+    req.session._garbage = Date();
+    req.session.touch();
+    next();
+  }
+}
+
+
+module.exports = checkLogin;

+ 80 - 0
server/middleware/upload.js

@@ -0,0 +1,80 @@
+const path = require('path');
+const fs = require('fs');
+const formidable = require('formidable')
+const {handle} = require("../tools/handle_cjs");
+const result = require("../tools/result");
+const log = require("../logger").logger("uploadMiddleware","info");
+
+
+module.exports = function (config){
+  if(!config){
+    throw new Error('config is undefined');
+  }
+  let _config = {
+    tmp: config.tmp || './tmp',
+    maxFileSize: config.maxFileSize || (1024*1024*2),// 默认2mb
+  };
+
+  return async function upload(req,res,next){
+    log.info(`[文件上传] 开始接收上传文件`)
+    let form = formidable.formidable({
+      uploadDir: _config.tmp,
+      multiples: true,
+      keepExtensions: true,
+      type: true,
+      maxFileSize: _config.maxFileSize,
+      filename(_fileName, _extName) {
+        // console.log('---------')
+        // console.log(_fileName+'.'+_extName);
+        // console.log('---------')
+        let time = new Date();
+        // 生成随机数
+        let random = Math.floor(Math.random() * 1000);
+        // 生成随机字符
+        let str = 'abcdefghijklmnopqrstuvwxyz';
+        let randomStr = '';
+        let fileName = '';
+        for (let i = 0; i < 5; i++) {
+          randomStr += str.charAt(Math.floor(Math.random() * str.length));
+        }
+        fileName = `${time.getTime()}-${random}-${randomStr}-${_fileName}${_extName}`;
+        // // 随机从fileName中抽取2-6个字符添加至随机位置
+        // let randomIndex = Math.floor(Math.random() * (fileName.length - 2)) + 1;
+        // let randomLength = Math.floor(Math.random() * 5) + 2;
+        // let randomStr2 = '';
+        // for (let i = 0; i < randomLength; i++) {
+        //   randomStr2 += str.charAt(Math.floor(Math.random() * str.length));
+        // }
+        // fileName = fileName.slice(0, randomIndex) + randomStr2 + fileName.slice(randomIndex);
+        return fileName;
+      }
+    });
+    let allFile = [];
+    let [err] = await handle(fs.promises.mkdir(_config.tmp));
+    if(!err){
+      // res.status(500);
+      console.log(err);
+      log.info(`[文件上传] 无法存储文件 ${err.message}`);
+      result.ServerError(res, err, `无法存储文件 ${err.message}`);
+      return;
+    }
+    await form.on('progress', function (bytesReceived, bytesExpected) { //在控制台打印文件上传进度
+      let progressInfo = {
+        value: bytesReceived, //当前进度
+        total: bytesExpected //总进度
+      };
+      let bar_progress = Math.floor((progressInfo.value / progressInfo.total) * 100);
+      // console.log(`当前进度: ${bar_progress}`);
+    }).parse(req, function (err, fields, files) {
+      if (err) {
+        // res.status(500);
+        log.info(`[文件上传] 文件上传失败 ${err.message}`);
+        result.ServerError(res, err, `文件上传失败 ${err.message}`);
+      } else {
+        req.files = files;
+        req.body = fields;
+        next();
+      }
+    })
+  }
+};

+ 3 - 3
server/router/captcha.js

@@ -4,7 +4,7 @@
  * @Date: 2022-01-17 17:52:23
  * @LastEditors: kindring
  * @LastEditTime: 2022-01-26 17:36:02
- * @LastDescript: 
+ * @LastDescript:
  */
 const router = require('express').Router();
 const svgCaptcha = require('svg-captcha');
@@ -18,9 +18,9 @@ router.get('/', async(req, res) => {
         color: true, // 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
         // background: '#cc9966', // 验证码图片背景颜色
     });
-    console.log('test captcha')
+    // console.log('test captcha')
     req.session.captcha = captcha.text.toLowerCase();
-    console.log(req.session.captcha);
+    // console.log(req.session.captcha);
     res.type('svg');
     res.status(200).send(captcha.data);
 });

+ 1 - 0
server/router/index.js

@@ -16,5 +16,6 @@ router.use('/product',r_product);
 router.use('/solution',r_solution);
 router.use('/news',r_news);
 router.use('/user',r_user);
+router.use('/base',require('./r_base'));
 
 module.exports =  router ;

+ 99 - 0
server/router/r_base.js

@@ -0,0 +1,99 @@
+const router = require('express').Router();
+const {paramFail, ServerError, success, controlError, searchSuccess} = require("../tools/result");
+const c = require("../control/c_base");
+const typeTool = require("../tools/typeTool_cjs");
+const decode = require("../tools/decode_cjs");
+const time = require("../tools/time_cjs");
+const {toSqlString} = require("../tools/searchSql");
+const progressField = require('../map/progressField');
+const {isEmpty} = require("../tools/typeTool_cjs");
+const checkLogin = require("../middleware/checkSession");
+const upload = require("../middleware/upload");
+const config_path = require("../configs/path");
+const log = require("../logger").logger("r_base","info");
+
+router.get('/carousel', checkLogin(progressField.session_hfy), async (req, res) => {
+  try{
+    let [err, data] = await c.getCarousel();
+    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;
+    // 从数据库中读取数据.
+    let {key, l, p, type} = req.query;
+    console.log(req.query);
+    console.log(type);
+    [err, result] = await c.searchFiles(type, key, p, l);
+    if(err){
+      log.info(`[文件资源] 加载失败 ${err.message}`);
+      return controlError(res, err, null);
+    }
+    console.log(result);
+    searchSuccess(res,
+      result.arr,
+      result.total,
+      result.page,
+      result.limit,
+    );
+  }catch (e){
+    console.log(e)
+    ServerError(res, e, `[文件资源] 加载失败 ${e.message}`);
+  }
+})
+router.post('/fileUp',checkLogin(progressField.session_hfy) ,upload({
+  tmp:config_path.tmp,
+  maxFileSize: 1024*1024*10,// 10M
+}),async (req,res)=>{
+  try{
+    let err, data;
+    let files = req.files;
+    let type = req.body.type;
+    if(!type){
+      type = req.query.type;
+    }
+    // console.log(type);
+    type = typeTool.toNumber(type);
+    // console.log(type);
+    [err, data] = await c.uploadFile(type, files);
+    if(err){
+      log.warn(`[文件上传] 上传文件失败 ${err.eMsg||err.message}`);
+      controlError(res, err,`上传文件失败 ${err.eMsg||err.message}`);
+    }
+    // 返回地址数组
+    return success(res, data);
+  }catch (e){
+    console.log(e);
+    ServerError(res, e, `上传文件异常 ${e.message}`);
+  }
+});
+
+router.delete('/file/:fileId',checkLogin(progressField.session_hfy),async (req,res)=>{
+  try{
+    let err, data;
+    let fileId = req.params.fileId;
+    if(!fileId){
+      return paramFail(res, '缺少参数');
+    }
+    fileId = typeTool.toNumber(fileId);
+    [err, data] = await c.deleteFile(fileId);
+    if(err){
+      log.warn(`[文件删除] 删除文件失败 ${err.eMsg||err.message}`);
+      return controlError(res, err,`删除文件失败 ${err.eMsg||err.message}`);
+    }
+    return success(res, data);
+  }catch (e) {
+    console.log(e);
+    ServerError(res, e, `删除文件移除 ${e.message}`);
+  }
+})
+
+
+module.exports =  router;

+ 1 - 1
server/router/r_news.js

@@ -1,7 +1,7 @@
 const router = require('express').Router();
 const {paramFail, ServerError, success, controlError, searchSuccess} = require("../tools/result");
 const c_solution = require("../control/c_solution");
-const typeTool = require("../tools/typeTool");
+const typeTool = require("../tools/typeTool_cjs");
 const c = require("../control/c_news");
 const log = require("../logger").logger("r_news","info");
 

+ 3 - 1
server/router/r_product.js

@@ -2,7 +2,7 @@ const router = require('express').Router();
 const {paramFail, ServerError, success, controlError, searchSuccess} = require("../tools/result");
 const c = require('../control/product');
 const log = require("../logger").logger("r_product","info")
-const typeTool = require('../tools/typeTool');
+const typeTool = require('../tools/typeTool_cjs');
 
 /**
  * 加载产品列表,根据类型
@@ -33,6 +33,7 @@ router.get(
   '/search',
   async (req, res) => {
       try{
+        log.info(`[搜索产品] `)
         let err, result;
         let {key, l, p, type} = req.query;
         type = type || 'all';
@@ -41,6 +42,7 @@ router.get(
         log.info(`page=${p},limit=${l}`);
         [err, result] = await c.searchProduct(type, key, p, l);
         if(err){
+          log.info(`[搜索产品] err=${err}`);
           return controlError(res, err, null);}
         log.info(`result len=${result.arr.length}`)
         searchSuccess(res,

+ 1 - 1
server/router/r_solution.js

@@ -1,7 +1,7 @@
 const router = require('express').Router();
 const {paramFail, ServerError, success, controlError, searchSuccess} = require("../tools/result");
 const c = require("../control/c_solution");
-const typeTool = require("../tools/typeTool");
+const typeTool = require("../tools/typeTool_cjs");
 const log = require("../logger").logger("r_solution","info");
 
 

+ 69 - 24
server/router/r_user.js

@@ -1,16 +1,27 @@
 const router = require('express').Router();
 const {paramFail, ServerError, success, controlError, searchSuccess} = require("../tools/result");
 const c = require("../control/c_user");
-const typeTool = require("../tools/typeTool");
-const time = require("../../until/time");
+const typeTool = require("../tools/typeTool_cjs");
+const decode = require("../tools/decode_cjs");
+const time = require("../tools/time_cjs");
 const {toSqlString} = require("../tools/searchSql");
-const log = require("../logger").logger("r_solution","info");
+const progressField = require('../map/progressField');
+const {isEmpty} = require("../tools/typeTool_cjs");
+const checkLogin = require("../middleware/checkSession");
+const log = require("../logger").logger("r_user","info");
 router.post('/login', async (req, res) => {
   try{
-    let err, userData;
-    let {account, passwd, captcha} = req.body;
-    if(!account || !passwd || !captcha){
-      paramFail(res, "account or password or captcha is required");
+    // let isOk = null;
+    let loginData = {};
+    // let {owner, passwd, captcha} = req.body;
+    let body = req.body;
+    if(isEmpty(body)){
+      paramFail(res, "body is required");
+      return;
+    }
+    let {owner, passwd, captcha} = req.body;
+    if(!owner || !passwd || !captcha){
+      paramFail(res, "owner or password or captcha is required");
       return;
     }
     // 检查验证码
@@ -18,39 +29,73 @@ router.post('/login', async (req, res) => {
 
     // 转义解码参数
     let safePasswd = toSqlString(passwd);
-    account = toSqlString(account);
-    passwd = decode.decodePasswd(passwd);
-
-
+    owner = toSqlString(owner);
 
+    // 解密模块.
+    passwd = decode.decodePasswd(passwd);
+    // 转为数据库存储数据
+    passwd = decode.passwdToSave(passwd);
     // 生成登陆记录
-    let loginData = {
+    loginData = {
       loginIp: req.ip.match(/\d+\.\d+\.\d+\.\d+/),
-      account: account,
+      account: owner,
       passwd: safePasswd,
       loginTime: time.getUnixTimeStamp(),
       isLogin: false
     }
+
     // 加密模块
     // 尝试查找账户
-    [err,userData] = await c.login(account, passwd);
+    let [err,userData] = await c.login(owner, passwd);
     if(err){
-      log.warn(`[用户登录] 登陆失败 ${err.message}`);
-      paramFail(res, `登陆失败 ${err.message}`);
+      console.log(err);
+      log.warn(`[用户登录] 登陆失败 ${err.eMsg||err.message}`);
+      controlError(res, err,`登陆失败 ${err.eMsg||err.message}`);
     }else{
+      console.log(userData);
       loginData.isLogin = true;
-      loginData.userId = userData['userId'];
+      loginData.userId = userData['id'];
       // 存储用户状态到session
-      // req.session[progressField.session_hfy] = userData;
-      // res.json({
-      //   rcode: codeMap.ok,
-      //   data: userData
-      // });
+      req.session[progressField.session_hfy] = userData;
+      success(res, userData);
     }
-    let [Err,isOk] = await c_user.saveLoginData(loginData);
+    // todo 登录日志
+    // [err,isOk] = await c_user.saveLoginData(loginData);
+  }catch (e) {
+    console.log(e);
+    ServerError(res, null, e.message);
+  }
+});
+router.post('/loadAccounts', checkLogin(progressField.session_hfy), async (req, res) => {
+  try{
+    let err, result;
+    let {key, l, p, type} = req.query;
+    let account = req.session[progressField.session_hfy];
+    type = type || 'all';
+    l = typeTool.toNumber(l);
+    p = typeTool.toNumber(p);
+    [err, result] = await c.loadAccount(account.userId, p, l);
+    if(err){
+      log.info(`[用户列表] 加载失败 ${err.message}`);
+      return controlError(res, err, null);}
+    searchSuccess(res,
+      result.arr,
+      result.total,
+      result.page,
+      result.limit,
+    );
   }catch (e) {
     ServerError(res, null, e.message);
   }
 });
 
-module.exports =  router ;
+router.get('/logout', async (req, res) => {
+  try{
+    req.session[progressField.session_hfy] = null;
+    success(res, null);
+  }catch (e) {
+    console.log(e);
+    ServerError(res, null, e.message);
+  }
+});
+module.exports =  router;

+ 54 - 0
server/tools/decode_cjs.js

@@ -0,0 +1,54 @@
+// 加解密模块
+const crypto = require('crypto');
+
+function md5(str) {
+    let md5 = crypto.createHash('md5');
+    md5.update(str);
+    return md5.digest('hex');
+}
+
+// 将密码原文转为加密字符串
+// 获取md5摘要, 获取前面2位与后面2位, 反转首尾, 生成新的md5摘要, 重新将前后两位添加至首尾
+
+/**
+ * 将密码原文转为数据库存储字符串
+ * @param passwdStr 密码原文
+ * @returns {string}
+ * @constructor
+ * 获取md5摘要, 获取前面2位与后面2位, 反转首尾, 生成新的md5摘要, 重新将前后两位添加至首尾
+ */
+function passwdToSave(passwdStr) {
+  let _passwd = '';
+  let _passwdHead = '';
+  let _passwdTail = '';
+  // 获取md5摘要
+  _passwd = md5(passwdStr);
+  // 获取前面2位与后面2位
+  _passwdHead = _passwd.substring(0, 2);
+  _passwdTail = _passwd.substring(-2);
+  // 反转首尾
+  _passwd = _passwd.split('').reverse().join('');
+  // 生成新的md5摘要
+  _passwd = md5(_passwd);
+  // 重新将前后两位添加至首尾
+  _passwd = _passwdHead + _passwd + _passwdTail;
+  return _passwd;
+}
+
+/**
+ * 将加密字符串转为密码原文
+ * @param passwdStr
+ * @returns {*|string}
+ */
+function decodePasswd(passwdStr) {
+  let _passwd = '';
+  // 反转首尾
+  _passwd = passwdStr.split('').reverse().join('');
+  return _passwd;
+}
+
+module.exports = {
+    md5,
+    decodePasswd,
+    passwdToSave
+}

+ 20 - 0
server/tools/filePathTool.js

@@ -0,0 +1,20 @@
+const config_path = require("../configs/path.json");
+const dbField = require("../map/dbField");
+const {toNumber} = require("./typeTool_cjs");
+
+function filePathToUrl(fileType, fileName){
+  let basePath = config_path.baseFiles;
+  fileType = toNumber(fileType);
+  if(fileType === dbField.db_base.fileType.image){
+    basePath = config_path.baseImages;
+  }else if(fileType === dbField.db_base.fileType.video){
+    basePath = config_path.baseVideos;
+  }else{
+    basePath = config_path.baseFiles;
+  }
+  return `${basePath}/${fileName}`;
+}
+
+module.exports = {
+  filePathToUrl
+}

+ 13 - 10
server/tools/handle.js → server/tools/handle_cjs.js

@@ -1,19 +1,22 @@
 /*
- * @Description: 
+ * @Description:
  * @Autor: kindring
  * @Date: 2021-12-14 15:19:56
  * @LastEditors: kindring
  * @LastEditTime: 2021-12-14 17:17:09
- * @LastDescript: 
+ * @LastDescript:
  */
-function handle(promise) {
+function handle_cjs(promise) {
     return new Promise(resolve => {
         try{
-            promise.then(val => {
-                resolve([null, val])
-            }).catch(err => {
-                resolve([err])
-            })
+          if(!promise || typeof promise.then !== 'function'){
+            return resolve([null,promise]);
+          }
+          promise.then(val => {
+              resolve([null, val])
+          }).catch(err => {
+              resolve([err])
+          })
         }catch(err){
             resolve([err])
         }
@@ -26,7 +29,7 @@ function handleAll() {
         try{
             // arguments 转数组
             let arr = [...arguments]
-            console.log(arr)
+            // console.log(arr)
             // console.log(typeof arr)
             Promise.all(arr).then(val => {
                 console.log(val);
@@ -43,6 +46,6 @@ function handleAll() {
 }
 
 module.exports = {
-    handle,
+    handle: handle_cjs,
     handleAll
 };

+ 20 - 1
server/tools/result.js

@@ -1,6 +1,15 @@
 const rCode = require('../map/rcodeMap');
 const log = require('../logger').logger('resultHandle','info');
+
+// 计算耗时 res.queryTime
+function comTime(res,msg){
+  let time = new Date() - res.queryTime;
+  log.info(`[queryInfo] ${msg} Time consuming: ${time}ms`);
+}
+
+
 function success(res,data) {
+  comTime(res,'success');
   res.json({
     code: rCode.OK,
     data: data
@@ -8,6 +17,7 @@ function success(res,data) {
 }
 
 function searchSuccess(res, data, total, page, limit) {
+  comTime(res,'searchSuccess');
   res.json({
     code: rCode.OK,
     data: data,
@@ -19,7 +29,8 @@ function searchSuccess(res, data, total, page, limit) {
 }
 
 function ServerError(res,code,msg) {
-  log.error(`result to server error ${msg}`)
+  comTime(res,'ServerError');
+  log.error(`result to server error ${msg}`);
   res.json({
     code: code?code:rCode.ServerError,
     msg: msg
@@ -27,6 +38,7 @@ function ServerError(res,code,msg) {
 }
 
 function paramFail(res,msg) {
+  comTime(res,'paramFail');
   res.json({
     code: rCode.NotParam,
     msg: msg
@@ -34,6 +46,7 @@ function paramFail(res,msg) {
 }
 
 function notLogin(res,msg) {
+  comTime(res,'notLogin');
   res.json({
     code: rCode.NotLogin,
     msg: msg
@@ -41,6 +54,7 @@ function notLogin(res,msg) {
 }
 
 function notPermission(res,msg) {
+  comTime(res);
   res.json({
     code: rCode.NotPermission,
     msg: msg
@@ -48,6 +62,7 @@ function notPermission(res,msg) {
 }
 
 function customError(res,msg) {
+  comTime(res,'customError');
   res.json({
     code: rCode.CustomError,
     msg: msg
@@ -58,11 +73,14 @@ function controlError(res,err,msg) {
   let errorCode,errorMsg;
   if(msg){ errorMsg = msg }
   if(err){
+    console.log(err);
     if(err.eCode){
       errorCode = err.eCode;
     }
     if(!msg && err.eMsg){
       errorMsg = err.eMsg;
+    }else if(msg){
+      errorMsg = msg;
     }else{
       errorMsg = err.message;
     }
@@ -100,6 +118,7 @@ module.exports = {
   success,
   searchSuccess,
   ServerError,
+  notLogin,
   paramFail,
   controlError,
 }

+ 74 - 0
server/tools/saveFiles_cjs.js

@@ -0,0 +1,74 @@
+const fs = require('fs');
+const path = require('path');
+const {handle} = require("./handle_cjs");
+async function mvFile(file, targetPath, newFileName, maxRename = 10 , renameTotal = 0){
+  try {
+    // 判断路径是否存在,不存在则创建
+    let err, exists;
+    [err] = await handle(fs.promises.mkdir(targetPath, {recursive: true}));
+    if (err) {
+      return [err, null];
+    }
+    // 拼接新的文件路径
+    let newFilePath = path.join(targetPath, newFileName);
+    let ext = null;
+    let name = null;
+    let newName = '';
+    // 判断文件是否存在
+    [err, exists] = await handle(fs.existsSync(newFilePath));
+    if (err) {
+      return [err, null];
+    }
+    // 如果文件存在,则重命名在最后加上 _n n为数字
+    if (exists) {
+      renameTotal++;// 1
+      if (renameTotal >= maxRename) {
+        return [{message: `重命名次数超过最大值`}, null];
+      }
+      ext = path.extname(newFileName);
+      name = path.basename(newFileName, ext)
+      newName = `${name}_${renameTotal}${ext}`;
+      newFilePath = path.join(targetPath, newName);
+      [err, exists] = await handle(fs.existsSync(newFilePath));
+      if (err) {
+        return [err, null];
+      }
+      if(exists){
+        return await mvFile(file, targetPath, newFileName, maxRename, renameTotal);
+      }
+    }
+    // console.log(file);
+    // 移动文件
+    [err] = await handle(fs.promises.rename(file.filepath, newFilePath));
+    if (err) {
+      return [err, null];
+    }
+    return [null, newName || newFileName];
+  } catch (e) {
+    return [e, null];
+  }
+
+}
+
+async function rmFile(filePath){
+  // 判断是否为文件夹
+  let err, stats ,res;
+  [err, stats] = await handle(fs.promises.stat(filePath));
+  if(err){
+    return [err,null];
+  }
+  if(stats.isDirectory()){
+    return [{message:`${filePath} 为文件夹`},null];
+  }
+  // 删除文件
+  [err,res] = await handle(fs.promises.unlink(filePath));
+  if(err){
+    return [err,null];
+  }
+  return [null,res];
+}
+
+module.exports = {
+  mvFile,
+  rmFile
+}

+ 3 - 5
server/tools/searchSql.js

@@ -1,5 +1,5 @@
-const {toNumber} = require("../tools/typeTool");
-const {handle} = require("./handle");
+const {toNumber} = require("./typeTool_cjs");
+const {handle} = require("./handle_cjs");
 const codeMap = require("../map/rcodeMap");
 function limitSql (limit = 20,page = 1){
   let sql = '';
@@ -36,10 +36,8 @@ async function searchHandle(errorText,dbFn,_params,page,limit){
         [err,response] = await handle(Promise.all([arrPromise]))
     }
     if(err){
-
-        console.log(err);
         return [
-          {eCode:codeMap.ServerError,eMsg:`${errorText}`},
+          {eCode:codeMap.ServerError,eMsg:`${errorText}`,eDetail:err.message},
           null
         ]
     }

+ 77 - 0
server/tools/time_cjs.js

@@ -0,0 +1,77 @@
+/**
+ * 时间戳转时间字符串
+ * @param timestamp
+ * @returns {string}
+ */
+function timestampToTime(timestamp) {
+  timestamp = timestamp.toString().length === 10 ? timestamp * 1000 : timestamp;
+  let date = new Date(timestamp); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+  let Y = date.getFullYear() + '-';
+  let M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
+  let D = date.getDate() + ' ';
+  let h = date.getHours() + ':';
+  let m = date.getMinutes() + ':';
+  let s = date.getSeconds();
+  return Y+M+D+h+m+s;
+}
+
+/**
+ * 时间字符串转时间戳
+ * @param timeStr
+ * @param isSecond
+ * @returns {number}
+ */
+function timeStrToTimeStamp(timeStr,isSecond){
+  let date = new Date(timeStr);
+  if(isSecond){
+    return date.getTime()/1000;
+  }else{
+    return date.getTime();
+  }
+}
+
+/**
+ * 时间戳转时间字符串
+ * @param time 时间戳
+ * @param format 格式 yyyy-MM-dd HH:mm:ss
+ * @returns {*} 格式化后的时间字符串
+ */
+function timeFormat(time, format) {
+  let t = new Date(time);
+  let tf = function(i) {
+    return (i < 10 ? '0' : '') + i
+  };
+  return format.replace(/yyyy|MM|dd|HH|mm|ss/g, function(a) {
+    switch (a) {
+      case 'yyyy':
+        return tf(t.getFullYear());
+        break;
+      case 'MM':
+        return tf(t.getMonth() + 1);
+        break;
+      case 'mm':
+        return tf(t.getMinutes());
+        break;
+      case 'dd':
+        return tf(t.getDate());
+        break;
+      case 'HH':
+        return tf(t.getHours());
+        break;
+      case 'ss':
+        return tf(t.getSeconds());
+        break;
+    }
+  })
+}
+
+function getUnixTimeStamp(){
+  return Math.round(new Date().getTime()/1000);
+}
+
+module.exports = {
+  timestampToTime,
+  timeStrToTimeStamp,
+  timeFormat,
+  getUnixTimeStamp
+}

+ 12 - 1
server/tools/typeTool.js → server/tools/typeTool_cjs.js

@@ -50,9 +50,20 @@ function isObject(v){
     return v instanceof Object
 }
 
+// 判断 obj 或者 arr 是否为空
+function isEmpty(v){
+    if(isArray(v)){
+        return v.length === 0
+    }else if(isObject(v)){
+        return Object.keys(v).length === 0
+    }else{
+        return !v
+    }
+}
 
 module.exports = {
     toNumber,
     toString,
-    toBoolean
+    toBoolean,
+    isEmpty
 }


+ 3 - 3
store/index.js

@@ -5,10 +5,10 @@ export const modules = {
   index: {
     actions : {
       async nuxtServerInit ({ commit }, { req }) {
-
+        console.log('nuxtServerInit');
         if(req.session){
-          if (req.session.user) {
-            commit('login/'+login_types.mutations.SET_USER_INFO, req.session.user)
+          if (req.session.owner) {
+            commit('login/'+login_types.mutations.userLogin, req.session.owner)
           }
           if (req.session.captcha) {
             // 更新login 模块的captcha

+ 1 - 0
tailwind.config.js

@@ -1,4 +1,5 @@
 /** @type {import('tailwindcss').Config} */
+
 module.exports = {
   content: [],
   theme: {

+ 0 - 2
until/FcCrypto/FcCrypto.js

@@ -1,2 +0,0 @@
-// 加密模块,使用
-::

+ 63 - 63
until/business.js

@@ -1,5 +1,5 @@
 // 统一处理业务逻辑
-import code from '~/map/rcodeMap'
+import code from '~/map/rcodeMap_esm'
 let rcodeHandleMap = {
 };
 function registerHandle(rcode,handle){
@@ -17,68 +17,68 @@ function toLogin(){
 
 
 let defaultFns = {
-    [code.notMatch]: {
-        msg: code[code.notMatch].msg,
-        ok:  code[code.notMatch].ok,
-        type:  code[code.notMatch].type,
-    },
-    [code.ok]: {
-        msg: code[code.ok].msg,
-        ok:  code[code.ok].ok,
-        type:  code[code.ok].type,
-    },
-    [code.notParam]: {
-        msg: code[code.notParam].msg,
-        ok:  code[code.notParam].ok,
-        type:  code[code.notParam].type,
-    },
-    [code.notLogin]: {
-        fn: toLogin,
-        msg: code[code.notLogin].msg,
-        ok:  code[code.notLogin].ok,
-        type:  code[code.notLogin].type,
-    },
-    [code.serverError]: {
-        // fn: toLogin,
-        msg: code[code.serverError].msg,
-        ok:  code[code.serverError].ok,
-        type:  code[code.serverError].type,
-    },
-    [code.permissionDenied]: {
-        msg: code[code.permissionDenied].msg,
-        ok:  code[code.permissionDenied].ok,
-        type:  code[code.permissionDenied].type,
-    },
-    [code.customError]: {
-        msg: code[code.customError].msg,
-        ok:  code[code.customError].ok,
-        type:  code[code.customError].type,
-    },
-    [code.notFound]: {
-        msg: code[code.notFound].msg,
-        ok:  code[code.notFound].ok,
-        type:  code[code.notFound].type,
-    },
-    [code.apiError]: {
-        msg: code[code.apiError].msg,
-        ok:  code[code.apiError].ok,
-        type:  code[code.apiError].type,
-    },
-    [code.saveError]: {
-        msg: code[code.saveError].msg,
-        ok:  code[code.saveError].ok,
-        type:  code[code.saveError].type,
-    },
-    [code.dataRepeat]: {
-        msg: code[code.dataRepeat].msg,
-        ok:  code[code.dataRepeat].ok,
-        type:  code[code.dataRepeat].type,
-    },
-    [code.timeout]: {
-        msg: '请求超时,'+code[code.dataRepeat].msg,
-        ok:  code[code.dataRepeat].ok,
-        type:  code[code.dataRepeat].type,
-    },
+    // [code.NotMATCH]: {
+    //     msg: code[code.NotMATCH].msg,
+    //     ok:  code[code.NotMATCH].ok,
+    //     type:  code[code.NotMATCH].type,
+    // },
+    // [code.ok]: {
+    //     msg: code[code.ok].msg,
+    //     ok:  code[code.ok].ok,
+    //     type:  code[code.ok].type,
+    // },
+    // [code.NotParam]: {
+    //     msg: code[code.NotParam].msg,
+    //     ok:  code[code.NotParam].ok,
+    //     type:  code[code.NotParam].type,
+    // },
+    // [code.NotLogin]: {
+    //     fn: toLogin,
+    //     msg: code[code.NotLogin].msg,
+    //     ok:  code[code.NotLogin].ok,
+    //     type:  code[code.NotLogin].type,
+    // },
+    // [code.ServerError]: {
+    //     // fn: toLogin,
+    //     msg: code[code.ServerError].msg,
+    //     ok:  code[code.ServerError].ok,
+    //     type:  code[code.ServerError].type,
+    // },
+    // [code.NotPermission]: {
+    //     msg: code[code.NotPermission].msg,
+    //     ok:  code[code.NotPermission].ok,
+    //     type:  code[code.NotPermission].type,
+    // },
+    // [code.customError]: {
+    //     msg: code[code.customError].msg,
+    //     ok:  code[code.customError].ok,
+    //     type:  code[code.customError].type,
+    // },
+    // [code.NotFound]: {
+    //     msg: code[code.NotFound].msg,
+    //     ok:  code[code.NotFound].ok,
+    //     type:  code[code.NotFound].type,
+    // },
+    // [code.ApiError]: {
+    //     msg: code[code.ApiError].msg,
+    //     ok:  code[code.ApiError].ok,
+    //     type:  code[code.ApiError].type,
+    // },
+    // [code.SaveError]: {
+    //     msg: code[code.SaveError].msg,
+    //     ok:  code[code.SaveError].ok,
+    //     type:  code[code.SaveError].type,
+    // },
+    // [code.DataRepeat]: {
+    //     msg: code[code.DataRepeat].msg,
+    //     ok:  code[code.DataRepeat].ok,
+    //     type:  code[code.DataRepeat].type,
+    // },
+    // [code.timeout]: {
+    //     msg: '请求超时,'+code[code.DataRepeat].msg,
+    //     ok:  code[code.DataRepeat].ok,
+    //     type:  code[code.DataRepeat].type,
+    // },
 }
 /**
  * 检查api返回值对应含义,普通服务器接口

+ 81 - 0
until/domTool.js

@@ -0,0 +1,81 @@
+export function comDomHeight(el){
+    let parent = el.offsetParent;
+    let parentWidth = parent.offsetWidth;
+    return parentWidth;
+}
+
+/**
+ * 指定dom进入全屏
+ * @param element
+ */
+export function launchIntoFullscreen(element) {
+    if(element.requestFullscreen){
+        element.requestFullscreen();
+    }
+    else if(element.mozRequestFullScreen) {
+        element.mozRequestFullScreen();
+    }
+    else if(element.webkitRequestFullscreen) {
+        element.webkitRequestFullscreen();
+    }
+    else if(element.msRequestFullscreen) {
+        element.msRequestFullscreen();
+    }
+}
+
+/**
+ * 退出全屏
+ */
+export function exitFullscreen() {
+    if(document.exitFullscreen) {
+        document.exitFullscreen();
+    } else if(document.mozCancelFullScreen) {
+        document.mozCancelFullScreen();
+    } else if(document.webkitExitFullscreen) {
+        document.webkitExitFullscreen();
+    }
+}
+
+/**
+ * 检测是否处于全屏状态
+ * @returns {*|boolean}
+ */
+export function isFullscreenEnabled(){
+    // console.log(document.fullscreenEnabled);
+    if(document.fullscreenEnabled){
+        return document.fullscreenElement
+    }else if(document.mozFullScreenEnabled){
+        return document.mozFullscreenElement
+    }else if(document.webkitFullscreenEnabled){
+        return document.webkitFullscreenElement
+    }else if(document.msFullscreenEnabled){
+        return document.msFullscreenElement
+    }else{
+        return document.mozFullScreenEnabled ||
+            document.webkitFullscreenEnabled ||
+            document.msFullscreenEnabled || false;
+    }
+}
+
+function comDomStyle(el,lineNum,gap){
+    if(!el){
+        return -1;
+    }
+    let el_width = el.clientWidth;
+    console.log(el_width);
+    // 获取余数部分
+    let remainder = el_width % lineNum;
+    // 将子元素居中至于父元素中
+    let itemAllWidth = (el_width - remainder) / lineNum;
+    // 计算元素实际宽度
+    let itemWidth = itemAllWidth - (gap * 2);
+    let boxPadding = remainder / 2;
+    let boxStyle = `padding:0 ${boxPadding}px;`;
+    let itemStyle = `width:${itemWidth}px;height:${itemWidth}px;margin:${gap}px;`
+    return {
+        boxStyle,
+        itemStyle,
+    }
+}
+
+export default {comDomHeight,launchIntoFullscreen,exitFullscreen,isFullscreenEnabled,comDomStyle}

+ 0 - 8
until/time.js

@@ -69,11 +69,3 @@ export function getUnixTimeStamp(){
   return Math.round(new Date().getTime()/1000);
 }
 
-if(module && module.exports){
-  module.exports = {
-    timestampToTime,
-    timeStrToTimeStamp,
-    timeFormat,
-    getUnixTimeStamp
-  }
-}

+ 5 - 0
yarn.lock

@@ -3497,6 +3497,11 @@ crypto-browserify@^3.11.0:
     randombytes "^2.0.0"
     randomfill "^1.0.3"
 
+crypto-js@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
+  integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
+
 css-blank-pseudo@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-6.0.0.tgz#2bc6f812a5f60296c04c55b1696bad4300dcdbcc"