Merge pull request #26 from zsjy/dev

1.2.8
This commit is contained in:
mjking 2024-08-06 18:05:50 +08:00 committed by GitHub
commit 61e3eea1cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 234 additions and 85 deletions

View File

@ -10,10 +10,10 @@
本仓库为 `Halo 2.x` 主题仓库。
## [主题预览](https://www.sw0.top/?preview-theme=theme-dream2-plus)
## [主题预览](https://www.hcjike.com/?preview-theme=theme-dream2-plus)
- [主题文档](https://www.sw0.top/docs/halo-theme-dream2.0)
- [侧边栏组件](https://www.sw0.top/docs/halo-theme-dream2.0/theme/sidebar-assembly)
- [主题文档](https://www.hcjike.com/docs/halo-theme-dream2.0)
- [侧边栏组件](https://www.hcjike.com/docs/halo-theme-dream2.0/theme/sidebar-assembly)
## 开发中功能
- 开发中功能已发布为[预发行版](https://github.com/zsjy/halo-theme-dream2.0-plus/releases),开发中的功能不保证留存到正式版,包括但不限于:主题配置位置、配置方式、页面样式等等。

View File

@ -3,7 +3,7 @@
"version": "1.2.1",
"description": "梦之城,童话梦境,动漫类型博客主题。",
"main": "index.js",
"author": "智识家园",
"author": "宏尘极客",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"release": "eslint src/**/*.js && gulp release",

View File

@ -1101,7 +1101,7 @@ spec:
label: 关闭
- $formkit: group
name: search
label: 搜索
label: 搜索页面
help: 使用新版搜索需要 halo >= 2.17.0 & 搜索插件 >= 1.5.0。
value:
search_enable: false

View File

@ -5227,6 +5227,7 @@ button.swiper-pagination-bullet {
/* 搜索界面 */
.search {
margin-top: 1.4rem !important;
.search-form-inner {
width: 100%;
@ -5288,7 +5289,11 @@ button.swiper-pagination-bullet {
}
}
b {
.description {
display: inline-block;
}
b, mark {
color: var(--light-z);
background: var(--theme);
}

0
src/js/search.js Normal file
View File

View File

@ -109,7 +109,8 @@ const Utils = {
timeout = 10000,
returnRaw = false,
contentType,
resultType = 'json'
resultType = 'json',
noErrorTip = false,
}) {
return new Promise((resolve, reject) => {
method = method.toUpperCase()
@ -142,7 +143,9 @@ const Utils = {
? err.responseJSON.title
: '请求失败'
: '请求失败'
Qmsg.error(errMsg)
if(!noErrorTip) {
Qmsg.error(errMsg)
}
reject(errMsg)
},
})

File diff suppressed because one or more lines are too long

0
templates/assets/js/search.min.js vendored Normal file
View File

View File

@ -1 +1 @@
(()=>{const a={isMobile:()=>!!(navigator.userAgent.match(/Android/i)||navigator.userAgent.match(/webOS/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/iPod/i)||navigator.userAgent.match(/BlackBerry/i)||navigator.userAgent.match(/Windows Phone/i)),cachedScript:(e,t)=>$.ajax(jQuery.extend({url:e,type:"get",dataType:"script",cache:!0,success:t},$.isPlainObject(e)&&e)),formatDate(e,t="yyyy-MM-dd"){e=new Date(e),/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(e.getFullYear()+"").substr(4-RegExp.$1.length)));var a,r,n={"M+":e.getMonth()+1,"d+":e.getDate(),"h+":e.getHours(),"m+":e.getMinutes(),"s+":e.getSeconds()};for(a in n)new RegExp(`(${a})`).test(t)&&(r=n[a]+"",t=t.replace(RegExp.$1,1===RegExp.$1.length?r:r.padStart(2,"0")));return t},getUrlParams(){var e=location.search,e=(e="string"!=typeof e?e.toString():e).replace(/^[^\?]*\?/i,"").split(/&/),a={};return e.length<1||Array.isArray(e)&&e.forEach(function(e){if(!e)return!1;var t,e=e.split(/=/);2<=e.length&&(t=e.splice(0,1),a[t]=e.join("="))}),a},randomColor(e){var t=["#F8D800","#0396FF","#EA5455","#7367F0","#32CCBC","#F6416C","#28C76F","#9F44D3","#F55555","#736EFE","#E96D71","#DE4313","#D939CD","#4C83FF","#F072B6","#C346C2","#5961F9","#FD6585","#465EFB","#FFC600","#FA742B","#5151E5","#BB4E75","#FF52E5","#49C628","#00EAFF","#F067B4","#F067B4","#ff9a9e","#00f2fe","#4facfe","#f093fb","#6fa3ef","#bc99c4","#46c47c","#f9bb3c","#e8583d","#f68e5f"];return t[e%t.length]},request:({url:e="",method:r="GET",data:n,headers:o={},timeout:s=1e4,returnRaw:i=!1,contentType:l,resultType:c="json"})=>new Promise((t,a)=>{r=r.toUpperCase(),$.ajax({url:e,type:r,headers:{"API-Authorization":DreamConfig.access_key||"dream",...o},async:!0,dataType:c,contentType:l,timeout:s,data:n,success(e){i?t(e):200===e.status?t(e.data||""):a(e)},error(e){e=e&&e.responseJSON?e.responseJSON.title:"请求失败";Qmsg.error(e),a(e)}})}),initLikeButton(e,t){t=encrypt("agree-"+t);let a=localStorage.getItem(t);a=a?JSON.parse(decrypt(a)):[],$(e).each(function(){var e=$(this),t=e.attr("data-id");a.includes(t)&&e.removeClass("like")})},initLikeEvent(e,t,o){let s=encrypt("agree-"+t);$("body").on("click",e,function(e){e.stopPropagation();let r=$(this),n=r.attr("data-id");a.request({url:"/apis/api.halo.run/v1alpha1/trackers/upvote",method:"POST",contentType:"application/json;charset=UTF-8",returnRaw:!0,resultType:"text",data:JSON.stringify({group:"moments"===t?"moment.halo.run":"content.halo.run",plural:t,name:n})}).then(e=>{var t=(t=localStorage.getItem(s))?JSON.parse(decrypt(t)):[],a=+(r.attr("data-likes")||0)+1,t=(t.push(n),r.removeClass("like"),encrypt(JSON.stringify(t)));localStorage.setItem(s,t),o(r).html(a),Qmsg.success("点赞成功")})})},sleep:(t=250)=>new Promise(e=>setTimeout(e,t)),foldBlock(e){var t,a=e.height();e.is(".fold")?e.removeClass("fold").addClass("unfold"):(t=document.documentElement.scrollTop||document.body.scrollTop||window.pageYOffset,e.addClass("fold").removeClass("unfold"),$("body,html").scrollTop(t-a+e.height()))},removeClassByPrefix(e,t){var a=e.className.split(" ").filter(function(e){return 0!==e.lastIndexOf(t,0)});e.className=a.join(" ").trim()},animateScroll(e,t,a,r){let n,o=e.getBoundingClientRect(),s=window.scrollY,i=s+o.top-a,l=(i-s)/t,c=s>i?-1:1;n=window.requestAnimationFrame(function e(){(s+=l)*c<i*c?(window.scrollTo(0,s),n=window.requestAnimationFrame(e)):(window.scrollTo(0,i),window.cancelAnimationFrame(n),r&&r())})}};window.Utils=a})();
(()=>{const a={isMobile:()=>!!(navigator.userAgent.match(/Android/i)||navigator.userAgent.match(/webOS/i)||navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPad/i)||navigator.userAgent.match(/iPod/i)||navigator.userAgent.match(/BlackBerry/i)||navigator.userAgent.match(/Windows Phone/i)),cachedScript:(e,t)=>$.ajax(jQuery.extend({url:e,type:"get",dataType:"script",cache:!0,success:t},$.isPlainObject(e)&&e)),formatDate(e,t="yyyy-MM-dd"){e=new Date(e),/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(e.getFullYear()+"").substr(4-RegExp.$1.length)));var a,r,n={"M+":e.getMonth()+1,"d+":e.getDate(),"h+":e.getHours(),"m+":e.getMinutes(),"s+":e.getSeconds()};for(a in n)new RegExp(`(${a})`).test(t)&&(r=n[a]+"",t=t.replace(RegExp.$1,1===RegExp.$1.length?r:r.padStart(2,"0")));return t},getUrlParams(){var e=location.search,e=(e="string"!=typeof e?e.toString():e).replace(/^[^\?]*\?/i,"").split(/&/),a={};return e.length<1||Array.isArray(e)&&e.forEach(function(e){if(!e)return!1;var t,e=e.split(/=/);2<=e.length&&(t=e.splice(0,1),a[t]=e.join("="))}),a},randomColor(e){var t=["#F8D800","#0396FF","#EA5455","#7367F0","#32CCBC","#F6416C","#28C76F","#9F44D3","#F55555","#736EFE","#E96D71","#DE4313","#D939CD","#4C83FF","#F072B6","#C346C2","#5961F9","#FD6585","#465EFB","#FFC600","#FA742B","#5151E5","#BB4E75","#FF52E5","#49C628","#00EAFF","#F067B4","#F067B4","#ff9a9e","#00f2fe","#4facfe","#f093fb","#6fa3ef","#bc99c4","#46c47c","#f9bb3c","#e8583d","#f68e5f"];return t[e%t.length]},request:({url:e="",method:r="GET",data:n,headers:o={},timeout:s=1e4,returnRaw:i=!1,contentType:l,resultType:c="json",noErrorTip:d=!1})=>new Promise((t,a)=>{r=r.toUpperCase(),$.ajax({url:e,type:r,headers:{"API-Authorization":DreamConfig.access_key||"dream",...o},async:!0,dataType:c,contentType:l,timeout:s,data:n,success(e){i?t(e):200===e.status?t(e.data||""):a(e)},error(e){e=e&&e.responseJSON?e.responseJSON.title:"请求失败";d||Qmsg.error(e),a(e)}})}),initLikeButton(e,t){t=encrypt("agree-"+t);let a=localStorage.getItem(t);a=a?JSON.parse(decrypt(a)):[],$(e).each(function(){var e=$(this),t=e.attr("data-id");a.includes(t)&&e.removeClass("like")})},initLikeEvent(e,t,o){let s=encrypt("agree-"+t);$("body").on("click",e,function(e){e.stopPropagation();let r=$(this),n=r.attr("data-id");a.request({url:"/apis/api.halo.run/v1alpha1/trackers/upvote",method:"POST",contentType:"application/json;charset=UTF-8",returnRaw:!0,resultType:"text",data:JSON.stringify({group:"moments"===t?"moment.halo.run":"content.halo.run",plural:t,name:n})}).then(e=>{var t=(t=localStorage.getItem(s))?JSON.parse(decrypt(t)):[],a=+(r.attr("data-likes")||0)+1,t=(t.push(n),r.removeClass("like"),encrypt(JSON.stringify(t)));localStorage.setItem(s,t),o(r).html(a),Qmsg.success("点赞成功")})})},sleep:(t=250)=>new Promise(e=>setTimeout(e,t)),foldBlock(e){var t,a=e.height();e.is(".fold")?e.removeClass("fold").addClass("unfold"):(t=document.documentElement.scrollTop||document.body.scrollTop||window.pageYOffset,e.addClass("fold").removeClass("unfold"),$("body,html").scrollTop(t-a+e.height()))},removeClassByPrefix(e,t){var a=e.className.split(" ").filter(function(e){return 0!==e.lastIndexOf(t,0)});e.className=a.join(" ").trim()},animateScroll(e,t,a,r){let n,o=e.getBoundingClientRect(),s=window.scrollY,i=s+o.top-a,l=(i-s)/t,c=s>i?-1:1;n=window.requestAnimationFrame(function e(){(s+=l)*c<i*c?(window.scrollTo(0,s),n=window.requestAnimationFrame(e)):(window.scrollTo(0,i),window.cancelAnimationFrame(n),r&&r())})}};window.Utils=a})();

76
templates/moment.html Normal file
View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<th:block
th:insert="~{common/layout :: layout (title = ${title} + ' - ' + ${site.title}, canonical = @{/moments}, content = ~{::content}, isPost = true)}"
th:with="isJournals = true, enableShare = ${theme.config.page_config.enable_journals_share}, baseEnableComment = ${theme.config.page_config.enable_journals_comment}"
xmlns:th="https://www.thymeleaf.org">
<th:block th:fragment="content">
<div class="card card-content journal">
<p class="journal-date">
<i class="ri-send-plane-line"></i>
<em th:text="${#dates.format(moment.spec.releaseTime,'yyyy年MM月dd日 HH:mm:ss')}"></em>
</p>
<div class="journal-content fold">
<div class="main-content not-toc" data-target="Moment" th:data-id="${moment.metadata.name}">
[(${moment.spec.content.html})]
<mew-photos th:if="${!#lists.isEmpty(moment.spec.content.medium)}">
<img th:each="momentItem : ${moment.spec.content.medium}" th:if="${momentItem.type.name == 'PHOTO'}" th:src="${momentItem.url}" />
</mew-photos>
<th:block th:if="${!#lists.isEmpty(moment.spec.content.medium)}" th:each="momentItem : ${moment.spec.content.medium}">
<mew-video th:if="${momentItem.type.name == 'VIDEO'}" th:src="${momentItem.url}"></mew-video>
</th:block>
<th:block th:if="${!#lists.isEmpty(moment.spec.content.medium)}" th:each="momentItem : ${moment.spec.content.medium}">
<mew-music th:if="${momentItem.type.name == 'AUDIO'}" th:url="${momentItem.url}"
th:with="list = ${#strings.listSplit(momentItem.url,'/')}, size = ${#lists.size(list)}"
th:name="${size >= 0 ? list[size - 1] : momentItem.url}"
th:cover="${#theme.assets('/img/music.webp')}"
></mew-music>
</th:block>
</div>
</div>
<div class="journal-operation">
<span class="journal-operation-item">
<a class="like" th:data-id="${moment.metadata.name}" th:data-likes="${moment.stats.upvote}">
<i class="ri-heart-3-line"></i>
<em th:text="${(moment.stats.upvote != 0)? moment.stats.upvote : '喜欢'}"></em>
</a>
</span>
<span class="journal-operation-item" th:if="${enableComment}">
<a class="comment">
<i class="ri-message-3-line"></i>
<em th:text="${(moment.stats.totalComment != 0)? moment.stats.totalComment : '评论'}"></em>
</a>
</span>
<span class="journal-operation-item" th:if="${enableShare}">
<a class="share"><i class="ri-share-forward-line"></i><em>分享</em></a>
</span>
<div th:if="${theme.config.page_config.enable_journals_owner}" class="journal-operation-owner" th:title="${moment.owner.displayName}">
<img th:unless="${#strings.isEmpty(moment.owner.avatar)}" class="avatar" th:src="${moment.owner.avatar}"
alt="avatar">
<div th:if="${#strings.isEmpty(moment.owner.avatar)}" class="no-avatar">
<span class="avatar-info">[[${#strings.substring(moment.owner.displayName, 0, 1)}]]</span>
</div>
<span class="name">[[${moment.owner.displayName}]]</span>
</div>
</div>
<div class="journal-comment comment-wrapper-z-index" th:if="${enableComment}">
<th:block th:if="${theme.config.enhance.enable_pjax}">
<comment-widget
group="moment.halo.run"
kind="Moment"
version="v1alpha1"
th:name="${moment.metadata.name}"
with-replies="false"
></comment-widget>
</th:block>
<th:block th:unless="${theme.config.enhance.enable_pjax}">
<halo:comment
group="moment.halo.run"
kind="Moment"
th:attr="name=${moment.metadata.name}"
/>
</th:block>
</div>
</div>
</th:block>
</th:block>

View File

@ -1,76 +1,141 @@
<!DOCTYPE html>
<th:block
th:insert="~{common/layout :: layout (title = ${theme.config.page_config.search.search_title} + ' - ' + ${site.title}, canonical = @{/search}, content = ~{::content}, isPost = true)}"
th:with="isJournals = true, enableShare = false, baseEnableComment = false"
xmlns:th="https://www.thymeleaf.org">
<th:block th:fragment="content">
<div class="card card-content search">
<div class="card-tab">
<div>[[${#strings.replace(title, ' - ' + site.title, '')}]]</div>
</div>
<div class="search-box">
<script th:inline="javascript">
$(function () {
var searchForm = document.getElementById('halo-search-form')
searchForm.addEventListener('submit', function (event) {
var searchInput = document.getElementById('halo-search-form-text-input')
var query = searchInput.value.trim()
if (!query || query.startsWith('*')) {
event.preventDefault()
Qmsg.warning(!query ? '请输入内容' : '不能输入星号(*)作为内容开头')
return
}
if(DreamConfig.pjax_state) {
event.preventDefault()
searchForm.setAttribute('data-pjax', '')
}
})
document.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
if (!searchForm.contains(document.activeElement)) {
event.preventDefault(); // 阻止默认行为
searchForm.dispatchEvent(new Event('submit')); // 触发submit事件
}
}
});
})
</script>
<form id="halo-search-form" class="search-form-inner" method="get" action="/search" role="search">
<input type="hidden" name="limit" th:value="${theme.config.page_config.search.search_limit}">
<input
id="halo-search-form-text-input"
class="text-input"
type="search"
name="keyword"
th:title="${theme.config.page_config.search.search_placeholder}"
th:placeholder="${theme.config.page_config.search.search_placeholder}"
th:value="${searchResult.keyword}"
/>
<button class="search-form-btn" th:title="${theme.config.page_config.search.search_btn_title}" type="submit">
<i class="ri-search-line"></i>
</button>
</form>
</div>
</div>
<div th:if="${searchResult.hits.size() > 0}" class="widget card search" th:each="hit : ${searchResult.hits}">
<a th:href="${hit.permalink}" th:target="${theme.config.page_config.search.search_target}" >
<div class="card-content main">
<h2 class="title" th:utext="${hit.title}"></h2>
<div class="main-content not-toc">
<span th:utext="${hit.description}"></span>
</div>
<hr/>
<div class="meta">
<div></div>
<em th:text="'最后更新于 ' + ${#dates.format(hit.updateTimestamp,'yyyy年MM月dd日 HH:mm:ss')}"></em>
</div>
th:insert="~{common/layout :: layout (title = ${theme.config.page_config.search.search_title} + ' - ' + ${site.title}, canonical = @{/search}, content = ~{::content}, isPost = true)}"
th:with="isJournals = true, enableShare = false, baseEnableComment = false"
xmlns:th="https://www.thymeleaf.org">
<th:block th:fragment="content">
<div class="card card-content search">
<div class="card-tab">
<div>[[${#strings.replace(title, ' - ' + site.title, '')}]]</div>
</div>
<div class="search-box">
<script th:inline="javascript">
$(function () {
var searchForm = document.getElementById('dream-search-form')
var target = [[${theme.config.page_config.search.search_target}]]
var searchInput = document.getElementById('halo-search-form-text-input')
var searchResult = $('#dream-search-result')
var searchResultEmpty = $('#dream-search-result-empty')
searchForm.addEventListener('submit', function (event) {
event.preventDefault()
findResult(searchInput.value)
})
document.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
if (searchForm.contains(document.activeElement)) {
event.preventDefault()
findResult(searchInput.value)
}
}
})
// 监听输入事件
searchInput.addEventListener('input', function (event) {
findResult(event.target.value)
})
function removeHTMLTags(str) {
const regex = /<(?!\/?mark\b)[^>]*>/g
return str.replace(regex, '')
}
function findResult(keyword) {
if (!keyword) {
searchResult.empty()
searchResultEmpty.show()
return
}
Utils.request({
url: '/apis/api.halo.run/v1alpha1/indices/post',
contentType: 'application/json;charset=UTF-8',
returnRaw: true,
data: {
keyword,
limit: $('#halo-search-form-limit').val(),
highlightPreTag: '<mark>',
highlightPostTag: '</mark>'
},
noErrorTip: true
})
.then((_res) => {
if (_res.hits.length > 0) {
searchResultEmpty.hide()
searchResult.empty()
for (var i = 0; i < _res.hits.length; i++) {
try {
var hit = _res.hits[i]
var title = removeHTMLTags(hit.title)
var description = hit.content ? removeHTMLTags(hit.content) : ''
searchResult.append('<div class="widget card search">\n' +
'<div class="card-content main">\n' +
'<a href="' + hit.permalink + '" ' + ' target="' + target + '">\n' +
'<h2 class="title">' + title + '</h2>\n' +
'</a>\n' +
(description ?
('<div class="main-content not-toc description">\n' + description +
'\n</div>\n') : '') +
'<hr/>\n' +
'<div class="meta">\n' +
'<div></div>\n' +
'<em>' + '最后更新于 ' + Utils.formatDate(hit.updateTimestamp, 'yyyy年MM月dd日') + '</em>\n' +
'</div>\n' +
'</div>\n')
// eslint-disable-next-line no-empty
} catch (e) {
}
}
} else {
searchResultEmpty.show()
searchResult.empty()
}
})
.catch((err) => {
searchResultEmpty.show()
searchResult.empty()
})
}
function getParameterByName(name, url = window.location.href) {
name = name.replace(/[\[\]]/g, '\\$&')
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url)
if (!results) return null
if (!results[2]) return ''
return decodeURIComponent(results[2].replace(/\+/g, ' '))
}
var keyword = getParameterByName('keyword')
if (keyword) {
var event = new Event('input', {bubbles: true})
searchInput.dispatchEvent(event)
}
})
</script>
<form id="dream-search-form" class="search-form-inner" method="get" action="/search" role="search">
<input id="halo-search-form-limit" type="hidden" name="limit"
th:value="${theme.config.page_config.search.search_limit}">
<input
id="halo-search-form-text-input"
class="text-input"
type="search"
name="keyword"
th:title="${theme.config.page_config.search.search_placeholder}"
th:placeholder="${theme.config.page_config.search.search_placeholder}"
th:value="${searchResult.keyword}"
/>
<button class="search-form-btn" th:title="${theme.config.page_config.search.search_btn_title}"
type="submit">
<i class="ri-search-line"></i>
</button>
</form>
</div>
</div>
</a>
</div>
<div th:unless="${searchResult.hits.size() > 0}" class="widget card search" >
<div class="result-empty">
<div class="result-empty-tips" th:utext="${theme.config.page_config.search.search_empty_tips}"></div>
</div>
</div>
</th:block>
<div id="dream-search-result">
</div>
<div id="dream-search-result-empty" class="widget card search">
<div class="result-empty">
<div class="result-empty-tips" th:utext="${theme.config.page_config.search.search_empty_tips}"></div>
</div>
</div>
</th:block>
</th:block>

View File

@ -8,9 +8,9 @@ spec:
displayName: Dream2.0 Plus
author:
# 作者名称
name: 智识家园
name: 宏尘极客
# 作者网址
website: https://www.sw0.top
website: https://www.hcjike.com
customTemplates:
page:
- name: 安全链接页面模版
@ -30,7 +30,7 @@ spec:
settingName: theme-dream2-plus-setting
configMapName: theme-dream2-plus-configMap
# 版本号
version: 1.2.7.beta1.0
version: 1.2.8
# 最低支持的 Halo 版本
require: ">=2.15.0"
# 许可