Selaa lähdekoodia

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

evoxwht 2 vuotta sitten
vanhempi
commit
8ba0348f01

+ 4 - 0
crmeb/app/services/system/SystemCrudServices.php

@@ -148,6 +148,10 @@ class SystemCrudServices extends BaseServices
                 'addSoftDelete',
             ],
             'form' => [
+                [
+                    'value' => '',
+                    'label' => '请选择',
+                ],
                 [
                     'value' => 'input',
                     'label' => '输入框',

+ 5 - 0
template/admin/package-lock.json

@@ -23078,6 +23078,11 @@
         "diff-match-patch": "^1.0.0"
       }
     },
+    "vue-cropper": {
+      "version": "0.5.11",
+      "resolved": "https://registry.npmmirror.com/vue-cropper/-/vue-cropper-0.5.11.tgz",
+      "integrity": "sha512-UeA3qL2BLCTGkOEAxEsxSNFO+qLYAn6YRHv4oS32cP9lMhF1vFmnAf/z+ZamtR0/Fh3sbZeZUCLVR2Ol2/dpTQ=="
+    },
     "vue-eslint-parser": {
       "version": "7.11.0",
       "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz",

+ 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>

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

@@ -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>
@@ -262,8 +262,8 @@ export default {
     align-items: center;
     white-space: nowrap;
     &-photo {
-      width: 25px;
-      height: 25px;
+      width: 30px;
+      height: 30px;
       border-radius: 100%;
     }
   }

+ 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" footer-hide 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>

+ 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;
     },

+ 8 - 9
template/admin/src/utils/auth.js

@@ -19,15 +19,14 @@ export default {
   async install(Vue, options) {
     Vue.directive('auth', {
       inserted(el, binding, vnode) {
-        const { value } = binding;
-        const access = store.state.userInfo.access;
-
-        if (value && value instanceof Array && value.length && access && access.length) {
-          const isPermission = includeArray(value, access);
-          if (!isPermission) {
-            el.parentNode && el.parentNode.removeChild(el);
-          }
-        }
+        // const { value } = binding;
+        // const access = store.state.userInfo.access;
+        // if (value && value instanceof Array && value.length && access && access.length) {
+        //   const isPermission = includeArray(value, access);
+        //   if (!isPermission) {
+        //     el.parentNode && el.parentNode.removeChild(el);
+        //   }
+        // }
       },
     });
   },

+ 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

+ 2 - 1
template/uni-app/pages/goods_cate/goods_cate1.vue

@@ -346,7 +346,8 @@
 		overflow: hidden;
 		padding: 0 14rpx;
 		background-color: #fff;
-		position: relative;
+		position: relative;
+		padding-bottom: 200rpx;
 	}
 
 	.productSort .conter .listw {

+ 21 - 6
template/uni-app/pages/order_addcart/order_addcart.vue

@@ -258,18 +258,18 @@
 			this.canShow = false
 			if (this.isLogin == true) {
 				this.hotPage = 1;
-				this.hostProduct = [],
-					this.hotScroll = false,
-					this.getHostProduct();
+				this.hostProduct = []
+				this.hotScroll = false
+				this.getHostProduct();
 				this.loadend = false;
 				this.page = 1;
 				this.cartList.valid = [];
-				this.getCartList();
+				this.getCartList(1);
 				this.loadendInvalid = false;
 				this.pageInvalid = 1;
 				this.cartList.invalid = [];
 				this.getInvalidList();
-				this.getCartNum();
+				// this.getCartNum();
 				this.goodsHidden = true;
 				this.footerswitch = true;
 				this.hostProduct = [];
@@ -769,7 +769,7 @@
 					})
 				});
 			},
-			async getCartList() {
+			async getCartList(init) {
 				uni.showLoading({
 					title: this.$t(`加载中`),
 					mask: true
@@ -782,6 +782,21 @@
 				}
 				getCartCounts().then(async c => {
 					that.cartCount = c.data.count;
+					if (init) {
+						this.adding = false
+						this.$store.commit('indexData/setCartNum', c.data.count > 99 ? '..' : c.data
+							.count)
+						if (c.data.count > 0) {
+							wx.setTabBarBadge({
+								index: 2,
+								text: c.data.count + ''
+							})
+						} else {
+							wx.hideTabBarRedDot({
+								index: 2
+							})
+						}
+					}
 					for (let i = 0; i < Math.ceil(c.data.ids.length / that.limit); i++) {
 						let cartList = await this.getCartData(data)
 						let valid = cartList.valid