mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-03-07 04:20:47 +08:00
Compare commits
15 Commits
codex/fix-
...
codex/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac81bccd20 | ||
|
|
351447e3d1 | ||
|
|
453d8fa68b | ||
|
|
2c5b38ee9e | ||
|
|
b5fd5a3edc | ||
|
|
ee717aced2 | ||
|
|
9a9152593e | ||
|
|
856d3dd513 | ||
|
|
0e42a3335a | ||
|
|
d96aae59d2 | ||
|
|
122722d0e9 | ||
|
|
1e503e26f2 | ||
|
|
e979350d40 | ||
|
|
906998a07f | ||
|
|
56aed4603e |
@@ -1,9 +1,9 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<BaseImage alt="OpenIsle" src="https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png" width="200">
|
<img alt="OpenIsle" src="https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/image.png" width="200">
|
||||||
<br>
|
<br>
|
||||||
高效的开源社区前后端平台
|
高效的开源社区前后端平台
|
||||||
<br><br><br>
|
<br><br><br>
|
||||||
<BaseImage alt="Image" src="https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/22752cfac5a04a9c90c41995b9f55fed.png" width="1200">
|
<img alt="Image" src="https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/22752cfac5a04a9c90c41995b9f55fed.png" width="1200">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 💡 简介
|
## 💡 简介
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class Draft {
|
|||||||
|
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
@Column(columnDefinition = "TEXT")
|
@Column(columnDefinition = "LONGTEXT")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class Post {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
@Column(nullable = false, columnDefinition = "TEXT")
|
@Column(nullable = false, columnDefinition = "LONGTEXT")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
@CreationTimestamp
|
@CreationTimestamp
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ body {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
white-space: break-spaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-content-text pre .line-numbers {
|
.info-content-text pre .line-numbers {
|
||||||
@@ -188,7 +189,6 @@ body {
|
|||||||
font-family: 'Maple Mono', monospace;
|
font-family: 'Maple Mono', monospace;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
white-space: break-spaces;
|
|
||||||
background-color: var(--code-highlight-background-color);
|
background-color: var(--code-highlight-background-color);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div v-if="show" class="cropper-modal">
|
<div v-if="show" class="cropper-modal">
|
||||||
<div class="cropper-body">
|
<div class="cropper-body">
|
||||||
<div class="cropper-wrapper">
|
<div class="cropper-wrapper">
|
||||||
<BaseImage ref="image" :src="src" alt="to crop" />
|
<img ref="image" :src="src" alt="to crop" />
|
||||||
</div>
|
</div>
|
||||||
<div class="cropper-actions">
|
<div class="cropper-actions">
|
||||||
<button class="cropper-btn" @click="$emit('close')">取消</button>
|
<button class="cropper-btn" @click="$emit('close')">取消</button>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<DropdownMenu v-if="isLogin" ref="userMenu" :items="headerMenuItems">
|
<DropdownMenu v-if="isLogin" ref="userMenu" :items="headerMenuItems">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<BaseImage class="avatar-img" :src="avatar" alt="avatar" />
|
<img class="avatar-img" :src="avatar" alt="avatar" />
|
||||||
<i class="fas fa-caret-down dropdown-icon"></i>
|
<i class="fas fa-caret-down dropdown-icon"></i>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
1299
frontend_nuxt/package-lock.json
generated
1299
frontend_nuxt/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@
|
|||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"ldrs": "^1.0.0",
|
"ldrs": "^1.0.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
|
"mermaid": "^10.9.4",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"nuxt": "latest",
|
"nuxt": "latest",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ const md = new MarkdownIt({
|
|||||||
linkify: true,
|
linkify: true,
|
||||||
breaks: true,
|
breaks: true,
|
||||||
highlight: (str, lang) => {
|
highlight: (str, lang) => {
|
||||||
|
if (lang === 'mermaid') {
|
||||||
|
return `<pre class="mermaid">${str}</pre>`
|
||||||
|
}
|
||||||
let code = ''
|
let code = ''
|
||||||
if (lang && hljs.getLanguage(lang)) {
|
if (lang && hljs.getLanguage(lang)) {
|
||||||
code = hljs.highlight(str, { language: lang, ignoreIllegals: true }).value
|
code = hljs.highlight(str, { language: lang, ignoreIllegals: true }).value
|
||||||
@@ -182,7 +185,7 @@ const SANITIZE_CFG = {
|
|||||||
allowedClasses: {
|
allowedClasses: {
|
||||||
a: ['mention-link'],
|
a: ['mention-link'],
|
||||||
img: ['emoji'],
|
img: ['emoji'],
|
||||||
pre: ['code-block'],
|
pre: ['code-block', 'mermaid'],
|
||||||
div: ['line-numbers', 'line-number'],
|
div: ['line-numbers', 'line-number'],
|
||||||
code: ['hljs', /^language-/],
|
code: ['hljs', /^language-/],
|
||||||
button: ['copy-code-btn'],
|
button: ['copy-code-btn'],
|
||||||
@@ -208,8 +211,15 @@ const SANITIZE_CFG = {
|
|||||||
/** @section 渲染 & 事件 */
|
/** @section 渲染 & 事件 */
|
||||||
export function renderMarkdown(text) {
|
export function renderMarkdown(text) {
|
||||||
const raw = md.render(text || '')
|
const raw = md.render(text || '')
|
||||||
// ⭐ 核心:对最终 HTML 进行一次净化
|
const html = sanitizeHtml(raw, SANITIZE_CFG)
|
||||||
return sanitizeHtml(raw, SANITIZE_CFG)
|
if (typeof window !== 'undefined') {
|
||||||
|
setTimeout(async () => {
|
||||||
|
const mermaid = await import('mermaid')
|
||||||
|
mermaid.default.initialize({ startOnLoad: false })
|
||||||
|
mermaid.default.run({ nodes: document.querySelectorAll('.mermaid') })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleMarkdownClick(e) {
|
export function handleMarkdownClick(e) {
|
||||||
|
|||||||
@@ -4,6 +4,17 @@ export default class TimeManager {
|
|||||||
if (Number.isNaN(date.getTime())) return ''
|
if (Number.isNaN(date.getTime())) return ''
|
||||||
|
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
|
const diffMs = now.getTime() - date.getTime()
|
||||||
|
|
||||||
|
if (diffMs >= 0 && diffMs < 60 * 1000) {
|
||||||
|
return '刚刚'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffMs >= 0 && diffMs < 60 * 60 * 1000) {
|
||||||
|
const mins = Math.floor(diffMs / 60_000)
|
||||||
|
return `${mins || 1}分钟前`
|
||||||
|
}
|
||||||
|
|
||||||
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||||
const startOfDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
const startOfDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
||||||
const diffDays = Math.floor((startOfToday - startOfDate) / 86400000)
|
const diffDays = Math.floor((startOfToday - startOfDate) / 86400000)
|
||||||
|
|||||||
Reference in New Issue
Block a user