u
This commit is contained in:
1 parent
9aa4a72321
commit
9bf7c40b70
40 files changed
+918
-189
No files matched your search
+1
@@ -17,6 +17,7 @@ public class AutoMetaObjectHandler implements MetaObjectHandler {
|
||||
|
||||
if (ObjectUtils.isNotEmpty(userId) && userId > 0){
|
||||
this.strictInsertFill(metaObject, "createBy", Long.class, userId);
|
||||
this.strictUpdateFill(metaObject, "updateBy", Long.class, userId);
|
||||
}
|
||||
|
||||
Date date = new Date();
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package cn.link56.forum.service;
|
||||
|
||||
import cn.link56.forum.mapper.PostMapper;
|
||||
import cn.link56.forum.model.po.PostPO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class PostService extends ServiceImpl<PostMapper, PostPO> {
|
||||
@Resource
|
||||
private PostMapper postMapper;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package cn.link56.forum.service;
|
||||
|
||||
import cn.link56.forum.mapper.PostTagMapper;
|
||||
import cn.link56.forum.model.po.PostTagPO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class PostTagService extends ServiceImpl<PostTagMapper, PostTagPO> {
|
||||
@Resource
|
||||
private PostTagMapper postTagMapper;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.link56.post.controller;
|
||||
|
||||
import cn.link56.common.Result;
|
||||
import cn.link56.post.model.dto.PostAddDTO;
|
||||
import cn.link56.post.model.vo.PostEditVO;
|
||||
import cn.link56.post.model.vo.PostTagVO;
|
||||
import cn.link56.post.service.PostService;
|
||||
import cn.link56.post.service.PostTagService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/post")
|
||||
public class PostController {
|
||||
|
||||
@Resource
|
||||
private PostService postService;
|
||||
|
||||
@PostMapping("/add")
|
||||
public Result<PostEditVO> addPost(@RequestBody @Validated PostAddDTO postAddDTO) {
|
||||
PostEditVO postEditVO = postService.addPost(postAddDTO);
|
||||
return Result.success(postEditVO);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.link56.post.controller;
|
||||
|
||||
import cn.link56.common.Result;
|
||||
import cn.link56.post.mapper.PostTagMapper;
|
||||
import cn.link56.post.model.vo.PostTagVO;
|
||||
import cn.link56.post.service.PostTagService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/post/tag")
|
||||
public class PostTagController {
|
||||
@Resource
|
||||
private PostTagService postTagService;
|
||||
|
||||
@GetMapping
|
||||
public Result<List<PostTagVO>> listTags(){
|
||||
List<PostTagVO> list = postTagService.listTags();
|
||||
|
||||
return Result.success(list);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package cn.link56.forum.mapper;
|
||||
package cn.link56.post.mapper;
|
||||
|
||||
import cn.link56.forum.model.po.PostCommentPO;
|
||||
import cn.link56.post.model.po.PostCommentPO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package cn.link56.forum.mapper;
|
||||
package cn.link56.post.mapper;
|
||||
|
||||
import cn.link56.forum.model.po.PostFavoritePO;
|
||||
import cn.link56.post.model.po.PostFavoritePO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package cn.link56.forum.mapper;
|
||||
package cn.link56.post.mapper;
|
||||
|
||||
import cn.link56.forum.model.po.PostLikePO;
|
||||
import cn.link56.post.model.po.PostLikePO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package cn.link56.forum.mapper;
|
||||
package cn.link56.post.mapper;
|
||||
|
||||
import cn.link56.forum.model.po.PostPO;
|
||||
import cn.link56.post.model.po.PostPO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
+5
-2
@@ -1,9 +1,12 @@
|
||||
package cn.link56.forum.mapper;
|
||||
package cn.link56.post.mapper;
|
||||
|
||||
import cn.link56.forum.model.po.PostTagPO;
|
||||
import cn.link56.post.model.po.PostTagPO;
|
||||
import cn.link56.post.model.vo.PostTagVO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface PostTagMapper extends BaseMapper<PostTagPO> {
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package cn.link56.forum.mapper;
|
||||
package cn.link56.post.mapper;
|
||||
|
||||
import cn.link56.forum.model.po.PostViewPO;
|
||||
import cn.link56.post.model.po.PostViewPO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.link56.post.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PostAddDTO {
|
||||
@NotBlank(message = "帖子标题不能为空")
|
||||
private String title;
|
||||
@NotBlank(message = "帖子内容不能为空")
|
||||
private String content;
|
||||
@NotBlank(message = "帖子摘要不能为空")
|
||||
private String summary;
|
||||
private String cover;
|
||||
private List<String> tags;
|
||||
|
||||
private Integer status;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cn.link56.forum.model.po;
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cn.link56.forum.model.po;
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cn.link56.forum.model.po;
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
+19
-9
@@ -1,12 +1,16 @@
|
||||
package cn.link56.forum.model.po;
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.sf.jsqlparser.expression.DateTimeLiteralExpression;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@@ -28,33 +32,39 @@ public class PostPO {
|
||||
@TableField("b_summary")
|
||||
private String summary;
|
||||
|
||||
@TableField("b_tags")
|
||||
private String tags;
|
||||
@TableField("b_tag")
|
||||
private String tag;
|
||||
|
||||
@TableField("b_cover")
|
||||
private String cover;
|
||||
|
||||
@TableField("b_publish_time")
|
||||
private String publishTime;
|
||||
|
||||
@TableField("b_status")
|
||||
private Integer status;
|
||||
|
||||
@TableField(
|
||||
value = "b_create_user_id",
|
||||
value = "b_create_by",
|
||||
fill = FieldFill.INSERT
|
||||
)
|
||||
private Long createUserId;
|
||||
private Long createBy;
|
||||
|
||||
@TableField(
|
||||
value = "b_create_time",
|
||||
fill = FieldFill.INSERT
|
||||
)
|
||||
private Long createTime;
|
||||
private Date createTime;
|
||||
|
||||
@TableField(
|
||||
value = "b_update_user_id",
|
||||
value = "b_update_by",
|
||||
fill = FieldFill.INSERT_UPDATE
|
||||
)
|
||||
private Long updateUserId;
|
||||
private Long updateBy;
|
||||
|
||||
@TableField(
|
||||
value = "b_update_time",
|
||||
fill = FieldFill.INSERT_UPDATE
|
||||
)
|
||||
private Long updateTime;
|
||||
private Date updateTime;
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cn.link56.forum.model.po;
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cn.link56.forum.model.po;
|
||||
package cn.link56.post.model.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.link56.post.model.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PostEditVO {
|
||||
private Long id;
|
||||
private String title;
|
||||
private String content;
|
||||
private String summary;
|
||||
private String cover;
|
||||
private List<String> tags;
|
||||
private Integer status;
|
||||
private Date publishTime;
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package cn.link56.post.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PostTagVO {
|
||||
private Long id;
|
||||
private String name;
|
||||
}
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package cn.link56.forum.service;
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.forum.mapper.PostCommentMapper;
|
||||
import cn.link56.forum.model.po.PostCommentPO;
|
||||
import cn.link56.post.mapper.PostCommentMapper;
|
||||
import cn.link56.post.model.po.PostCommentPO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package cn.link56.forum.service;
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.forum.mapper.PostFavoriteMapper;
|
||||
import cn.link56.forum.model.po.PostFavoritePO;
|
||||
import cn.link56.post.mapper.PostFavoriteMapper;
|
||||
import cn.link56.post.model.po.PostFavoritePO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package cn.link56.forum.service;
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.forum.mapper.PostLikeMapper;
|
||||
import cn.link56.forum.model.po.PostLikePO;
|
||||
import cn.link56.post.mapper.PostLikeMapper;
|
||||
import cn.link56.post.model.po.PostLikePO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.common.context.UserContext;
|
||||
import cn.link56.post.mapper.PostMapper;
|
||||
import cn.link56.post.model.dto.PostAddDTO;
|
||||
import cn.link56.post.model.po.PostPO;
|
||||
import cn.link56.post.model.vo.PostEditVO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class PostService extends ServiceImpl<PostMapper, PostPO> {
|
||||
@Resource
|
||||
private PostMapper postMapper;
|
||||
|
||||
@Transactional
|
||||
public PostEditVO addPost(PostAddDTO postAddDTO) {
|
||||
PostPO postPO = new PostPO();
|
||||
|
||||
BeanUtils.copyProperties(postAddDTO, postPO);
|
||||
|
||||
postPO.setTag(StringUtils.join(postAddDTO.getTags(), ","));
|
||||
postPO.setCompanyId(UserContext.getCompanyId());
|
||||
|
||||
postMapper.insert(postPO);
|
||||
|
||||
PostEditVO postEditVO = new PostEditVO();
|
||||
|
||||
BeanUtils.copyProperties(postPO, postEditVO);
|
||||
postEditVO.setTags(postAddDTO.getTags());
|
||||
|
||||
return postEditVO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.post.mapper.PostTagMapper;
|
||||
import cn.link56.post.model.po.PostTagPO;
|
||||
import cn.link56.post.model.vo.PostTagVO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class PostTagService extends ServiceImpl<PostTagMapper, PostTagPO> {
|
||||
@Resource
|
||||
private PostTagMapper postTagMapper;
|
||||
|
||||
public List<PostTagVO> listTags() {
|
||||
LambdaQueryWrapper<PostTagPO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PostTagPO::getCanuse,"1");
|
||||
|
||||
List<PostTagPO> postTagPOS = postTagMapper.selectList(wrapper);
|
||||
|
||||
return postTagPOS.stream().map(postTagPO -> {
|
||||
PostTagVO postTagVO = new PostTagVO();
|
||||
BeanUtils.copyProperties(postTagPO, postTagVO);
|
||||
|
||||
return postTagVO;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package cn.link56.forum.service;
|
||||
package cn.link56.post.service;
|
||||
|
||||
import cn.link56.forum.mapper.PostViewMapper;
|
||||
import cn.link56.forum.model.po.PostViewPO;
|
||||
import cn.link56.post.mapper.PostViewMapper;
|
||||
import cn.link56.post.model.po.PostViewPO;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -16,6 +16,9 @@ logging:
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: GMT+8
|
||||
mail:
|
||||
host: smtp.163.com #邮箱服务器地址
|
||||
username: hi_itao@163.com #邮箱账号
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.13.2",
|
||||
"dayjs": "^1.11.19",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.12.0",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
|
||||
+57
-17
@@ -11,6 +11,9 @@ importers:
|
||||
'@element-plus/icons-vue':
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2(vue@3.5.25(typescript@5.9.3))
|
||||
'@vueuse/core':
|
||||
specifier: ^14.1.0
|
||||
version: 14.1.0(vue@3.5.25(typescript@5.9.3))
|
||||
'@wangeditor/editor':
|
||||
specifier: ^5.1.23
|
||||
version: 5.1.23
|
||||
@@ -23,6 +26,9 @@ importers:
|
||||
dayjs:
|
||||
specifier: ^1.11.19
|
||||
version: 1.11.19
|
||||
echarts:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
element-plus:
|
||||
specifier: ^2.12.0
|
||||
version: 2.12.0(vue@3.5.25(typescript@5.9.3))
|
||||
@@ -496,42 +502,36 @@ packages:
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.1':
|
||||
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
|
||||
@@ -596,67 +596,56 @@ packages:
|
||||
resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.53.3':
|
||||
resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.53.3':
|
||||
resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.53.3':
|
||||
resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.53.3':
|
||||
resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.53.3':
|
||||
resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.53.3':
|
||||
resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==}
|
||||
@@ -732,6 +721,9 @@ packages:
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
|
||||
'@types/web-bluetooth@0.0.21':
|
||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.49.0':
|
||||
resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -897,12 +889,25 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
'@vueuse/core@14.1.0':
|
||||
resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/core@9.13.0':
|
||||
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
|
||||
|
||||
'@vueuse/metadata@14.1.0':
|
||||
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
|
||||
|
||||
'@vueuse/metadata@9.13.0':
|
||||
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
|
||||
|
||||
'@vueuse/shared@14.1.0':
|
||||
resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/shared@9.13.0':
|
||||
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
||||
|
||||
@@ -1203,6 +1208,9 @@ packages:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
echarts@6.0.0:
|
||||
resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
|
||||
|
||||
electron-to-chromium@1.5.267:
|
||||
resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
|
||||
|
||||
@@ -2277,6 +2285,9 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.0.0'
|
||||
|
||||
tslib@2.3.0:
|
||||
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||
|
||||
type-check@0.4.0:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -2474,6 +2485,9 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
zrender@6.0.0:
|
||||
resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
|
||||
|
||||
zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
|
||||
@@ -2974,6 +2988,8 @@ snapshots:
|
||||
|
||||
'@types/web-bluetooth@0.0.16': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.21': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
@@ -3213,6 +3229,13 @@ snapshots:
|
||||
typescript: 5.9.3
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
'@vueuse/core@14.1.0(vue@3.5.25(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 14.1.0
|
||||
'@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3))
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
'@vueuse/core@9.13.0(vue@3.5.25(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.16
|
||||
@@ -3223,8 +3246,14 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/metadata@14.1.0': {}
|
||||
|
||||
'@vueuse/metadata@9.13.0': {}
|
||||
|
||||
'@vueuse/shared@14.1.0(vue@3.5.25(typescript@5.9.3))':
|
||||
dependencies:
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
'@vueuse/shared@9.13.0(vue@3.5.25(typescript@5.9.3))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3))
|
||||
@@ -3521,6 +3550,11 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
echarts@6.0.0:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
zrender: 6.0.0
|
||||
|
||||
electron-to-chromium@1.5.267: {}
|
||||
|
||||
element-plus@2.12.0(vue@3.5.25(typescript@5.9.3)):
|
||||
@@ -4818,6 +4852,8 @@ snapshots:
|
||||
picomatch: 4.0.3
|
||||
typescript: 5.9.3
|
||||
|
||||
tslib@2.3.0: {}
|
||||
|
||||
type-check@0.4.0:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
@@ -4975,4 +5011,8 @@ snapshots:
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
zrender@6.0.0:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
|
||||
zwitch@2.0.4: {}
|
||||
@@ -0,0 +1,147 @@
|
||||
<script setup lang="ts">
|
||||
import type { UploadImageResponse } from '@/utils/file'
|
||||
import { Delete } from '@element-plus/icons-vue'
|
||||
import { ref } from 'vue'
|
||||
import { uploadImage, uploadToAliyun } from '@/utils/file'
|
||||
import SvgIcon from './SvgIcon.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
autoUploadAliyun: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', val: string): void
|
||||
(e: 'change', val: UploadImageResponse | null): void
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
async function handleClickUploadImage() {
|
||||
if (loading.value)
|
||||
return
|
||||
|
||||
try {
|
||||
const res = await uploadImage()
|
||||
loading.value = true
|
||||
|
||||
if (props.autoUploadAliyun) {
|
||||
const aliyunRes = await uploadToAliyun(res.blob, res.name, res.size)
|
||||
|
||||
emit('update:modelValue', aliyunRes.url)
|
||||
}
|
||||
else {
|
||||
emit('update:modelValue', res.url)
|
||||
}
|
||||
emit('change', res)
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleRemove(e: MouseEvent) {
|
||||
e.stopPropagation()
|
||||
emit('update:modelValue', '')
|
||||
emit('change', null)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="upload-card" :class="{ loading }" @click="handleClickUploadImage">
|
||||
<div
|
||||
v-if="props.modelValue && !loading"
|
||||
class="remove-icon"
|
||||
@click="handleRemove"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</div>
|
||||
|
||||
<div v-if="!props.modelValue && !loading" class="upload-inner">
|
||||
<SvgIcon icon="material-symbols:arrow-upload-ready-rounded" class="upload-icon" />
|
||||
<div class="upload-text">
|
||||
点击上传
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="loading" class="upload-inner">
|
||||
<SvgIcon icon="line-md:loading-twotone-loop" class="upload-icon" />
|
||||
<div class="upload-text">
|
||||
上传中...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img v-else :src="props.modelValue" alt="已上传图片" class="upload-image">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-card {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s ease;
|
||||
padding: 4px;
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
|
||||
&:hover {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-inner {
|
||||
color: #999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.upload-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -67,11 +67,11 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
const toolbarClass = computed(() => {
|
||||
return props.mode === 'view' ? 'toolbar-view' : 'toolbar-edit'
|
||||
return props.mode === 'view' ? 'wang-toolbar-view' : 'wang-toolbar-edit'
|
||||
})
|
||||
|
||||
const editorClass = computed(() => {
|
||||
return props.mode === 'view' ? 'editor-view' : 'editor-edit'
|
||||
return props.mode === 'view' ? 'wang-editor-view' : 'wang-editor-edit'
|
||||
})
|
||||
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
@@ -210,13 +210,13 @@ function doGenerateDirectory() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editor-container">
|
||||
<div class="wang-editor-container">
|
||||
<div class="main">
|
||||
<Toolbar v-if="props.mode === 'edit'" :class="toolbarClass" class="toolbar" :editor="editorRef" :default-config="toolbarConfig" />
|
||||
<Editor key="main" v-model="valueHtml" :class="editorClass" class="editor" :default-config="editorConfig" @on-change="handleChange" @on-created="handleCreated" />
|
||||
</div>
|
||||
|
||||
<div v-if="showDirecotry" class="directory">
|
||||
<div v-if="showDirecotry" class="wang-editor-directory">
|
||||
<el-collapse v-model="activeCollapseNames">
|
||||
<el-collapse-item title="目录" name="1">
|
||||
<el-anchor :offset="70">
|
||||
@@ -232,9 +232,9 @@ function doGenerateDirectory() {
|
||||
<style lang="scss" scoped>
|
||||
.toolbar {
|
||||
}
|
||||
.toolbar-view {
|
||||
.wang-toolbar-view {
|
||||
}
|
||||
.toolbar-edit {
|
||||
.wang-toolbar-edit {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.editor {
|
||||
@@ -243,7 +243,7 @@ function doGenerateDirectory() {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
.editor-view {
|
||||
.wang-editor-view {
|
||||
height: auto;
|
||||
:deep(.w-e-text-container) {
|
||||
background-color: transparent;
|
||||
@@ -253,18 +253,18 @@ function doGenerateDirectory() {
|
||||
padding: 0 0px !important;
|
||||
}
|
||||
}
|
||||
.editor-edit {
|
||||
height: 500px !important;
|
||||
.wang-editor-edit {
|
||||
height: 600px !important;
|
||||
border: 1px solid #ccc;
|
||||
border-top: transparent;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
.wang-editor-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.directory {
|
||||
.wang-editor-directory {
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
margin-left: 16px;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { effectScope, nextTick, onScopeDispose, ref, watch } from "vue";
|
||||
import { useElementSize } from "@vueuse/core";
|
||||
import * as echarts from "echarts/core";
|
||||
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from "echarts/charts";
|
||||
import type { BarSeriesOption, GaugeSeriesOption, LineSeriesOption, PictorialBarSeriesOption, PieSeriesOption, RadarSeriesOption, ScatterSeriesOption } from "echarts/charts";
|
||||
import { DatasetComponent, GridComponent, LegendComponent, TitleComponent, ToolboxComponent, TooltipComponent, TransformComponent } from "echarts/components";
|
||||
import type { DatasetComponentOption, GridComponentOption, LegendComponentOption, TitleComponentOption, ToolboxComponentOption, TooltipComponentOption } from "echarts/components";
|
||||
import { LabelLayout, UniversalTransition } from "echarts/features";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import type { BarSeriesOption, GaugeSeriesOption, LineSeriesOption, PictorialBarSeriesOption, PieSeriesOption, RadarSeriesOption, ScatterSeriesOption } from 'echarts/charts'
|
||||
import type { DatasetComponentOption, GridComponentOption, LegendComponentOption, TitleComponentOption, ToolboxComponentOption, TooltipComponentOption } from 'echarts/components'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { BarChart, GaugeChart, LineChart, PictorialBarChart, PieChart, RadarChart, ScatterChart } from 'echarts/charts'
|
||||
import { DatasetComponent, GridComponent, LegendComponent, TitleComponent, ToolboxComponent, TooltipComponent, TransformComponent } from 'echarts/components'
|
||||
import * as echarts from 'echarts/core'
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { effectScope, nextTick, onScopeDispose, ref, watch } from 'vue'
|
||||
|
||||
export type ECOption = echarts.ComposeOption<
|
||||
| BarSeriesOption
|
||||
@@ -22,7 +22,7 @@ export type ECOption = echarts.ComposeOption<
|
||||
| GridComponentOption
|
||||
| ToolboxComponentOption
|
||||
| DatasetComponentOption
|
||||
>;
|
||||
>
|
||||
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
@@ -41,13 +41,13 @@ echarts.use([
|
||||
GaugeChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
CanvasRenderer,
|
||||
])
|
||||
|
||||
interface ChartHooks {
|
||||
onRender?: (chart: echarts.ECharts) => void | Promise<void>;
|
||||
onUpdated?: (chart: echarts.ECharts) => void | Promise<void>;
|
||||
onDestroy?: (chart: echarts.ECharts) => void | Promise<void>;
|
||||
onRender?: (chart: echarts.ECharts) => void | Promise<void>
|
||||
onUpdated?: (chart: echarts.ECharts) => void | Promise<void>
|
||||
onDestroy?: (chart: echarts.ECharts) => void | Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,33 +57,33 @@ interface ChartHooks {
|
||||
* @param darkMode dark mode
|
||||
*/
|
||||
export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: ChartHooks = {}) {
|
||||
const scope = effectScope();
|
||||
const scope = effectScope()
|
||||
|
||||
const darkMode = ref(false);
|
||||
const darkMode = ref(false)
|
||||
|
||||
const domRef = ref<HTMLElement | null>(null);
|
||||
const initialSize = { width: 0, height: 0 };
|
||||
const { width, height } = useElementSize(domRef, initialSize);
|
||||
const domRef = ref<HTMLElement | null>(null)
|
||||
const initialSize = { width: 0, height: 0 }
|
||||
const { width, height } = useElementSize(domRef, initialSize)
|
||||
|
||||
let chart: echarts.ECharts | null = null;
|
||||
const chartOptions: T = optionsFactory();
|
||||
let chart: echarts.ECharts | null = null
|
||||
const chartOptions: T = optionsFactory()
|
||||
|
||||
const {
|
||||
onRender = (instance) => {
|
||||
const textColor = darkMode.value ? "rgb(224, 224, 224)" : "rgb(31, 31, 31)";
|
||||
const maskColor = darkMode.value ? "rgba(0, 0, 0, 0.4)" : "rgba(255, 255, 255, 0.8)";
|
||||
const textColor = darkMode.value ? 'rgb(224, 224, 224)' : 'rgb(31, 31, 31)'
|
||||
const maskColor = darkMode.value ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.8)'
|
||||
|
||||
instance.showLoading({
|
||||
textColor,
|
||||
fontSize: 14,
|
||||
maskColor
|
||||
});
|
||||
maskColor,
|
||||
})
|
||||
},
|
||||
onUpdated = (instance) => {
|
||||
instance.hideLoading();
|
||||
instance.hideLoading()
|
||||
},
|
||||
onDestroy
|
||||
} = hooks;
|
||||
onDestroy,
|
||||
} = hooks
|
||||
|
||||
/**
|
||||
* whether can render chart
|
||||
@@ -91,12 +91,12 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
||||
* when domRef is ready and initialSize is valid
|
||||
*/
|
||||
function canRender() {
|
||||
return domRef.value && initialSize.width > 0 && initialSize.height > 0;
|
||||
return domRef.value && initialSize.width > 0 && initialSize.height > 0
|
||||
}
|
||||
|
||||
/** is chart rendered */
|
||||
function isRendered() {
|
||||
return Boolean(domRef.value && chart);
|
||||
return Boolean(domRef.value && chart)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,59 +105,61 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
||||
* @param callback callback function
|
||||
*/
|
||||
async function updateOptions(callback: (opts: T, optsFactory: () => T) => ECOption = () => chartOptions) {
|
||||
if (!isRendered()) return;
|
||||
if (!isRendered())
|
||||
return
|
||||
|
||||
const updatedOpts = callback(chartOptions, optionsFactory);
|
||||
const updatedOpts = callback(chartOptions, optionsFactory)
|
||||
|
||||
Object.assign(chartOptions, updatedOpts);
|
||||
Object.assign(chartOptions, updatedOpts)
|
||||
|
||||
if (isRendered()) {
|
||||
chart?.clear();
|
||||
chart?.clear()
|
||||
}
|
||||
|
||||
chart?.setOption({ ...updatedOpts, backgroundColor: "transparent" });
|
||||
chart?.setOption({ ...updatedOpts, backgroundColor: 'transparent' })
|
||||
|
||||
await onUpdated?.(chart!);
|
||||
await onUpdated?.(chart!)
|
||||
}
|
||||
|
||||
function setOptions(options: T) {
|
||||
chart?.setOption(options);
|
||||
chart?.setOption(options)
|
||||
}
|
||||
|
||||
/** render chart */
|
||||
async function render() {
|
||||
if (!isRendered()) {
|
||||
const chartTheme = darkMode.value ? "dark" : "light";
|
||||
const chartTheme = darkMode.value ? 'dark' : 'light'
|
||||
|
||||
await nextTick();
|
||||
await nextTick()
|
||||
|
||||
chart = echarts.init(domRef.value, chartTheme);
|
||||
chart = echarts.init(domRef.value, chartTheme)
|
||||
|
||||
chart.setOption({ ...chartOptions, backgroundColor: "transparent" });
|
||||
chart.setOption({ ...chartOptions, backgroundColor: 'transparent' })
|
||||
|
||||
await onRender?.(chart);
|
||||
await onRender?.(chart)
|
||||
}
|
||||
}
|
||||
|
||||
/** resize chart */
|
||||
function resize() {
|
||||
chart?.resize();
|
||||
chart?.resize()
|
||||
}
|
||||
|
||||
/** destroy chart */
|
||||
async function destroy() {
|
||||
if (!chart) return;
|
||||
if (!chart)
|
||||
return
|
||||
|
||||
await onDestroy?.(chart);
|
||||
chart?.dispose();
|
||||
chart = null;
|
||||
await onDestroy?.(chart)
|
||||
chart?.dispose()
|
||||
chart = null
|
||||
}
|
||||
|
||||
/** change chart theme */
|
||||
async function changeTheme() {
|
||||
await destroy();
|
||||
await render();
|
||||
await onUpdated?.(chart!);
|
||||
await destroy()
|
||||
await render()
|
||||
await onUpdated?.(chart!)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,47 +169,47 @@ export function useEcharts<T extends ECOption>(optionsFactory: () => T, hooks: C
|
||||
* @param h height
|
||||
*/
|
||||
async function renderChartBySize(w: number, h: number) {
|
||||
initialSize.width = w;
|
||||
initialSize.height = h;
|
||||
initialSize.width = w
|
||||
initialSize.height = h
|
||||
|
||||
// size is abnormal, destroy chart
|
||||
if (!canRender()) {
|
||||
await destroy();
|
||||
await destroy()
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// resize chart
|
||||
if (isRendered()) {
|
||||
resize();
|
||||
resize()
|
||||
}
|
||||
|
||||
// render chart
|
||||
await render();
|
||||
await render()
|
||||
|
||||
if (chart) {
|
||||
await onUpdated?.(chart);
|
||||
await onUpdated?.(chart)
|
||||
}
|
||||
}
|
||||
|
||||
scope.run(() => {
|
||||
watch([width, height], ([newWidth, newHeight]) => {
|
||||
renderChartBySize(newWidth, newHeight);
|
||||
});
|
||||
renderChartBySize(newWidth, newHeight)
|
||||
})
|
||||
|
||||
watch(darkMode, () => {
|
||||
changeTheme();
|
||||
});
|
||||
});
|
||||
changeTheme()
|
||||
})
|
||||
})
|
||||
|
||||
onScopeDispose(() => {
|
||||
destroy();
|
||||
scope.stop();
|
||||
});
|
||||
destroy()
|
||||
scope.stop()
|
||||
})
|
||||
|
||||
return {
|
||||
domRef,
|
||||
updateOptions,
|
||||
setOptions
|
||||
};
|
||||
setOptions,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { AddPostParams, PostEditorForm } from '@/types/post'
|
||||
import { httpGet, httpPostJson } from '@/request/utils'
|
||||
|
||||
export function addPostApi(params: AddPostParams) {
|
||||
return httpPostJson<PostEditorForm>('/post/add', params)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { PostTag } from '@/types/post/tag'
|
||||
import { httpGet, httpPostJson } from '@/request/utils'
|
||||
|
||||
export function getPostTagApi() {
|
||||
return httpGet<PostTag[]>('/post/tag')
|
||||
}
|
||||
@@ -31,6 +31,7 @@ declare module 'vue' {
|
||||
'IIc:outlineSettings': typeof import('~icons/ic/outline-settings')['default']
|
||||
'IIc:twotoneHeadphones': typeof import('~icons/ic/twotone-headphones')['default']
|
||||
'IIconoir:infoCircleSolid': typeof import('~icons/iconoir/info-circle-solid')['default']
|
||||
ImageUploadCard: typeof import('./../components/ImageUploadCard.vue')['default']
|
||||
'IMajesticons:settingsCogLine': typeof import('~icons/majesticons/settings-cog-line')['default']
|
||||
'IMaterialSymbols:call': typeof import('~icons/material-symbols/call')['default']
|
||||
'IMaterialSymbols:chatBubbleOutline': typeof import('~icons/material-symbols/chat-bubble-outline')['default']
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export interface PostEditorForm {
|
||||
id: number
|
||||
title: string
|
||||
content: string
|
||||
summary: string
|
||||
cover: string
|
||||
tags: string[]
|
||||
status: number
|
||||
publishTime: string
|
||||
updateTime: string
|
||||
}
|
||||
|
||||
export interface AddPostParams {
|
||||
title: string
|
||||
content: string
|
||||
summary: string
|
||||
cover: string
|
||||
status: number
|
||||
tags: string[]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface PostTag {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
@@ -149,10 +149,12 @@ export interface UploadToAliyunResponse {
|
||||
fileSize: string
|
||||
}
|
||||
|
||||
export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, onProgress?: (percent: number) => void): Promise<UploadToAliyunResponse> {
|
||||
export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, showProgress: boolean = true, onProgress?: (percent: number) => void): Promise<UploadToAliyunResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 在开始上传时显示上传进度
|
||||
showUploadProgress('uploading')
|
||||
if (showProgress) {
|
||||
showUploadProgress('uploading')
|
||||
}
|
||||
|
||||
getAliyunKeyApi()
|
||||
.then(async (res) => {
|
||||
@@ -177,7 +179,9 @@ export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, o
|
||||
if (event.lengthComputable && onProgress) {
|
||||
const percent = (event.loaded / event.total) * 100
|
||||
onProgress(Number.parseFloat(percent.toFixed(2))) // 保留两位小数
|
||||
updateUploadProgress(percent) // 更新进度条
|
||||
if (showProgress) {
|
||||
updateUploadProgress(percent) // 更新进度条
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,12 +195,13 @@ export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, o
|
||||
fileMapName: allFilePath,
|
||||
})
|
||||
|
||||
updateUploadProgress(100) // 上传完毕
|
||||
ElMessage.success('上传成功')
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
if (showProgress) {
|
||||
updateUploadProgress(100) // 上传完毕
|
||||
ElMessage.success('上传成功')
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
}
|
||||
|
||||
resolve({
|
||||
url: allFilePath,
|
||||
@@ -205,36 +210,42 @@ export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, o
|
||||
})
|
||||
}
|
||||
else {
|
||||
updateUploadProgress(0, 'failed') // 上传失败
|
||||
ElMessage.error('上传失败,请稍后再试')
|
||||
if (showProgress) {
|
||||
updateUploadProgress(0, 'failed') // 上传失败
|
||||
ElMessage.error('上传失败,请稍后再试')
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
}
|
||||
|
||||
reject(new Error(`OSS上传失败: ${xhr.responseText}`))
|
||||
}
|
||||
}
|
||||
|
||||
xhr.onerror = () => {
|
||||
updateUploadProgress(0, 'failed') // 上传失败
|
||||
ElMessage.error('上传失败,请稍后再试')
|
||||
if (showProgress) {
|
||||
updateUploadProgress(0, 'failed') // 上传失败
|
||||
ElMessage.error('上传失败,请稍后再试')
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
}
|
||||
reject(new Error(`OSS上传异常: ${xhr.responseText}`))
|
||||
}
|
||||
|
||||
xhr.send(formData)
|
||||
}
|
||||
catch (error) {
|
||||
updateUploadProgress(0, 'failed') // 上传失败
|
||||
ElMessage.error('上传失败,请稍后再试')
|
||||
if (showProgress) {
|
||||
updateUploadProgress(0, 'failed') // 上传失败
|
||||
ElMessage.error('上传失败,请稍后再试')
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
setTimeout(() => {
|
||||
closeUploadProgress() // 关闭上传进度条
|
||||
}, 500)
|
||||
}
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,10 +1,324 @@
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import type { PostEditorForm } from '@/types/post'
|
||||
import type { PostTag } from '@/types/post/tag'
|
||||
import type { UploadImageResponse } from '@/utils/file'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import CustomLoading from '@/components/CustomLoading.vue'
|
||||
import ImageUploadCard from '@/components/ImageUploadCard.vue'
|
||||
import WangEditor from '@/components/wang-editor/index.vue'
|
||||
import { addPostApi } from '@/request/api/post'
|
||||
import { getPostTagApi } from '@/request/api/post/tag'
|
||||
import { uploadImage, uploadToAliyun } from '@/utils/file'
|
||||
|
||||
const wangEditorRef = ref<any>(null)
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const loading = reactive({
|
||||
init: true,
|
||||
confirm: false,
|
||||
})
|
||||
|
||||
const showPushDrawer = ref<boolean>(false)
|
||||
const imageUpload = ref<UploadImageResponse | null>(null)
|
||||
|
||||
const tagOptions = ref<PostTag[]>([])
|
||||
const statusOptions = [
|
||||
{
|
||||
label: '草稿',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '发布',
|
||||
value: 1,
|
||||
},
|
||||
]
|
||||
|
||||
const oldForm = ref<PostEditorForm>({
|
||||
id: 0,
|
||||
title: '',
|
||||
content: '',
|
||||
summary: '',
|
||||
cover: '',
|
||||
tags: [],
|
||||
publishTime: '',
|
||||
updateTime: '',
|
||||
status: 0,
|
||||
})
|
||||
|
||||
const form = ref<PostEditorForm>({
|
||||
id: 0,
|
||||
title: '',
|
||||
content: '',
|
||||
summary: '',
|
||||
cover: '',
|
||||
tags: [],
|
||||
publishTime: '',
|
||||
updateTime: '',
|
||||
status: 0,
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
title: [
|
||||
{ required: true, message: '文章标题不能为空', trigger: 'blur' },
|
||||
],
|
||||
summary: [
|
||||
{ required: true, message: '摘要不能为空', trigger: 'blur' },
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '状态不能为空', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
const isUpdate = computed(() => form.value.id !== 0)
|
||||
|
||||
onMounted(() => {
|
||||
loading.init = false
|
||||
|
||||
oldForm.value = JSON.parse(JSON.stringify(form.value))
|
||||
})
|
||||
|
||||
async function handleShowPushDrawer() {
|
||||
loading.init = true
|
||||
showPushDrawer.value = true
|
||||
|
||||
if (!isUpdate.value) {
|
||||
form.value.summary = (wangEditorRef.value.getText() || '').slice(0, 100)
|
||||
}
|
||||
|
||||
await getPostTagApi().then((res) => {
|
||||
if (res.isSuccess) {
|
||||
tagOptions.value = res.data
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '获取帖子标签失败,请稍后再试')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
ElMessage.error('获取帖子标签失败,请稍后再试')
|
||||
})
|
||||
|
||||
loading.init = false
|
||||
}
|
||||
|
||||
async function handleSave(formEl: FormInstance | undefined) {
|
||||
if (!formEl)
|
||||
return
|
||||
|
||||
await formEl.validate(async (valid) => {
|
||||
if (!valid)
|
||||
return
|
||||
|
||||
if (form.value.content === '') {
|
||||
ElMessage.warning('文章内容不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
loading.confirm = true
|
||||
|
||||
if (form.value.cover !== oldForm.value.cover) {
|
||||
if (form.value.cover && imageUpload.value !== null) {
|
||||
const aliyunRes = await uploadToAliyun(imageUpload.value.blob, imageUpload.value.name, imageUpload.value.size, false)
|
||||
form.value.cover = aliyunRes.url
|
||||
}
|
||||
}
|
||||
|
||||
addPostApi({
|
||||
title: form.value.title,
|
||||
content: form.value.content,
|
||||
summary: form.value.summary,
|
||||
status: form.value.status,
|
||||
cover: form.value.cover,
|
||||
tags: form.value.tags,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
console.log(res)
|
||||
}
|
||||
else {
|
||||
ElMessage.error(res.message || '保存失败,请稍后再试')
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
ElMessage.error('保存失败,请稍后再试')
|
||||
}).finally(() => {
|
||||
loading.confirm = false
|
||||
|
||||
showPushDrawer.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="" />
|
||||
<div class="editor-container">
|
||||
<div class="toolbar">
|
||||
<input v-model="form.title" placeholder="输入标题" class="editor-title-input" maxlength="100">
|
||||
<div class="toolbar-extra">
|
||||
<div v-if="form.publishTime" class="time">
|
||||
更新于:{{ form.publishTime }}
|
||||
</div>
|
||||
<el-button type="primary" @click="handleShowPushDrawer()">
|
||||
发布
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<WangEditor ref="wangEditorRef" v-model="form.content" :show-direcotry="true" />
|
||||
</div>
|
||||
|
||||
<el-drawer v-model="showPushDrawer" title="发布文章">
|
||||
<CustomLoading v-if="loading.init" :in-container="true" />
|
||||
|
||||
<el-form
|
||||
v-else
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRules"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="文章标题" prop="title">
|
||||
<el-input
|
||||
v-model="form.title"
|
||||
placeholder="请输入文字标题"
|
||||
size="large"
|
||||
show-word-limit
|
||||
maxlength="100"
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签" prop="tags">
|
||||
<el-select v-model="form.tags" placeholder="请选择标签">
|
||||
<el-option
|
||||
v-for="item in tagOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="文章封面 " prop="cover">
|
||||
<ImageUploadCard
|
||||
v-model="form.cover"
|
||||
@change="(data) => {
|
||||
imageUpload = data
|
||||
}"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="文章摘要" prop="summary">
|
||||
<el-input
|
||||
v-model="form.summary"
|
||||
placeholder="请输入文章摘要"
|
||||
size="large"
|
||||
show-word-limit
|
||||
maxlength="100"
|
||||
type="textarea"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择状态">
|
||||
<el-option
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div style="flex: auto">
|
||||
<el-button>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" :loading="loading.confirm" @click="handleSave(formRef)">
|
||||
确认并发布
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//
|
||||
.editor-container {
|
||||
padding: 16px 32px;
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
.editor-title-input {
|
||||
width: 1000px;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
padding: 8px 0px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
resize: none;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.toolbar-extra {
|
||||
.time {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-card {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s ease;
|
||||
padding: 4px;
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
&.loading {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.upload-inner {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.upload-loading-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-cover {
|
||||
height: 192px;
|
||||
width: 128px;
|
||||
}
|
||||
</style>
|
||||
@@ -13,7 +13,7 @@
|
||||
v-model:visible="showPushDrawer"
|
||||
:width="450"
|
||||
unmountOnClose
|
||||
ok-text="确认并发布"
|
||||
ok-text=" "
|
||||
@before-open="handleBeforeOpenPushDrawer"
|
||||
:ok-loading="loading.init || loading.confirm"
|
||||
:on-before-ok="handleConfirmPush">
|
||||
|
||||
Reference in new issue
Block a user