mirror of
https://github.com/blossom-editor/blossom
synced 2024-11-17 14:39:21 +08:00
Compare commits
6 Commits
d6e6430397
...
7b1c898b90
Author | SHA1 | Date | |
---|---|---|---|
|
7b1c898b90 | ||
|
20361dceb6 | ||
|
a9b864efcb | ||
|
a718002501 | ||
|
7d14414605 | ||
|
b1d7721edc |
@ -39,10 +39,57 @@ public enum UserParamEnum {
|
||||
*/
|
||||
WEB_BLOG_LINKS(false, 0, ""),
|
||||
/**
|
||||
* 博客端专题特殊形式, 0:false;1:是
|
||||
* 博客端专题特殊形式
|
||||
* 0:否;1:是
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
WEB_BLOG_SUBJECT_TITLE(false, 0, "0"),
|
||||
/**
|
||||
* 是否在文章内容的顶部显示文章的标题
|
||||
* 0:否;1:是
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_SHOW_ARTICLE_NAME(false, 0, "1"),
|
||||
/**
|
||||
* 博客主题色
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_COLOR(false, 0, "rgb(104, 104, 104)"),
|
||||
// ----------< 博客水印 >----------
|
||||
/**
|
||||
* 启用博客水印
|
||||
* 0:否;1:是
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_WATERMARK_ENABLED(false, 0, "0"),
|
||||
/**
|
||||
* 水印内容
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_WATERMARK_CONTENT(false, 0, ""),
|
||||
/**
|
||||
* 水印字体大小
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_WATERMARK_FONTSIZE(false, 0, "15"),
|
||||
/**
|
||||
* 水印颜色
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_WATERMARK_COLOR(false, 0, "rgba(157, 157, 157, 0.2)"),
|
||||
/**
|
||||
* 水印密集度
|
||||
*
|
||||
* @since 1.15.0
|
||||
*/
|
||||
WEB_BLOG_WATERMARK_GAP(false, 0, "100"),
|
||||
;
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ package com.blossom.backend.base.paramu.pojo;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 修改参数
|
||||
@ -21,6 +22,7 @@ public class UserParamUpdReq {
|
||||
/**
|
||||
* 参数值
|
||||
*/
|
||||
@NotNull(message = "参数值为必填项")
|
||||
private String paramValue;
|
||||
|
||||
/**
|
||||
|
@ -50,6 +50,16 @@ public interface ArticleMapper extends BaseMapper<ArticleEntity> {
|
||||
*/
|
||||
void updContentById(ArticleEntity entity);
|
||||
|
||||
/**
|
||||
* 查询某段时间内编辑过内容的文章数
|
||||
*
|
||||
* @param beginUpdTime 开始修改日期
|
||||
* @param endUpdTime 结束修改日期
|
||||
*/
|
||||
ArticleStatRes statUpdArticleCount(@Param("beginUpdTime") String beginUpdTime,
|
||||
@Param("endUpdTime") String endUpdTime,
|
||||
@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 修改某段日期内修改的文章数据
|
||||
*
|
||||
|
@ -19,6 +19,7 @@ import com.blossom.backend.server.doc.pojo.DocTreeRes;
|
||||
import com.blossom.backend.server.utils.ArticleUtil;
|
||||
import com.blossom.backend.server.utils.DocUtil;
|
||||
import com.blossom.common.base.exception.XzException404;
|
||||
import com.blossom.common.base.util.DateUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
@ -188,6 +189,7 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
|
||||
if (req.getHtml() != null) {
|
||||
req.setHtml(req.getHtml().replaceAll("<p><br></p>", ""));
|
||||
}
|
||||
req.setUpdMarkdownTime(DateUtils.date());
|
||||
baseMapper.updContentById(req);
|
||||
referenceService.bind(req.getUserId(), req.getId(), req.getName(), req.getReferences());
|
||||
logService.insert(req.getId(), 0, req.getMarkdown());
|
||||
|
@ -118,6 +118,10 @@ public class ArticleEntity extends AbstractPOJO implements Serializable {
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 文章内容的修改时间
|
||||
*/
|
||||
private Date updMarkdownTime;
|
||||
|
||||
//region ============================== 非数据库字段 ==============================
|
||||
/**
|
||||
|
@ -42,12 +42,12 @@ public class ArticleStatJob {
|
||||
}
|
||||
|
||||
for (UserEntity user : users) {
|
||||
ArticleStatRes statCount = statService.statCount(toDayBegin, toDayEnd, user.getId());
|
||||
statService.updByDate(1, toDay, statCount.getArticleCount(), user.getId());
|
||||
ArticleStatRes statCount = statService.statUpdArticleCount(toDayBegin, toDayEnd, user.getId());
|
||||
statService.updByDate(ArticleStatTypeEnum.ARTICLE_HEATMAP, toDay, statCount.getArticleCount(), user.getId());
|
||||
|
||||
String toMouth = DateUtils.format(DateUtils.beginOfMonth(DateUtils.date()), DateUtils.PATTERN_YYYYMMDD);
|
||||
ArticleStatRes statWord = statService.statCount(null, null, user.getId());
|
||||
statService.updByDate(2, toMouth, statWord.getArticleWords(), user.getId());
|
||||
statService.updByDate(ArticleStatTypeEnum.ARTICLE_WORDS, toMouth, statWord.getArticleWords(), user.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,16 @@ public class ArticleStatService extends ServiceImpl<ArticleStatMapper, ArticleSt
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章统计, 文章数, 总字数
|
||||
*
|
||||
* @param beginUpdTime 修改日期的开始范围
|
||||
* @param endUpdTime 修改日期的结束范围
|
||||
*/
|
||||
public ArticleStatRes statUpdArticleCount(String beginUpdTime, String endUpdTime, Long userId) {
|
||||
return articleMapper.statUpdArticleCount(beginUpdTime, endUpdTime, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章统计, 文章数, 总字数
|
||||
*
|
||||
@ -161,7 +171,7 @@ public class ArticleStatService extends ServiceImpl<ArticleStatMapper, ArticleSt
|
||||
return;
|
||||
}
|
||||
for (ArticleWordsRes words : req.getWordsList()) {
|
||||
this.updByDate(ArticleStatTypeEnum.ARTICLE_WORDS.getType(), words.getDate() + "-01", words.getValue(), userId);
|
||||
this.updByDate(ArticleStatTypeEnum.ARTICLE_WORDS, words.getDate() + "-01", words.getValue(), userId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,26 +184,26 @@ public class ArticleStatService extends ServiceImpl<ArticleStatMapper, ArticleSt
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updByDate(Integer type, String date, Integer value, Long userId) {
|
||||
public void updByDate(ArticleStatTypeEnum type, String date, Integer value, Long userId) {
|
||||
if (value == null) {
|
||||
value = 0;
|
||||
}
|
||||
LambdaQueryWrapper<ArticleStatEntity> existWhere = new LambdaQueryWrapper<>();
|
||||
existWhere
|
||||
.eq(ArticleStatEntity::getUserId, userId)
|
||||
.eq(ArticleStatEntity::getType, type)
|
||||
.eq(ArticleStatEntity::getType, type.getType())
|
||||
.eq(ArticleStatEntity::getStatDate, date);
|
||||
if (baseMapper.exists(existWhere)) {
|
||||
LambdaQueryWrapper<ArticleStatEntity> updWhere = new LambdaQueryWrapper<>();
|
||||
updWhere.eq(ArticleStatEntity::getUserId, userId)
|
||||
.eq(ArticleStatEntity::getType, type)
|
||||
.eq(ArticleStatEntity::getType, type.getType())
|
||||
.eq(ArticleStatEntity::getStatDate, date);
|
||||
ArticleStatEntity upd = new ArticleStatEntity();
|
||||
upd.setStatValue(value);
|
||||
baseMapper.update(upd, updWhere);
|
||||
} else {
|
||||
ArticleStatEntity ist = new ArticleStatEntity();
|
||||
ist.setType(type);
|
||||
ist.setType(type.getType());
|
||||
ist.setStatDate(DateUtils.parse(date, DateUtils.PATTERN_YYYYMMDD));
|
||||
ist.setStatValue(value);
|
||||
ist.setUserId(userId);
|
||||
|
@ -9,7 +9,6 @@ import com.blossom.backend.server.article.draft.pojo.ArticleUpdTagReq;
|
||||
import com.blossom.backend.server.doc.DocService;
|
||||
import com.blossom.backend.server.folder.pojo.*;
|
||||
import com.blossom.backend.server.utils.DocUtil;
|
||||
import com.blossom.common.base.enums.YesNo;
|
||||
import com.blossom.common.base.exception.XzException404;
|
||||
import com.blossom.common.base.pojo.DelReq;
|
||||
import com.blossom.common.base.pojo.R;
|
||||
@ -46,7 +45,7 @@ public class FolderController {
|
||||
if (userId == null) {
|
||||
return R.ok(new ArrayList<>());
|
||||
}
|
||||
return R.ok(baseService.subjects(userId, YesNo.YES));
|
||||
return R.ok(baseService.subjects(userId, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,8 +54,8 @@ public class FolderController {
|
||||
* @param starStatus 公开状态
|
||||
*/
|
||||
@GetMapping("/subjects")
|
||||
public R<List<FolderSubjectRes>> listSubject(@RequestParam("starStatus") Integer starStatus) {
|
||||
return R.ok(baseService.subjects(AuthContext.getUserId(), YesNo.getValue(starStatus)));
|
||||
public R<List<FolderSubjectRes>> listSubject() {
|
||||
return R.ok(baseService.subjects(AuthContext.getUserId(), false, true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@ -56,33 +55,38 @@ public class FolderService extends ServiceImpl<FolderMapper, FolderEntity> {
|
||||
* @param userId 用户ID
|
||||
* @param starStatus 公开状态
|
||||
*/
|
||||
public List<FolderSubjectRes> subjects(Long userId, @NotNull YesNo starStatus) {
|
||||
public List<FolderSubjectRes> subjects(Long userId, boolean openStatus, boolean starStatus) {
|
||||
// 1. 查询所有专题
|
||||
FolderEntity where = new FolderEntity();
|
||||
where.setTags(TagEnum.subject.name());
|
||||
where.setUserId(userId);
|
||||
where.setStarStatus(starStatus.getValue());
|
||||
List<FolderEntity> allOpenSubject = baseMapper.listAll(where);
|
||||
if (CollUtil.isEmpty(allOpenSubject)) {
|
||||
if (openStatus) {
|
||||
where.setOpenStatus(YesNo.YES.getValue());
|
||||
}
|
||||
if (starStatus) {
|
||||
where.setStarStatus(YesNo.YES.getValue());
|
||||
}
|
||||
List<FolderEntity> allSubjects = baseMapper.listAll(where);
|
||||
if (CollUtil.isEmpty(allSubjects)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 专题的ID
|
||||
List<Long> allOpenSubjectIds = allOpenSubject.stream().map(FolderEntity::getId).collect(Collectors.toList());
|
||||
List<Long> allSubjectIds = allSubjects.stream().map(FolderEntity::getId).collect(Collectors.toList());
|
||||
|
||||
// 2. 查询全部专题的子文件夹
|
||||
List<FolderEntity> allOpenSubjectChildFolders = baseMapper.recursiveToChildren(CollUtil.newArrayList(allOpenSubjectIds));
|
||||
allOpenSubjectIds.addAll(allOpenSubjectChildFolders.stream().map(FolderEntity::getId).collect(Collectors.toList()));
|
||||
List<FolderEntity> allSubjectChildFolders = baseMapper.recursiveToChildren(CollUtil.newArrayList(allSubjectIds));
|
||||
allSubjectIds.addAll(allSubjectChildFolders.stream().map(FolderEntity::getId).collect(Collectors.toList()));
|
||||
|
||||
// 3. 查询这些文件夹下的所有文章
|
||||
ArticleEntity articleWhere = new ArticleEntity();
|
||||
articleWhere.setPids(allOpenSubjectIds);
|
||||
articleWhere.setPids(allSubjectIds);
|
||||
articleWhere.setUserId(userId);
|
||||
List<ArticleEntity> articles = articleMapper.listAll(articleWhere);
|
||||
|
||||
List<FolderSubjectRes> results = new ArrayList<>();
|
||||
|
||||
for (FolderEntity subject : allOpenSubject) {
|
||||
for (FolderEntity subject : allSubjects) {
|
||||
// 专题对象, 包含字数, 更新日期等信息
|
||||
FolderSubjectRes result = subject.to(FolderSubjectRes.class);
|
||||
// 默认专题字数
|
||||
@ -90,7 +94,7 @@ public class FolderService extends ServiceImpl<FolderMapper, FolderEntity> {
|
||||
// 默认专题修改时间
|
||||
result.setSubjectUpdTime(subject.getCreTime());
|
||||
// 4. 这个专题下的所有文件夹ID
|
||||
List<Long> subjectAllId = DocUtil.getChildrenIds(subject.getId(), allOpenSubjectChildFolders);
|
||||
List<Long> subjectAllId = DocUtil.getChildrenIds(subject.getId(), allSubjectChildFolders);
|
||||
// 5. 遍历文章, 将文章归属到某个专题下, 并统计相关字数, 日期等信息
|
||||
for (ArticleEntity article : articles) {
|
||||
if (subjectAllId.contains(article.getPid())) {
|
||||
|
@ -92,12 +92,24 @@
|
||||
html = #{html},
|
||||
words = #{words},
|
||||
toc = #{toc},
|
||||
version = version + 1
|
||||
version = version + 1,
|
||||
upd_markdown_time = #{updMarkdownTime}
|
||||
where id = #{id}
|
||||
and user_id = #{userId}
|
||||
</update>
|
||||
|
||||
<!-- ======================================== 统计 ======================================== -->
|
||||
<select id="statUpdArticleCount" resultType="com.blossom.backend.server.article.draft.pojo.ArticleStatRes">
|
||||
select count(*) as articleCount
|
||||
from blossom_article
|
||||
where user_id = #{userId}
|
||||
<if test="beginUpdTime != null and beginUpdTime != ''">
|
||||
and upd_markdown_time >= #{beginUpdTime}
|
||||
</if>
|
||||
<if test="endUpdTime != null and endUpdTime != ''">
|
||||
and #{endUpdTime} >= upd_markdown_time
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="statCount" resultType="com.blossom.backend.server.article.draft.pojo.ArticleStatRes">
|
||||
select count(*) as articleCount, sum(words) as articleWords
|
||||
|
@ -641,4 +641,7 @@ CREATE TABLE IF NOT EXISTS `base_user_param`
|
||||
COLLATE = utf8mb4_bin;
|
||||
|
||||
-- Code that might be wrong goes last
|
||||
alter table blossom_folder add column star_status tinyint(1) NOT NULL DEFAULT 0 COMMENT '收藏 0:否,1:是';
|
||||
-- since 1.14.0
|
||||
alter table blossom_folder add column star_status tinyint(1) NOT NULL DEFAULT 0 COMMENT '收藏 0:否,1:是';
|
||||
-- since 1.15.0
|
||||
alter table blossom_article add column upd_markdown_time datetime COMMENT '文章内容的修改时间';
|
@ -97,7 +97,14 @@ export const DEFAULT_USER_INFO = {
|
||||
WEB_GONG_WANG_AN_BEI: '',
|
||||
WEB_BLOG_URL_ERROR_TIP_SHOW: 1,
|
||||
WEB_BLOG_LINKS: '',
|
||||
WEB_BLOG_SUBJECT_TITLE: false
|
||||
WEB_BLOG_SUBJECT_TITLE: false,
|
||||
WEB_BLOG_COLOR: '',
|
||||
WEB_BLOG_SHOW_ARTICLE_NAME: true,
|
||||
WEB_BLOG_WATERMARK_ENABLED: false,
|
||||
WEB_BLOG_WATERMARK_CONTENT: '',
|
||||
WEB_BLOG_WATERMARK_FONTSIZE: 15,
|
||||
WEB_BLOG_WATERMARK_COLOR: '',
|
||||
WEB_BLOG_WATERMARK_GAP: 100
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@
|
||||
<div class="tree-menu-level2" :style="rMenuLevel2">
|
||||
<div v-if="curDoc.o === 0" @click="open(1)"><span class="iconbl bl-a-cloudupload-line"></span>公开{{ curDocType }}</div>
|
||||
<div v-if="curDoc.o === 1" @click="open(0)"><span class="iconbl bl-a-clouddownload-line"></span>取消{{ curDocType }}公开</div>
|
||||
<div v-if="curDoc.star === 0" @click="star(1)"><span class="iconbl bl-star-fill"></span>收藏{{ curDocType }}</div>
|
||||
<div v-if="curDoc.star === 0" @click="star(1)"><span class="iconbl bl-star-line"></span>收藏{{ curDocType }}</div>
|
||||
<div v-if="curDoc.star === 1" @click="star(0)"><span class="iconbl bl-star-line"></span>取消收藏{{ curDocType }}</div>
|
||||
<div v-if="curDoc.ty === 3 && !curDoc.t.includes('toc')" @click="addArticleTag('toc')">
|
||||
<span class="iconbl bl-list-ordered"></span>设为专题目录
|
||||
|
@ -56,7 +56,7 @@ let maxWords = 0
|
||||
const subjects = ref<any>([])
|
||||
|
||||
const getSubjects = () => {
|
||||
subjectsApi({ starStatus: 1 }).then((resp) => {
|
||||
subjectsApi().then((resp) => {
|
||||
subjects.value = resp.data
|
||||
if (!isEmpty(resp.data)) {
|
||||
maxWords = resp.data
|
||||
|
@ -76,7 +76,7 @@
|
||||
<ArticleSubjects></ArticleSubjects>
|
||||
</bl-col>
|
||||
|
||||
<bl-col width="100%" height="calc(100% - 45px - 270px - 330px)">
|
||||
<bl-col width="100%" height="calc(100% - 33px - 270px - 330px)">
|
||||
<bl-row class="container-name">待办事项</bl-row>
|
||||
<bl-row class="container-sub-name">Todo List</bl-row>
|
||||
<bl-row style="padding-bottom: 10px; height: calc(100% - 80px)">
|
||||
|
@ -4,24 +4,24 @@
|
||||
<el-tab-pane label="客户端配置" name="client">
|
||||
<div class="tab-content"><ConfigClient></ConfigClient></div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="服务器配置" name="server" :lazy="true" v-if="userStore.userinfo.type === 1">
|
||||
<el-tab-pane label="服务器配置" name="server" :lazy="true" v-if="userStore.userinfo.type === 1 && userStore.isLogin">
|
||||
<div class="tab-content"><ConfigServer ref="ConfigServerRef"></ConfigServer></div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="博客配置" name="blog" :lazy="true">
|
||||
<el-tab-pane label="博客配置" name="blog" :lazy="true" v-if="userStore.isLogin">
|
||||
<div class="tab-content"><ConfigBlog ref="ConfigBlogRef"></ConfigBlog></div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改个人信息" name="userinfo" :lazy="true">
|
||||
<el-tab-pane label="修改个人信息" name="userinfo" :lazy="true" v-if="userStore.isLogin">
|
||||
<div class="tab-content"><ConfigUserinfo ref="ConfigUserinfoRef"></ConfigUserinfo></div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改登录密码" name="password" :lazy="true">
|
||||
<el-tab-pane label="修改登录密码" name="password" :lazy="true" v-if="userStore.isLogin">
|
||||
<div class="tab-content"><ConfigUpdPwd></ConfigUpdPwd></div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="添加使用账号" name="adduser" :lazy="true" v-if="userStore.userinfo.type === 1">
|
||||
<el-tab-pane label="添加使用账号" name="adduser" :lazy="true" v-if="userStore.userinfo.type === 1 && userStore.isLogin">
|
||||
<div class="tab-content">
|
||||
<ConfigAddUser></ConfigAddUser>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="实时访问流量" name="flow" v-if="userStore.isLogin && userStore.userinfo.type === 1" :lazy="true">
|
||||
<el-tab-pane label="实时访问流量" name="flow" :lazy="true" v-if="userStore.isLogin && userStore.userinfo.type === 1 && userStore.isLogin">
|
||||
<SentinelResources></SentinelResources>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
@ -25,6 +25,30 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider></el-divider>
|
||||
|
||||
<el-form-item label="博客主题色">
|
||||
<bl-row class="colors">
|
||||
<el-input
|
||||
size="default"
|
||||
v-model="userParamForm.WEB_BLOG_COLOR"
|
||||
:style="{
|
||||
width: '180px',
|
||||
marginRight: '15px',
|
||||
'--el-input-text-color': userParamForm.WEB_BLOG_COLOR,
|
||||
'--el-input-border-color': userParamForm.WEB_BLOG_COLOR
|
||||
}"></el-input>
|
||||
<el-color-picker
|
||||
size="default"
|
||||
popper-class="theme-color-picker"
|
||||
v-model="userParamForm.WEB_BLOG_COLOR"
|
||||
color-format="rgb"
|
||||
@change="changeBlogColor" />
|
||||
<div class="color-item" v-for="color in colors" :key="color" :style="{ backgroundColor: color }" @click="changeBlogColor(color)"></div>
|
||||
</bl-row>
|
||||
<div class="conf-tip">博客主题色,主要影响专题样式颜色以及文章中的链接颜色。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="博客名称">
|
||||
<el-input size="default" v-model="userParamForm.WEB_LOGO_NAME" @change="(cur: any) => updParam('WEB_LOGO_NAME', cur)"></el-input>
|
||||
<div class="conf-tip">博客左上角名称,以及在浏览器标签中的名称。</div>
|
||||
@ -35,6 +59,17 @@
|
||||
<div class="conf-tip">博客左上角 Logo 的访问地址,以及在浏览器标签中的 Logo。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否显示文章标题">
|
||||
<bl-row>
|
||||
<el-switch
|
||||
v-model="userParamForm.WEB_BLOG_SHOW_ARTICLE_NAME"
|
||||
size="default"
|
||||
style="margin-right: 10px"
|
||||
@change="(cur: boolean) => updParam('WEB_BLOG_SHOW_ARTICLE_NAME', cur ? '1' : '0')" />
|
||||
</bl-row>
|
||||
<div class="conf-tip">是否在文章内容的顶部显示文章的标题。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开启专题特殊样式">
|
||||
<bl-row>
|
||||
<el-switch
|
||||
@ -46,19 +81,83 @@
|
||||
<div class="conf-tip">是否在博客文档列表中以特殊的样式显示专题。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="自定义信息">
|
||||
<el-form-item label="开启文章水印">
|
||||
<bl-row>
|
||||
<el-switch
|
||||
size="default"
|
||||
v-model="userParamForm.WEB_BLOG_WATERMARK_ENABLED"
|
||||
@change="(cur: boolean) => updParam('WEB_BLOG_WATERMARK_ENABLED', cur ? '1' : '0')" />
|
||||
</bl-row>
|
||||
<div class="conf-tip">是否开启博客文章背景文字水印。</div>
|
||||
<div class="conf-watermark" v-if="userParamForm.WEB_BLOG_WATERMARK_ENABLED">
|
||||
<bl-row>
|
||||
<bl-row width="60%">
|
||||
<div class="label">水印内容</div>
|
||||
<el-input
|
||||
size="default"
|
||||
v-model="userParamForm.WEB_BLOG_WATERMARK_CONTENT"
|
||||
@change="(cur: any) => updParam('WEB_BLOG_WATERMARK_CONTENT', cur)"></el-input>
|
||||
</bl-row>
|
||||
<bl-row width="40%">
|
||||
<div class="label">水印大小</div>
|
||||
<el-input-number
|
||||
size="default"
|
||||
v-model="userParamForm.WEB_BLOG_WATERMARK_FONTSIZE"
|
||||
@change="(cur: any) => updParam('WEB_BLOG_WATERMARK_FONTSIZE', cur.toString())"></el-input-number>
|
||||
</bl-row>
|
||||
</bl-row>
|
||||
|
||||
<bl-row style="margin-top: 10px">
|
||||
<bl-row width="60%">
|
||||
<div class="label">水印颜色</div>
|
||||
<el-input
|
||||
size="default"
|
||||
style="width: calc(100% - 120px); margin-right: 8px"
|
||||
v-model="userParamForm.WEB_BLOG_WATERMARK_COLOR"
|
||||
@change="(cur: any) => updParam('WEB_BLOG_WATERMARK_COLOR', cur)">
|
||||
<template #suffix>
|
||||
<el-tooltip effect="light" placement="top" transition="none">
|
||||
<template #content> 使用透明颜色会有更好的显示效果<br />如: rgba(157, 157, 157, 0.2)</template>
|
||||
<span class="iconbl bl-admonish-line"></span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tooltip effect="light" placement="top" transition="none">
|
||||
<template #content> 使用透明颜色会有更好的效果<br />如: rgba(157, 157, 157, 0.2)</template>
|
||||
<el-color-picker
|
||||
size="default"
|
||||
popper-class="theme-color-picker"
|
||||
v-model="userParamForm.WEB_BLOG_WATERMARK_COLOR"
|
||||
color-format="rgba"
|
||||
show-alpha
|
||||
@change="changeWatermarkColor" />
|
||||
</el-tooltip>
|
||||
</bl-row>
|
||||
<bl-row width="40%">
|
||||
<div class="label">密集程度</div>
|
||||
<el-input-number
|
||||
size="default"
|
||||
v-model="userParamForm.WEB_BLOG_WATERMARK_GAP"
|
||||
:step="10"
|
||||
@change="(cur: any) => updParam('WEB_BLOG_WATERMARK_GAP', cur.toString())"></el-input-number>
|
||||
</bl-row>
|
||||
</bl-row>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="ICP备案号">
|
||||
<el-input size="default" v-model="userParamForm.WEB_IPC_BEI_AN_HAO" @change="(cur: any) => updParam('WEB_IPC_BEI_AN_HAO', cur)"></el-input>
|
||||
<div class="conf-tip">如果博客作为你的域名首页,你可能需要配置 ICP 备案号,该内容会跳转至中国大陆《ICP/IP地址/域名信息备案管理系统》。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="首页自定义信息">
|
||||
<el-input
|
||||
size="default"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
v-model="userParamForm.WEB_GONG_WANG_AN_BEI"
|
||||
@change="(cur: any) => updParam('WEB_GONG_WANG_AN_BEI', cur)"></el-input>
|
||||
<div class="conf-tip">自定义信息,可填写 HTML 内容。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="ICP备案号">
|
||||
<el-input size="default" v-model="userParamForm.WEB_IPC_BEI_AN_HAO" @change="(cur: any) => updParam('WEB_IPC_BEI_AN_HAO', cur)"></el-input>
|
||||
<div class="conf-tip">如果博客作为你的域名首页,你可能需要配置 ICP 备案号,该内容会跳转至中国大陆《ICP/IP地址/域名信息备案管理系统》。</div>
|
||||
<div class="conf-tip">首页底部的自定义信息,可填写 HTML 内容,也可在此自定义备案信息即跳转地址等信息。</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="外部链接">
|
||||
@ -98,18 +197,57 @@ const userParamForm = ref({
|
||||
WEB_GONG_WANG_AN_BEI: '',
|
||||
WEB_BLOG_URL_ERROR_TIP_SHOW: '',
|
||||
WEB_BLOG_LINKS: '',
|
||||
WEB_BLOG_SUBJECT_TITLE: false
|
||||
WEB_BLOG_SUBJECT_TITLE: false,
|
||||
WEB_BLOG_COLOR: '',
|
||||
WEB_BLOG_SHOW_ARTICLE_NAME: true,
|
||||
WEB_BLOG_WATERMARK_ENABLED: false,
|
||||
WEB_BLOG_WATERMARK_CONTENT: '',
|
||||
WEB_BLOG_WATERMARK_FONTSIZE: 15,
|
||||
WEB_BLOG_WATERMARK_COLOR: '',
|
||||
WEB_BLOG_WATERMARK_GAP: 100
|
||||
})
|
||||
|
||||
const colors = ['rgb(136, 118, 87)', 'rgb(119, 150, 73)', 'rgb(128, 164, 146)', 'rgb(110, 155, 197)', 'rgb(97, 94, 168)', 'rgb(104, 104, 104)']
|
||||
|
||||
const changeBlogColor = (color: string) => {
|
||||
if (!color) {
|
||||
userParamForm.value.WEB_BLOG_COLOR = 'rgb(104, 104, 104)'
|
||||
} else {
|
||||
userParamForm.value.WEB_BLOG_COLOR = color
|
||||
}
|
||||
updParam('WEB_BLOG_COLOR', userParamForm.value.WEB_BLOG_COLOR)
|
||||
}
|
||||
|
||||
const changeWatermarkColor = (color: string) => {
|
||||
if (!color) {
|
||||
userParamForm.value.WEB_BLOG_WATERMARK_COLOR = 'rgba(157, 157, 157, 0.2)'
|
||||
} else {
|
||||
userParamForm.value.WEB_BLOG_WATERMARK_COLOR = color
|
||||
}
|
||||
updParam('WEB_BLOG_WATERMARK_COLOR', userParamForm.value.WEB_BLOG_WATERMARK_COLOR)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数列表
|
||||
*/
|
||||
const getParamList = () => {
|
||||
userParamListApi().then((resp) => {
|
||||
userParamForm.value = { ...resp.data, ...{ WEB_BLOG_SUBJECT_TITLE: resp.data.WEB_BLOG_SUBJECT_TITLE === '1' } }
|
||||
userParamForm.value = {
|
||||
...resp.data,
|
||||
...{
|
||||
WEB_BLOG_SUBJECT_TITLE: resp.data.WEB_BLOG_SUBJECT_TITLE === '1',
|
||||
WEB_BLOG_SHOW_ARTICLE_NAME: resp.data.WEB_BLOG_SHOW_ARTICLE_NAME === '1',
|
||||
WEB_BLOG_WATERMARK_ENABLED: resp.data.WEB_BLOG_WATERMARK_ENABLED === '1',
|
||||
WEB_BLOG_WATERMARK_FONTSIZE: Number(resp.data.WEB_BLOG_WATERMARK_FONTSIZE),
|
||||
WEB_BLOG_WATERMARK_GAP: Number(resp.data.WEB_BLOG_WATERMARK_GAP)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新参数
|
||||
*/
|
||||
const refreshParam = () => {
|
||||
userParamRefreshApi().then((_) => {
|
||||
Notify.success('', '刷新成功')
|
||||
@ -118,6 +256,12 @@ const refreshParam = () => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改参数
|
||||
*
|
||||
* @param paramName 参数名
|
||||
* @param paramValue 参数值
|
||||
*/
|
||||
const updParam = (paramName: string, paramValue: string) => {
|
||||
userParamUpdApi({ paramName: paramName, paramValue: paramValue }).then((_resp) => {
|
||||
userStore.getUserinfo()
|
||||
@ -130,6 +274,9 @@ const toBlog = () => {
|
||||
openExtenal(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接配置模板
|
||||
*/
|
||||
const genWebLinksTemplate = () => {
|
||||
if (isNotBlank(userParamForm.value.WEB_BLOG_LINKS)) {
|
||||
Notify.info('你需要将内容清空后再生成模板', '提示')
|
||||
@ -165,4 +312,32 @@ defineExpose({ reload })
|
||||
white-space: pre;
|
||||
color: var(--bl-text-color-light);
|
||||
}
|
||||
|
||||
.colors {
|
||||
align-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.el-color-picker--small {
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
@include box(28px, 28px);
|
||||
margin: 0 0 0 15px;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
transition: transform 0.3s;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme-color-picker {
|
||||
z-index: 3001 !important;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -69,4 +69,31 @@
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.conf-watermark {
|
||||
@include box(100%, auto);
|
||||
color: var(--bl-text-color);
|
||||
border: 1px solid var(--el-border-color);
|
||||
background-color: var(--bl-bg-color);
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.label {
|
||||
min-width: 50px;
|
||||
margin: 0 10px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-input {
|
||||
width: calc(100% - 80px);
|
||||
}
|
||||
|
||||
.el-input-number {
|
||||
width: 130px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { onMounted, type StyleHTMLAttributes } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import { isNotBlank } from './assets/utils/obj'
|
||||
@ -15,6 +15,9 @@ const userStore = useUserStore()
|
||||
|
||||
onMounted(async () => {
|
||||
await userStore.getUserinfoOpen()
|
||||
initCustomTheme()
|
||||
console.log(userStore.userParams.WEB_BLOG_SHOW_ARTICLE_NAME);
|
||||
|
||||
|
||||
// 优先使用后台配置的博客名称, 否则使用默认
|
||||
if (isNotBlank(userStore.userParams.WEB_LOGO_NAME)) {
|
||||
@ -32,4 +35,42 @@ onMounted(async () => {
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
}
|
||||
})
|
||||
|
||||
const THEME_STYLE_TAG_ID = 'blossom-blog-theme-css'
|
||||
|
||||
/**
|
||||
* 初始化样式
|
||||
*/
|
||||
const initCustomTheme = () => {
|
||||
const rgb = userStore.userParams.WEB_BLOG_COLOR
|
||||
console.log(rgb)
|
||||
if (!rgb.toLowerCase().startsWith('rgb(')) {
|
||||
return
|
||||
}
|
||||
const prefix = rgb.substring(4, rgb.length - 1)
|
||||
let text = `:root {
|
||||
--el-color-primary: ${rgb};
|
||||
--el-color-primary-dark: ${rgb};
|
||||
--el-color-primary-dark-2: rgba(${prefix}, 0.8);
|
||||
--el-color-primary-light-1: rgba(${prefix}, 0.9);
|
||||
--el-color-primary-light-2: rgba(${prefix}, 0.8);
|
||||
--el-color-primary-light-3: rgba(${prefix}, 0.7);
|
||||
--el-color-primary-light-4: rgba(${prefix}, 0.6);
|
||||
--el-color-primary-light-5: rgba(${prefix}, 0.5);
|
||||
--el-color-primary-light-6: rgba(${prefix}, 0.4);
|
||||
--el-color-primary-light-7: rgba(${prefix}, 0.3);
|
||||
--el-color-primary-light-8: rgba(${prefix}, 0.2);
|
||||
--el-color-primary-light-9: rgba(${prefix}, 0.1);
|
||||
}`
|
||||
|
||||
let themeStyleTag = document.createElement('style') as unknown as StyleHTMLAttributes
|
||||
themeStyleTag.type = 'text/css'
|
||||
themeStyleTag.id = THEME_STYLE_TAG_ID
|
||||
themeStyleTag.innerHTML = text
|
||||
document
|
||||
.getElementsByTagName('head')
|
||||
.item(0)!
|
||||
.appendChild(themeStyleTag as Node)
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
|
@ -46,7 +46,14 @@ const DEFAULT_USER_INFO = {
|
||||
WEB_GONG_WANG_AN_BEI: '',
|
||||
WEB_BLOG_URL_ERROR_TIP_SHOW: 1,
|
||||
WEB_BLOG_LINKS: '',
|
||||
WEB_BLOG_SUBJECT_TITLE: '1'
|
||||
WEB_BLOG_SUBJECT_TITLE: '1',
|
||||
WEB_BLOG_SHOW_ARTICLE_NAME: '1',
|
||||
WEB_BLOG_COLOR: 'rgb(104, 104, 104)',
|
||||
WEB_BLOG_WATERMARK_ENABLED: '0',
|
||||
WEB_BLOG_WATERMARK_CONTENT: '',
|
||||
WEB_BLOG_WATERMARK_FONTSIZE: 15,
|
||||
WEB_BLOG_WATERMARK_COLOR: '',
|
||||
WEB_BLOG_WATERMARK_GAP: 100
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,14 +50,24 @@
|
||||
</div>
|
||||
|
||||
<div class="doc-content-container" ref="PreviewRef" :style="{ fontSize: getFontSize() }">
|
||||
<div class="article-name">{{ article.name }}</div>
|
||||
<div class="bl-preview" :style="{ fontSize: getFontSize() }" v-html="article.html"></div>
|
||||
<div class="article-name" v-if="userStore.userParams.WEB_BLOG_SHOW_ARTICLE_NAME === '1'">{{ article.name }}</div>
|
||||
|
||||
<el-watermark
|
||||
:font="{
|
||||
color: userStore.userParams.WEB_BLOG_WATERMARK_COLOR,
|
||||
fontSize: userStore.userParams.WEB_BLOG_WATERMARK_FONTSIZE,
|
||||
textBaseline: 'hanging'
|
||||
}"
|
||||
:content="article.id > 0 && userStore.userParams.WEB_BLOG_WATERMARK_ENABLED === '1' ? userStore.userParams.WEB_BLOG_WATERMARK_CONTENT : ''"
|
||||
:gap="[userStore.userParams.WEB_BLOG_WATERMARK_GAP, userStore.userParams.WEB_BLOG_WATERMARK_GAP]">
|
||||
<div class="bl-preview" :style="{ fontSize: getFontSize() }" v-html="article.html"></div>
|
||||
</el-watermark>
|
||||
</div>
|
||||
|
||||
<div class="toc-container" :style="tocStyle">
|
||||
<div class="viewer-toc">
|
||||
<div v-if="article.id != 0">
|
||||
<div class="toc-subtitle" style="font-size: 15px">《{{ article.name }}》</div>
|
||||
<div class="toc-subtitle" style="font-size: 15px">{{ article.name }}</div>
|
||||
<div class="toc-subtitle">
|
||||
<span class="iconbl bl-pen-line"></span> {{ article.words }} 字 | <span class="iconbl bl-read-line"></span> {{ article.uv }} |
|
||||
<span class="iconbl bl-like-line"></span> {{ article.likes }}
|
||||
|
@ -17,6 +17,7 @@
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
word-break: break-all;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,9 @@
|
||||
}
|
||||
|
||||
.toc-subtitle {
|
||||
width: 100%;
|
||||
@include flex(row, flex-start, center);
|
||||
@include font(12px);
|
||||
width: 100%;
|
||||
color: #ababab;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
@ -53,6 +53,7 @@
|
||||
|
||||
&:hover {
|
||||
font-weight: bold;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ const toToc = (articleId: number) => {
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
margin: 15px 16px;
|
||||
transition: 0.3s;
|
||||
transition: border 0.3s, box-shadow 0.3s;
|
||||
box-shadow: 0 0 12px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
@ -72,7 +72,7 @@ const toToc = (articleId: number) => {
|
||||
font-size: 12px;
|
||||
margin-right: 5px;
|
||||
opacity: 0;
|
||||
transition: 0.3s;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@ -87,7 +87,6 @@ const toToc = (articleId: number) => {
|
||||
text-shadow: 5px 5px 15px #000, -3px -3px 10px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.cover,
|
||||
.cover-name {
|
||||
opacity: 1;
|
||||
}
|
||||
@ -107,7 +106,7 @@ const toToc = (articleId: number) => {
|
||||
height: 35px;
|
||||
font-weight: 700;
|
||||
color: #cdcdcd;
|
||||
transition: 0.3s;
|
||||
transition: text-shadow 0.3s;
|
||||
}
|
||||
|
||||
.words {
|
||||
@ -134,7 +133,7 @@ const toToc = (articleId: number) => {
|
||||
box-shadow: 0 0 10px 1px #181818;
|
||||
text-align: center;
|
||||
transform: scale(0.8) rotate(45deg);
|
||||
transition: 0.3s;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.describes {
|
||||
|
Loading…
Reference in New Issue
Block a user