博客端与后端集成,博客端配置外置

This commit is contained in:
xiaozzzi 2023-12-12 21:39:07 +08:00
parent 853c6866f6
commit 3a38515103
41 changed files with 437 additions and 272 deletions

View File

@ -1,9 +1,16 @@
HELP.md HELP.md
target/ target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
!**/src/main/** #!**/src/main/**
!**/src/test/** !**/src/test/**
### static ###
/backend/src/main/resources/static/blog/**
!/backend/src/main/resources/static/blog/README.md
/backend/src/main/resources/static/editor/**
!/backend/src/main/resources/static/editor/README.md
### STS ### ### STS ###
.apt_generated .apt_generated
.classpath .classpath

View File

@ -14,14 +14,10 @@
<properties> <properties>
<java-jwt.version>4.3.0</java-jwt.version> <!-- JWT --> <java-jwt.version>4.3.0</java-jwt.version> <!-- JWT -->
<smart-doc.version>2.6.7-BL-SNAPSHOT</smart-doc.version> <smart-doc.version>2.6.7-BL-SNAPSHOT</smart-doc.version>
<thumbnailator.version>0.4.20</thumbnailator.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>com.blossom</groupId>
<artifactId>blossom-web</artifactId>
<version>1.10.0-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.blossom</groupId> <groupId>com.blossom</groupId>
@ -72,14 +68,9 @@
<dependency> <dependency>
<groupId>net.coobird</groupId> <groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId> <artifactId>thumbnailator</artifactId>
<version>0.4.20</version> <version>${thumbnailator.version}</version>
</dependency> </dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-thymeleaf</artifactId>-->
<!-- </dependency>-->
</dependencies> </dependencies>
<build> <build>

View File

@ -1,5 +1,6 @@
package com.blossom.backend.base.user; package com.blossom.backend.base.user;
import cn.hutool.core.util.ObjUtil;
import com.blossom.backend.base.param.ParamEnum; import com.blossom.backend.base.param.ParamEnum;
import com.blossom.backend.base.param.ParamService; import com.blossom.backend.base.param.ParamService;
import com.blossom.backend.base.sys.SysService; import com.blossom.backend.base.sys.SysService;
@ -10,6 +11,7 @@ import com.blossom.backend.server.article.stat.ArticleStatService;
import com.blossom.backend.base.auth.AuthContext; import com.blossom.backend.base.auth.AuthContext;
import com.blossom.backend.base.auth.annotation.AuthIgnore; import com.blossom.backend.base.auth.annotation.AuthIgnore;
import com.blossom.common.base.exception.XzException400; import com.blossom.common.base.exception.XzException400;
import com.blossom.common.base.exception.XzException404;
import com.blossom.common.base.pojo.R; import com.blossom.common.base.pojo.R;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -54,7 +56,9 @@ public class UserController {
if (userId == null) { if (userId == null) {
return R.ok(new BlossomUserRes()); return R.ok(new BlossomUserRes());
} }
BlossomUserRes user = userService.selectById(userId).to(BlossomUserRes.class); UserEntity u = userService.selectById(userId);
XzException404.throwBy(ObjUtil.isNull(u), "用户不存在");
BlossomUserRes user = u.to(BlossomUserRes.class);
ArticleStatRes stat = articleService.statCount(null, null, userId); ArticleStatRes stat = articleService.statCount(null, null, userId);
user.setArticleWords(stat.getArticleWords()); user.setArticleWords(stat.getArticleWords());
user.setArticleCount(stat.getArticleCount()); user.setArticleCount(stat.getArticleCount());

View File

@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.CacheControl;
import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@ -16,6 +17,7 @@ import org.springframework.web.servlet.config.annotation.*;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/** /**
@ -79,25 +81,8 @@ public class WebConfigurer implements WebMvcConfigurer {
} }
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
registry.addInterceptor(userTypeInterceptor()) configurer.setTaskExecutor(executor);
.addPathPatterns("/**")
.excludePathPatterns(
"/blog/**",
"/assets/**"
);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/blog/").setViewName("/blog/index.html");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/blog/**").addResourceLocations("classpath:/META-INF/resources/webjars/blossom-web/");
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/META-INF/resources/webjars/blossom-web/assets/");
} }
@Bean @Bean
@ -105,9 +90,36 @@ public class WebConfigurer implements WebMvcConfigurer {
return new UserTypeInterceptor(); return new UserTypeInterceptor();
} }
@Override @Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) { public void addInterceptors(InterceptorRegistry registry) {
configurer.setTaskExecutor(executor); registry.addInterceptor(userTypeInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(
"/blog/**",
"/editor/**"
);
}
/**
* 页面映射
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/blog/").setViewName("/blog/index.html");
registry.addViewController("/editor/").setViewName("/editor/index.html");
}
/***
* 静态文件处理
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/editor/**")
.addResourceLocations("classpath:/static/editor/")
.setCacheControl(CacheControl.maxAge(48, TimeUnit.HOURS).cachePublic());
registry.addResourceHandler("/blog/**")
.addResourceLocations("classpath:/static/blog/")
.setCacheControl(CacheControl.maxAge(48, TimeUnit.HOURS).cachePublic());
} }
} }

View File

@ -1,3 +1,7 @@
#server:
# servlet:
# context-path: /dev
spring: spring:
datasource: datasource:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
@ -74,9 +78,6 @@ project:
os-type: blossom os-type: blossom
blos: blos:
# 请以 /pic 结尾, 如果你在 nginx 中配置有代理, 注意别忘了添加你的代理路径 # 请以 /pic 结尾, 如果你在 nginx 中配置有代理, 注意别忘了添加你的代理路径
domain: "http://localhost:9999/pic/" domain: "http://localhost:9999/dev/pic/"
# 请以 / 开头, / 结尾, 简短的路径在文章中有更好的显示效果, 过长一定程度会使文章内容混乱 # 请以 / 开头, / 结尾, 简短的路径在文章中有更好的显示效果, 过长一定程度会使文章内容混乱
default-path: "/home/bl/img/" default-path: "/home/bl/img/"
server:
servlet:
context-path: /dev

View File

@ -50,6 +50,7 @@ project:
white-list: # 白名单 white-list: # 白名单
- /sentinel/** - /sentinel/**
- /blog/** - /blog/**
- /editor/**
- /assets/** - /assets/**
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ Cache/Redis ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ Cache/Redis ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
cache: # 缓存 cache: # 缓存

View File

@ -0,0 +1,83 @@
server:
port: 9997
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/blossom-all?useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true&allowMultiQueries=true&useSSL=false&&serverTimezone=GMT%2B8
username: root
password: jasmine888
hikari:
max-lifetime: 120000
logging:
level:
com.blossom: info
com.blossom.expand.tracker: info
com.blossom.backend.base.auth: info
org.springframework.boot.web.embedded.tomcat.TomcatWebServer: warn
com.baomidou.dynamic.datasource.DynamicRoutingDataSource: warn
com.zaxxer.hikari.HikariDataSource: warn
org.apache.coyote.http11.Http11NioProtocol: warn
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ project ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
project:
base:
version: @project.version@
ex:
# 异常打印格式 all/project
stack-trace: project
# format/line
format: format
# 动态日志级别配置
log:
duration: 600000 # 动态日志级别 6 分钟后失效
restore-duration: 30000 # 30 秒判断一次是否失效
db:
slow-interval: 200 # 慢SQL
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ Auth ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
auth: # 授权
enabled: true # 开启授权
# 请求日志: com.blossom.backend.base.auth.enums.LogTypeEnum
log-type: simple
# 授权 token 的方式, 注意, 如果使用 redis, 需要引入 redis 包, 否则会提示错误
# com.blossom.backend.base.auth.enums.AuthTypeEnum
type: caffeine
default-password: 123456 # 默认密码
password-encoder: bcrypt # 加密方式
clients: # 客户端
- client-id: blossom
grant-type: 'password'
duration: 21600 # 客户端授权时间 6 小时
multi-place-login: true # 客户端是否允许多地登录
white-list: # 白名单
- /sentinel/**
- /blog/**
- /editor/**
- /assets/**
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ Cache/Redis ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
cache: # 缓存
names-config: # 缓存键超时时间配置
- name: test_cache1
seconds: 10
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ Tracker ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 一个简易的链路追踪功能, 会在日志中输出 traceId
tracker:
collector:
enabled: false # 关闭收集器
max-cache: 5000
rate: 100
repository:
type: disk1 # 关闭存储
disk:
use-pid: true
log-dir: /logs/tracker
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ IAAS ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
iaas:
os-type: blossom
blos:
# 请以 /pic 结尾, 如果你在 nginx 中配置有代理, 注意别忘了添加你的代理路径
# 注意:在下方示例中, /bl 即为 nginx 反向代理路径, 如果你的访问路径中不包含反向代理或路径不同, 请酌情删除或修改
domain: "https://www.wangyunf.com/blall/pic/"
# 请以 / 开头, / 结尾, 简短的路径在文章中有更好的显示效果, 过长一定程度会使文章内容混乱
default-path: "/home/blall/img/"

View File

@ -0,0 +1,7 @@
博客静态文件放置位置,若在 blossom-web 项目中打包,需使用:
```bash
npm run build:spring
```
会自动将打包产物打包至该位置。

View File

@ -0,0 +1 @@
客户端静态文件放置位置。

View File

@ -28,17 +28,6 @@ public class WebMvcConfig implements WebMvcConfigurer {
*/ */
@Override @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/editor/**")
.addResourceLocations("classpath:/static/editor/")
.setCacheControl(CacheControl.maxAge(48, TimeUnit.HOURS).cachePublic());
/*
博客因无法获取域名, 暂不支持集成
registry.addResourceHandler("/blog/**")
.addResourceLocations("classpath:/static/blog/")
.setCacheControl(CacheControl.maxAge(4, TimeUnit.MINUTES).cachePublic());
*/
registry.addResourceHandler("/favicon.ico") registry.addResourceHandler("/favicon.ico")
.addResourceLocations("classpath:/static/favicon.png") .addResourceLocations("classpath:/static/favicon.png")
.setCacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS).cachePublic()); .setCacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS).cachePublic());

