mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-17 12:30:59 +08:00
Compare commits
24 Commits
feature/ho
...
codex/remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6497cb92af | ||
|
|
37bef0b2d7 | ||
|
|
3519a41a2e | ||
|
|
ab04a8b6b1 | ||
|
|
ea079e8b8a | ||
|
|
519656359f | ||
|
|
dc64785279 | ||
|
|
9421d004d4 | ||
|
|
90bd41e740 | ||
|
|
7d5c864f64 | ||
|
|
3f35add587 | ||
|
|
1e284e15df | ||
|
|
9d76926b8a | ||
|
|
d2ce203236 | ||
|
|
b2228296af | ||
|
|
7020ae19d0 | ||
|
|
227fb6f6cc | ||
|
|
0e46a67ea6 | ||
|
|
b20b705e46 | ||
|
|
4b3ffbab99 | ||
|
|
74039c89f9 | ||
|
|
10dca73d2f | ||
|
|
e37ed1b70b | ||
|
|
8500a7a914 |
@@ -1,4 +1,4 @@
|
|||||||
package com.openisle.schdule;
|
package com.openisle.scheduler;
|
||||||
|
|
||||||
import com.openisle.config.CachingConfig;
|
import com.openisle.config.CachingConfig;
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
@@ -4,7 +4,9 @@ NUXT_PUBLIC_WEBSOCKET_URL=https://127.0.0.1:8082
|
|||||||
NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000
|
NUXT_PUBLIC_WEBSITE_BASE_URL=http://localhost:3000
|
||||||
|
|
||||||
NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com
|
NUXT_PUBLIC_GOOGLE_CLIENT_ID=777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com
|
||||||
NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ
|
# NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liVkO1NPAX5JyWxJ
|
||||||
|
; 本地
|
||||||
|
NUXT_PUBLIC_GITHUB_CLIENT_ID=Ov23liOlrZnPKRF7s7NN
|
||||||
NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779
|
NUXT_PUBLIC_DISCORD_CLIENT_ID=1394985417044000779
|
||||||
NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ
|
NUXT_PUBLIC_TWITTER_CLIENT_ID=ZTRTU05KSk9KTTJrTTdrVC1tc1E6MTpjaQ
|
||||||
NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135
|
NUXT_PUBLIC_TELEGRAM_BOT_ID=8450237135
|
||||||
|
|||||||
@@ -6,8 +6,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="comment-bottom-container">
|
<div class="comment-bottom-container">
|
||||||
<div class="comment-submit" :class="{ disabled: isDisabled }" @click="submit">
|
<div class="comment-submit" :class="{ disabled: isDisabled }" @click="submit">
|
||||||
<template v-if="!loading"> 发布评论 </template>
|
<template v-if="!loading">
|
||||||
<template v-else> <loading-four /> 发布中... </template>
|
发布评论
|
||||||
|
<span class="shortcut-icon" v-if="!isMobile">
|
||||||
|
{{ isMac ? '⌘' : 'Ctrl' }} ⏎
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<loading-four /> 发布中...
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,6 +31,7 @@ import {
|
|||||||
} from '~/utils/vditor'
|
} from '~/utils/vditor'
|
||||||
import '~/assets/global.css'
|
import '~/assets/global.css'
|
||||||
import LoginOverlay from '~/components/LoginOverlay.vue'
|
import LoginOverlay from '~/components/LoginOverlay.vue'
|
||||||
|
import { useIsMobile } from '~/utils/screen'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CommentEditor',
|
name: 'CommentEditor',
|
||||||
@@ -52,12 +60,22 @@ export default {
|
|||||||
},
|
},
|
||||||
components: { LoginOverlay },
|
components: { LoginOverlay },
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
const isMobile = useIsMobile()
|
||||||
const vditorInstance = ref(null)
|
const vditorInstance = ref(null)
|
||||||
const text = ref('')
|
const text = ref('')
|
||||||
const editorId = ref(props.editorId)
|
const editorId = ref(props.editorId)
|
||||||
if (!editorId.value) {
|
if (!editorId.value) {
|
||||||
editorId.value = 'editor-' + useId()
|
editorId.value = 'editor-' + useId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMac = ref(false)
|
||||||
|
|
||||||
|
if (navigator.userAgentData) {
|
||||||
|
isMac.value = navigator.userAgentData.platform === 'macOS'
|
||||||
|
} else {
|
||||||
|
isMac.value = /Mac|iPhone|iPad|iPod/.test(navigator.userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
const getEditorTheme = getEditorThemeUtil
|
const getEditorTheme = getEditorThemeUtil
|
||||||
const getPreviewTheme = getPreviewThemeUtil
|
const getPreviewTheme = getPreviewThemeUtil
|
||||||
const applyTheme = () => {
|
const applyTheme = () => {
|
||||||
@@ -96,7 +114,27 @@ export default {
|
|||||||
applyTheme()
|
applyTheme()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// applyTheme()
|
// 不是手机的情况下不添加快捷键
|
||||||
|
if(!isMobile.value){
|
||||||
|
// 添加快捷键监听 (Ctrl+Enter 或 Cmd+Enter)
|
||||||
|
const handleKeydown = (e) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||||
|
e.preventDefault()
|
||||||
|
submit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = document.getElementById(editorId.value)
|
||||||
|
if (el) {
|
||||||
|
el.addEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (el) {
|
||||||
|
el.removeEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -134,7 +172,7 @@ export default {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return { submit, isDisabled, editorId }
|
return { submit, isDisabled, editorId, isMac, isMobile}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -174,10 +212,16 @@ export default {
|
|||||||
.comment-submit:hover {
|
.comment-submit:hover {
|
||||||
background-color: var(--primary-color-hover);
|
background-color: var(--primary-color-hover);
|
||||||
}
|
}
|
||||||
|
/** 评论按钮快捷键样式 */
|
||||||
@media (max-width: 768px) {
|
.shortcut-icon {
|
||||||
.comment-editor-container {
|
padding: 2px 6px;
|
||||||
margin-bottom: 10px;
|
border-radius: 6px;
|
||||||
}
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.comment-submit.disabled .shortcut-icon {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ const gotoTag = (t) => {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
transition: background-color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:hover {
|
.menu-item:hover {
|
||||||
@@ -408,6 +409,7 @@ const gotoTag = (t) => {
|
|||||||
gap: 5px;
|
gap: 5px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: background-color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-item:hover {
|
.section-item:hover {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="message-bottom-container">
|
<div class="message-bottom-container">
|
||||||
<div class="message-submit" :class="{ disabled: isDisabled }" @click="submit">
|
<div class="message-submit" :class="{ disabled: isDisabled }" @click="submit">
|
||||||
<template v-if="!loading"> 发送 </template>
|
<template v-if="!loading">
|
||||||
|
发送
|
||||||
|
<span class="shortcut-icon" v-if="!isMobile"> {{ isMac ? '⌘' : 'Ctrl' }} ⏎ </span>
|
||||||
|
</template>
|
||||||
<template v-else> <loading-four /> 发送中... </template>
|
<template v-else> <loading-four /> 发送中... </template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -21,6 +24,8 @@ import {
|
|||||||
getEditorTheme as getEditorThemeUtil,
|
getEditorTheme as getEditorThemeUtil,
|
||||||
getPreviewTheme as getPreviewThemeUtil,
|
getPreviewTheme as getPreviewThemeUtil,
|
||||||
} from '~/utils/vditor'
|
} from '~/utils/vditor'
|
||||||
|
import { useIsMobile } from '~/utils/screen'
|
||||||
|
import { isMac } from '~/utils/device'
|
||||||
import '~/assets/global.css'
|
import '~/assets/global.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -44,6 +49,7 @@ export default {
|
|||||||
const vditorInstance = ref(null)
|
const vditorInstance = ref(null)
|
||||||
const text = ref('')
|
const text = ref('')
|
||||||
const editorId = ref(props.editorId)
|
const editorId = ref(props.editorId)
|
||||||
|
const isMobile = useIsMobile()
|
||||||
if (!editorId.value) {
|
if (!editorId.value) {
|
||||||
editorId.value = 'editor-' + useId()
|
editorId.value = 'editor-' + useId()
|
||||||
}
|
}
|
||||||
@@ -84,6 +90,28 @@ export default {
|
|||||||
applyTheme()
|
applyTheme()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 不是手机的情况下不添加快捷键
|
||||||
|
if (!isMobile.value) {
|
||||||
|
// 添加快捷键监听 (Ctrl+Enter 或 Cmd+Enter)
|
||||||
|
const handleKeydown = (e) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||||
|
e.preventDefault()
|
||||||
|
submit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = document.getElementById(editorId.value)
|
||||||
|
if (el) {
|
||||||
|
el.addEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (el) {
|
||||||
|
el.removeEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -121,7 +149,7 @@ export default {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return { submit, isDisabled, editorId }
|
return { submit, isDisabled, editorId, isMac, isMobile }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -168,4 +196,17 @@ export default {
|
|||||||
.message-submit:not(.disabled):hover {
|
.message-submit:not(.disabled):hover {
|
||||||
background-color: var(--primary-color-hover);
|
background-color: var(--primary-color-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 评论按钮快捷键样式 */
|
||||||
|
.shortcut-icon {
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.comment-submit.disabled .shortcut-icon {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
90
frontend_nuxt/config/uploadConfig.js
Normal file
90
frontend_nuxt/config/uploadConfig.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* 文件上传配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const UPLOAD_CONFIG = {
|
||||||
|
// 视频文件配置
|
||||||
|
VIDEO: {
|
||||||
|
// 文件大小限制 (字节)
|
||||||
|
MAX_SIZE: 20 * 1024 * 1024,
|
||||||
|
|
||||||
|
// 支持的输入格式
|
||||||
|
SUPPORTED_FORMATS: ['mp4', 'webm', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'm4v', 'ogv'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图片文件配置
|
||||||
|
IMAGE: {
|
||||||
|
MAX_SIZE: 5 * 1024 * 1024, // 5MB
|
||||||
|
TARGET_SIZE: 5 * 1024 * 1024, // 5MB
|
||||||
|
SUPPORTED_FORMATS: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// 音频文件配置
|
||||||
|
AUDIO: {
|
||||||
|
MAX_SIZE: 5 * 1024 * 1024, // 5MB
|
||||||
|
TARGET_SIZE: 5 * 1024 * 1024, // 5MB
|
||||||
|
SUPPORTED_FORMATS: ['mp3', 'wav', 'ogg', 'aac', 'm4a'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// 通用文件配置
|
||||||
|
GENERAL: {
|
||||||
|
MAX_SIZE: 100 * 1024 * 1024, // 100MB
|
||||||
|
CHUNK_SIZE: 5 * 1024 * 1024, // 5MB 分片大小
|
||||||
|
},
|
||||||
|
|
||||||
|
// 用户体验配置
|
||||||
|
UI: {
|
||||||
|
SUCCESS_DURATION: 2000,
|
||||||
|
ERROR_DURATION: 3000,
|
||||||
|
WARNING_DURATION: 3000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件类型配置
|
||||||
|
*/
|
||||||
|
export function getFileTypeConfig(filename) {
|
||||||
|
const ext = filename.split('.').pop().toLowerCase()
|
||||||
|
|
||||||
|
if (UPLOAD_CONFIG.VIDEO.SUPPORTED_FORMATS.includes(ext)) {
|
||||||
|
return { type: 'video', config: UPLOAD_CONFIG.VIDEO }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UPLOAD_CONFIG.IMAGE.SUPPORTED_FORMATS.includes(ext)) {
|
||||||
|
return { type: 'image', config: UPLOAD_CONFIG.IMAGE }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UPLOAD_CONFIG.AUDIO.SUPPORTED_FORMATS.includes(ext)) {
|
||||||
|
return { type: 'audio', config: UPLOAD_CONFIG.AUDIO }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { type: 'general', config: UPLOAD_CONFIG.GENERAL }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文件大小
|
||||||
|
*/
|
||||||
|
export function formatFileSize(bytes) {
|
||||||
|
if (bytes === 0) return '0 B'
|
||||||
|
const k = 1024
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算压缩节省的费用 (示例函数)
|
||||||
|
*/
|
||||||
|
export function calculateSavings(originalSize, compressedSize, costPerMB = 0.01) {
|
||||||
|
const originalMB = originalSize / (1024 * 1024)
|
||||||
|
const compressedMB = compressedSize / (1024 * 1024)
|
||||||
|
const savedMB = originalMB - compressedMB
|
||||||
|
const savedCost = savedMB * costPerMB
|
||||||
|
|
||||||
|
return {
|
||||||
|
savedMB: savedMB.toFixed(2),
|
||||||
|
savedCost: savedCost.toFixed(4),
|
||||||
|
originalCost: (originalMB * costPerMB).toFixed(4),
|
||||||
|
compressedCost: (compressedMB * costPerMB).toFixed(4),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { defineNuxtConfig } from 'nuxt/config'
|
|||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
devServer: {
|
devServer: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 3000
|
port: 3000,
|
||||||
},
|
},
|
||||||
ssr: true,
|
ssr: true,
|
||||||
modules: ['@nuxt/image'],
|
modules: ['@nuxt/image'],
|
||||||
@@ -97,26 +97,7 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
vite: {
|
vite: {
|
||||||
build: {
|
optimizeDeps: {},
|
||||||
// increase warning limit and split large libraries into separate chunks
|
build: {},
|
||||||
// chunkSizeWarningLimit: 1024,
|
|
||||||
// rollupOptions: {
|
|
||||||
// output: {
|
|
||||||
// manualChunks(id) {
|
|
||||||
// if (id.includes('node_modules')) {
|
|
||||||
// if (id.includes('vditor')) {
|
|
||||||
// return 'vditor'
|
|
||||||
// }
|
|
||||||
// if (id.includes('echarts')) {
|
|
||||||
// return 'echarts'
|
|
||||||
// }
|
|
||||||
// if (id.includes('highlight.js')) {
|
|
||||||
// return 'highlight'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
2425
frontend_nuxt/package-lock.json
generated
2425
frontend_nuxt/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@
|
|||||||
"ldrs": "^1.0.0",
|
"ldrs": "^1.0.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"mermaid": "^10.9.4",
|
"mermaid": "^10.9.4",
|
||||||
|
"nanoid": "^5.1.5",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"nuxt": "latest",
|
"nuxt": "latest",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
|
|||||||
@@ -424,7 +424,8 @@ const sanitizeDescription = (text) => stripMarkdown(text)
|
|||||||
|
|
||||||
.topic-container {
|
.topic-container {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: calc(var(--header-height) + 1px);
|
top: var(--header-height);
|
||||||
|
padding-top: 10px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background-color: var(--background-color-blur);
|
background-color: var(--background-color-blur);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -486,6 +487,7 @@ const sanitizeDescription = (text) => stripMarkdown(text)
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 1px solid var(--normal-border-color);
|
border-bottom: 1px solid var(--normal-border-color);
|
||||||
|
transition: background-color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-item:hover {
|
.article-item:hover {
|
||||||
|
|||||||
28
frontend_nuxt/utils/device.js
Normal file
28
frontend_nuxt/utils/device.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
export const isClient = typeof window !== 'undefined' && typeof document !== 'undefined'
|
||||||
|
|
||||||
|
export const isMac = getIsMac()
|
||||||
|
|
||||||
|
function getIsMac() {
|
||||||
|
if (!isClient) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 优先使用现代浏览器的 navigator.userAgentData API
|
||||||
|
if (navigator.userAgentData && navigator.userAgentData.platform) {
|
||||||
|
return navigator.userAgentData.platform === 'macOS'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 降级到传统的 User-Agent 检测
|
||||||
|
if (navigator.userAgent) {
|
||||||
|
return /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认返回false
|
||||||
|
return false
|
||||||
|
} catch (error) {
|
||||||
|
// 异常处理,记录错误并返回默认值
|
||||||
|
console.warn('检测Mac设备时发生错误:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { getToken, authState } from './auth'
|
|||||||
import { searchUsers, fetchFollowings, fetchAdmins } from './user'
|
import { searchUsers, fetchFollowings, fetchAdmins } from './user'
|
||||||
import { tiebaEmoji } from './tiebaEmoji'
|
import { tiebaEmoji } from './tiebaEmoji'
|
||||||
import vditorPostCitation from './vditorPostCitation.js'
|
import vditorPostCitation from './vditorPostCitation.js'
|
||||||
|
import { checkFileSize, formatFileSize } from './videoCompressor.js'
|
||||||
|
|
||||||
export function getEditorTheme() {
|
export function getEditorTheme() {
|
||||||
return document.documentElement.dataset.theme === 'dark' ? 'dark' : 'classic'
|
return document.documentElement.dataset.theme === 'dark' ? 'dark' : 'classic'
|
||||||
@@ -91,7 +92,26 @@ export function createVditor(editorId, options = {}) {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
handler: async (files) => {
|
handler: async (files) => {
|
||||||
const file = files[0]
|
const file = files[0]
|
||||||
vditor.tip('图片上传中', 0)
|
const ext = file.name.split('.').pop().toLowerCase()
|
||||||
|
const videoExts = ['mp4', 'webm', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'm4v', 'ogv']
|
||||||
|
|
||||||
|
// 检查文件大小
|
||||||
|
const sizeCheck = checkFileSize(file)
|
||||||
|
if (!sizeCheck.isValid) {
|
||||||
|
console.log(
|
||||||
|
'文件大小不能超过',
|
||||||
|
formatFileSize(sizeCheck.maxSize),
|
||||||
|
',当前文件',
|
||||||
|
formatFileSize(sizeCheck.actualSize),
|
||||||
|
)
|
||||||
|
vditor.tip(
|
||||||
|
`文件大小不能超过 ${formatFileSize(sizeCheck.maxSize)},当前文件 ${formatFileSize(sizeCheck.actualSize)}`,
|
||||||
|
3000,
|
||||||
|
)
|
||||||
|
return '文件过大'
|
||||||
|
}
|
||||||
|
|
||||||
|
vditor.tip('文件上传中', 0)
|
||||||
vditor.disabled()
|
vditor.disabled()
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${API_BASE_URL}/api/upload/presign?filename=${encodeURIComponent(file.name)}`,
|
`${API_BASE_URL}/api/upload/presign?filename=${encodeURIComponent(file.name)}`,
|
||||||
@@ -110,7 +130,6 @@ export function createVditor(editorId, options = {}) {
|
|||||||
return '上传失败'
|
return '上传失败'
|
||||||
}
|
}
|
||||||
|
|
||||||
const ext = file.name.split('.').pop().toLowerCase()
|
|
||||||
const imageExts = [
|
const imageExts = [
|
||||||
'apng',
|
'apng',
|
||||||
'bmp',
|
'bmp',
|
||||||
@@ -132,6 +151,8 @@ export function createVditor(editorId, options = {}) {
|
|||||||
md = ``
|
md = ``
|
||||||
} else if (audioExts.includes(ext)) {
|
} else if (audioExts.includes(ext)) {
|
||||||
md = `<audio controls="controls" src="${info.fileUrl}"></audio>`
|
md = `<audio controls="controls" src="${info.fileUrl}"></audio>`
|
||||||
|
} else if (videoExts.includes(ext)) {
|
||||||
|
md = `<video width="600" controls>\n <source src="${info.fileUrl}" type="video/${ext}">\n 你的浏览器不支持 video 标签。\n</video>`
|
||||||
} else {
|
} else {
|
||||||
md = `[${file.name}](${info.fileUrl})`
|
md = `[${file.name}](${info.fileUrl})`
|
||||||
}
|
}
|
||||||
|
|||||||
30
frontend_nuxt/utils/videoCompressor.js
Normal file
30
frontend_nuxt/utils/videoCompressor.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 视频上传工具
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { UPLOAD_CONFIG } from '../config/uploadConfig.js'
|
||||||
|
|
||||||
|
// 导出配置供外部使用
|
||||||
|
export const VIDEO_CONFIG = UPLOAD_CONFIG.VIDEO
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件大小是否超出限制
|
||||||
|
*/
|
||||||
|
export function checkFileSize(file) {
|
||||||
|
return {
|
||||||
|
isValid: file.size <= VIDEO_CONFIG.MAX_SIZE,
|
||||||
|
actualSize: file.size,
|
||||||
|
maxSize: VIDEO_CONFIG.MAX_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文件大小显示
|
||||||
|
*/
|
||||||
|
export function formatFileSize(bytes) {
|
||||||
|
if (bytes === 0) return '0 B'
|
||||||
|
const k = 1024
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user