Bladeren bron

!243 合并 oss 私有库功能
update 优化 支持 oss 私有库功能

疯狂的狮子Li 2 jaren geleden
bovenliggende
commit
cd9c3c3f4f

+ 4 - 18
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysOssController.java

@@ -15,6 +15,8 @@ import com.ruoyi.common.core.validate.QueryGroup;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.common.utils.file.FileUtils;
+import com.ruoyi.oss.core.OssClient;
+import com.ruoyi.oss.factory.OssFactory;
 import com.ruoyi.system.domain.SysOss;
 import com.ruoyi.system.domain.bo.SysOssBo;
 import com.ruoyi.system.domain.vo.SysOssVo;
@@ -80,7 +82,7 @@ public class SysOssController extends BaseController {
         if (ObjectUtil.isNull(file)) {
             throw new ServiceException("上传文件不能为空");
         }
-        SysOss oss = iSysOssService.upload(file);
+        SysOssVo oss = iSysOssService.upload(file);
         Map<String, String> map = new HashMap<>(2);
         map.put("url", oss.getUrl());
         map.put("fileName", oss.getOriginalName());
@@ -96,23 +98,7 @@ public class SysOssController extends BaseController {
     @SaCheckPermission("system:oss:download")
     @GetMapping("/download/{ossId}")
     public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
-        SysOssVo sysOss = iSysOssService.getById(ossId);
-        if (ObjectUtil.isNull(sysOss)) {
-            throw new ServiceException("文件数据不存在!");
-        }
-        FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
-        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
-        long data;
-        try {
-            data = HttpUtil.download(sysOss.getUrl(), response.getOutputStream(), false);
-        } catch (HttpException e) {
-            if (e.getMessage().contains("403")) {
-                throw new ServiceException("无读取权限, 请在对应的OSS开启'公有读'权限!");
-            } else {
-                throw new ServiceException(e.getMessage());
-            }
-        }
-        response.setContentLength(Convert.toInt(data));
+        iSysOssService.download(ossId,response);
     }
 
     /**

+ 2 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java

@@ -12,6 +12,7 @@ import com.ruoyi.common.helper.LoginHelper;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.file.MimeTypeUtils;
 import com.ruoyi.system.domain.SysOss;
+import com.ruoyi.system.domain.vo.SysOssVo;
 import com.ruoyi.system.service.ISysOssService;
 import com.ruoyi.system.service.ISysUserService;
 import lombok.RequiredArgsConstructor;
@@ -115,7 +116,7 @@ public class SysProfileController extends BaseController {
             if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
                 return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
             }
-            SysOss oss = iSysOssService.upload(avatarfile);
+            SysOssVo oss = iSysOssService.upload(avatarfile);
             String avatar = oss.getUrl();
             if (userService.updateUserAvatar(getUsername(), avatar)) {
                 ajax.put("imgUrl", avatar);

+ 28 - 5
ruoyi-oss/src/main/java/com/ruoyi/oss/core/OssClient.java

@@ -2,6 +2,7 @@ package com.ruoyi.oss.core;
 
 import cn.hutool.core.util.IdUtil;
 import com.amazonaws.ClientConfiguration;
+import com.amazonaws.HttpMethod;
 import com.amazonaws.Protocol;
 import com.amazonaws.auth.AWSCredentials;
 import com.amazonaws.auth.AWSCredentialsProvider;
@@ -16,12 +17,15 @@ import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.oss.constant.OssConstant;
 import com.ruoyi.oss.entity.UploadResult;
+import com.ruoyi.oss.enumd.AccessPolicyType;
 import com.ruoyi.oss.enumd.PolicyType;
 import com.ruoyi.oss.exception.OssException;
 import com.ruoyi.oss.properties.OssProperties;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.net.URL;
+import java.util.Date;
 
 /**
  * S3 存储协议 所有兼容S3协议的云厂商均支持
@@ -57,7 +61,7 @@ public class OssClient {
                 .withClientConfiguration(clientConfig)
                 .withCredentials(credentialsProvider)
                 .disableChunkedEncoding();
-            if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)){
+            if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
                 // minio 使用https限制使用域名访问 需要此配置 站点填域名
                 build.enablePathStyleAccess();
             }
@@ -79,9 +83,10 @@ public class OssClient {
                 return;
             }
             CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
-            createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
+            AccessPolicyType accessPolicy = getAccessPolicy();
+            createBucketRequest.setCannedAcl(accessPolicy.getAcl());
             client.createBucket(createBucketRequest);
-            client.setBucketPolicy(bucketName, getPolicy(bucketName, PolicyType.READ));
+            client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
         } catch (Exception e) {
             throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
         }
@@ -98,7 +103,7 @@ public class OssClient {
             metadata.setContentLength(inputStream.available());
             PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
             // 设置上传对象的 Acl 为公共读
-            putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
+            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
             client.putObject(putObjectRequest);
         } catch (Exception e) {
             throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
@@ -138,7 +143,7 @@ public class OssClient {
         String endpoint = properties.getEndpoint();
         String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
         // 云服务商直接返回
-        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)){
+        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
             if (StringUtils.isNotBlank(domain)) {
                 return header + domain;
             }
@@ -167,6 +172,24 @@ public class OssClient {
         return configKey;
     }
 
+    public String getPrivateUrl(String objectKey, Integer second) {
+        GeneratePresignedUrlRequest generatePresignedUrlRequest =
+            new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
+                .withMethod(HttpMethod.GET)
+                .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
+        URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
+        return url.toString();
+    }
+
+    /**
+     * 获取当前桶权限类型
+     *
+     * @return 当前桶权限类型code
+     */
+    public AccessPolicyType getAccessPolicy() {
+        return AccessPolicyType.getByType(properties.getAccessPolicy());
+    }
+
     private static String getPolicy(String bucketName, PolicyType policyType) {
         StringBuilder builder = new StringBuilder();
         builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");

+ 55 - 0
ruoyi-oss/src/main/java/com/ruoyi/oss/enumd/AccessPolicyType.java

@@ -0,0 +1,55 @@
+package com.ruoyi.oss.enumd;
+
+import com.amazonaws.services.s3.model.CannedAccessControlList;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 桶访问策略配置
+ *
+ * @author 陈賝
+ */
+@Getter
+@AllArgsConstructor
+public enum AccessPolicyType {
+
+    /**
+     * private
+     */
+    PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE),
+
+    /**
+     * public
+     */
+    PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ),
+
+    /**
+     * custom
+     */
+    CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ);
+
+    /**
+     * 桶 权限类型
+     */
+    private final String type;
+
+    /**
+     * 文件对象 权限类型
+     */
+    private final CannedAccessControlList acl;
+
+    /**
+     * 桶策略类型
+     */
+    private final PolicyType policyType;
+
+    public static AccessPolicyType getByType(String type) {
+        for (AccessPolicyType value : values()) {
+            if (value.getType().equals(type)) {
+                return value;
+            }
+        }
+        throw new RuntimeException("'type' not found By " + type);
+    }
+
+}

