diff --git a/pom.xml b/pom.xml index c1ebee85..5b8319b2 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.ruoyi ruoyi - ${ruoyi.version} + 3.3.0 管理系统 微服务系统 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java index abe93c0c..3e8f71c9 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/JwtUtils.java @@ -1,6 +1,5 @@ package com.ruoyi.common.core.utils; -import java.util.Map; import com.ruoyi.common.core.constant.SecurityConstants; import com.ruoyi.common.core.constant.TokenConstants; import com.ruoyi.common.core.text.Convert; @@ -8,10 +7,12 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import java.util.Map; + /** * Jwt工具类 * - * @author ruoyi + * @author xiejs */ public class JwtUtils { diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/OssController.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/OssController.java new file mode 100644 index 00000000..5cd82cbc --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/OssController.java @@ -0,0 +1,74 @@ +package com.ruoyi.file.controller; + +import com.aliyun.oss.common.utils.BinaryUtil; +import com.aliyun.oss.model.MatchMode; +import com.aliyun.oss.model.PolicyConditions; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.file.utils.OssClient; +import io.swagger.annotations.Api; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +import static com.ruoyi.file.utils.OssClient.*; + +/** + * oss服务控制器 + * + * @author xiejs + * @since 2022-03-15 + */ +@RestController +@RequestMapping("oss") +@Api(tags = "OSS管理") +@Log4j2 +public class OssController { + + @Autowired + OssClient ossClient; + + + @GetMapping("/policy") + public R policy() { + String host = HTTPS + bucketName + DOT + endpoint; + String dir = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + + Map respMap = null; + try { + //10分钟过期 + long expireTime = 600; + long expireEndTime = System.currentTimeMillis() + expireTime * 1000; + Date expiration = new Date(expireEndTime); + PolicyConditions policyConds = new PolicyConditions(); + policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); + policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); + + String postPolicy = ossClient.getOssClient().generatePostPolicy(expiration, policyConds); + byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8); + String encodedPolicy = BinaryUtil.toBase64String(binaryData); + String postSignature = ossClient.getOssClient().calculatePostSignature(postPolicy); + + respMap = new LinkedHashMap<>(); + respMap.put("accessid", keyId); + respMap.put("policy", encodedPolicy); + respMap.put("signature", postSignature); + respMap.put("dir", dir); + respMap.put("host", host); + respMap.put("expire", String.valueOf(expireEndTime / 1000)); + + } catch (Exception e) { + log.error(e.getMessage()); + } + + return R.ok(respMap); + } + +} diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/AliyunOssFileServiceImpl.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/AliyunOssFileServiceImpl.java index 7f348648..f87217c9 100644 --- a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/AliyunOssFileServiceImpl.java +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/AliyunOssFileServiceImpl.java @@ -2,10 +2,9 @@ package com.ruoyi.file.service; import cn.hutool.core.date.DateUtil; import com.aliyun.oss.OSS; -import com.aliyun.oss.OSSClientBuilder; import com.ruoyi.common.core.text.UUID; -import com.ruoyi.file.config.AliyunOssProperties; import com.ruoyi.file.utils.FileUploadUtils; +import com.ruoyi.file.utils.OssClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; @@ -16,6 +15,8 @@ import java.io.IOException; import java.io.InputStream; import java.util.Date; +import static com.ruoyi.file.utils.OssClient.*; + /** * 阿里云oss文件上传实现 * @@ -26,22 +27,17 @@ import java.util.Date; @Primary public class AliyunOssFileServiceImpl implements ISysFileService { - public static final String HTTPS = "https://"; - - public static final String DOT = "."; - - public static final String SLASH = "/"; - @Autowired - private AliyunOssProperties aliyunOssProperties; + private OssClient ossClient; + @Override public String uploadFile(MultipartFile file) throws Exception { Assert.notNull(file, "file is null"); try { - String endpoint = aliyunOssProperties.getEndpoint(); - String bucketName = aliyunOssProperties.getBucketName(); - OSS ossClient = this.getOssClient(); + String endpoint = OssClient.endpoint; + String bucketName = OssClient.bucketName; + OSS oss = ossClient.getOssClient(); //获取流 InputStream is = file.getInputStream(); //获取文件后缀 @@ -49,9 +45,9 @@ public class AliyunOssFileServiceImpl implements ISysFileService { //获取文件名称 String fileName = this.getDataTime() + DOT + extension; //执行文件上传 bucket名称 文件名称 文件流 - ossClient.putObject(bucketName, fileName, is); + oss.putObject(bucketName, fileName, is); //关闭ossClient - ossClient.shutdown(); + oss.shutdown(); //拼接文件地址 return HTTPS + bucketName + DOT + endpoint + SLASH + fileName; } catch (IOException e) { @@ -62,8 +58,8 @@ public class AliyunOssFileServiceImpl implements ISysFileService { @Override public void removeFile(String url) { - String endpoint = aliyunOssProperties.getEndpoint(); - String bucketName = aliyunOssProperties.getBucketName(); + String endpoint = OssClient.endpoint; + String bucketName = OssClient.bucketName; String host = HTTPS + bucketName + DOT + endpoint + SLASH; //如果路径中不包含host @@ -73,14 +69,13 @@ public class AliyunOssFileServiceImpl implements ISysFileService { String objectName = url.substring(host.length()); - - OSS ossClient = this.getOssClient(); + OSS oss = ossClient.getOssClient(); //执行删除 - ossClient.deleteObject(bucketName, objectName); + oss.deleteObject(bucketName, objectName); //关闭ossClient - ossClient.shutdown(); + oss.shutdown(); } @@ -94,16 +89,4 @@ public class AliyunOssFileServiceImpl implements ISysFileService { return today + SLASH + UUID.randomUUID(); } - /** - * 获取oss实例 - * - * @return OSS - */ - private OSS getOssClient() { - String endpoint = aliyunOssProperties.getEndpoint(); - String keyId = aliyunOssProperties.getKeyId(); - String keySecret = aliyunOssProperties.getKeySecret(); - return new OSSClientBuilder().build(endpoint, - keyId, keySecret); - } } diff --git a/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/OssClient.java b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/OssClient.java new file mode 100644 index 00000000..ade3f32a --- /dev/null +++ b/ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/OssClient.java @@ -0,0 +1,57 @@ +package com.ruoyi.file.utils; + +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.ruoyi.file.config.AliyunOssProperties; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; + +/** + * 获取ossClient + * @author xiejs + * @since 2022-03-15 + */ +@Component +public class OssClient implements BeanPostProcessor { + + public static final String HTTPS = "https://"; + + public static final String DOT = "."; + + public static final String SLASH = "/"; + + public static String endpoint; + public static String keyId; + public static String keySecret; + public static String bucketName; + + @Autowired + private AliyunOssProperties aliyunOssProperties; + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + OssClient.endpoint = aliyunOssProperties.getEndpoint(); + OssClient.keyId = aliyunOssProperties.getKeyId(); + OssClient.keySecret = aliyunOssProperties.getKeySecret(); + OssClient.bucketName = aliyunOssProperties.getBucketName(); + return bean; + } + + + /** + * 获取oss实例 + * + * @return OSS + */ + public OSS getOssClient() { + String endpoint = aliyunOssProperties.getEndpoint(); + String keyId = aliyunOssProperties.getKeyId(); + String keySecret = aliyunOssProperties.getKeySecret(); + return new OSSClientBuilder().build(endpoint, + keyId, keySecret); + } + + +} diff --git a/ruoyi-ui/src/api/common.js b/ruoyi-ui/src/api/common.js index 11ee3eea..2bee12da 100644 --- a/ruoyi-ui/src/api/common.js +++ b/ruoyi-ui/src/api/common.js @@ -3,7 +3,7 @@ import request from '@/utils/request' //上传图片 -export function uploadImg(data){ +export function uploadImg(data) { return request({ url: '/file/upload', method: 'post', @@ -12,10 +12,18 @@ export function uploadImg(data){ } //删除图片 -export function removeImg(url){ +export function removeImg(url) { return request({ url: '/file/remove', method: 'delete', params: url }) } + +//获取oss上传秘钥 +export function policy() { + return request({ + url: '/file/oss/policy', + method: 'get', + }) +} diff --git a/ruoyi-ui/src/components/OssUpload/multiUpload.vue b/ruoyi-ui/src/components/OssUpload/multiUpload.vue new file mode 100644 index 00000000..28ff917d --- /dev/null +++ b/ruoyi-ui/src/components/OssUpload/multiUpload.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/ruoyi-ui/src/components/OssUpload/singleUpload.vue b/ruoyi-ui/src/components/OssUpload/singleUpload.vue new file mode 100644 index 00000000..63c2bf1e --- /dev/null +++ b/ruoyi-ui/src/components/OssUpload/singleUpload.vue @@ -0,0 +1,143 @@ + + + + + + diff --git a/ruoyi-ui/src/utils/index.js b/ruoyi-ui/src/utils/index.js index 9679e757..d17f0ed6 100644 --- a/ruoyi-ui/src/utils/index.js +++ b/ruoyi-ui/src/utils/index.js @@ -1,390 +1,399 @@ -import { parseTime } from './ruoyi' - -/** - * 表格时间格式化 - */ -export function formatDate(cellValue) { - if (cellValue == null || cellValue == "") return ""; - var date = new Date(cellValue) - var year = date.getFullYear() - var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 - var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() - var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() - var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() - var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() - return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds -} - -/** - * @param {number} time - * @param {string} option - * @returns {string} - */ -export function formatTime(time, option) { - if (('' + time).length === 10) { - time = parseInt(time) * 1000 - } else { - time = +time - } - const d = new Date(time) - const now = Date.now() - - const diff = (now - d) / 1000 - - if (diff < 30) { - return '刚刚' - } else if (diff < 3600) { - // less 1 hour - return Math.ceil(diff / 60) + '分钟前' - } else if (diff < 3600 * 24) { - return Math.ceil(diff / 3600) + '小时前' - } else if (diff < 3600 * 24 * 2) { - return '1天前' - } - if (option) { - return parseTime(time, option) - } else { - return ( - d.getMonth() + - 1 + - '月' + - d.getDate() + - '日' + - d.getHours() + - '时' + - d.getMinutes() + - '分' - ) - } -} - -/** - * @param {string} url - * @returns {Object} - */ -export function getQueryObject(url) { - url = url == null ? window.location.href : url - const search = url.substring(url.lastIndexOf('?') + 1) - const obj = {} - const reg = /([^?&=]+)=([^?&=]*)/g - search.replace(reg, (rs, $1, $2) => { - const name = decodeURIComponent($1) - let val = decodeURIComponent($2) - val = String(val) - obj[name] = val - return rs - }) - return obj -} - -/** - * @param {string} input value - * @returns {number} output value - */ -export function byteLength(str) { - // returns the byte length of an utf8 string - let s = str.length - for (var i = str.length - 1; i >= 0; i--) { - const code = str.charCodeAt(i) - if (code > 0x7f && code <= 0x7ff) s++ - else if (code > 0x7ff && code <= 0xffff) s += 2 - if (code >= 0xDC00 && code <= 0xDFFF) i-- - } - return s -} - -/** - * @param {Array} actual - * @returns {Array} - */ -export function cleanArray(actual) { - const newArray = [] - for (let i = 0; i < actual.length; i++) { - if (actual[i]) { - newArray.push(actual[i]) - } - } - return newArray -} - -/** - * @param {Object} json - * @returns {Array} - */ -export function param(json) { - if (!json) return '' - return cleanArray( - Object.keys(json).map(key => { - if (json[key] === undefined) return '' - return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) - }) - ).join('&') -} - -/** - * @param {string} url - * @returns {Object} - */ -export function param2Obj(url) { - const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') - if (!search) { - return {} - } - const obj = {} - const searchArr = search.split('&') - searchArr.forEach(v => { - const index = v.indexOf('=') - if (index !== -1) { - const name = v.substring(0, index) - const val = v.substring(index + 1, v.length) - obj[name] = val - } - }) - return obj -} - -/** - * @param {string} val - * @returns {string} - */ -export function html2Text(val) { - const div = document.createElement('div') - div.innerHTML = val - return div.textContent || div.innerText -} - -/** - * Merges two objects, giving the last one precedence - * @param {Object} target - * @param {(Object|Array)} source - * @returns {Object} - */ -export function objectMerge(target, source) { - if (typeof target !== 'object') { - target = {} - } - if (Array.isArray(source)) { - return source.slice() - } - Object.keys(source).forEach(property => { - const sourceProperty = source[property] - if (typeof sourceProperty === 'object') { - target[property] = objectMerge(target[property], sourceProperty) - } else { - target[property] = sourceProperty - } - }) - return target -} - -/** - * @param {HTMLElement} element - * @param {string} className - */ -export function toggleClass(element, className) { - if (!element || !className) { - return - } - let classString = element.className - const nameIndex = classString.indexOf(className) - if (nameIndex === -1) { - classString += '' + className - } else { - classString = - classString.substr(0, nameIndex) + - classString.substr(nameIndex + className.length) - } - element.className = classString -} - -/** - * @param {string} type - * @returns {Date} - */ -export function getTime(type) { - if (type === 'start') { - return new Date().getTime() - 3600 * 1000 * 24 * 90 - } else { - return new Date(new Date().toDateString()) - } -} - -/** - * @param {Function} func - * @param {number} wait - * @param {boolean} immediate - * @return {*} - */ -export function debounce(func, wait, immediate) { - let timeout, args, context, timestamp, result - - const later = function() { - // 据上一次触发时间间隔 - const last = +new Date() - timestamp - - // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait - if (last < wait && last > 0) { - timeout = setTimeout(later, wait - last) - } else { - timeout = null - // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 - if (!immediate) { - result = func.apply(context, args) - if (!timeout) context = args = null - } - } - } - - return function(...args) { - context = this - timestamp = +new Date() - const callNow = immediate && !timeout - // 如果延时不存在,重新设定延时 - if (!timeout) timeout = setTimeout(later, wait) - if (callNow) { - result = func.apply(context, args) - context = args = null - } - - return result - } -} - -/** - * This is just a simple version of deep copy - * Has a lot of edge cases bug - * If you want to use a perfect deep copy, use lodash's _.cloneDeep - * @param {Object} source - * @returns {Object} - */ -export function deepClone(source) { - if (!source && typeof source !== 'object') { - throw new Error('error arguments', 'deepClone') - } - const targetObj = source.constructor === Array ? [] : {} - Object.keys(source).forEach(keys => { - if (source[keys] && typeof source[keys] === 'object') { - targetObj[keys] = deepClone(source[keys]) - } else { - targetObj[keys] = source[keys] - } - }) - return targetObj -} - -/** - * @param {Array} arr - * @returns {Array} - */ -export function uniqueArr(arr) { - return Array.from(new Set(arr)) -} - -/** - * @returns {string} - */ -export function createUniqueString() { - const timestamp = +new Date() + '' - const randomNum = parseInt((1 + Math.random()) * 65536) + '' - return (+(randomNum + timestamp)).toString(32) -} - -/** - * Check if an element has a class - * @param {HTMLElement} elm - * @param {string} cls - * @returns {boolean} - */ -export function hasClass(ele, cls) { - return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) -} - -/** - * Add class to element - * @param {HTMLElement} elm - * @param {string} cls - */ -export function addClass(ele, cls) { - if (!hasClass(ele, cls)) ele.className += ' ' + cls -} - -/** - * Remove class from element - * @param {HTMLElement} elm - * @param {string} cls - */ -export function removeClass(ele, cls) { - if (hasClass(ele, cls)) { - const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') - ele.className = ele.className.replace(reg, ' ') - } -} - -export function makeMap(str, expectsLowerCase) { - const map = Object.create(null) - const list = str.split(',') - for (let i = 0; i < list.length; i++) { - map[list[i]] = true - } - return expectsLowerCase - ? val => map[val.toLowerCase()] - : val => map[val] -} - -export const exportDefault = 'export default ' - -export const beautifierConf = { - html: { - indent_size: '2', - indent_char: ' ', - max_preserve_newlines: '-1', - preserve_newlines: false, - keep_array_indentation: false, - break_chained_methods: false, - indent_scripts: 'separate', - brace_style: 'end-expand', - space_before_conditional: true, - unescape_strings: false, - jslint_happy: false, - end_with_newline: true, - wrap_line_length: '110', - indent_inner_html: true, - comma_first: false, - e4x: true, - indent_empty_lines: true - }, - js: { - indent_size: '2', - indent_char: ' ', - max_preserve_newlines: '-1', - preserve_newlines: false, - keep_array_indentation: false, - break_chained_methods: false, - indent_scripts: 'normal', - brace_style: 'end-expand', - space_before_conditional: true, - unescape_strings: false, - jslint_happy: true, - end_with_newline: true, - wrap_line_length: '110', - indent_inner_html: true, - comma_first: false, - e4x: true, - indent_empty_lines: true - } -} - -// 首字母大小 -export function titleCase(str) { - return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) -} - -// 下划转驼峰 -export function camelCase(str) { - return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) -} - -export function isNumberStr(str) { - return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) -} - +import { parseTime } from './ruoyi' + +/** + * 表格时间格式化 + */ +export function formatDate(cellValue) { + if (cellValue == null || cellValue == "") return ""; + var date = new Date(cellValue) + var year = date.getFullYear() + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str, expectsLowerCase) { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str) { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} + +/** + * 获取uuid + */ +export function getUUID () { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16) + }) +} + diff --git a/ruoyi-ui/src/views/mall/product/brand-add-or-update.vue b/ruoyi-ui/src/views/mall/product/brand-add-or-update.vue index f52548f7..e2b07acd 100644 --- a/ruoyi-ui/src/views/mall/product/brand-add-or-update.vue +++ b/ruoyi-ui/src/views/mall/product/brand-add-or-update.vue @@ -1,5 +1,5 @@