添加vue-router
This commit is contained in:
parent
26a2be3751
commit
0f7149f1b0
@ -86,6 +86,12 @@
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-spring</artifactId>
|
||||
<version>1.9.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.amqp</groupId>-->
|
||||
<!-- <artifactId>spring-rabbit-test</artifactId>-->
|
||||
|
@ -0,0 +1,77 @@
|
||||
package xyz.longicorn.driver.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
public class AuthFilter extends BasicHttpAuthenticationFilter {
|
||||
|
||||
//判断是否允许通过
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
return super.isAccessAllowed(request, response, mappedValue);
|
||||
}
|
||||
|
||||
//是否允许登录
|
||||
@Override
|
||||
protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
|
||||
return super.isLoginAttempt(request, response);
|
||||
}
|
||||
|
||||
// 创建shiro token
|
||||
@Override
|
||||
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
|
||||
return super.createToken(request, response);
|
||||
}
|
||||
|
||||
//isAccessAllowed为false时调用,验证失败
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
|
||||
this.sendChallenge(request, response);
|
||||
responseError(response, "he he !");
|
||||
return false;
|
||||
}
|
||||
|
||||
//shiro验证成功调用
|
||||
@Override
|
||||
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
|
||||
return super.onLoginSuccess(token, subject, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
|
||||
if (!isLoginAttempt(request, response)) {
|
||||
responseError(response, "verify token failure");
|
||||
return false;
|
||||
}
|
||||
return super.preHandle(request, response);
|
||||
}
|
||||
|
||||
private void setCors(ServletResponse response) {
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
// httpServletResponse.addHeader("Access-Control-Allow-Credentials", "true");
|
||||
httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
|
||||
httpServletResponse.addHeader("Access-Control-Allow-Methods", "*");
|
||||
httpServletResponse.addHeader("Access-Control-Allow-Headers", "*");
|
||||
}
|
||||
|
||||
private void responseError(ServletResponse response, Object msg) {
|
||||
setCors(response);
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
httpResponse.setStatus(401);
|
||||
httpResponse.setCharacterEncoding("UTF-8");
|
||||
httpResponse.setContentType("application/json;charset=UTF-8");
|
||||
try {
|
||||
String rj = msg instanceof String ? (String) msg : new ObjectMapper().writeValueAsString(msg);
|
||||
httpResponse.getWriter().append(rj);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package xyz.longicorn.driver.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
@Bean
|
||||
public MybatisPlusInterceptor paginationInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package xyz.longicorn.driver.config;
|
||||
|
||||
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
|
||||
import org.apache.shiro.mgt.DefaultSubjectDAO;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.realm.Realm;
|
||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class ShiroConfig {
|
||||
@Autowired
|
||||
private UserRealm userRealm;
|
||||
|
||||
//ShiroFilter过滤所有请求
|
||||
@Bean(name = "shiroFilterFactoryBean")
|
||||
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
|
||||
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
|
||||
Map<String, Filter> filterMap = new LinkedHashMap<>();
|
||||
filterMap.put("auth", new AuthFilter()); // 设置验证过滤器
|
||||
|
||||
shiroFilterFactoryBean.setFilters(filterMap);
|
||||
//给ShiroFilter配置安全管理器
|
||||
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
|
||||
//配置系统公共资源
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
//匿名资源 一定是在受限资源上面
|
||||
map.put("/api/**", "anon");
|
||||
//受限资源需要认证和授权
|
||||
map.put("/**", "auth");
|
||||
|
||||
// 设置认证界面路径
|
||||
// shiroFilterFactoryBean.setLoginUrl("/user/login");
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public DefaultWebSecurityManager defaultWebSecurityManager(UserRealm userRealm) {
|
||||
//设置自定义Realm
|
||||
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
|
||||
securityManager.setRealm(userRealm);
|
||||
//关闭shiro自带的session
|
||||
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
|
||||
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
|
||||
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
|
||||
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
|
||||
securityManager.setSubjectDAO(subjectDAO);
|
||||
return securityManager;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package xyz.longicorn.driver.config;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@Component
|
||||
public class UserRealm extends AuthorizingRealm {
|
||||
// 处理授权 获取凭证对该凭证赋权
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
|
||||
//获取身份信息
|
||||
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
|
||||
System.out.println("用户凭证:" + primaryPrincipal);
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
|
||||
|
||||
Set<String> roleSet = new HashSet<>(); // 角色列表
|
||||
info.setRoles(roleSet);
|
||||
|
||||
Set<String> permsSet = new HashSet<>(); // 权限列表
|
||||
info.setStringPermissions(permsSet);
|
||||
return info;
|
||||
}
|
||||
|
||||
// 处理认证 验证请求的凭证是否合法
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
|
||||
throws AuthenticationException {
|
||||
String credentials = (String) authenticationToken.getCredentials(); // 获取凭证
|
||||
System.out.println("用户凭证:" + credentials);
|
||||
//TODO 完成token的验证 返回数据库信息
|
||||
return new SimpleAuthenticationInfo(credentials, credentials, getName());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package xyz.longicorn.driver.controller;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import xyz.longicorn.driver.dto.ApiResult;
|
||||
import xyz.longicorn.driver.dto.FileItem;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/file")
|
||||
public class FileController {
|
||||
@PostMapping("/rename")
|
||||
public ApiResult rename(@Validated @RequestBody FileItem item) {
|
||||
return ApiResult.success(null);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import xyz.longicorn.driver.pojo.FileInfo;
|
||||
import xyz.longicorn.driver.service.FileService;
|
||||
import xyz.longicorn.driver.util.ImageUtils;
|
||||
import xyz.longicorn.driver.util.FileUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
@ -28,7 +28,7 @@ public class PreviewController {
|
||||
if (path == null) return;
|
||||
// 设置输出头
|
||||
resp.addHeader("Content-Type", "image/jpeg");
|
||||
ImageUtils.getPreview(path, resp.getOutputStream());
|
||||
FileUtils.getImagePreview(path, resp.getOutputStream());
|
||||
}
|
||||
|
||||
// 生成预览图
|
||||
@ -64,7 +64,7 @@ public class PreviewController {
|
||||
resp.getWriter().println("file not found!");
|
||||
return null;
|
||||
}
|
||||
if (!ImageUtils.isImage(info.getType())) { //判断是否是图片
|
||||
if (!FileUtils.isImage(info.getType())) { //判断是否是图片
|
||||
resp.getWriter().println("file can not preview!");
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package xyz.longicorn.driver.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import xyz.longicorn.driver.dto.ApiResult;
|
||||
import xyz.longicorn.driver.pojo.ShareInfo;
|
||||
import xyz.longicorn.driver.service.ShareService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/share")
|
||||
public class ShareController {
|
||||
@Resource
|
||||
private ShareService shareService;
|
||||
/**
|
||||
* 创建分享
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/create")
|
||||
public ApiResult create(ShareInfo info){
|
||||
return ApiResult.success(shareService.create(info));
|
||||
}
|
||||
@RequestMapping("/info")
|
||||
public ApiResult info(String id){
|
||||
return ApiResult.success(shareService.getById(id));
|
||||
}
|
||||
public ApiResult list(ShareInfo info){
|
||||
return ApiResult.success(shareService.create(info));
|
||||
}
|
||||
}
|
@ -7,10 +7,12 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
import xyz.longicorn.driver.config.BizException;
|
||||
import xyz.longicorn.driver.dto.ApiResult;
|
||||
import xyz.longicorn.driver.dto.FastUploadFile;
|
||||
import xyz.longicorn.driver.pojo.enums.Category;
|
||||
import xyz.longicorn.driver.pojo.FileInfo;
|
||||
import xyz.longicorn.driver.pojo.FolderInfo;
|
||||
import xyz.longicorn.driver.service.FileService;
|
||||
import xyz.longicorn.driver.service.FolderService;
|
||||
import xyz.longicorn.driver.util.FileUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
@ -26,15 +28,28 @@ public class UploadController {
|
||||
private FileService fileService;
|
||||
@Resource
|
||||
private FolderService folderService;
|
||||
|
||||
private final static Map<String, String> FILE_MIME_TYPE = new HashMap<>();
|
||||
|
||||
public UploadController() {
|
||||
FILE_MIME_TYPE.put("text/plain", "txt");
|
||||
FILE_MIME_TYPE.put("image/png", "png");
|
||||
FILE_MIME_TYPE.put("image/jpeg", "jpg");
|
||||
FILE_MIME_TYPE.put("image/jpg", "jpg");
|
||||
FILE_MIME_TYPE.put("image/bmp", "bmp");
|
||||
FILE_MIME_TYPE.put("image/webp", "webp");
|
||||
FILE_MIME_TYPE.put("image/gif", "gif");
|
||||
FILE_MIME_TYPE.put("image/svg+xml", "svg");
|
||||
FILE_MIME_TYPE.put("audio/mpeg", "mp3");
|
||||
FILE_MIME_TYPE.put("video/mp4", "mp4");
|
||||
FILE_MIME_TYPE.put("application/msword", "doc");
|
||||
FILE_MIME_TYPE.put("application/pdf", "pdf");
|
||||
FILE_MIME_TYPE.put("application/vnd.ms-excel", "xls");
|
||||
FILE_MIME_TYPE.put("application/vnd.ms-powerpoint", "ppt");
|
||||
FILE_MIME_TYPE.put("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx");
|
||||
FILE_MIME_TYPE.put("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx");
|
||||
FILE_MIME_TYPE.put("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx");
|
||||
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@ -59,7 +74,22 @@ public class UploadController {
|
||||
fileInfo.setHash(md5);//
|
||||
fileInfo.setName(file.getOriginalFilename());
|
||||
fileInfo.setSize(file.getSize());
|
||||
fileInfo.setType(getType(file.getContentType()));
|
||||
String type = getType(file.getContentType());// 获取文件的扩展名类型
|
||||
fileInfo.setType(type);
|
||||
|
||||
if (FileUtils.isImage(type)) {
|
||||
fileInfo.setCategory(Category.Image);
|
||||
} else if (FileUtils.isDocument(type)) {
|
||||
fileInfo.setCategory(Category.Document);
|
||||
} else if (FileUtils.isVideo(type)) {
|
||||
fileInfo.setCategory(Category.Video);
|
||||
} else if (FileUtils.isAudio(type)) {
|
||||
fileInfo.setCategory(Category.Audio);
|
||||
} else {
|
||||
fileInfo.setCategory(Category.Other);
|
||||
}
|
||||
|
||||
|
||||
if (f != null) { // 系统已经存在了该文件
|
||||
// 不保存上传文件 直接copy数据
|
||||
fileInfo.setPath(f.getPath());
|
||||
|
@ -0,0 +1,8 @@
|
||||
package xyz.longicorn.driver.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import xyz.longicorn.driver.pojo.ShareInfo;
|
||||
@Mapper
|
||||
public interface ShareInfoMapper extends BaseMapper<ShareInfo> {
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package xyz.longicorn.driver.dto;
|
||||
|
||||
public class ShareDto {
|
||||
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
package xyz.longicorn.driver.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.models.auth.In;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import xyz.longicorn.driver.pojo.enums.Category;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
@ -18,12 +16,16 @@ import java.util.Date;
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class FileInfo implements Serializable {
|
||||
|
||||
|
||||
private Long id;
|
||||
private Integer uid;
|
||||
private String name;
|
||||
private String hash;
|
||||
private Long folderId;
|
||||
private String type;
|
||||
|
||||
private Category category;
|
||||
private Long size;
|
||||
private String path;
|
||||
private Date createTime;
|
||||
|
@ -0,0 +1,14 @@
|
||||
package xyz.longicorn.driver.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import xyz.longicorn.driver.dao.ShareInfoMapper;
|
||||
import xyz.longicorn.driver.pojo.ShareInfo;
|
||||
|
||||
@Service
|
||||
public class ShareService extends ServiceImpl<ShareInfoMapper, ShareInfo> {
|
||||
public ShareInfo create(ShareInfo info) {
|
||||
this.save(info);
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package xyz.longicorn.driver.util;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.coobird.thumbnailator.Thumbnails;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class FileUtils {
|
||||
private static final List<String> ImageTypes = Arrays.asList("png", "jpg", "jpeg", "gif", "bmp", "webp");
|
||||
private static final List<String> DocumentsTypes = Arrays.asList("doc", "pdf", "docx", "ppt", "pptx", "xls", "xlsx");
|
||||
private static final List<String> VideoTypes = Arrays.asList("mp4", "mkv", "mpg");
|
||||
private static final List<String> AudioTypes = Arrays.asList("mp3", "wav", "ogg", "flac");
|
||||
|
||||
/**
|
||||
* 根据类型判断是否是图片
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean isImage(String type) {
|
||||
return ImageTypes.contains(type.toLowerCase());
|
||||
}
|
||||
public static boolean isDocument(String type) {
|
||||
return DocumentsTypes.contains(type.toLowerCase());
|
||||
}
|
||||
public static boolean isVideo(String type) {return VideoTypes.contains(type.toLowerCase());
|
||||
}
|
||||
public static boolean isAudio(String type) {
|
||||
return AudioTypes.contains(type.toLowerCase());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void getImagePreview(String imagePath, OutputStream os) {
|
||||
Thumbnails.of(new File(imagePath))
|
||||
.size(256, 256) // 尺寸 256x256
|
||||
.outputFormat("jpg") // 格式
|
||||
.toOutputStream(os);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package xyz.longicorn.driver.util;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.coobird.thumbnailator.Thumbnails;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ImageUtils {
|
||||
private static final List<String> imageTypes = Arrays.asList("png", "jpg", "jpeg", "gif", "bmp", "webp");
|
||||
|
||||
/**
|
||||
* 根据类型判断是否是图片
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static boolean isImage(String type) {
|
||||
return imageTypes.contains(type.toLowerCase());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void getPreview(String imagePath, OutputStream os) {
|
||||
Thumbnails.of(new File(imagePath))
|
||||
.size(256, 256) // 尺寸 256x256
|
||||
.outputFormat("jpg") // 格式
|
||||
.toOutputStream(os);
|
||||
}
|
||||
}
|
@ -70,3 +70,4 @@ mybatis-plus:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
# 实体类所在的包
|
||||
type-aliases-package: xyz.longicorn.driver.pojo
|
||||
type-enums-package: xyz.longicorn.driver.pojo.enums
|
||||
|
@ -15,17 +15,33 @@ create table folder_info
|
||||
);
|
||||
|
||||
# 编号、用户编号、文件名、目录编号 、类型、大小、位置、创建时间、状态、hash
|
||||
create table file_info(
|
||||
id bigint(20) not null primary key auto_increment,
|
||||
uid int(10) not null,
|
||||
name varchar(50) not null,
|
||||
hash varchar(32) not null comment '文件MD5特征码',
|
||||
folder_id bigint(20) null default 0 comment '所在目录的编号,0表示根目录',
|
||||
type varchar(5) null,
|
||||
size int null default 0,
|
||||
path varchar(500) not null comment '文件的物理存储位置,可以是本地路径也可以是一个网址', -- file://d:/a/b/c.txt http://
|
||||
create_time datetime default current_timestamp,
|
||||
update_time datetime on update current_timestamp,
|
||||
status tinyint(1) default 1,
|
||||
index ix_file_name(name)
|
||||
);
|
||||
create table file_info
|
||||
(
|
||||
id bigint(20) not null primary key auto_increment,
|
||||
uid int(10) not null,
|
||||
name varchar(50) not null,
|
||||
hash varchar(32) not null comment '文件MD5特征码',
|
||||
folder_id bigint(20) null default 0 comment '所在目录的编号,0表示根目录',
|
||||
type varchar(5) null,
|
||||
size int null default 0,
|
||||
path varchar(500) not null comment '文件的物理存储位置,可以是本地路径也可以是一个网址', -- file://d:/a/b/c.txt http://
|
||||
create_time datetime default current_timestamp,
|
||||
update_time datetime on update current_timestamp,
|
||||
status tinyint(1) default 1,
|
||||
index ix_file_name (name)
|
||||
);
|
||||
-- 编号、分享名称、用户编号、分享的数据编号、分类类型(file|folder)、提取码、分享有效期、状态
|
||||
create table share_info
|
||||
(
|
||||
id varchar(20) not null primary key,
|
||||
title varchar(20) not null,
|
||||
uid int(10) not null,
|
||||
file_id bigint(20) not null,
|
||||
type tinyint(1) default 1 comment '分类类型 1:文件 2:文件夹',
|
||||
password varchar(20) null,
|
||||
live int default 0 comment '有效期',
|
||||
create_time datetime default current_timestamp,
|
||||
update_time datetime on update current_timestamp,
|
||||
status tinyint(1) default 1,
|
||||
index ix_file_name (title)
|
||||
);
|
||||
|
12
driver/src/test/java/xyz/longicorn/driver/TestEnum.java
Normal file
12
driver/src/test/java/xyz/longicorn/driver/TestEnum.java
Normal file
@ -0,0 +1,12 @@
|
||||
package xyz.longicorn.driver;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import xyz.longicorn.driver.pojo.enums.Category;
|
||||
|
||||
public class TestEnum {
|
||||
@Test
|
||||
void testCategory(){
|
||||
Category c = Category.Audio;
|
||||
System.out.println(c);
|
||||
}
|
||||
}
|
1352
web/package-lock.json
generated
1352
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
133
web/src/App.vue
133
web/src/App.vue
@ -1,131 +1,14 @@
|
||||
<script setup>
|
||||
import { ref } from "@vue/reactivity";
|
||||
import FileIcon from "./components/FileIcon.vue";
|
||||
const data = ref({
|
||||
display: "block",
|
||||
activeIndex: 'all'
|
||||
});
|
||||
const tableData = [
|
||||
{
|
||||
id: 1,
|
||||
modify_time: "2016-05-03",
|
||||
name: "我的音乐",
|
||||
type: "folder",
|
||||
size: "-",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
modify_time: "2016-05-03",
|
||||
name: "我的文档",
|
||||
type: "folder",
|
||||
size: "-",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
modify_time: "2016-05-03",
|
||||
name: "我的视频",
|
||||
type: "folder",
|
||||
size: "-",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
modify_time: "2016-05-03",
|
||||
name: "我的图片",
|
||||
type: "folder",
|
||||
size: "-",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
modify_time: "2016-05-03",
|
||||
name: "APP.jpg",
|
||||
type: "image",
|
||||
size: "103KB",
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container>
|
||||
<el-aside width="200px" class="main-left-nav">
|
||||
<el-menu :default-active="data.activeIndex" class="main-type-filter" mode="vertical">
|
||||
<el-menu-item index="all">所有文件</el-menu-item>
|
||||
<el-menu-item index="rencent">最近上传</el-menu-item>
|
||||
<el-menu-item index="picture">图片</el-menu-item>
|
||||
<el-menu-item index="document">文档</el-menu-item>
|
||||
<el-menu-item index="video">视频</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header></el-header>
|
||||
<el-main>
|
||||
<el-button @click="data.display = data.display == 'list' ? 'block' : 'list'">{{ data.display == "list" ? "block"
|
||||
:
|
||||
"list"
|
||||
}}</el-button>
|
||||
<div class="pan-file-list-wrapper">
|
||||
<div class="pan-file-list-wrapper-table" v-if="data.display == 'list'">
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column type="selection" width="40px" />
|
||||
<el-table-column label="文件名称">
|
||||
<template #default="file">
|
||||
<div>
|
||||
<span>
|
||||
<FileIcon :file="file.row" style="margin-right: 5px; width: 20px" />
|
||||
</span>
|
||||
<span>{{ file.row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="size" label="大小" width="180" />
|
||||
<el-table-column prop="type" label="类型" width="180" />
|
||||
<el-table-column prop="modify_time" width="180" label="修改时间" />
|
||||
</el-table>
|
||||
</div>
|
||||
<el-row v-else style="margin-top: 20px" :gutter="20">
|
||||
<el-col :xs="8" :md="6" :lg="4" v-for="(file, i) in 13" :key="i">
|
||||
<div class="grid-content bg-purple" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
||||
<!-- 所有子页面显示在这里 -->
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
<script>
|
||||
export default {
|
||||
name: "App.vue"
|
||||
}
|
||||
</script>
|
||||
|
||||
.el-menu-item {
|
||||
transition: all 0.5s;
|
||||
opacity: 0.8;
|
||||
<style>
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.main-left-nav,
|
||||
.main-type-filter {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
height: 80px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.bg-purple {
|
||||
--un-bg-opacity: 1;
|
||||
background-color: rgba(192, 132, 252, var(--un-bg-opacity));
|
||||
}
|
||||
|
||||
.pan-file-list-wrapper-table {
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
387
web/src/Main.vue
387
web/src/Main.vue
@ -1,387 +0,0 @@
|
||||
<script>
|
||||
import {ArrowDown, Grid, FolderAdd,Search} from '@element-plus/icons-vue'
|
||||
import FileIcon from "./components/FileIcon.vue";
|
||||
import {dayjs, ElMessage, ElMessageBox} from 'element-plus'
|
||||
import api from "./service/api";
|
||||
import qs from "qs";
|
||||
import FileUploader from "./components/file-uploader/Index.vue";
|
||||
import FileBlockItem from "./components/FileBlockItem.vue";
|
||||
|
||||
export default {
|
||||
setup(){
|
||||
return {Search}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentActiveIndex: "all",
|
||||
display: 'block',
|
||||
/**
|
||||
* @var {FileItem[]}
|
||||
*/
|
||||
fileData: [],
|
||||
currentPath: '/',
|
||||
fileMenuOption: [
|
||||
{
|
||||
name: '打开',
|
||||
slug: 'open',
|
||||
},
|
||||
{
|
||||
name: '下载',
|
||||
slug: 'download',
|
||||
},
|
||||
{
|
||||
name: '分享',
|
||||
slug: 'share',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: '复制',
|
||||
slug: 'copy',
|
||||
},
|
||||
{
|
||||
name: '移动',
|
||||
slug: 'move',
|
||||
},
|
||||
{
|
||||
name: '重命名',
|
||||
slug: 'rename',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: '删除',
|
||||
slug: 'delete',
|
||||
},
|
||||
],
|
||||
createFolder: {
|
||||
visible: false,
|
||||
value: '',
|
||||
loading: false,
|
||||
message: ''
|
||||
},
|
||||
search:{
|
||||
value:''
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentPathList() {
|
||||
if (this.currentPath == '/') return []
|
||||
const arr = this.currentPath.replace(/^\//, '').split('/');
|
||||
const pathList = [], last = arr.pop();
|
||||
let prefix = '';
|
||||
arr.forEach(name => {
|
||||
prefix += '/' + name;
|
||||
pathList.push({name: name, path: prefix})
|
||||
})
|
||||
pathList.push({name: last, path: null})
|
||||
return pathList;
|
||||
}
|
||||
},
|
||||
components: {FileBlockItem, FileUploader, FileIcon, ArrowDown, Grid, FolderAdd},
|
||||
mounted() {
|
||||
// window.addEventListener('popstate',()=>console.log(location.href))
|
||||
window.addEventListener('hashchange', this.handleHashChange) // 添加监听
|
||||
this.handleHashChange();
|
||||
},
|
||||
unmounted() {
|
||||
window.removeEventListener('hashchange', this.handleHashChange); // 取消监听
|
||||
},
|
||||
methods: {
|
||||
// 加载目录下的所有文件
|
||||
async loadFileByPath(path = '/') {
|
||||
this.currentPath = path // 保存了当前的请求路径
|
||||
// 调用接口
|
||||
try {
|
||||
this.fileData = [];
|
||||
const list = await api.folder.list(path);
|
||||
this.fileData = list;
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message)
|
||||
}
|
||||
},
|
||||
// 获取获取当前目录路径
|
||||
getCurrentPath() {
|
||||
const hash = location.hash;
|
||||
const params = qs.parse(hash.substr(2))
|
||||
return params.path || '/';
|
||||
},
|
||||
//记载所有的子文件
|
||||
handleHashChange() {
|
||||
this.loadFileByPath(this.getCurrentPath()) // 有参数则使用 无参数则根目录
|
||||
},
|
||||
formatDate(time, format = 'MM-DD') {
|
||||
return dayjs(time).format(format);
|
||||
},
|
||||
formatSize(a, b = 2) {
|
||||
if (0 == a) return "0 B";
|
||||
let c = 1024, d = b || 2, e = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
|
||||
f = Math.floor(Math.log(a) / Math.log(c));
|
||||
return parseFloat((a / Math.pow(c, f)).toFixed(d)) + " " + e[f];
|
||||
},
|
||||
// 对文件名进行处理
|
||||
formatName(name) {
|
||||
if (!name || name.length < 15) return name;
|
||||
const extIndex = name.lastIndexOf('.'),
|
||||
ext = name.substr(extIndex);
|
||||
return name.substr(0, (15 - ext.length)) + "**" + ext;
|
||||
},
|
||||
fileMenuClick(e) {
|
||||
window.alert(JSON.stringify(e));
|
||||
},
|
||||
createFolderClick() {
|
||||
ElMessageBox.prompt(
|
||||
'', '新建文件夹',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
closeOnClickModal: false,
|
||||
// inputPattern:/[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
|
||||
inputValidator(value) {
|
||||
return !!value;
|
||||
},
|
||||
inputErrorMessage: '必须填写文件夹名称',
|
||||
inputPlaceholder: '请输入文件夹名称'
|
||||
})
|
||||
.then(({value}) => {
|
||||
// 此处开始进行创建
|
||||
})
|
||||
.catch()
|
||||
},
|
||||
async handleCreateFolder() {
|
||||
this.createFolder.message = ''
|
||||
if (!this.createFolder.value) {
|
||||
this.createFolder.message = '请填写文件夹名称'
|
||||
return;
|
||||
}
|
||||
// 开始新建
|
||||
this.createFolder.loading = true;
|
||||
try {
|
||||
await api.folder.create(this.getCurrentPath(), this.createFolder.value);
|
||||
this.createFolder.visible = false;
|
||||
this.handleHashChange();
|
||||
} catch (e) {
|
||||
this.createFolder.message = e.message;
|
||||
} finally {
|
||||
this.createFolder.loading = false // 取消loading
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="main-pan-app" @contextmenu="$refs.fileMenu.hideContextMenu">
|
||||
<el-container>
|
||||
<el-aside class="pan-left-aside">
|
||||
<div class="logo-block">
|
||||
<svg class="logo-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="1000" height="1000">
|
||||
<path
|
||||
d="M549.766 97.925c19.974 4.609 50.703 35.339 62.995 55.312 35.339-13.828 36.875 41.484 46.094 55.312 23.047-16.901 46.094-35.339 46.094-55.312 0-21.51-30.729-35.339-30.729-41.484 0-12.292 38.411-12.292 41.484-12.292 50.703 0 106.016 39.948 106.016 96.797 0 27.656-9.219 41.484-33.802 70.677h32.266c21.51 0 27.656-3.073 55.312 7.682C923.125 294.591 960 348.367 960 389.852c0 3.073 0 9.219-1.536 13.828-4.609 3.073-12.292 3.073-16.901 4.609-15.365 1.536-32.266 6.146-47.63 7.682-47.63 0-50.703 0-53.776-1.536-1.536 1.536-1.536 0-1.536 4.609 0 3.073 7.682 44.557 10.755 67.604 9.219 56.849 12.292 115.234 19.974 173.62 1.536 7.682 7.682 15.365 9.219 23.047 3.073 13.828 6.146 27.656 6.146 39.948 0 153.646-245.833 208.958-321.119 208.958h-86.042c-115.234-9.219-248.906-52.24-301.145-138.281-4.609-7.682-18.437-39.948-18.437-50.703v-36.875c3.073-16.901 7.682-33.802 21.51-50.703v-84.505l12.292-132.135c-12.292 1.536-33.802 1.536-38.411 1.536-21.51 0-38.411-3.073-61.458-6.146-7.682-1.536-18.437-3.073-24.583-6.146-4.609-1.536-3.073-12.292-3.073-13.828 0-44.557 44.557-107.552 98.333-119.844 4.609-1.536 13.828-1.536 19.974-3.073l41.484-1.536c-13.828-10.755-36.875-46.094-36.875-59.922v-29.193c13.828-58.385 62.995-82.969 106.016-82.969 1.536 0 41.484 0 41.484 12.292 0 6.146-30.729 19.974-30.729 41.484 0 1.536 6.146 32.266 16.901 32.266 3.073 0-1.536-3.073 3.073-3.073 3.073 0 32.266 10.755 36.875 10.755h12.292l3.073-3.073c-19.974-16.901-30.729-36.875-36.875-53.776 12.292 7.682 18.438 9.219 29.193 9.219 27.656 0 46.094-15.365 78.359-29.193 24.583-10.755 52.24-13.828 78.359-18.437-12.292-9.219-24.583-16.901-36.875-23.047l38.411-1.536c7.68 1.537 15.362 4.61 23.044 6.146z"
|
||||
p-id="2967"></path>
|
||||
</svg>
|
||||
<span>牛牛的网盘</span>
|
||||
</div>
|
||||
<el-menu :default-active="currentActiveIndex" class="pan-left-menu">
|
||||
<el-menu-item index="all">
|
||||
<span>所有文件</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="最近上传">
|
||||
<span>所有文件</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="picture">
|
||||
<span>我的图片</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="document">
|
||||
<span>我的文档</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="video">
|
||||
<span>我的视频</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header class="pan-header">
|
||||
<div class="d-flex">
|
||||
<div class="d-flex flex-1">
|
||||
<div class="left-tool-bar" style="padding-top: 15px;">
|
||||
<file-uploader @upload-success="handleHashChange" :current-folder="currentPath"/>
|
||||
|
||||
<el-button @click="createFolder.visible = true" size="default"
|
||||
style="margin-left: 10px" round>
|
||||
<el-icon>
|
||||
<folder-add/>
|
||||
</el-icon>
|
||||
<span style="margin-left: 4px;">新建文件夹</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="pan-header-user-right">
|
||||
<li><el-input
|
||||
v-model="search.value"
|
||||
class="search-box" clearable
|
||||
style="border-radius:30px;"
|
||||
placeholder="搜索你的文件"
|
||||
:prefix-icon="Search"
|
||||
/></li>
|
||||
<li>帮助</li>
|
||||
<li>
|
||||
<el-dropdown>
|
||||
<div class="el-dropdown-link">
|
||||
<el-avatar class="avatar" :size="30"
|
||||
src="https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png"/>
|
||||
<span class="username">张三</span>
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>修改资料</el-dropdown-item>
|
||||
<el-dropdown-item>登录日志查看</el-dropdown-item>
|
||||
<el-dropdown-item>退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<div style="line-height: 30px;" class="d-flex">
|
||||
<!-- 当前路径 -->
|
||||
<div style="flex:1;display: flex">
|
||||
<div v-if="currentPath === '/'" style="font-weight: 700;font-size: 14px;">全部文件</div>
|
||||
<div v-else>
|
||||
<el-breadcrumb separator="/" style="line-height: 30px">
|
||||
<el-breadcrumb-item>
|
||||
<a href="#?path=/">全部文件</a>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="p in currentPathList" :key="p.path">
|
||||
<a v-if="p.path" :href="'#?path=' + p.path">{{ p.name }}</a>
|
||||
<span v-else>{{ p.name }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex">
|
||||
<span
|
||||
style="margin-right: 10px;color:#999;font-size: 12px;">已全部加载,共{{
|
||||
fileData.length
|
||||
}}个</span>
|
||||
<el-radio-group v-model="display" size="small">
|
||||
<el-radio-button label="block">
|
||||
<el-icon :size="16">
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M384 128H213.333333a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333V213.333333a85.333333 85.333333 0 0 0-85.333333-85.333333zM810.666667 128h-170.666667a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333V213.333333a85.333333 85.333333 0 0 0-85.333333-85.333333zM384 554.666667H213.333333a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333v-170.666667a85.333333 85.333333 0 0 0-85.333333-85.333333zM810.666667 554.666667h-170.666667a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333v-170.666667a85.333333 85.333333 0 0 0-85.333333-85.333333z"
|
||||
p-id="2910"></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
</el-radio-button>
|
||||
<el-radio-button label="list">
|
||||
<el-icon :size="16">
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M146.285714 749.714286v109.714285c0 9.728-8.557714 18.285714-18.285714 18.285715h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285715v-109.714285c0-9.728 8.557714-18.285714 18.285714-18.285715h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285715z m0-219.428572v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285714-18.285714h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285714z m0-219.428571v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285714-18.285714h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285714z m877.714286 438.857143v109.714285c0 9.728-8.557714 18.285714-18.285714 18.285715h-768a18.797714 18.797714 0 0 1-18.285715-18.285715v-109.714285c0-9.728 8.557714-18.285714 18.285715-18.285715h768c9.728 0 18.285714 8.557714 18.285714 18.285715zM146.285714 91.428571v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285714-18.285714h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285714z m877.714286 438.857143v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-768a18.797714 18.797714 0 0 1-18.285715-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285715-18.285714h768c9.728 0 18.285714 8.557714 18.285714 18.285714z m0-219.428571v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-768a18.797714 18.797714 0 0 1-18.285715-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285715-18.285714h768c9.728 0 18.285714 8.557714 18.285714 18.285714z m0-219.428572v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-768a18.797714 18.797714 0 0 1-18.285715-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285715-18.285714h768c9.728 0 18.285714 8.557714 18.285714 18.285714z"></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="display-type-list" v-if="display == 'list'">
|
||||
<el-table :data="fileData" style="width: 100%">
|
||||
<el-table-column type="selection"/>
|
||||
<el-table-column label="名称">
|
||||
<!-- 自定义单元格内容 -->
|
||||
<template #default="file">
|
||||
<div class="list-file-info">
|
||||
<FileIcon class="list-file-icon" style="width: 40px;" :file="file.row"
|
||||
:ext="file.row.type"/>
|
||||
<span class="list-file-name">{{ file.row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="类型" width="200"/>
|
||||
<el-table-column prop="size" label="大小" width="200"/>
|
||||
<el-table-column label="创建时间" width="200">
|
||||
<template #default="file">
|
||||
<span class="list-file-time">{{ formatDate(file.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="display-type-block" v-else>
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="12" :sm="6" :md="4" :lg="3" :xl="2" v-for="(file, index) in fileData"
|
||||
:key="index">
|
||||
<file-block-item :file="file" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
<!-- <context-menu class="file-cxt-menu" element-id="myMenu1" ref="fileMenu" :options="fileMenuOption"-->
|
||||
<!-- @option-clicked="fileMenuClick"/>-->
|
||||
<!-- 新建目录 -->
|
||||
<el-dialog v-model="createFolder.visible" title="新建文件夹" width="500px">
|
||||
<div>
|
||||
<el-input placeholder="请输入文件夹名称" v-model="createFolder.value"/>
|
||||
<div style="color: red;height: 30px;line-height: 30px;">{{ createFolder.message }}</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<!-- 将此模板的内容指定插入到footer的位置-->
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="createFolder.visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="createFolder.loading" @click="handleCreateFolder">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less">
|
||||
|
||||
|
||||
// 平铺显示
|
||||
.file-block-item {
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
|
||||
.file-image {
|
||||
height: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> span {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
margin-top: 3px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -37,6 +37,15 @@
|
||||
border-radius: 30px;
|
||||
}
|
||||
}
|
||||
.share-create-dialog{
|
||||
.item{
|
||||
margin: 20px 0;
|
||||
}
|
||||
.title{
|
||||
line-height: 32px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
.logo-icon{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="file-block-item" @click="showFile(file,$event)"
|
||||
@contextmenu.prevent.stop="$parent.$refs.fileMenu.showMenu($event,file)">
|
||||
<div class="file-block-item" @click="showFile(file,$event)">
|
||||
<div class="file-image">
|
||||
<FileIcon :file="file" ref="fileItemIcon" style="width:90%"/>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dropdown>
|
||||
<el-button type="primary" round>
|
||||
<el-button type="primary">
|
||||
<label for="file_for_one">
|
||||
<el-icon class="el-icon--right">
|
||||
<Upload/>
|
||||
|
@ -8,9 +8,11 @@ import './assets/main.less'
|
||||
import ContextMenu from 'vue-simple-context-menu'
|
||||
import 'vue-simple-context-menu/dist/vue-simple-context-menu.css';
|
||||
// 导入入口组件
|
||||
import App from "./Main.vue";
|
||||
import App from "./App.vue";
|
||||
import index from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
app.component('ContextMenu', ContextMenu);
|
||||
app.use(ElementPlus)
|
||||
.use(index) // 使用路由实例
|
||||
.mount("#app");
|
||||
|
362
web/src/pages/All.vue
Normal file
362
web/src/pages/All.vue
Normal file
@ -0,0 +1,362 @@
|
||||
<template>
|
||||
<div style="line-height: 30px;" class="d-flex">
|
||||
<!-- 当前路径 -->
|
||||
<div style="flex:1;display: flex">
|
||||
<div v-if="currentPath === '/'" style="font-weight: 700;font-size: 14px;">全部文件</div>
|
||||
<div v-else>
|
||||
<el-breadcrumb separator="/" style="line-height: 30px">
|
||||
<el-breadcrumb-item>
|
||||
<a href="#?path=/">全部文件</a>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="p in currentPathList" :key="p.path">
|
||||
<a v-if="p.path" :href="'#?path=' + p.path">{{ p.name }}</a>
|
||||
<span v-else>{{ p.name }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex">
|
||||
<span
|
||||
style="margin-right: 10px;color:#999;font-size: 12px;">已全部加载,共{{
|
||||
fileData.length
|
||||
}}个</span>
|
||||
<el-radio-group v-model="display" size="small">
|
||||
<el-radio-button label="block">
|
||||
<el-icon :size="16">
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M384 128H213.333333a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333V213.333333a85.333333 85.333333 0 0 0-85.333333-85.333333zM810.666667 128h-170.666667a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333V213.333333a85.333333 85.333333 0 0 0-85.333333-85.333333zM384 554.666667H213.333333a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333v-170.666667a85.333333 85.333333 0 0 0-85.333333-85.333333zM810.666667 554.666667h-170.666667a85.333333 85.333333 0 0 0-85.333333 85.333333v170.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h170.666667a85.333333 85.333333 0 0 0 85.333333-85.333333v-170.666667a85.333333 85.333333 0 0 0-85.333333-85.333333z"
|
||||
p-id="2910"></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
</el-radio-button>
|
||||
<el-radio-button label="list">
|
||||
<el-icon :size="16">
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M146.285714 749.714286v109.714285c0 9.728-8.557714 18.285714-18.285714 18.285715h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285715v-109.714285c0-9.728 8.557714-18.285714 18.285714-18.285715h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285715z m0-219.428572v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285714-18.285714h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285714z m0-219.428571v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285714-18.285714h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285714z m877.714286 438.857143v109.714285c0 9.728-8.557714 18.285714-18.285714 18.285715h-768a18.797714 18.797714 0 0 1-18.285715-18.285715v-109.714285c0-9.728 8.557714-18.285714 18.285715-18.285715h768c9.728 0 18.285714 8.557714 18.285714 18.285715zM146.285714 91.428571v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-109.714286a18.797714 18.797714 0 0 1-18.285714-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285714-18.285714h109.714286c9.728 0 18.285714 8.557714 18.285714 18.285714z m877.714286 438.857143v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-768a18.797714 18.797714 0 0 1-18.285715-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285715-18.285714h768c9.728 0 18.285714 8.557714 18.285714 18.285714z m0-219.428571v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-768a18.797714 18.797714 0 0 1-18.285715-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285715-18.285714h768c9.728 0 18.285714 8.557714 18.285714 18.285714z m0-219.428572v109.714286c0 9.728-8.557714 18.285714-18.285714 18.285714h-768a18.797714 18.797714 0 0 1-18.285715-18.285714v-109.714286c0-9.728 8.557714-18.285714 18.285715-18.285714h768c9.728 0 18.285714 8.557714 18.285714 18.285714z"></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="display-type-list" v-if="display == 'list'">
|
||||
<el-table :data="fileData" style="width: 100%">
|
||||
<el-table-column type="selection"/>
|
||||
<el-table-column label="名称">
|
||||
<!-- 自定义单元格内容 -->
|
||||
<template #default="file">
|
||||
<div class="list-file-info">
|
||||
<FileIcon class="list-file-icon" style="width: 40px;" :file="file.row"
|
||||
:ext="file.row.type"/>
|
||||
<span class="list-file-name">{{ file.row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="类型" width="200"/>
|
||||
<el-table-column prop="size" label="大小" width="200"/>
|
||||
<el-table-column label="创建时间" width="200">
|
||||
<template #default="file">
|
||||
<span class="list-file-time">{{ formatDate(file.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="display-type-block" v-else>
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="12" :sm="6" :md="4" :lg="3" :xl="2" v-for="(file, index) in fileData"
|
||||
:key="index">
|
||||
<file-block-item :file="file"
|
||||
@contextmenu.prevent.stop="$refs.fileMenu.showMenu($event,file)"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<!-- 右键菜单 -->
|
||||
<context-menu class="file-cxt-menu" element-id="myMenu1" ref="fileMenu" :options="fileMenuOption"
|
||||
@option-clicked="fileMenuClick"/>
|
||||
<!-- 新建目录 -->
|
||||
<el-dialog v-model="createFolder.visible" title="新建文件夹" width="500px">
|
||||
<div>
|
||||
<el-input placeholder="请输入文件夹名称" v-model="createFolder.value"/>
|
||||
<div style="color: red;height: 30px;line-height: 30px;">{{ createFolder.message }}</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<!-- 将此模板的内容指定插入到footer的位置-->
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="createFolder.visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="createFolder.loading" @click="handleCreateFolder">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 分享 -->
|
||||
<el-dialog v-model="share.visible" title="分享文件" width="600px">
|
||||
<div class="share-create-dialog">
|
||||
<h2>{{ share.current?.name }}</h2>
|
||||
<div class="live d-flex item">
|
||||
<div class="title">有效期:</div>
|
||||
<div class="content">
|
||||
<el-radio-group v-model="share.live">
|
||||
<el-radio-button label="1">1天</el-radio-button>
|
||||
<el-radio-button label="7">7天</el-radio-button>
|
||||
<el-radio-button label="30">30天</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="password d-flex item">
|
||||
<div class="title">提取码:</div>
|
||||
<div class="content">
|
||||
<el-radio-group v-model="share.password">
|
||||
<el-radio-button label="yes">需要</el-radio-button>
|
||||
<el-radio-button label="no">不需要</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<span style="color: #999;font-size: 12px;float: left;">配合净网行动, 网盘严厉打击色情低俗等不良信息的传播行为</span>
|
||||
<el-button type="primary" :loading="share.loading" @click="handleCreateFolder">创建分享</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {ArrowDown, Grid, FolderAdd, Search} from '@element-plus/icons-vue'
|
||||
import FileIcon from "../components/FileIcon.vue";
|
||||
import {dayjs, ElMessage, ElMessageBox} from 'element-plus'
|
||||
import api from "../service/api";
|
||||
import qs from "qs";
|
||||
import FileBlockItem from "../components/FileBlockItem.vue";
|
||||
|
||||
export default {
|
||||
name: "All",
|
||||
data() {
|
||||
return {
|
||||
|
||||
display: 'block',
|
||||
/**
|
||||
* @var {FileItem[]}
|
||||
*/
|
||||
fileData: [],
|
||||
currentPath: '/',
|
||||
fileMenuOption: [
|
||||
{
|
||||
name: '打开',
|
||||
slug: 'open',
|
||||
},
|
||||
{
|
||||
name: '下载',
|
||||
slug: 'download',
|
||||
},
|
||||
{
|
||||
name: '分享',
|
||||
slug: 'share',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: '复制',
|
||||
slug: 'copy',
|
||||
},
|
||||
{
|
||||
name: '移动',
|
||||
slug: 'move',
|
||||
},
|
||||
{
|
||||
name: '重命名',
|
||||
slug: 'rename',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: '删除',
|
||||
slug: 'delete',
|
||||
},
|
||||
],
|
||||
createFolder: {
|
||||
visible: false,
|
||||
value: '',
|
||||
loading: false,
|
||||
message: ''
|
||||
},
|
||||
share: {
|
||||
/**
|
||||
* @var {FileItem}
|
||||
*/
|
||||
current: null,
|
||||
visible: false,
|
||||
loading: false,
|
||||
/**
|
||||
* @var {ShareInfo}
|
||||
*/
|
||||
info: null,
|
||||
live: 1,
|
||||
password: 'yes'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentPathList() {
|
||||
if (this.currentPath == '/') return []
|
||||
const arr = this.currentPath.replace(/^\//, '').split('/');
|
||||
const pathList = [], last = arr.pop();
|
||||
let prefix = '';
|
||||
arr.forEach(name => {
|
||||
prefix += '/' + name;
|
||||
pathList.push({name: name, path: prefix})
|
||||
})
|
||||
pathList.push({name: last, path: null})
|
||||
return pathList;
|
||||
}
|
||||
},
|
||||
|
||||
components: {FileBlockItem, FileIcon, ArrowDown, Grid, FolderAdd},
|
||||
mounted() {
|
||||
console.log('记载所有的文件')
|
||||
window.addEventListener('popstate',()=>console.log(location.href))
|
||||
window.addEventListener('hashchange', this.handleHashChange) // 添加监听
|
||||
this.handleHashChange();
|
||||
},
|
||||
unmounted() {
|
||||
window.removeEventListener('hashchange', this.handleHashChange); // 取消监听
|
||||
},
|
||||
methods: {
|
||||
// 加载目录下的所有文件
|
||||
async loadFileByPath(path = '/') {
|
||||
this.currentPath = path // 保存了当前的请求路径
|
||||
// 调用接口
|
||||
try {
|
||||
this.fileData = [];
|
||||
const list = await api.folder.list(path);
|
||||
this.fileData = list;
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message)
|
||||
}
|
||||
},
|
||||
// 获取获取当前目录路径
|
||||
getCurrentPath() {
|
||||
const hash = location.hash;
|
||||
const params = qs.parse(hash.substr(2))
|
||||
return params.path || '/';
|
||||
},
|
||||
//记载所有的子文件
|
||||
handleHashChange() {
|
||||
this.loadFileByPath(this.getCurrentPath()) // 有参数则使用 无参数则根目录
|
||||
},
|
||||
formatDate(time, format = 'MM-DD') {
|
||||
return dayjs(time).format(format);
|
||||
},
|
||||
formatSize(a, b = 2) {
|
||||
if (0 == a) return "0 B";
|
||||
let c = 1024, d = b || 2, e = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
|
||||
f = Math.floor(Math.log(a) / Math.log(c));
|
||||
return parseFloat((a / Math.pow(c, f)).toFixed(d)) + " " + e[f];
|
||||
},
|
||||
// 对文件名进行处理
|
||||
formatName(name) {
|
||||
if (!name || name.length < 15) return name;
|
||||
const extIndex = name.lastIndexOf('.'),
|
||||
ext = name.substr(extIndex);
|
||||
return name.substr(0, (15 - ext.length)) + "**" + ext;
|
||||
},
|
||||
fileMenuClick(data) {
|
||||
const {item, option} = data;
|
||||
console.log(data)
|
||||
if (option.slug == 'share') {
|
||||
// 分享
|
||||
this.share.current = item
|
||||
this.share.visible = true;
|
||||
} else if (option.slug == 'rename') {
|
||||
this.renameFile(item);
|
||||
} else if (option.slug == 'delete') {
|
||||
this.deleteFile(item);
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {FileItem} file
|
||||
*/
|
||||
renameFile(file) {
|
||||
ElMessageBox.prompt('', '请输入新的文件名', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputValue: file.name,
|
||||
closeOnClickModal: false
|
||||
}).then(({value}) => {
|
||||
api.file.rename({
|
||||
...file,
|
||||
name: value
|
||||
}).then(() => {
|
||||
file.name = value;
|
||||
}).catch();
|
||||
}).catch();
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {FileItem} file
|
||||
*/
|
||||
deleteFile(file) {
|
||||
ElMessageBox.confirm(
|
||||
'确定要删除选中文件吗?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
api.file.delete(file).then(() => this.handleHashChange()).catch();
|
||||
}).catch();
|
||||
},
|
||||
createFolderClick() {
|
||||
ElMessageBox.prompt(
|
||||
'', '新建文件夹',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
closeOnClickModal: false,
|
||||
// inputPattern:/[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
|
||||
inputValidator(value) {
|
||||
return !!value;
|
||||
},
|
||||
inputErrorMessage: '必须填写文件夹名称',
|
||||
inputPlaceholder: '请输入文件夹名称'
|
||||
})
|
||||
.then(({value}) => {
|
||||
// 此处开始进行创建
|
||||
})
|
||||
.catch()
|
||||
},
|
||||
async handleCreateFolder() {
|
||||
this.createFolder.message = ''
|
||||
if (!this.createFolder.value) {
|
||||
this.createFolder.message = '请填写文件夹名称'
|
||||
return;
|
||||
}
|
||||
// 开始新建
|
||||
this.createFolder.loading = true;
|
||||
try {
|
||||
await api.folder.create(this.getCurrentPath(), this.createFolder.value);
|
||||
this.createFolder.visible = false;
|
||||
this.handleHashChange();
|
||||
} catch (e) {
|
||||
this.createFolder.message = e.message;
|
||||
} finally {
|
||||
this.createFolder.loading = false // 取消loading
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
13
web/src/pages/Documents.vue
Normal file
13
web/src/pages/Documents.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<h1>文档</h1>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Documents"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
81
web/src/pages/Login.vue
Normal file
81
web/src/pages/Login.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<style scoped>
|
||||
#page-login {
|
||||
width: 500px;
|
||||
margin: 200px auto 0;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div id="page-login">
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="loginForm"
|
||||
:rules="rules"
|
||||
status-icon
|
||||
label-width="120px"
|
||||
class="demo-ruleForm"
|
||||
>
|
||||
<el-form-item label="" prop="username">
|
||||
<el-input v-model="loginForm.username" placeholder="用户名" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="password">
|
||||
<el-input v-model="loginForm.password" placeholder="登录密码" type="password" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button style="width: 100%" type="primary" @click="submitForm">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {defineComponent, reactive, ref} from 'vue'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Login',
|
||||
method:{
|
||||
gotoMain(){
|
||||
this.$router.push('/');
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
/**
|
||||
*
|
||||
* @type {Ref<FormInstance>}
|
||||
*/
|
||||
const form = ref(null);
|
||||
// 使用reactive将对象转换成响应对象
|
||||
const loginForm = reactive({
|
||||
password: '',
|
||||
username: ''
|
||||
})
|
||||
const checkPassword = (rule, value, callback) => {
|
||||
if (!value) callback(Error('请填写密码')) // 验证不通过
|
||||
else callback() // 验证通过
|
||||
}
|
||||
const rules = reactive({
|
||||
username: [
|
||||
{required: true, message: '请填写用户名称', trigger: 'blur'}
|
||||
],
|
||||
password: [
|
||||
{validator: checkPassword, trigger: 'blur'}
|
||||
]
|
||||
})
|
||||
const router = useRouter(); // 获取路由对象
|
||||
const submitForm = () => {
|
||||
form.value.validate((isValid) => {
|
||||
if (isValid) {
|
||||
ElMessage.success('登录成功')
|
||||
router.replace('/')
|
||||
} else {
|
||||
ElMessage.info('验证不通过')
|
||||
}
|
||||
})
|
||||
};
|
||||
return {
|
||||
loginForm, submitForm, rules, form
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
157
web/src/pages/Main.vue
Normal file
157
web/src/pages/Main.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<script>
|
||||
import {ArrowDown, Grid, FolderAdd, Search} from '@element-plus/icons-vue'
|
||||
|
||||
import FileUploader from "../components/file-uploader/Index.vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
return {Search}
|
||||
},
|
||||
components: {ArrowDown, Grid, FolderAdd, FileUploader},
|
||||
data() {
|
||||
return {
|
||||
currentActiveIndex: "/",
|
||||
currentPath: '/',
|
||||
search: {
|
||||
value: ''
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.currentActiveIndex = this.$route.path
|
||||
},
|
||||
methods: {
|
||||
//记载所有的子文件
|
||||
handleHashChange() {
|
||||
ElMessage.info({
|
||||
title: '成功 加载页面'
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="main-pan-app">
|
||||
<el-container>
|
||||
<el-aside class="pan-left-aside">
|
||||
<div class="logo-block">
|
||||
<svg class="logo-icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="1000" height="1000">
|
||||
<path
|
||||
d="M549.766 97.925c19.974 4.609 50.703 35.339 62.995 55.312 35.339-13.828 36.875 41.484 46.094 55.312 23.047-16.901 46.094-35.339 46.094-55.312 0-21.51-30.729-35.339-30.729-41.484 0-12.292 38.411-12.292 41.484-12.292 50.703 0 106.016 39.948 106.016 96.797 0 27.656-9.219 41.484-33.802 70.677h32.266c21.51 0 27.656-3.073 55.312 7.682C923.125 294.591 960 348.367 960 389.852c0 3.073 0 9.219-1.536 13.828-4.609 3.073-12.292 3.073-16.901 4.609-15.365 1.536-32.266 6.146-47.63 7.682-47.63 0-50.703 0-53.776-1.536-1.536 1.536-1.536 0-1.536 4.609 0 3.073 7.682 44.557 10.755 67.604 9.219 56.849 12.292 115.234 19.974 173.62 1.536 7.682 7.682 15.365 9.219 23.047 3.073 13.828 6.146 27.656 6.146 39.948 0 153.646-245.833 208.958-321.119 208.958h-86.042c-115.234-9.219-248.906-52.24-301.145-138.281-4.609-7.682-18.437-39.948-18.437-50.703v-36.875c3.073-16.901 7.682-33.802 21.51-50.703v-84.505l12.292-132.135c-12.292 1.536-33.802 1.536-38.411 1.536-21.51 0-38.411-3.073-61.458-6.146-7.682-1.536-18.437-3.073-24.583-6.146-4.609-1.536-3.073-12.292-3.073-13.828 0-44.557 44.557-107.552 98.333-119.844 4.609-1.536 13.828-1.536 19.974-3.073l41.484-1.536c-13.828-10.755-36.875-46.094-36.875-59.922v-29.193c13.828-58.385 62.995-82.969 106.016-82.969 1.536 0 41.484 0 41.484 12.292 0 6.146-30.729 19.974-30.729 41.484 0 1.536 6.146 32.266 16.901 32.266 3.073 0-1.536-3.073 3.073-3.073 3.073 0 32.266 10.755 36.875 10.755h12.292l3.073-3.073c-19.974-16.901-30.729-36.875-36.875-53.776 12.292 7.682 18.438 9.219 29.193 9.219 27.656 0 46.094-15.365 78.359-29.193 24.583-10.755 52.24-13.828 78.359-18.437-12.292-9.219-24.583-16.901-36.875-23.047l38.411-1.536c7.68 1.537 15.362 4.61 23.044 6.146z"
|
||||
p-id="2967"></path>
|
||||
</svg>
|
||||
<span>牛牛的网盘</span>
|
||||
</div>
|
||||
<el-menu :default-active="currentActiveIndex" class="pan-left-menu" :router="true">
|
||||
<el-menu-item index="/">
|
||||
<span>所有文件</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/histories">
|
||||
<span>最近上传</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/pictures">
|
||||
<span>我的图片</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/documents">
|
||||
<span>我的文档</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/videos">
|
||||
<span>我的视频</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header class="pan-header">
|
||||
<div class="d-flex">
|
||||
<div class="d-flex flex-1">
|
||||
<div class="left-tool-bar" style="padding-top: 15px;">
|
||||
<file-uploader @upload-success="handleHashChange" :current-folder="currentPath"/>
|
||||
<el-button @click="createFolder.visible = true" size="default"
|
||||
style="margin-left: 10px">
|
||||
<el-icon>
|
||||
<folder-add/>
|
||||
</el-icon>
|
||||
<span style="margin-left: 4px;">新建文件夹</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="pan-header-user-right">
|
||||
<li>
|
||||
<el-input
|
||||
v-model="search.value"
|
||||
class="search-box" clearable
|
||||
style="border-radius:30px;"
|
||||
placeholder="搜索你的文件"
|
||||
:prefix-icon="Search"
|
||||
/>
|
||||
</li>
|
||||
<li>帮助</li>
|
||||
<li>
|
||||
<el-dropdown>
|
||||
<div class="el-dropdown-link">
|
||||
<el-avatar class="avatar" :size="30"
|
||||
src="https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png"/>
|
||||
<span class="username">张三</span>
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>修改资料</el-dropdown-item>
|
||||
<el-dropdown-item>登录日志查看</el-dropdown-item>
|
||||
<el-dropdown-item>退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<!-- 子路由显示的地方 -->
|
||||
<router-view/>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less">
|
||||
|
||||
|
||||
// 平铺显示
|
||||
.file-block-item {
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
|
||||
.file-image {
|
||||
height: 130px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> span {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
margin-top: 3px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
13
web/src/pages/Pictures.vue
Normal file
13
web/src/pages/Pictures.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<h1>图片</h1>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Pictures"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
13
web/src/pages/Reg.vue
Normal file
13
web/src/pages/Reg.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<h1>Reg</h1>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Reg.vue"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
13
web/src/pages/Videos.vue
Normal file
13
web/src/pages/Videos.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<h1>视频</h1>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Videos"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
13
web/src/router/index.js
Normal file
13
web/src/router/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
import {createRouter, createWebHistory} from 'vue-router'
|
||||
import routes from "./routes";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
});
|
||||
router.beforeEach((to,from,next)=>{
|
||||
console.log(from.path ,'==>',to.path)
|
||||
next()
|
||||
})
|
||||
|
||||
export default router;
|
52
web/src/router/routes.js
Normal file
52
web/src/router/routes.js
Normal file
@ -0,0 +1,52 @@
|
||||
import Main from '../pages/Main.vue'
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {RouteRecordRaw[]}
|
||||
*/
|
||||
const routes = [
|
||||
{
|
||||
name: 'Main',
|
||||
path: '/',
|
||||
component: Main, // 需要在父页面添加 <router-view /> 来显示子路由页面
|
||||
// 配置子路由
|
||||
children: [
|
||||
{
|
||||
name: 'All',
|
||||
path: '/',
|
||||
component: () => import('../pages/All.vue'),
|
||||
},
|
||||
{
|
||||
name: 'Histories',
|
||||
path: '/histories',
|
||||
component: () => import('../pages/All.vue'),
|
||||
},
|
||||
{
|
||||
name: 'Pictures',
|
||||
path: '/pictures',
|
||||
component: () => import('../pages/Pictures.vue'),
|
||||
},
|
||||
{
|
||||
name: 'Documents',
|
||||
path: '/documents',
|
||||
component: () => import('../pages/Documents.vue'),
|
||||
},
|
||||
{
|
||||
name: 'Videos',
|
||||
path: '/videos',
|
||||
component: () => import('../pages/Videos.vue'),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Reg',
|
||||
path: '/reg',
|
||||
component: () => import('../pages/Reg.vue')
|
||||
},
|
||||
{
|
||||
name: 'Login',
|
||||
path: '/login',
|
||||
component: () => import('../pages/Login.vue')
|
||||
}
|
||||
];
|
||||
export default routes
|
@ -83,6 +83,12 @@ export default {
|
||||
fastUpload(params) {
|
||||
return request('/api/fast-upload', 'POST', params);
|
||||
},
|
||||
delete(file) {
|
||||
return request('/api/file/delete', 'POST', file);
|
||||
},
|
||||
rename(file) {
|
||||
return request('/api/file/rename', 'POST', file);
|
||||
},
|
||||
upload(parent, file, onProcess = null) {
|
||||
// 上传文件到某个目录
|
||||
const postData = new FormData(); // 将数据封装成form表单
|
||||
@ -119,5 +125,14 @@ export default {
|
||||
})
|
||||
// return request('/api/upload', 'FILE', postData, onProcess)
|
||||
}
|
||||
},
|
||||
share: {
|
||||
/**
|
||||
*
|
||||
* @param {ShareInfo} info
|
||||
*/
|
||||
create(info) {
|
||||
return request('/api/share/create', 'POST', info)
|
||||
}
|
||||
}
|
||||
}
|
15
web/src/service/type.d.ts
vendored
15
web/src/service/type.d.ts
vendored
@ -20,3 +20,18 @@ declare type FileIconInstance = {
|
||||
*/
|
||||
showPreview: Function
|
||||
}
|
||||
/**
|
||||
* 分享信息
|
||||
*/
|
||||
declare type ShareInfo = {
|
||||
id?: string,
|
||||
title: string,
|
||||
uid: number,
|
||||
fileId: number,
|
||||
type: 1 | 2,
|
||||
password: string,
|
||||
live: number,
|
||||
createTime?: string,
|
||||
updateTime?: string,
|
||||
status: number
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user