+ 5 - 0
ruoyi-oss/src/main/java/com/ruoyi/oss/properties/OssProperties.java

@@ -50,4 +50,9 @@ public class OssProperties {
      */
     private String isHttps;
 
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    private String accessPolicy;
+
 }

+ 4 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOssConfig.java

@@ -82,4 +82,8 @@ public class SysOssConfig extends BaseEntity {
      */
     private String remark;
 
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    private String accessPolicy;
 }

+ 6 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/bo/SysOssConfigBo.java

@@ -98,4 +98,10 @@ public class SysOssConfigBo extends BaseEntity {
      */
     private String remark;
 
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    @NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String accessPolicy;
+
 }

+ 5 - 0
ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/SysOssConfigVo.java

@@ -82,4 +82,9 @@ public class SysOssConfigVo {
      */
     private String remark;
 
+    /**
+     * 桶权限类型(0private 1public 2custom)
+     */
+    private String accessPolicy;
+
 }

+ 5 - 1
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOssService.java

@@ -7,6 +7,8 @@ import com.ruoyi.system.domain.bo.SysOssBo;
 import com.ruoyi.system.domain.vo.SysOssVo;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.Collection;
 import java.util.List;
 
@@ -23,7 +25,9 @@ public interface ISysOssService {
 
     SysOssVo getById(Long ossId);
 
-    SysOss upload(MultipartFile file);
+    SysOssVo upload(MultipartFile file);
+
+    void download(Long ossId, HttpServletResponse response) throws IOException;
 
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 

+ 51 - 3
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOssServiceImpl.java

@@ -1,6 +1,9 @@
 package com.ruoyi.system.service.impl;
 
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -8,10 +11,13 @@ import com.ruoyi.common.constant.CacheNames;
 import com.ruoyi.common.core.domain.PageQuery;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.BeanCopyUtils;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.file.FileUtils;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.oss.core.OssClient;
 import com.ruoyi.oss.entity.UploadResult;
+import com.ruoyi.oss.enumd.AccessPolicyType;
 import com.ruoyi.oss.factory.OssFactory;
 import com.ruoyi.system.domain.SysOss;
 import com.ruoyi.system.domain.bo.SysOssBo;
@@ -20,14 +26,17 @@ import com.ruoyi.system.mapper.SysOssMapper;
 import com.ruoyi.system.service.ISysOssService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.cache.annotation.Cacheable;
+import org.springframework.http.MediaType;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 文件上传 服务层实现
@@ -44,6 +53,8 @@ public class SysOssServiceImpl implements ISysOssService {
     public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
         Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        List<SysOssVo> filterResult = result.getRecords().stream().map(this::matchingUrl).collect(Collectors.toList());
+        result.setRecords(filterResult);
         return TableDataInfo.build(result);
     }
 
@@ -53,7 +64,7 @@ public class SysOssServiceImpl implements ISysOssService {
         for (Long id : ossIds) {
             SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
             if (ObjectUtil.isNotNull(vo)) {
-                list.add(vo);
+                list.add(this.matchingUrl(vo));
             }
         }
         return list;
@@ -80,7 +91,28 @@ public class SysOssServiceImpl implements ISysOssService {
     }
 
     @Override
-    public SysOss upload(MultipartFile file) {
+    public void download(Long ossId, HttpServletResponse response) throws IOException {
+        SysOssVo sysOss = this.matchingUrl(SpringUtils.getAopProxy(this).getById(ossId));
+        if (ObjectUtil.isNull(sysOss)) {
+            throw new ServiceException("文件数据不存在!");
+        }
+        FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
+        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
+        long data;
+        try {
+            data = HttpUtil.download(sysOss.getUrl(), response.getOutputStream(), false);
+        } catch (HttpException e) {
+            if (e.getMessage().contains("403")) {
+                throw new ServiceException("无读取权限, 请在对应的OSS开启'公有读'权限!");
+            } else {
+                throw new ServiceException(e.getMessage());
+            }
+        }
+        response.setContentLength(Convert.toInt(data));
+    }
+
+    @Override
+    public SysOssVo upload(MultipartFile file) {
         String originalfileName = file.getOriginalFilename();
         String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
         OssClient storage = OssFactory.instance();
@@ -98,7 +130,9 @@ public class SysOssServiceImpl implements ISysOssService {
         oss.setOriginalName(originalfileName);
         oss.setService(storage.getConfigKey());
         baseMapper.insert(oss);
-        return oss;
+        SysOssVo sysOssVo = new SysOssVo();
+        BeanCopyUtils.copy(oss, sysOssVo);
+        return this.matchingUrl(sysOssVo);
     }
 
     @Override
@@ -114,4 +148,18 @@ public class SysOssServiceImpl implements ISysOssService {
         return baseMapper.deleteBatchIds(ids) > 0;
     }
 
+    /**
+     * 匹配Url
+     *
+     * @param oss OSS对象
+     * @return oss 匹配Url的OSS对象
+     */
+    private SysOssVo matchingUrl(SysOssVo oss) {
+        OssClient storage = OssFactory.instance(oss.getService());
+        // 仅修改桶类型为 private 的URL,临时URL时长为120s
+        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
+            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
+        }
+        return oss;
+    }
 }

+ 23 - 1
ruoyi-ui/src/views/system/oss/config.vue

@@ -80,6 +80,7 @@
       <el-table-column label="桶名称" align="center" prop="bucketName" />
       <el-table-column label="前缀" align="center" prop="prefix" />
       <el-table-column label="域" align="center" prop="region" />
+      <el-table-column label="桶权限类型" align="center" prop="accessPolicy" :formatter="accessPolicyStateFormat" />
       <el-table-column label="状态" align="center" prop="status">
         <template slot-scope="scope">
           <el-switch
@@ -151,6 +152,13 @@
             >{{dict.label}}</el-radio>
           </el-radio-group>
         </el-form-item>
+        <el-form-item label="桶权限类型">
+          <el-radio-group v-model="form.accessPolicy">
+            <el-radio label="0">private</el-radio>
+            <el-radio label="1">public</el-radio>
+            <el-radio label="2">custom</el-radio>
+          </el-radio-group>
+        </el-form-item>
         <el-form-item label="域" prop="region">
           <el-input v-model="form.region" placeholder="请输入域" />
         </el-form-item>
@@ -259,6 +267,9 @@ export default {
             trigger: "blur",
           },
         ],
+        accessPolicy:[
+          { required: true, message: "accessPolicy不能为空", trigger: "blur" }
+        ]
       },
     };
   },