View File

@ -389,7 +389,7 @@
<br /> <br />
<span style="font-size: 13px">加载中...</span> <span style="font-size: 13px">加载中...</span>
</div> </div>
<div class="html-loading-version">Blossom | v1.9.0</div> <div class="html-loading-version">Blossom | v1.10.0</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,9 +2,9 @@
interface ImportMetaEnv { interface ImportMetaEnv {
/** /**
* 使 * URL
*/ */
readonly RENDERER_VITE_MODE: string readonly VITE_BLOSSOM_BASE_URI: string
} }
interface ImportMeta { interface ImportMeta {

View File

@ -160,9 +160,10 @@ $width-item: 210px;
.subject-icon, .subject-icon,
.menu-icon-img { .menu-icon-img {
@include box(55px, 55px); @include box(50px, 50px);
@include absolute('', 2px, 8px, ''); @include absolute('', 5px, 8px, '');
@include themeFilter(drop-shadow(0 0 3px rgb(62, 62, 62)), drop-shadow(0 0 3px #000000)); @include themeFilter(drop-shadow(0 0 3px rgb(62, 62, 62)), drop-shadow(0 0 3px #000000));
border-radius: 4px;
} }
.infos { .infos {

View File

@ -21,7 +21,8 @@
</bl-row> </bl-row>
</div> </div>
<div class="note-container"> <div class="note-container">
<section :class="['note', note.top == 1 ? 'note-top' : '']" v-for="note in notes" :key="note.id"> <div v-if="isEmpty(notes)" class="placeholder">无便签</div>
<section v-else v-for="note in notes" :key="note.id" :class="['note', note.top == 1 ? 'note-top' : '']">
<div class="cd" @click="saveToStorage(note.content)" @click.right="copyContent(note.content)"> <div class="cd" @click="saveToStorage(note.content)" @click.right="copyContent(note.content)">
<img src="@renderer/assets/imgs/note/cd.png" /> <img src="@renderer/assets/imgs/note/cd.png" />
</div> </div>
@ -53,11 +54,12 @@
import { nextTick, onMounted, ref } from 'vue' import { nextTick, onMounted, ref } from 'vue'
import { CopyDocument } from '@element-plus/icons-vue' import { CopyDocument } from '@element-plus/icons-vue'
import { noteAllApi, noteUpdApi, noteDelApi, noteTopApi } from '@renderer/api/note' import { noteAllApi, noteUpdApi, noteDelApi, noteTopApi } from '@renderer/api/note'
import NoteEditor from '@renderer/views/note/NoteEditor.vue'
import { TempTextareaKey } from '@renderer/views/article/scripts/article' import { TempTextareaKey } from '@renderer/views/article/scripts/article'
import { Local } from '@renderer/assets/utils/storage' import { Local } from '@renderer/assets/utils/storage'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { writeText } from '@renderer/assets/utils/electron' import { writeText } from '@renderer/assets/utils/electron'
import { isEmpty } from 'lodash'
import NoteEditor from '@renderer/views/note/NoteEditor.vue'
onMounted(() => { onMounted(() => {
getNoteList() getNoteList()
@ -140,6 +142,11 @@ const notes = ref<any>([])
overflow: auto; overflow: auto;
overflow-y: overlay; overflow-y: overlay;
.placeholder {
@include font(14px, 300);
color: var(--bl-text-color-light);
}
.note { .note {
position: relative; position: relative;
@include box(250px, 354px); @include box(250px, 354px);

View File

@ -18,7 +18,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { ref } from 'vue'
const isLoading = ref(false) const isLoading = ref(false)
const delDone = ref({ done: false, msg: '' }) const delDone = ref({ done: false, msg: '' })

View File

@ -1 +0,0 @@
VITE_BLOSSOM_BASE_URI=/

View File

@ -1 +0,0 @@
VITE_BLOSSOM_BASE_URI=/dev

View File

@ -1 +0,0 @@
/// <reference types="vite/client" />

1
blossom-web/env/.env.spring vendored Normal file
View File

@ -0,0 +1 @@
VITE_BL_API_BASE_URI=../

20
blossom-web/env/env.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
/**
* API URL
*/
readonly VITE_BL_API_BASE_URI: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}

32
blossom-web/global.d.ts vendored Normal file
View File

@ -0,0 +1,32 @@
export {}
declare global {
interface Window {
blconfig: {
SYS: {
NAME: string
LOGO: string
GONG_WANG_AN_BEI: string
ICP_BEI_AN_HAO: string
EMAIL: string
}
THEME: {
LOGO_STYLE: {
'border-radius': string
}
SUBJECT_TITLE: string
}
DOMAIN: {
PRD: string
USER_ID: string
}
LINKS: [
{
NAME: string
URL: string
LOGO: string
}
]
}
}
}

View File

@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="referrer" content="no-referrer" /> <meta name="referrer" content="no-referrer" />
<title>Blossom</title> <title>Blossom</title>
<script src="/config.js"></script>
<style> <style>
* { * {
font-family: 'ubuntu mono', 'Consolas', 'Menlo', 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; font-family: 'ubuntu mono', 'Consolas', 'Menlo', 'PingFang SC', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;

View File

@ -4,7 +4,9 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dev:spring": "vite --mode spring",
"build": "run-p type-check build-only", "build": "run-p type-check build-only",
"build:spring": "vite build --mode spring",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --noEmit --skipLibCheck" "type-check": "vue-tsc --noEmit --skipLibCheck"

View File

@ -1,73 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.blossom</groupId>
<artifactId>blossom-web</artifactId>
<version>1.10.0-SNAPSHOT</version>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub MetaCode Apache Maven Packages</name>
<url>https://maven.pkg.github.com/metacode-project/metacode-maven-packages</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<!-- NB! Set <version> to the latest released version of frontend-maven-plugin, like in README.md -->
<version>1.14.0</version>
<configuration>
<installDirectory>target</installDirectory>
<nodeVersion>v16.13.1</nodeVersion>
<yarnVersion>v1.22.19</yarnVersion>
</configuration>
<executions>
<execution>
<id>install node and yarn</id>
<goals>
<goal>install-node-and-yarn</goal>
</goals>
</execution>
<execution>
<id>yarn config set registry</id>
<goals>
<goal>yarn</goal>
</goals>
</execution>
<execution>
<id>yarn install</id>
<goals>
<goal>yarn</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>yarn build</id>
<goals>
<goal>yarn</goal>
</goals>
<configuration>
<arguments>build</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>dist</directory>
<targetPath>META-INF/resources/webjars/blossom-web</targetPath>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,51 @@
window.blconfig = {
/**
* 基础配置
*/
SYS: {
// 修改该值可以改变网页左上角名称, 你可以改为你的名称
NAME: 'Blossom',
// 公网安备号
GONG_WANG_AN_BEI: '',
// ICP 备案号
ICP_BEI_AN_HAO: '',
// 邮箱
EMAIL: ''
},
/**
* 博客样式当前可设置样式如下
* 1. 左上角 LOGO 样式
* 2. 专题文件夹的特殊样式显示
*/
THEME: {
LOGO_STYLE: {
// 左上角 LOGO 的圆角设置
'border-radius': '50%'
},
// 是否以特殊样式显示专题文件夹
SUBJECT_TITLE: true
},
/**
* 服务器的地址
*/
DOMAIN: {
// 将该值填写为你的后台访问地址, 与 blossom 客户端登录页面填写的地址相同
PRD: 'http://localhost:9999',
// 将该值填写你开放为博客的用户ID
USER_ID: 1
},
/**
* 可以填写你自己的网站该信息会展示在右上角的更多按钮中以及首页的所有文章
* NAME: 网站名称
* URL: 网站地址
* LOGO: 网站LOGO地址
*/
LINKS: [
// 下方是一个示例
// {
// NAME: 'Blossom 双链笔记软件',
// URL: 'https://www.wangyunf.com/blossom-doc/index.html',
// LOGO: 'https://www.wangyunf.com/bl/pic/home/bl/img/U1/head/blossom_logo.png'
// }
]
}

View File

@ -6,16 +6,18 @@ import { Local } from '@/assets/utils/storage'
import { isNotNull } from '@/assets/utils/obj' import { isNotNull } from '@/assets/utils/obj'
import pinia from '@/stores/store-config' import pinia from '@/stores/store-config'
import { storeKey as authKey, useUserStore } from '@/stores/user' import { storeKey as authKey, useUserStore } from '@/stores/user'
import SYSTEM from '@/assets/constants/blossom' import { getApiBaseUrl, getUserId } from '@/scripts/env'
const userStore = useUserStore(pinia) const userStore = useUserStore(pinia)
const userId = getUserId()
const baseUrl = getApiBaseUrl()
export class Request { export class Request {
/** axios 实例 */ /** axios 实例 */
instance: AxiosInstance instance: AxiosInstance
/** 基础配置url和超时时间 */ /** 基础配置url和超时时间 */
baseConfig: AxiosRequestConfig = { baseConfig: AxiosRequestConfig = {
baseURL: SYSTEM.DOMAIN.PRD, baseURL: baseUrl,
timeout: 60000 timeout: 60000
} }
/** /**
@ -34,11 +36,10 @@ export class Request {
if (isNotNull(tokenCache) && isNotNull(tokenCache.token)) { if (isNotNull(tokenCache) && isNotNull(tokenCache.token)) {
token = tokenCache.token token = tokenCache.token
} }
console.log(config.url, 'token: ' + tokenCache.token)
config.headers = { config.headers = {
...config.headers, ...config.headers,
...{ ...{
'Blossom-User-Id': SYSTEM.DOMAIN.USER_ID, 'Blossom-User-Id': userId,
Authorization: 'Bearer ' + token Authorization: 'Bearer ' + token
} }
} }

View File

@ -1,57 +1,5 @@
const blossom = { const blossom = {
/** SYS: { VERSION: 'v1.10.0' }
*
*/
SYS: {
// 修改该值可以改变网页左上角名称, 你可以改为你的名称
NAME: 'Blossom',
// 博客左上角 LOGO 文件名称, 文件需要放在 src/assets/imgs/logo/ 路径下
LOGO: 'blossom_logo.png',
// 版本
VERSION: 'v1.9.0',
// 公网安备号
GONG_WANG_AN_BEI: 'X公网安备 XXXXXXXXXX号',
// ICP 备案号
ICP_BEI_AN_HAO: '京ICP备123123123号',
// 邮箱
EMAIL: ''
},
/**
*
* 1. LOGO
* 2.
*/
THEME: {
LOGO_STYLE: {
// 左上角 LOGO 的圆角设置
'border-radius': '50%'
},
// 是否以特殊样式显示专题文件夹
SUBJECT_TITLE: true
},
/**
*
*/
DOMAIN: {
// 将该值填写为你的后台访问地址, 与 blossom 客户端登录页面填写的地址相同
PRD: import.meta.env.VITE_BLOSSOM_BASE_URI,
// 将该值填写你开放为博客的用户ID
USER_ID: 1
},
/**
*
* NAME: 网站名称
* URL: 网站地址
* LOGO: 网站LOGO, src/assets/imgs/linklogo/
*/
LINKS: [
// 下方是一个示例
// {
// NAME: '我的个人主页',
// URL: 'https://www.wangyunf.com',
// LOGO: 'luban.png'
// }
] as any[]
} }
export default blossom export default blossom

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,34 +1,25 @@
<template> <template>
<div class="notfound-root"> <div class="notfound-root">
<div class="notfount"> <div class="notfount">404</div>
404 <div class="desc">您可能想前往....</div>
</div>
<div class="desc">
未找到该页面, 您可能想前往
</div>
<div class="router"> <div class="router">
<div class="item" v-for="link in SYSTEM.LINKS" @click="toView(link.URL)"> <div class="item" @click="toRoute('/home')"><span class="iconbl bl-a-home1-line"></span>回到首页</div>
<img :src="getImg(link.LOGO)" style="width: 25px;">{{ link.NAME }} <div class="item" v-for="link in getLinks()" @click="toView(link.URL)"><img :src="link.LOGO" style="width: 25px" />{{ link.NAME }}</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import SYSTEM from '@/assets/constants/blossom'
import { toView } from '@/assets/utils/util' import { toView } from '@/assets/utils/util'
import { toRoute } from '@/router'
const getImg = (img: string) => { import { getLinks } from '@/scripts/env'
return new URL(`../assets/imgs/linklogo/${img}`, import.meta.url).href
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.notfound-root { .notfound-root {
@include box(100%, 100%); @include box(100%, 100%);
@include flex(column, center, center); @include flex(column, center, center);
background-image: linear-gradient(to bottom right, #3e464e, #0c0c11); background-image: linear-gradient(to right bottom, rgb(62, 70, 78), rgb(33, 33, 33));
.notfount { .notfount {
@include font(260px, 700); @include font(260px, 700);
@ -37,11 +28,13 @@ const getImg = (img: string) => {
.desc { .desc {
@include font(30px, 300); @include font(30px, 300);
color: rgb(88, 88, 88); color: #9b9b9b;
padding-bottom: 10px;
} }
.router { .router {
color: rgb(88, 88, 88); font-weight: 300;
color: #9b9b9b;
img { img {
margin-right: 5px; margin-right: 5px;
@ -50,7 +43,18 @@ const getImg = (img: string) => {
.item { .item {
@include flex(row, flex-start, center); @include flex(row, flex-start, center);
margin-top: 10px; margin-top: 10px;
height: 35px;
cursor: pointer; cursor: pointer;
img {
margin: 5px 10px 5px 0;
}
.iconbl {
font-size: 30px;
padding-right: 5px;
color: #9b9b9b;
}
} }
} }
} }

View File

@ -0,0 +1,90 @@
import { isNull } from '@/assets/utils/obj'
const springBaseUrl = import.meta.env.VITE_BL_API_BASE_URI
const print = () => {
console.log('Blossom-web ===> 环境:', import.meta.env.MODE)
console.log('Blossom-web ===> window.blconfig.DOMAIN:', window.blconfig.DOMAIN.PRD)
console.log('Blossom-web ===> SpringBaseUrl:', springBaseUrl)
if (import.meta.env.DEV) {
console.log(window.blconfig)
}
}
print()
/**
* Spring
* @returns
*/
export const isSpring = () => {
return import.meta.env.MODE === 'spring'
}
export const getApiBaseUrl = () => {
if (isSpring()) {
return '../'
}
return window.blconfig.DOMAIN.PRD
}
export const getUserId = () => {
if (isNull(window.blconfig.DOMAIN.USER_ID)) {
return 1
}
return window.blconfig.DOMAIN.USER_ID
}
//#region ------------------------------------------- 基础信息 -------------------------------------------
export const getSysName = () => {
if (isNull(window.blconfig.SYS.NAME)) {
return 'Blossom'
}
return window.blconfig.SYS.NAME
}
export const getEmail = () => {
if (isNull(window.blconfig.SYS.EMAIL)) {
return ''
}
return window.blconfig.SYS.EMAIL
}
export const getGwab = () => {
if (isNull(window.blconfig.SYS.GONG_WANG_AN_BEI)) {
return ''
}
return window.blconfig.SYS.GONG_WANG_AN_BEI
}
export const getIpc = () => {
if (isNull(window.blconfig.SYS.ICP_BEI_AN_HAO)) {
return ''
}
return window.blconfig.SYS.ICP_BEI_AN_HAO
}
//#endregion
export const getThemeLogoStyle = () => {
if (isNull(window.blconfig.THEME.LOGO_STYLE)) {
return {
'border-radius': '50%'
}
}
return window.blconfig.THEME.LOGO_STYLE
}
export const getThemeSubjecTitle = () => {
if (isNull(window.blconfig.THEME.SUBJECT_TITLE)) {
return true
}
return window.blconfig.THEME.SUBJECT_TITLE
}
export const getLinks = () => {
if (isNull(window.blconfig.LINKS)) {
return []
}
return window.blconfig.LINKS
}

View File

@ -12,18 +12,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, onMounted, computed } from 'vue' import { ref, watch, computed } from 'vue'
import router from '@/router' import router from '@/router'
import { checkToken } from '@/scripts/auth'
import IndexHeader from './index/IndexHeader.vue' import IndexHeader from './index/IndexHeader.vue'
import type { RouteRecordName } from 'vue-router' import type { RouteRecordName } from 'vue-router'
const includeRouter = ref<any>(['Home']) const includeRouter = ref<any>(['Home'])
onMounted(() => {
// checkToken()
})
const curRoute = ref<RouteRecordName>('Home') const curRoute = ref<RouteRecordName>('Home')
const isHome = computed(() => { const isHome = computed(() => {
@ -42,8 +37,6 @@ watch(
}, },
{ immediate: true } { immediate: true }
) )
onMounted(() => {})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.index-root { .index-root {

View File

@ -27,7 +27,7 @@
import { computed } from 'vue' import { computed } from 'vue'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { isNotBlank } from '@/assets/utils/obj' import { isNotBlank } from '@/assets/utils/obj'
import SYSTEM from '@/assets/constants/blossom' import { getThemeSubjecTitle } from '@/scripts/env'
//#region ----------------------------------------< >-------------------------------------- //#region ----------------------------------------< >--------------------------------------
@ -60,7 +60,7 @@ const tags = computed(() => {
}) })
const titleClass = computed(() => { const titleClass = computed(() => {
if (!SYSTEM.THEME.SUBJECT_TITLE) { if (!getThemeSubjecTitle()) {
return 'doc-title' return 'doc-title'
} }

View File

@ -1,8 +1,5 @@
<template> <template>
<div class="blossom-home-root"> <div class="blossom-home-root">
<!-- <div class="home-header">
<IndexHeader></IndexHeader>
</div> -->
<div class="home-main"> <div class="home-main">
<div class="home-main-userinfo"> <div class="home-main-userinfo">
<UserInfo></UserInfo> <UserInfo></UserInfo>
@ -18,16 +15,16 @@
</div> </div>
<div class="home-footer"> <div class="home-footer">
<div class="about-us"> <div class="about-us">
<span>{{ blossom.SYS.VERSION + (isNotBlank(blossom.SYS.EMAIL) ? ' | 邮箱:' + blossom.SYS.EMAIL : '') }}</span> <span>{{ blossom.SYS.VERSION + (isNotBlank(getEmail()) ? ' | 邮箱:' + getEmail() : '') }}</span>
</div> </div>
<div class="icp"> <div class="icp">
<div style="cursor: pointer" @click="openNew('http://www.beian.gov.cn/portal/index.do')"> <div style="cursor: pointer" @click="openNew('http://www.beian.gov.cn/portal/index.do')">
<!-- <img style="height: 14px; width: 14px" src="@/assets/imgs/common/gong_wang_an_bei_img.png" /> --> <!-- <img style="height: 14px; width: 14px" src="@/assets/imgs/common/gong_wang_an_bei_img.png" /> -->
{{ blossom.SYS.GONG_WANG_AN_BEI }} {{ getGwab() }}
</div> </div>
<div v-if="isNotBlank(blossom.SYS.GONG_WANG_AN_BEI)">|</div> <div v-if="isNotBlank(getGwab())">|</div>
<div style="cursor: pointer" @click="openNew('https://beian.miit.gov.cn/')"> <div style="cursor: pointer" @click="openNew('https://beian.miit.gov.cn/')">
{{ blossom.SYS.ICP_BEI_AN_HAO }} {{ getIpc() }}
</div> </div>
</div> </div>
</div> </div>
@ -36,12 +33,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import IndexHeader from './IndexHeader.vue'
import UserInfo from './HomeUserInfo.vue' import UserInfo from './HomeUserInfo.vue'
import ChartLineWords from './ChartLineWords.vue' import ChartLineWords from './ChartLineWords.vue'
import HomeSubject from './HomeSubject.vue' import HomeSubject from './HomeSubject.vue'
import blossom from '@/assets/constants/blossom' import blossom from '@/assets/constants/blossom'
import { isNotBlank } from '@/assets/utils/obj' import { isNotBlank } from '@/assets/utils/obj'
import { getGwab, getIpc, getEmail } from '@/scripts/env'
const ChartLineWordsRef = ref() const ChartLineWordsRef = ref()
onMounted(() => { onMounted(() => {
@ -57,8 +54,6 @@ const openNew = (url: string) => {
.blossom-home-root { .blossom-home-root {
@include box(100%, 100%); @include box(100%, 100%);
@include flex(column, center, center); @include flex(column, center, center);
// box-shadow: 0 0 10px #24272c;
// background-image: linear-gradient(to bottom right, #3e464e, #212121);
position: relative; position: relative;
overflow: hidden; overflow: hidden;

View File

@ -15,7 +15,7 @@
<div class="userinfo-content-btns"> <div class="userinfo-content-btns">
<ul> <ul>
<li @click="toRoute('/articles')">所有文章 <span class="iconbl bl-sendmail-line"></span></li> <li @click="toRoute('/articles')">所有文章 <span class="iconbl bl-sendmail-line"></span></li>
<li v-for="link in SYSTEM.LINKS" @click="toView(link.URL)"> <li v-for="link in getLinks()" @click="toView(link.URL)">
{{ link.NAME }} {{ link.NAME }}
</li> </li>
</ul> </ul>
@ -33,9 +33,10 @@
import { toRoute } from '@/router' import { toRoute } from '@/router'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { userinfoApi } from '@/api/blossom' import { userinfoApi } from '@/api/blossom'
import ChartHeatmap from './ChartHeatmap.vue'
import SYSTEM from '@/assets/constants/blossom'
import { toView } from '@/assets/utils/util' import { toView } from '@/assets/utils/util'
import SYSTEM from '@/assets/constants/blossom'
import { getLinks } from '@/scripts/env'
import ChartHeatmap from './ChartHeatmap.vue'
const userinfo = ref({ const userinfo = ref({
avatar: '', avatar: '',
@ -74,7 +75,7 @@ onMounted(() => {
padding-left: 20px; padding-left: 20px;
.name { .name {
@include font(38px, 200); @include font(38px, 300);
height: 65px; height: 65px;
color: #ffffff; color: #ffffff;
letter-spacing: 5px; letter-spacing: 5px;
@ -82,7 +83,7 @@ onMounted(() => {
} }
.desc { .desc {
@include font(13px, 500); @include font(13px, 300);
height: 20px; height: 20px;
color: #7a7a7a; color: #7a7a7a;
letter-spacing: 1px; letter-spacing: 1px;
@ -102,7 +103,7 @@ onMounted(() => {
ol, ol,
ul { ul {
@include font(15px, 500); @include font(15px, 300);
color: #7a7a7a; color: #7a7a7a;
text-align: left; text-align: left;

View File

@ -2,9 +2,9 @@
<div :class="['blossom-header-root', props.bg ? 'blossom-header-bg' : '']"> <div :class="['blossom-header-root', props.bg ? 'blossom-header-bg' : '']">
<bl-row class="head-row" width="auto" height="100%"> <bl-row class="head-row" width="auto" height="100%">
<div class="blossom-logo" @click="toLogin"> <div class="blossom-logo" @click="toLogin">
<img :src="logo" :style="SYSTEM.THEME.LOGO_STYLE" /> <img src="/blog-logo.png" :style="getThemeLogoStyle()" />
</div> </div>
<div class="project-name" @click="toRoute('/home')">{{ SYSTEM.SYS.NAME }}</div> <div class="project-name" @click="toRoute('/home')">{{ getSysName() }}</div>
</bl-row> </bl-row>
<bl-row class="head-row" width="auto" height="100%"> <bl-row class="head-row" width="auto" height="100%">
@ -17,11 +17,11 @@
:offset="-5" :offset="-5"
transition="el-zoom-in-top"> transition="el-zoom-in-top">
<template #reference> <template #reference>
<div v-show="SYSTEM.LINKS != undefined && SYSTEM.LINKS.length > 0" class="popper-target">更多</div> <div v-show="getLinks().length > 0" class="popper-target">更多</div>
</template> </template>
<div class="popper-content"> <div class="popper-content">
<div class="item" v-for="link in SYSTEM.LINKS" @click="toView(link.URL)"> <div class="item" v-for="link in getLinks()" @click="toView(link.URL)">
<img :src="getImg(link.LOGO)" style="width: 25px" />{{ link.NAME }} <img :src="link.LOGO" style="width: 25px" />{{ link.NAME }}
</div> </div>
</div> </div>
</el-popover> </el-popover>
@ -59,7 +59,7 @@ import { toRoute } from '@/router'
import { toView } from '@/assets/utils/util' import { toView } from '@/assets/utils/util'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { logout } from '@/scripts/auth' import { logout } from '@/scripts/auth'
import SYSTEM from '@/assets/constants/blossom' import { getLinks, getSysName, getThemeLogoStyle } from '@/scripts/env'
const userStore = useUserStore() const userStore = useUserStore()
@ -70,12 +70,6 @@ const props = defineProps({
} }
}) })
const logo = new URL(`../../assets/imgs/logo/${SYSTEM.SYS.LOGO}`, import.meta.url).href
const getImg = (img: string) => {
return new URL(`../../assets/imgs/linklogo/${img}`, import.meta.url).href
}
let recount: NodeJS.Timeout | undefined let recount: NodeJS.Timeout | undefined
const tryLoginCount = ref(0) const tryLoginCount = ref(0)

View File

@ -2,9 +2,9 @@
<div class="login-root"> <div class="login-root">
<div class="login-form"> <div class="login-form">
<bl-col just="center" class="logo" height="auto"> <bl-col just="center" class="logo" height="auto">
<img :src="logo" :style="SYSTEM.THEME.LOGO_STYLE" /> <img src="/blog-logo.png" :style="getThemeLogoStyle()" />
<br /> <br />
<bl-row class="title" just="center">{{ SYSTEM.SYS.NAME }}</bl-row> <bl-row class="title" just="center">{{ getSysName() }}</bl-row>
</bl-col> </bl-col>
<el-input class="login-input" size="default" v-model="formLogin.username"> <el-input class="login-input" size="default" v-model="formLogin.username">
<template #prepend>用户名</template> <template #prepend>用户名</template>
@ -24,9 +24,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { toRoute } from '@/router' import { toRoute } from '@/router'
import { login } from '@/scripts/auth' import { login } from '@/scripts/auth'
import SYSTEM from '@/assets/constants/blossom' import { getSysName, getThemeLogoStyle } from '@/scripts/env'
const logo = new URL(`../../assets/imgs/logo/${SYSTEM.SYS.LOGO}`, import.meta.url).href
const formLogin = ref({ username: '', password: '' }) const formLogin = ref({ username: '', password: '' })

View File

@ -1,11 +1,12 @@
{ {
"extends": "@vue/tsconfig/tsconfig.web.json", "extends": "@vue/tsconfig/tsconfig.web.json",
"include": [ "include": [
"env.d.ts", "env/env.d.ts",
"src/**/*", "src/**/*",
"src/**/*.vue", "src/**/*.vue",
"src/**/*.d.ts", "src/**/*.d.ts",
"declaration.d.ts" "declaration.d.ts",
"global.d.ts"
], ],
"compilerOptions": { "compilerOptions": {
"baseUrl": "./", "baseUrl": "./",

View File

@ -1,7 +1,7 @@
import { fileURLToPath, URL } from 'node:url' import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx' import vueJsx from '@vitejs/plugin-vue-jsx'
@ -10,20 +10,23 @@ import ElementPlus from 'unplugin-element-plus/vite'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { resolve } from 'path'
const npm_lifecycle_event = process.env.npm_lifecycle_event
const isSpring = () => {
return npm_lifecycle_event && npm_lifecycle_event === 'build:spring'
}
console.log('当前运行脚本: ', npm_lifecycle_event)
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: './', base: './',
envDir: resolve('env'),
server: { server: {
host: "0.0.0.0", host: '0.0.0.0',
// 开发环境代理
proxy: {
'/dev': {
target:'http://127.0.0.1:9999/',
},
},
port: 5174, port: 5174,
//vue3 vite配置热更新不用手动刷新
hmr: true hmr: true
}, },
plugins: [ plugins: [
@ -31,19 +34,19 @@ export default defineConfig({
vueJsx(), vueJsx(),
visualizer({ visualizer({
emitFile: false, emitFile: false,
filename: "stats.html", //分析图生成的文件名 filename: 'stats.html', // 分析图生成的文件名
open: true //如果存在本地服务端口,将在打包后自动展示 open: true //如果存在本地服务端口,将在打包后自动展示
}), }),
// ElementPlus 按需引入的插件 // ElementPlus 按需引入的插件
AutoImport({ AutoImport({
resolvers: [ElementPlusResolver()], resolvers: [ElementPlusResolver()]
}), }),
Components({ Components({
resolvers: [ElementPlusResolver()], resolvers: [ElementPlusResolver()]
}), }),
ElementPlus({ ElementPlus({
// options // options
}), })
], ],
resolve: { resolve: {
alias: { alias: {
@ -53,22 +56,24 @@ export default defineConfig({
css: { css: {
preprocessorOptions: { preprocessorOptions: {
scss: { scss: {
/**使 /**
* 使
* '@import "@/assets/scss/globalVariable1.scss"; * '@import "@/assets/scss/globalVariable1.scss";
* @import"@/assets/scss/globalVariable2.scss";' * @import"@/assets/scss/globalVariable2.scss";'
**/ */
additionalData: '@import "@/assets/styles/config.scss";', additionalData: '@import "@/assets/styles/config.scss";'
} }
} }
}, },
build: { build: {
outDir: isSpring() ? '../blossom-backend/backend/src/main/resources/static/blog' : 'dist',
// 警告大小, 单位kb // 警告大小, 单位kb
// chunkSizeWarningLimit: 1000, // chunkSizeWarningLimit: 1000,
rollupOptions: { rollupOptions: {
output: { output: {
manualChunks(id) { manualChunks(id) {
if (id.includes('node_modules')) { if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString(); return id.toString().split('node_modules/')[1].split('/')[0].toString()
} }
} }
} }