Просмотр исходного кода

Merge branch 'v5.0.0dev' of https://gitee.com/ZhongBangKeJi/CRMEB into v5.0.0dev

liaofei 2 лет назад
Родитель
Сommit
8f8415a966

+ 1 - 1
crmeb/app/dao/system/SystemMenusDao.php

@@ -112,7 +112,7 @@ class SystemMenusDao extends BaseDao
      * @param string $key
      * @return array
      */
-    public function column(array $where, string $field, string $key)
+    public function column(array $where, string $field, string $key = '')
     {
         return $this->search($where)->column($field, $key);
     }

+ 5 - 2
crmeb/app/services/system/SystemMenusServices.php

@@ -228,8 +228,11 @@ class SystemMenusServices extends BaseServices
      */
     public function delete(int $id)
     {
-        if ($this->dao->count(['pid' => $id])) {
-            throw new AdminException(400613);
+        $ids = $this->dao->column(['pid' => $id], 'id');
+        if (count($ids)) {
+            foreach ($ids as $value) {
+                $this->delete($value);
+            }
         }
         return $this->dao->delete($id);
     }

+ 26 - 21
crmeb/app/services/system/SystemRouteServices.php

@@ -83,32 +83,33 @@ class SystemRouteServices extends BaseServices
      */
     public function getTreeList(array $where, string $appName = 'adminapi')
     {
-        $list = app()->make(SystemRouteCateServices::class)
-            ->selectList(['app_name' => $appName], '*', 0, 0, 'id asc,sort desc', [
-                'children' => function ($query) use ($where) {
-                    $query->where('app_name', $where['app_name'])
-                        ->when('' !== $where['name_like'], function ($q) use ($where) {
-                            $q->where('name|path', 'LIKE', '%' . $where['name_like'] . '%');
-                        });
-                }
-            ])
-            ->toArray();
+        return $this->cacheDriver()->remember('ROUTE_LIST', function () use ($where, $appName) {
+            $list = app()->make(SystemRouteCateServices::class)
+                ->selectList(['app_name' => $appName], '*', 0, 0, 'id asc,sort desc', [
+                    'children' => function ($query) use ($where) {
+                        $query->where('app_name', $where['app_name'])
+                            ->when('' !== $where['name_like'], function ($q) use ($where) {
+                                $q->where('name|path', 'LIKE', '%' . $where['name_like'] . '%');
+                            });
+                    }
+                ])
+                ->toArray();
 
-        foreach ($list as $key => $item) {
-            if (!empty($item['children'])) {
-                foreach ($item['children'] as $k => $v) {
-                    if (isset($v['cate_id']) && isset($v['method'])) {
-                        if ($v['method'] === 'DELETE') {
-                            $v['method'] = 'DEL';
+            foreach ($list as $key => $item) {
+                if (!empty($item['children'])) {
+                    foreach ($item['children'] as $k => $v) {
+                        if (isset($v['cate_id']) && isset($v['method'])) {
+                            if ($v['method'] === 'DELETE') {
+                                $v['method'] = 'DEL';
+                            }
+                            $v['pid'] = $v['cate_id'];
+                            $list[$key]['children'][$k] = $v;
                         }
-                        $v['pid'] = $v['cate_id'];
-                        $list[$key]['children'][$k] = $v;
                     }
                 }
             }
-        }
-
-        return get_tree_children($list);
+            return get_tree_children($list);
+        }, 600);
     }
 
     /**
@@ -265,11 +266,14 @@ class SystemRouteServices extends BaseServices
         foreach ($list as $key => $value) {
             foreach ($value as $item) {
                 if (!$this->diffRoute($data, $item['rule'], $item['method']) && strstr($item['rule'], '<MISS>') === false) {
+                    $pathAndAction = explode('/', $item['route']);
                     $save[] = [
                         'name' => $item['option']['real_name'] ?? $item['name'],
                         'path' => $item['rule'],
                         'cate_id' => $key,
                         'app_name' => $app,
+                        'file_path' => 'app/' . $app . '/controller/' . str_replace('.', '/', $pathAndAction[0]) . '.php',
+                        'action' => $pathAndAction[1],
                         'type' => isset($item['option']['is_common']) && $item['option']['is_common'] ? 1 : 0,
                         'method' => $item['method'],
                         'add_time' => date('Y-m-d H:i:s'),
@@ -304,6 +308,7 @@ class SystemRouteServices extends BaseServices
                 app()->make(SystemMenusServices::class)->deleteMenu($item['path'], $item['method']);
             }
         }
+        $this->cacheDriver()->clear();
     }
 
     /**

Разница между файлами не показана из-за своего большого размера
+ 33 - 23477
crmeb/public/install/crmeb.sql


+ 1 - 0
template/admin/package.json

@@ -60,6 +60,7 @@
     "vue-awesome-swiper": "^4.1.1",
     "vue-clipboard2": "^0.3.3",
     "vue-codemirror": "^4.0.6",
+    "vue-cropper": "^0.5.11",
     "vue-happy-scroll": "^2.1.1",
     "vue-i18n": "^7.8.0",
     "vue-pickers": "^2.5.3",

+ 15 - 0
template/admin/src/api/setting.js

@@ -9,6 +9,7 @@
 // +----------------------------------------------------------------------
 
 import request from '@/libs/request';
+import { getCookies } from '@/libs/util';
 
 /**
  * @description 设置 系统设置 应用设置头部
@@ -1122,3 +1123,17 @@ export function codeCrud(data) {
     data,
   });
 }
+/**
+ * @description 图片上传
+ */
+export function fileUpload(data) {
+  return request({
+    url: `file/upload`,
+    method: 'post',
+    headers: {
+      'Authori-zation': 'Bearer ' + getCookies('token'),
+      'content-type': 'multipart/form-data;' + 'Bearer ' + getCookies('token'),
+    },
+    data,
+  });
+}

+ 280 - 0
template/admin/src/components/cropperImg/index.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="cropper-content">
+    <div class="cropper-box">
+      <div class="cropper">
+        <vue-cropper
+          ref="cropper"
+          :img="option.img"
+          :outputSize="option.outputSize"
+          :outputType="option.outputType"
+          :info="option.info"
+          :canScale="option.canScale"
+          :autoCrop="option.autoCrop"
+          :autoCropWidth="option.autoCropWidth"
+          :autoCropHeight="option.autoCropHeight"
+          :fixed="option.fixed"
+          :fixedNumber="option.fixedNumber"
+          :full="option.full"
+          :fixedBox="option.fixedBox"
+          :canMove="option.canMove"
+          :canMoveBox="option.canMoveBox"
+          :original="option.original"
+          :centerBox="option.centerBox"
+          :height="option.height"
+          :infoTrue="option.infoTrue"
+          :maxImgSize="option.maxImgSize"
+          :enlarge="option.enlarge"
+          :mode="option.mode"
+          @realTime="realTime"
+          @imgLoad="imgLoad"
+        >
+        </vue-cropper>
+      </div>
+      <!--底部操作工具按钮-->
+      <div class="footer-btn">
+        <div class="scope-btn">
+          <input
+            type="file"
+            id="uploads"
+            style="position: absolute; clip: rect(0 0 0 0)"
+            accept="image/png, image/jpeg, image/gif, image/jpg"
+            @change="selectImg($event)"
+          />
+          <el-button size="mini" type="danger" plain icon="el-icon-zoom-in" @click="changeScale(1)">放大</el-button>
+          <el-button size="mini" type="danger" plain icon="el-icon-zoom-out" @click="changeScale(-1)">缩小</el-button>
+          <el-button size="mini" type="danger" plain @click="rotateLeft">↺ 左旋转</el-button>
+          <el-button size="mini" type="danger" plain @click="rotateRight">↻ 右旋转</el-button>
+        </div>
+      </div>
+    </div>
+    <!--预览效果图-->
+    <div class="show-preview">
+      <div class="preview">
+        <img :src="previews.url" :style="previews.img" />
+      </div>
+      <div class="upload-btn">
+        <label class="btn" for="uploads">选择图片</label>
+        <el-button size="mini" type="success" @click="uploadImg()">确认上传</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { VueCropper } from 'vue-cropper';
+// import { updateAvatar } from ''; //这里为文件上传的接口换成自己的文件
+import { fileUpload } from '@/api/setting';
+export default {
+  name: 'CropperImage',
+  components: {
+    VueCropper,
+  },
+  props: ['Name'],
+  data() {
+    return {
+      name: this.Name,
+      resImg: '',
+      previews: {},
+      option: {
+        img: this.Name, //裁剪图片的地址
+        outputSize: 1, //裁剪生成图片的质量(可选0.1 - 1)
+        outputType: 'png', //裁剪生成图片的格式(jpeg || png || webp)
+        info: true, //图片大小信息
+        canScale: true, //图片是否允许滚轮缩放
+        autoCrop: true, //是否默认生成截图框
+        autoCropWidth: 200, //默认生成截图框宽度
+        autoCropHeight: 200, //默认生成截图框高度
+        fixed: false, //是否开启截图框宽高固定比例
+        fixedNumber: [1.53, 1], //截图框的宽高比例
+        full: false, //false按原比例裁切图片,不失真
+        fixedBox: false, //固定截图框大小,不允许改变
+        canMove: true, //上传图片是否可以移动
+        canMoveBox: true, //截图框能否拖动
+        original: false, //上传图片按照原始比例渲染
+        centerBox: true, //截图框是否被限制在图片里面
+        height: false, //是否按照设备的dpr 输出等比例图片
+        infoTrue: false, //true为展示真实输出图片宽高,false展示看到的截图框宽高
+        maxImgSize: 3000, //限制图片最大宽度和高度
+        enlarge: 1, //图片根据截图框输出比例倍数
+        mode: '300px 300px', //图片默认渲染方式
+      },
+    };
+  },
+  methods: {
+    //初始化函数
+    imgLoad(msg) {
+      console.log('工具初始化函数=====' + msg);
+    },
+    //图片缩放
+    changeScale(num) {
+      num = num || 1;
+      this.$refs.cropper.changeScale(num);
+    },
+    //向左旋转
+    rotateLeft() {
+      this.$refs.cropper.rotateLeft();
+    },
+    //向右旋转
+    rotateRight() {
+      this.$refs.cropper.rotateRight();
+    },
+    // //实时预览函数
+    realTime(data) {
+      let that = this;
+      that.previews = data;
+      this.$refs.cropper.getCropBlob((data) => {
+        this.blobToDataURI(data, function (res) {
+          that.previewImg = res;
+        });
+      });
+    },
+    blobToDataURI(blob, callback) {
+      var reader = new FileReader();
+      reader.readAsDataURL(blob);
+      reader.onload = function (e) {
+        callback(e.target.result);
+      };
+    },
+    //选择图片
+    selectImg(e) {
+      let file = e.target.files[0];
+      if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(e.target.value)) {
+        this.$message({
+          message: '图片类型要求:jpeg、jpg、png',
+          type: 'error',
+        });
+        return false;
+      }
+      //转化为blob
+      let reader = new FileReader();
+      reader.onload = (e) => {
+        let data;
+        if (typeof e.target.result === 'object') {
+          data = window.URL.createObjectURL(new Blob([e.target.result]));
+        } else {
+          data = e.target.result;
+        }
+        this.option.img = data;
+      };
+      //转化为base64
+      reader.readAsDataURL(file);
+    },
+
+    base64ImgtoFile(dataurl, filename = 'file') {
+      //将base64格式分割:['data:image/png;base64','XXXX']
+      const arr = dataurl.split(',');
+      // .*? 表示匹配任意字符到下一个符合条件的字符 刚好匹配到:
+      // image/png
+      const mime = arr[0].match(/:(.*?);/)[1]; //image/png
+      //[image,png] 获取图片类型后缀
+      const suffix = mime.split('/')[1]; //png
+      const bstr = atob(arr[1]); //atob() 方法用于解码使用 base-64 编码的字符串
+      let n = bstr.length;
+      const u8arr = new Uint8Array(n);
+      while (n--) {
+        u8arr[n] = bstr.charCodeAt(n);
+      }
+      return new File([u8arr], `${filename}.${suffix}`, {
+        type: mime,
+      });
+    },
+
+    uploadFile(file) {
+      const formData = new FormData();
+      formData.append('file', file);
+      fileUpload(formData).then((res) => {
+        console.log(res);
+        if (res.status == 200) {
+          this.$emit('uploadImgSuccess', res.data);
+        } else {
+          this.$message({
+            message: '上传失败',
+            type: 'error',
+            duration: 1000,
+          });
+        }
+      });
+    },
+    //上传图片
+    uploadImg() {
+      this.$refs.cropper.getCropData((data) => {
+        this.resImg = this.base64ImgtoFile(data);
+        this.uploadFile(this.resImg);
+      });
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.btn {
+  outline: none;
+  display: inline-block;
+  line-height: 1;
+  white-space: nowrap;
+  cursor: pointer;
+  -webkit-appearance: none;
+  text-align: center;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  outline: 0;
+  -webkit-transition: 0.1s;
+  transition: 0.1s;
+  font-weight: 500;
+  padding: 8px 15px;
+  font-size: 12px;
+  border-radius: 3px;
+  color: #fff;
+  background-color: #409eff;
+  border-color: #409eff;
+  margin-right: 10px;
+}
+.cropper-content {
+  display: flex;
+  display: -webkit-flex;
+  justify-content: flex-end;
+  .cropper-box {
+    flex: 1;
+    width: 100%;
+    .cropper {
+      width: auto;
+      height: 300px;
+    }
+  }
+
+  .show-preview {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    align-items: center;
+    .preview {
+      overflow: hidden;
+      height: 200px;
+      width: 200px;
+      background: #cccccc;
+      transform: scale(0.8);
+      border-radius: 50%;
+    }
+  }
+}
+.footer-btn {
+  margin-top: 30px;
+  display: flex;
+  display: -webkit-flex;
+  justify-content: space-around;
+  .scope-btn {
+    display: flex;
+    display: -webkit-flex;
+    justify-content: space-between;
+    padding-right: 10px;
+  }
+  .upload-btn {
+    flex: 1;
+    -webkit-flex: 1;
+    display: flex;
+    display: -webkit-flex;
+    justify-content: center;
+  }
+}
+</style>

+ 14 - 5
template/admin/src/layout/component/aside.vue

@@ -1,11 +1,9 @@
 <template>
   <el-aside class="layout-aside" :class="setCollapseWidth" v-if="clientWidth > 1000">
     <Logo v-if="setShowLogo && menuList.length && getThemeConfig.layout !== 'columns'" />
-    <el-divider
-      v-if="menuList.length && !getThemeConfig.isCollapse && getThemeConfig.layout == 'columns'"
-      content-position="center"
-      >{{ catName }}</el-divider
-    >
+    <div v-if="menuList.length && !getThemeConfig.isCollapse && getThemeConfig.layout == 'columns'" class="cat-name">
+      {{ catName }}
+    </div>
     <el-scrollbar class="flex-auto" ref="layoutAsideRef">
       <Vertical :menuList="menuList" :class="setCollapseWidth" />
     </el-scrollbar>
@@ -132,3 +130,14 @@ export default {
   },
 };
 </script>
+<style lang="scss" scoped>
+.cat-name {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 50px;
+  border-bottom: 1px solid var(--prev-border-color-lighter);
+  font-weight: 500;
+  font-size: 15px;
+}
+</style>

+ 25 - 23
template/admin/src/layout/navBars/breadcrumb/setings.vue

@@ -19,7 +19,7 @@
             @click="onSetLayout('defaults')"
           >
             <section class="el-container el-circular">
-              <aside class="el-aside w10 mr5"></aside>
+              <aside class="el-aside w10 mr5" style="width: 17px"></aside>
               <section class="el-container is-vertical">
                 <header class="el-header mb5" style="height: 10px"></header>
                 <main class="el-main"></main>
@@ -31,6 +31,26 @@
               </div>
             </div> -->
           </div>
+          <!-- columns 布局 -->
+          <div
+            class="layout-drawer-content-item"
+            :class="{ 'drawer-layout-active': getThemeConfig.layout === 'columns' }"
+            @click="onSetLayout('columns')"
+          >
+            <section class="el-container el-circular">
+              <aside class="el-aside mr5" style="width: 10px"></aside>
+              <aside class="el-aside-dark mr5" style="width: 17px"></aside>
+              <section class="el-container is-vertical">
+                <!-- <header class="el-header" style="height: 10px"></header> -->
+                <main class="el-main"></main>
+              </section>
+            </section>
+            <!-- <div class="layout-tips-warp" :class="{ 'layout-tips-warp-active': getThemeConfig.layout === 'columns' }">
+              <div class="layout-tips-box">
+                <p class="layout-tips-txt">{{ $t('message.layout.sixColumns') }}</p>
+              </div>
+            </div> -->
+          </div>
           <!-- classic 布局 -->
           <div
             class="layout-drawer-content-item"
@@ -38,7 +58,7 @@
             @click="onSetLayout('classic')"
           >
             <section class="el-container is-vertical el-circular">
-              <header class="el-header mb5" style="height: 10px"></header>
+              <header class="el-aside mb5" style="height: 10px"></header>
               <section class="el-container">
                 <aside class="el-aside mr5" style="width: 20px"></aside>
                 <section class="el-container is-vertical">
@@ -52,6 +72,8 @@
               </div>
             </div> -->
           </div>
+          
+          
           <!-- transverse 布局 -->
           <div
             class="layout-drawer-content-item"
@@ -59,7 +81,7 @@
             @click="onSetLayout('transverse')"
           >
             <section class="el-container is-vertical el-circular">
-              <header class="el-header mb5" style="height: 10px"></header>
+              <header class="el-aside mb5" style="height: 10px"></header>
               <section class="el-container">
                 <section class="el-container is-vertical">
                   <main class="el-main"></main>
@@ -75,26 +97,6 @@
               </div>
             </div> -->
           </div>
-          <!-- columns 布局 -->
-          <div
-            class="layout-drawer-content-item"
-            :class="{ 'drawer-layout-active': getThemeConfig.layout === 'columns' }"
-            @click="onSetLayout('columns')"
-          >
-            <section class="el-container el-circular">
-              <aside class="el-aside-dark mr5" style="width: 10px"></aside>
-              <aside class="el-aside mr5" style="width: 17px"></aside>
-              <section class="el-container is-vertical">
-                <!-- <header class="el-header" style="height: 10px"></header> -->
-                <main class="el-main"></main>
-              </section>
-            </section>
-            <!-- <div class="layout-tips-warp" :class="{ 'layout-tips-warp-active': getThemeConfig.layout === 'columns' }">
-              <div class="layout-tips-box">
-                <p class="layout-tips-txt">{{ $t('message.layout.sixColumns') }}</p>
-              </div>
-            </div> -->
-          </div>
         </div>
         <!-- 全局主题 -->
         <!-- <el-divider :content-position="contentPosotion">{{ $t('message.layout.oneTitle') }}</el-divider>

+ 13 - 5
template/admin/src/layout/navBars/breadcrumb/user.vue

@@ -38,11 +38,11 @@
         :width="300"
         popper-class="el-popover-pupop-user-news"
       >
-        <el-badge :is-dot="true" @click.stop="isShowUserNewsPopover = !isShowUserNewsPopover" slot="reference">
+        <el-badge :is-dot="isDot" @click.stop="openNews" slot="reference">
           <i class="el-icon-bell" :title="$t('message.user.title4')"></i>
         </el-badge>
         <transition name="el-zoom-in-top">
-          <UserNews v-show="isShowUserNewsPopover" />
+          <UserNews v-show="isShowUserNewsPopover" @haveNews="initIsDot" />
         </transition>
       </el-popover>
     </div>
@@ -54,7 +54,7 @@
     </div>
     <el-dropdown :show-timeout="70" :hide-timeout="50" @command="onDropdownCommand">
       <span class="layout-navbars-breadcrumb-user-link">
-        <!-- <img :src="getUserInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" /> -->
+        <img :src="getUserInfos.head_pic" class="layout-navbars-breadcrumb-user-link-photo mr5" />
         {{ getUserInfos.account === '' ? 'test' : getUserInfos.account }}
         <i class="el-icon-arrow-down el-icon--right"></i>
       </span>
@@ -93,6 +93,7 @@ export default {
       disabledI18n: 'zh-cn',
       disabledSize: '',
       homePath: `${settings.routePre}/home`,
+      isDot: false,
     };
   },
   computed: {
@@ -116,6 +117,13 @@ export default {
     }
   },
   methods: {
+    initIsDot(status) {
+      this.isDot = status;
+    },
+    openNews() {
+      this.isShowUserNewsPopover = !this.isShowUserNewsPopover;
+      this.isDot = false;
+    },
     // 搜索点击
     onSearchClick() {
       this.$refs.searchRef.openSearch();
@@ -254,8 +262,8 @@ export default {
     align-items: center;
     white-space: nowrap;
     &-photo {
-      width: 25px;
-      height: 25px;
+      width: 30px;
+      height: 30px;
       border-radius: 100%;
     }
   }

+ 3 - 14
template/admin/src/layout/navBars/breadcrumb/userNews.vue

@@ -34,18 +34,7 @@ export default {
   name: 'layoutBreadcrumbUserNews',
   data() {
     return {
-      newsList: [
-        {
-          label: '关于版本发布的通知',
-          value: '基于 vue2.x + element ui,正式发布时间:2020年11月15日!',
-          time: '2020-11-15',
-        },
-        {
-          label: '关于学习交流的通知',
-          value: 'QQ群号码 665452019,欢迎小伙伴入群学习交流探讨!',
-          time: '2020-11-15',
-        },
-      ],
+      newsList: [],
       newOrderAudioLink: null,
     };
   },
@@ -165,12 +154,12 @@ export default {
       this.newsList = [];
     },
     // 前往通知中心点击
-    onGoToGiteeClick() {
-    },
+    onGoToGiteeClick() {},
     getNotict() {
       jnoticeRequest()
         .then((res) => {
           this.newsList = res.data || [];
+          this.$emit('haveNews', !!this.newsList.length);
         })
         .catch(() => {});
     },

+ 2 - 2
template/admin/src/layout/navBars/tagsView/tagsView.vue

@@ -19,11 +19,11 @@
             v-if="v.path !== tagsRoutePath && getThemeConfig.isTagsviewIcon"
           ></i>
           <span>{{ $t(v.meta.title) }}</span>
-          <i
+          <!-- <i
             class="el-icon-refresh-right layout-navbars-tagsview-ul-li-icon ml5"
             v-if="v.path === tagsRoutePath"
             @click.stop="refreshCurrentTagsView(v.path)"
-          ></i>
+          ></i> -->
           <i
             class="el-icon-close layout-navbars-tagsview-ul-li-icon ml5"
             v-if="!v.isAffix"

+ 9 - 0
template/admin/src/main.js

@@ -204,6 +204,15 @@ new Vue({
           }
         });
       }
+
+      if (to.name == 'product_productAdd') {
+        let route = to.matched[1].path.split(':')[0];
+        this.$store.state.menus.oneLvRoutes.map((e) => {
+          if (route.indexOf(e.path) != -1) {
+            to.meta.title = `${e.title} ${to.params.id ? 'ID:' + to.params.id : ''}`;
+          }
+        });
+      }
     },
   },
 });

+ 4 - 0
template/admin/src/pages/product/productAdd/index.vue

@@ -1832,6 +1832,10 @@ export default {
       return this.isMobile ? undefined : 15;
     },
   },
+  beforeRouteUpdate(to, from, next) {
+    this.bus.$emit('onTagsViewRefreshRouterView', this.$route.path);
+    next();
+  },
   created() {
     this.columns = this.columns2.slice(0, 8);
     this.getToken();

+ 15 - 9
template/admin/src/pages/setting/systemMenus/components/menusFrom.vue

@@ -39,7 +39,7 @@
               <Cascader :data="menuList" change-on-select v-model="formValidate.path" filterable></Cascader>
             </FormItem>
           </Col>
-          <Col v-bind="grid" v-if="!authType">
+          <!-- <Col v-bind="grid" v-if="!authType">
             <FormItem label="请求方式:" prop="methods">
               <Select v-model="formValidate.methods">
                 <Option value="">请求</Option>
@@ -49,15 +49,15 @@
                 <Option value="DELETE">DELETE</Option>
               </Select>
             </FormItem>
-          </Col>
-          <Col v-bind="grid" v-if="!authType">
+          </Col> -->
+          <!-- <Col v-bind="grid" v-if="!authType">
             <FormItem label="接口地址:">
               <Input v-model="formValidate.api_url" placeholder="请输入接口地址" prop="api_url"></Input>
             </FormItem>
-          </Col>
+          </Col> -->
           <Col v-bind="grid" v-show="authType">
             <FormItem label="路由地址:" prop="menu_path">
-              <Input v-model="formValidate.menu_path" placeholder="请输入路由地址">
+              <Input v-model="formValidate.menu_path" placeholder="请输入路由地址" @on-change="changeUnique">
                 <template #prepend>
                   <span>{{ $routeProStr }}</span>
                 </template>
@@ -69,7 +69,7 @@
               <Input v-model="formValidate.unique_auth" placeholder="请输入权限标识"></Input>
             </FormItem>
           </Col>
-          <Col v-bind="grid" v-show="authType">
+          <Col v-bind="grid" v-if="authType">
             <FormItem label="图标:">
               <Input
                 v-model="formValidate.icon"
@@ -80,12 +80,12 @@
             </FormItem>
           </Col>
 
-          <Col v-bind="grid">
+          <Col v-bind="grid" v-if="authType">
             <FormItem label="排序:">
               <Input type="number" v-model="formValidate.sort" placeholder="请输入排序" number></Input>
             </FormItem>
           </Col>
-          <Col v-bind="grid" v-show="authType">
+          <!-- <Col v-bind="grid" v-show="authType">
             <FormItem label="隐藏菜单:">
               <RadioGroup v-model="formValidate.is_show_path">
                 <Radio :label="item.value" v-for="(item, i) in isShowPathRadio" :key="i">
@@ -94,7 +94,7 @@
                 </Radio>
               </RadioGroup>
             </FormItem>
-          </Col>
+          </Col> -->
           <Col v-bind="grid">
             <FormItem label="状态:">
               <RadioGroup v-model="formValidate.is_show">
@@ -306,6 +306,12 @@ export default {
         this.ruleModal = false;
       }
     },
+    changeUnique(val) {
+      console.log(val.target.value);
+      let value = this.$routeProStr + val.target.value;
+      if (value.slice(0, 1) === '/') value = value.replace('/', '');
+      this.formValidate.unique_auth = value.replaceAll('/', '-');
+    },
     visible(type) {
       if (!type) {
         this.authType = true;

+ 14 - 7
template/admin/src/pages/setting/systemMenus/index.vue

@@ -25,7 +25,7 @@
         </Row>
         <Row type="flex">
           <Col v-bind="grid">
-            <Button v-auth="['setting-system_menus-add']" type="primary" @click="menusAdd('添加规则')" icon="md-add"
+            <Button type="primary" @click="menusAdd('添加规则')" icon="md-add"
               >添加规则
             </Button>
           </Col>
@@ -43,10 +43,14 @@
         :data="tableData"
         row-id="id"
       >
-        <vxe-table-column field="id" title="ID" tooltip min-width="70"></vxe-table-column>
         <vxe-table-column field="menu_name" tree-node title="按钮名称" min-width="100"></vxe-table-column>
         <vxe-table-column field="unique_auth" title="前端权限" min-width="200"></vxe-table-column>
-        <vxe-table-column field="menu_path" title="页面路由" min-width="240" tooltip="true"></vxe-table-column>
+        <vxe-table-column field="menu_path" title="路由" min-width="240" tooltip="true">
+          <template v-slot="{ row }">
+            <span v-if="row.auth_type == 1">页面:{{row.menu_path}}</span>
+            <span v-if="row.auth_type == 2">接口:[{{row.methods}}]{{row.api_url}}</span>
+          </template>
+        </vxe-table-column>
         <vxe-table-column field="flag" title="规则状态" min-width="120">
           <template v-slot="{ row }">
             <i-switch
@@ -57,14 +61,14 @@
               @on-change="onchangeIsShow(row)"
               size="large"
             >
-              <span slot="open">显示</span>
-              <span slot="close">隐藏</span>
+              <span slot="open">开启</span>
+              <span slot="close">关闭</span>
             </i-switch>
           </template>
         </vxe-table-column>
-        <vxe-table-column field="date" title="操作" align="center" width="250" fixed="right">
+        <vxe-table-column field="date" title="操作" align="right" width="250" fixed="right">
           <template v-slot="{ row }">
-            <span v-auth="['setting-system_menus-add']">
+            <span>
               <a @click="addRoute(row)" v-if="row.auth_type === 1">添加权限</a>
               <Divider type="vertical" v-if="row.auth_type === 1" />
               <a @click="addE(row, '添加子菜单')" v-if="row.auth_type === 1">添加子菜单</a>
@@ -554,6 +558,9 @@ export default {
     width: 200px;
     overflow-y: scroll;
     max-height: 600px;
+    /deep/ .el-tree-node__children .el-tree-node .el-tree-node__content {
+      padding-left: 14px !important;
+    }
   }
 }
 </style>

+ 30 - 1
template/admin/src/pages/setting/user/index.vue

@@ -2,6 +2,12 @@
   <div>
     <Card :bordered="false" dis-hover class="ivu-mt">
       <Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="160" label-position="right">
+        <FormItem label="头像">
+          <div class="avatar" @click="avatarMoadl = true">
+            <img v-if="formValidate.head_pic" :src="formValidate.head_pic" alt="" />
+            <img v-else src="../../../assets/images/f.png" alt="" />
+          </div>
+        </FormItem>
         <FormItem label="账号" prop="">
           <Input type="text" v-model="account" :disabled="true" class="input"></Input>
         </FormItem>
@@ -22,14 +28,19 @@
         </FormItem>
       </Form>
     </Card>
+    <Modal v-model="avatarMoadl" title="头像上传" width="700">
+      <CropperImg v-if="avatarMoadl" @uploadImgSuccess="uploadImgSuccess"></CropperImg>
+    </Modal>
   </div>
 </template>
 
 <script>
 import { updtaeAdmin } from '@/api/user';
 import { mapState } from 'vuex';
+import CropperImg from '@/components/cropperImg';
 export default {
   name: 'setting_user',
+  components: { CropperImg },
   computed: {
     ...mapState('media', ['isMobile']),
     ...mapState('userLevel', ['categoryId']),
@@ -43,7 +54,9 @@ export default {
   data() {
     return {
       account: '',
+      avatarMoadl: false,
       formValidate: {
+        avatar: '',
         real_name: '',
         pwd: '',
         new_pwd: '',
@@ -59,15 +72,21 @@ export default {
   },
   mounted() {
     this.account = this.$store.state.userInfo.userInfo.account;
+    this.formValidate.head_pic = this.$store.state.userInfo.userInfo.head_pic;
     this.formValidate.real_name = this.$store.state.userInfo.userInfo.real_name;
   },
   methods: {
+    uploadImgSuccess(data) {
+      this.avatarMoadl = false;
+      this.formValidate.head_pic = data.src;
+    },
     handleSubmit(name) {
       this.$refs[name].validate((valid) => {
         if (valid) {
           updtaeAdmin(this.formValidate)
             .then((res) => {
               this.$store.commit('userInfo/userRealName', this.formValidate.real_name);
+              this.$store.commit('userInfo/userRealHeadPic', this.formValidate.head_pic);
               this.$Message.success(res.msg);
             })
             .catch((res) => {
@@ -84,8 +103,18 @@ export default {
 };
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 .input {
   width: 400px;
 }
+.avatar {
+  width: 80px;
+  height: 80px;
+  img {
+    width: 100%;
+    height: 100%;
+    border-radius: 50%;
+    border: 1px solid #f2f2f2;
+  }
+}
 </style>

+ 38 - 25
template/admin/src/pages/system/backendRouting/index.vue

@@ -31,10 +31,10 @@
               <div></div>
               <div
                 class="tree-node"
-                :class="{ node: slotProps.model.method, open: formValidate.id == slotProps.model.id }"
+                :class="{ node: slotProps.model.method, open: formValidate.path == slotProps.model.path && formValidate.method == slotProps.model.method }"
                 @click.stop="onClick(slotProps.model)"
               >
-                <span class="" :class="{ open: formValidate.id == slotProps.model.id }">{{
+                <span class="" :class="{ open: formValidate.path == slotProps.model.path && formValidate.method == slotProps.model.method }">{{
                   slotProps.model.name
                 }}</span>
                 <Dropdown
@@ -76,9 +76,9 @@
                 class="req-method"
                 :style="{
                   color: methodsColor(slotProps.model.method),
-                  'font-weight': slotProps.model.id == formValidate.id ? '500' : '500',
+                  'font-weight': slotProps.model.pid == formValidate.pid ? '500' : '500',
                 }"
-                >{{ slotProps.model.method == 'delete' ? 'DEL' : slotProps.model.method || '' }}</span
+                >{{ slotProps.model.method }}</span
               >
 
               <!-- <span v-if="slotProps.model.method"></span> -->
@@ -172,15 +172,13 @@
               <Col span="24">
                 <div class="title">调用方式</div>
                 <FormItem label="路由地址:" prop="path">
-                  <Input
-                    v-if="isEdit"
-                    class="perW20"
-                    type="text"
-                    :rows="4"
-                    v-model.trim="formValidate.path"
-                    placeholder="请输入"
-                  />
-                  <span v-else>{{ formValidate.path || '' }}</span>
+                  <span>{{ formValidate.path || '' }}</span>
+                </FormItem>
+                <FormItem label="文件地址:" prop="path">
+                  <span>{{ formValidate.file_path || '' }}</span>
+                </FormItem>
+                <FormItem label="方法名:" prop="path">
+                  <span>{{ formValidate.action || '' }}</span>
                 </FormItem>
                 <FormItem label="请求参数:">
                   <vxe-table
@@ -241,7 +239,7 @@
                       <template #default="{ row }">
                         <vxe-button
                           type="text"
-                          v-if="row.type === 'array'"
+                          v-if="row.type === 'array' || row.type === 'object'"
                           status="primary"
                           @click="insertRow(row, 'xTable')"
                           >插入</vxe-button
@@ -302,7 +300,7 @@
                       <template #default="{ row }">
                         <vxe-button
                           type="text"
-                          v-if="row.type === 'array'"
+                          v-if="row.type === 'array' || row.type === 'object'"
                           status="primary"
                           @click="insertRow(row, 'resTable')"
                           >插入</vxe-button
@@ -347,10 +345,11 @@
                     keep-source
                     ref="codeTable"
                     row-id="id"
+                    is-tree-view
                     :print-config="{}"
                     :export-config="{}"
                     :loading="loading"
-                    :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
+                    :tree-config="{ rowField: 'id', parentField: 'parentId' }"
                     :data="formValidate.error_code"
                   >
                     <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
@@ -625,10 +624,17 @@ export default {
               res.data[0].expand = false;
               this.treeData = new Tree(res.data);
               this.$nextTick((e) => {
-                if (disk_type) document.querySelectorAll('.vtl-icon-caret-right')[0].click();
+                if (disk_type) {
+                  if (res.data[0].children[0].children.length) {
+                    document.querySelectorAll('.vtl-icon-caret-right')[0].click();
+                    document.querySelectorAll('.vtl-icon-caret-right')[1].click();
+                  } else {
+                    document.querySelectorAll('.vtl-icon-caret-right')[0].click();
+                  }
+                }
               });
               if (res.data[0].children && res.data[0].children.length) {
-                this.onClick(res.data[0].children[0]);
+                this.onClick(res.data[0]?.children[0]?.children[0]);
               }
             }
           })
@@ -668,6 +674,7 @@ export default {
       } else if (!this.formValidate.path) {
         return this.$Message.warning('请输入路由地址');
       }
+      console.log(await this.$refs.xTable.getTableData());
       this.formValidate.request = await this.$refs.xTable.getTableData().tableData;
       this.formValidate.response = await this.$refs.resTable.getTableData().tableData;
       this.formValidate.error_code = await this.$refs.codeTable.getTableData().tableData;
@@ -1007,7 +1014,7 @@ export default {
     .tree-list{
       margin-left:10px;
       padding: 0 15px;
-
+      margin-top: 10px;
     }
     .vtl-caret{
       padding-right: 2px;
@@ -1015,7 +1022,8 @@ export default {
     .req-method {
       display:block;
       padding: 0px 2px;
-      font-size: 12px;
+      font-size: 13px;
+      line-height: 13px;
       margin-right: 5px;
       border-radius: 4px;
 
@@ -1031,7 +1039,7 @@ export default {
       padding: 3px 7px 3px 0;
     }
     .node{
-      padding:7px 2px 7px 0px;
+      padding:3px 2px 3px 0px;
     }
     .open {
       // background-color: #fff1ef;
@@ -1064,8 +1072,8 @@ export default {
     justify-content: center;
     align-items: center;
     border-radius: 50%;
-    width: 20px;
-    height: 20px;
+    width: 18px;
+    height: 18px;
   }
 
   >>> .vtl-node-content:hover .add:hover {
@@ -1134,12 +1142,17 @@ export default {
     width: 100% !important;
   }
   >>> .vtl-tree-margin{
-    margin-left: 5px;
+    margin-left: 15px;
   }
   >>> .ivu-btn-icon-only.ivu-btn-small {
     width: 28px;
   }
-
+  >>> .tree-node > span{
+    font-size: 14px
+  }
+  >>> .tree-node.node > span{
+    font-size: 13px
+  }
   .nothing {
     display: flex;
     align-items: center;

+ 1 - 1
template/admin/src/pages/system/codeGeneration/components/TableForm.vue

@@ -77,7 +77,7 @@
           <div v-else>--</div>
         </template>
         <template slot-scope="{ row, index }" slot="action">
-          <a v-if="!foundation.primaryKey" @click="del(row, index)">删除</a>
+          <a v-if="!tableField[index].primaryKey" @click="del(row, index)">删除</a>
           <span v-else>--</span>
         </template>
       </Table>

+ 3 - 0
template/admin/src/store/module/userInfo.js

@@ -59,6 +59,9 @@ export default {
     userRealName(state, realName) {
       state.userInfo.real_name = realName;
     },
+    userRealHeadPic(state, headPic) {
+      state.userInfo.head_pic = headPic;
+    },
     uniqueAuth(state, uniqueAuth) {
       state.uniqueAuth = uniqueAuth;
     },

+ 3 - 3
template/admin/src/theme/common/transition.scss

@@ -38,15 +38,15 @@
 ------------------------------- */
 .breadcrumb-enter-active,
 .breadcrumb-leave-active {
-	transition: all 0.3s;
+	// transition: all 0.3s;
 }
 .breadcrumb-enter,
 .breadcrumb-leave-active {
 	opacity: 0;
-	transform: translateX(20px);
+	// transform: translateX(20px);
 }
 .breadcrumb-move {
-	transition: all 0.3s;
+	// transition: all 0.3s;
 }
 .breadcrumb-leave-active {
 	position: absolute;

+ 3 - 1
template/uni-app/pages/goods/order_confirm/index.vue

@@ -338,7 +338,7 @@
 					}
 
 				],
-				virtual_type: 0,
+				virtual_type: 0,
 				allPrice: 0,
 				formContent: '',
 				payType: '', //支付方式
@@ -946,6 +946,7 @@
 			onAddress: function() {
 				let that = this;
 				if (this.addressInfo.real_name) {
+					this.$refs.addressWindow.getAddressList();
 					that.textareaStatus = false;
 					that.address.address = true;
 					that.pagesUrl = '/pages/users/user_address_list/index?news=' + this.news + '&cartId=' + this
@@ -954,6 +955,7 @@
 						this.pinkId +
 						'&couponId=' +
 						this.couponId;
+
 				} else {
 					uni.navigateTo({
 						url: '/pages/users/user_address/index?cartId=' + this.cartId + '&pinkId=' + this