@@ -292,6 +303,7 @@ export default {
         endpoint: undefined,
         domain: undefined,
         isHttps: "N",
+        accessPolicy: "1",
         region: undefined,
         status: "1",
         remark: undefined,
@@ -382,7 +394,17 @@ export default {
       }).catch(() => {
         row.status = row.status === "0" ? "1" : "0";
       })
-    }
+    },
+    accessPolicyStateFormat(row) {
+        if (row.accessPolicy === "0") {
+          return <span class="el-tag el-tag--warning el-tag--medium el-tag--light">private</span>
+        } else if (row.accessPolicy === "1") {
+          return <span class="el-tag el-tag--success el-tag--medium el-tag--light">public</span>
+        } else if (row.accessPolicy === "2") {
+          return <span class="el-tag el-tag--medium el-tag--light">custom</span>
+        }
+
+      }
   }
 };
 </script>

+ 138 - 138
ruoyi-ui/src/views/system/role/selectUser.vue

@@ -1,138 +1,138 @@
-<template>
-  <!-- 授权用户 -->
-  <el-dialog title="选择用户" :visible.sync="visible" width="800px" top="5vh" append-to-body>
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
-      <el-form-item label="用户名称" prop="userName">
-        <el-input
-          v-model="queryParams.userName"
-          placeholder="请输入用户名称"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="手机号码" prop="phonenumber">
-        <el-input
-          v-model="queryParams.phonenumber"
-          placeholder="请输入手机号码"
-          clearable
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-    <el-row>
-      <el-table @row-click="clickRow" ref="table" :data="userList" @selection-change="handleSelectionChange" height="260px">
-        <el-table-column type="selection" width="55"></el-table-column>
-        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
-        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
-        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
-        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
-        <el-table-column label="状态" align="center" prop="status">
-          <template slot-scope="scope">
-            <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
-          <template slot-scope="scope">
-            <span>{{ parseTime(scope.row.createTime) }}</span>
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination
-        v-show="total>0"
-        :total="total"
-        :page.sync="queryParams.pageNum"
-        :limit.sync="queryParams.pageSize"
-        @pagination="getList"
-      />
-    </el-row>
-    <div slot="footer" class="dialog-footer">
-      <el-button type="primary" @click="handleSelectUser">确 定</el-button>
-      <el-button @click="visible = false">取 消</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import { unallocatedUserList, authUserSelectAll } from "@/api/system/role";
-export default {
-  dicts: ['sys_normal_disable'],
-  props: {
-    // 角色编号
-    roleId: {
-      type: [Number, String]
-    }
-  },
-  data() {
-    return {
-      // 遮罩层
-      visible: false,
-      // 选中数组值
-      userIds: [],
-      // 总条数
-      total: 0,
-      // 未授权用户数据
-      userList: [],
-      // 查询参数
-      queryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        roleId: undefined,
-        userName: undefined,
-        phonenumber: undefined
-      }
-    };
-  },
-  methods: {
-    // 显示弹框
-    show() {
-      this.queryParams.roleId = this.roleId;
-      this.getList();
-      this.visible = true;
-    },
-    clickRow(row) {
-      this.$refs.table.toggleRowSelection(row);
-    },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.userIds = selection.map(item => item.userId);
-    },
-    // 查询表数据
-    getList() {
-      unallocatedUserList(this.queryParams).then(res => {
-        this.userList = res.rows;
-        this.total = res.total;
-      });
-    },
-    /** 搜索按钮操作 */
-    handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.resetForm("queryForm");
-      this.handleQuery();
-    },
-    /** 选择授权用户操作 */
-    handleSelectUser() {
-      const roleId = this.queryParams.roleId;
-      const userIds = this.userIds.join(",");
-      if (userIds == "") {
-        this.$modal.msgError("请选择要分配的用户");
-        return;
-      }
-      authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {
-        this.$modal.msgSuccess(res.msg);
-        if (res.code === 200) {
-          this.visible = false;
-          this.$emit("ok");
-        }
-      });
-    }
-  }
-};
-</script>
+<template>
+  <!-- 授权用户 -->
+  <el-dialog title="选择用户" :visible.sync="visible" width="800px" top="5vh" append-to-body>
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
+      <el-form-item label="用户名称" prop="userName">
+        <el-input
+          v-model="queryParams.userName"
+          placeholder="请输入用户名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="手机号码" prop="phonenumber">
+        <el-input
+          v-model="queryParams.phonenumber"
+          placeholder="请输入手机号码"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row>
+      <el-table @row-click="clickRow" ref="table" :data="userList" @selection-change="handleSelectionChange" height="260px">
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
+        <el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
+        <el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
+        <el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
+        <el-table-column label="状态" align="center" prop="status">
+          <template slot-scope="scope">
+            <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+          <template slot-scope="scope">
+            <span>{{ parseTime(scope.row.createTime) }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total>0"
+        :total="total"
+        :page.sync="queryParams.pageNum"
+        :limit.sync="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </el-row>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="handleSelectUser">确 定</el-button>
+      <el-button @click="visible = false">取 消</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { unallocatedUserList, authUserSelectAll } from "@/api/system/role";
+export default {
+  dicts: ['sys_normal_disable'],
+  props: {
+    // 角色编号
+    roleId: {
+      type: [Number, String]
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      visible: false,
+      // 选中数组值
+      userIds: [],
+      // 总条数
+      total: 0,
+      // 未授权用户数据
+      userList: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        roleId: undefined,
+        userName: undefined,
+        phonenumber: undefined
+      }
+    };
+  },
+  methods: {
+    // 显示弹框
+    show() {
+      this.queryParams.roleId = this.roleId;
+      this.getList();
+      this.visible = true;
+    },
+    clickRow(row) {
+      this.$refs.table.toggleRowSelection(row);
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.userIds = selection.map(item => item.userId);
+    },
+    // 查询表数据
+    getList() {
+      unallocatedUserList(this.queryParams).then(res => {
+        this.userList = res.rows;
+        this.total = res.total;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 选择授权用户操作 */
+    handleSelectUser() {
+      const roleId = this.queryParams.roleId;
+      const userIds = this.userIds.join(",");
+      if (userIds == "") {
+        this.$modal.msgError("请选择要分配的用户");
+        return;
+      }
+      authUserSelectAll({ roleId: roleId, userIds: userIds }).then(res => {
+        this.$modal.msgSuccess(res.msg);
+        if (res.code === 200) {
+          this.visible = false;
+          this.$emit("ok");
+        }
+      });
+    }
+  }
+};
+</script>

+ 6 - 6
script/sql/ry_vue_4.X.sql

@@ -674,6 +674,7 @@ create table sys_oss_config (
   domain           varchar(255)           default ''      comment '自定义域名',
   is_https         char(1)                default 'N'     comment '是否https(Y=是,N=否)',
   region           varchar(255)           default ''      comment '域',
+  access_policy    char(1)     not null   default '1'     comment '桶权限类型(0=private 1=public 2=custom)',
   status           char(1)                default '1'     comment '状态(0=正常,1=停用)',
   ext1             varchar(255)           default ''      comment '扩展字段',
   create_by       varchar(64)             default ''      comment '创建者',
@@ -684,9 +685,8 @@ create table sys_oss_config (
   primary key (oss_config_id)
 ) engine=innodb comment='对象存储配置表';
 
-insert into sys_oss_config values (1, 'minio',  'ruoyi',            'ruoyi123',        'ruoyi',             '', '127.0.0.1:9000',                '','N', '',            '0', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
-insert into sys_oss_config values (2, 'qiniu',  'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 's3-cn-north-1.qiniucs.com',     '','N', '',            '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
-insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'oss-cn-beijing.aliyuncs.com',   '','N', '',            '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
-insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi-1250000000',  '', 'cos.ap-beijing.myqcloud.com',   '','N', 'ap-beijing',  '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
-insert into sys_oss_config values (5, 'image',  'ruoyi',            'ruoyi123',        'ruoyi',             'image', '127.0.0.1:9000',           '','N', '',            '1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
-
+insert into sys_oss_config values (1, 'minio',  'ruoyi',            'ruoyi123',        'ruoyi',             '', '127.0.0.1:9000',                '','N', '',             '1' ,'0', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
+insert into sys_oss_config values (2, 'qiniu',  'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 's3-cn-north-1.qiniucs.com',     '','N', '',             '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
+insert into sys_oss_config values (3, 'aliyun', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi',             '', 'oss-cn-beijing.aliyuncs.com',   '','N', '',             '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
+insert into sys_oss_config values (4, 'qcloud', 'XXXXXXXXXXXXXXX',  'XXXXXXXXXXXXXXX', 'ruoyi-1250000000',  '', 'cos.ap-beijing.myqcloud.com',   '','N', 'ap-beijing',   '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);
+insert into sys_oss_config values (5, 'image',  'ruoyi',            'ruoyi123',        'ruoyi',             'image', '127.0.0.1:9000',           '','N', '',             '1' ,'1', '', 'admin', sysdate(), 'admin', sysdate(), NULL);