mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-22 22:21:09 +08:00
Merge pull request #201 from nagisa77/codex/add-404-page-and-disable-editors
Add 404 and login overlays
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import Vditor from 'vditor'
|
||||
import 'vditor/dist/index.css'
|
||||
|
||||
@@ -34,13 +34,17 @@ export default {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const vditorInstance = ref(null)
|
||||
const text = ref('')
|
||||
|
||||
const isDisabled = computed(() => props.loading || !text.value.trim())
|
||||
const isDisabled = computed(() => props.loading || props.disabled || !text.value.trim())
|
||||
|
||||
const submit = () => {
|
||||
if (!vditorInstance.value || isDisabled.value) return
|
||||
@@ -79,12 +83,39 @@ export default {
|
||||
'image'
|
||||
],
|
||||
toolbarConfig: { pin: true },
|
||||
input(value) {
|
||||
text.value = value
|
||||
}
|
||||
})
|
||||
input(value) {
|
||||
text.value = value
|
||||
}
|
||||
})
|
||||
if (props.disabled || props.loading) {
|
||||
vditorInstance.value.disabled()
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.loading,
|
||||
val => {
|
||||
if (!vditorInstance.value) return
|
||||
if (val) {
|
||||
vditorInstance.value.disabled()
|
||||
} else if (!props.disabled) {
|
||||
vditorInstance.value.enable()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.disabled,
|
||||
val => {
|
||||
if (!vditorInstance.value) return
|
||||
if (val) {
|
||||
vditorInstance.value.disabled()
|
||||
} else if (!props.loading) {
|
||||
vditorInstance.value.enable()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return { submit, isDisabled }
|
||||
}
|
||||
}
|
||||
|
||||
39
open-isle-cli/src/components/LoginOverlay.vue
Normal file
39
open-isle-cli/src/components/LoginOverlay.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="login-overlay" @click="goLogin">
|
||||
<div class="login-overlay-text">
|
||||
请先登录,点击跳转到登录页面
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'LoginOverlay',
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const goLogin = () => {
|
||||
router.push('/login')
|
||||
}
|
||||
return { goLogin }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
z-index: 15;
|
||||
}
|
||||
</style>
|
||||
@@ -32,6 +32,10 @@ export default {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
@@ -48,6 +52,18 @@ export default {
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.disabled,
|
||||
val => {
|
||||
if (!vditorInstance.value) return
|
||||
if (val) {
|
||||
vditorInstance.value.disabled()
|
||||
} else if (!props.loading) {
|
||||
vditorInstance.value.enable()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
val => {
|
||||
@@ -118,6 +134,9 @@ export default {
|
||||
},
|
||||
after() {
|
||||
vditorInstance.value.setValue(props.modelValue)
|
||||
if (props.loading || props.disabled) {
|
||||
vditorInstance.value.disabled()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import SignupPageView from '../views/SignupPageView.vue'
|
||||
import NewPostPageView from '../views/NewPostPageView.vue'
|
||||
import SettingsPageView from '../views/SettingsPageView.vue'
|
||||
import ProfileView from '../views/ProfileView.vue'
|
||||
import NotFoundPageView from '../views/NotFoundPageView.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -55,6 +56,15 @@ const routes = [
|
||||
name: 'users',
|
||||
component: ProfileView
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
name: 'not-found',
|
||||
component: NotFoundPageView
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
redirect: '/404'
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<div class="new-post-form">
|
||||
<input class="post-title-input" v-model="title" placeholder="标题" />
|
||||
<div class="post-editor-container">
|
||||
<PostEditor v-model="content" :loading="isAiLoading" />
|
||||
<PostEditor v-model="content" :loading="isAiLoading" :disabled="!isLogin" />
|
||||
<LoginOverlay v-if="!isLogin" />
|
||||
</div>
|
||||
<div class="post-options">
|
||||
<div class="post-options-left">
|
||||
@@ -22,7 +23,12 @@
|
||||
<i class="fa-solid fa-floppy-disk"></i>
|
||||
存草稿
|
||||
</div>
|
||||
<div v-if="!isWaitingPosting" class="post-submit" @click="submitPost">发布</div>
|
||||
<div
|
||||
v-if="!isWaitingPosting"
|
||||
class="post-submit"
|
||||
:class="{ disabled: !isLogin }"
|
||||
@click="isLogin && submitPost"
|
||||
>发布</div>
|
||||
<div v-else class="post-submit-loading"> <i class="fa-solid fa-spinner fa-spin"></i> 发布中...</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,16 +37,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import PostEditor from '../components/PostEditor.vue'
|
||||
import CategorySelect from '../components/CategorySelect.vue'
|
||||
import TagSelect from '../components/TagSelect.vue'
|
||||
import { API_BASE_URL, toast } from '../main'
|
||||
import { getToken } from '../utils/auth'
|
||||
import { getToken, authState } from '../utils/auth'
|
||||
import LoginOverlay from '../components/LoginOverlay.vue'
|
||||
|
||||
export default {
|
||||
name: 'NewPostPageView',
|
||||
components: { PostEditor, CategorySelect, TagSelect },
|
||||
components: { PostEditor, CategorySelect, TagSelect, LoginOverlay },
|
||||
setup() {
|
||||
const title = ref('')
|
||||
const content = ref('')
|
||||
@@ -48,6 +55,7 @@ export default {
|
||||
const selectedTags = ref([])
|
||||
const isWaitingPosting = ref(false)
|
||||
const isAiLoading = ref(false)
|
||||
const isLogin = computed(() => authState.loggedIn)
|
||||
|
||||
const loadDraft = async () => {
|
||||
const token = getToken()
|
||||
@@ -237,7 +245,7 @@ export default {
|
||||
isWaitingPosting.value = false
|
||||
}
|
||||
}
|
||||
return { title, content, selectedCategory, selectedTags, submitPost, saveDraft, clearPost, isWaitingPosting, aiGenerate, isAiLoading }
|
||||
return { title, content, selectedCategory, selectedTags, submitPost, saveDraft, clearPost, isWaitingPosting, aiGenerate, isAiLoading, isLogin }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -296,6 +304,10 @@ export default {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.post-editor-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.post-submit {
|
||||
background-color: var(--primary-color);
|
||||
color: #fff;
|
||||
@@ -305,9 +317,17 @@ export default {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.post-submit.disabled {
|
||||
background-color: var(--primary-color-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.post-submit:hover {
|
||||
background-color: var(--primary-color-hover);
|
||||
}
|
||||
.post-submit.disabled:hover {
|
||||
background-color: var(--primary-color-disabled);
|
||||
}
|
||||
|
||||
.post-submit-loading {
|
||||
color: white;
|
||||
|
||||
33
open-isle-cli/src/views/NotFoundPageView.vue
Normal file
33
open-isle-cli/src/views/NotFoundPageView.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="not-found-page">
|
||||
<h1>404 - 页面不存在</h1>
|
||||
<p>你访问的页面不存在或已被删除。</p>
|
||||
<router-link to="/">返回首页</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NotFoundPageView'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.not-found-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100vh - var(--header-height));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.not-found-page h1 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.not-found-page a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -59,7 +59,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CommentEditor @submit="postComment" :loading="isWaitingPostingComment" />
|
||||
<div class="comment-editor-wrapper">
|
||||
<CommentEditor
|
||||
@submit="postComment"
|
||||
:loading="isWaitingPostingComment"
|
||||
:disabled="!loggedIn"
|
||||
/>
|
||||
<LoginOverlay v-if="!loggedIn" />
|
||||
</div>
|
||||
|
||||
<div class="comments-container">
|
||||
<BaseTimeline :items="comments">
|
||||
@@ -104,6 +111,7 @@ import ArticleTags from '../components/ArticleTags.vue'
|
||||
import ArticleCategory from '../components/ArticleCategory.vue'
|
||||
import ReactionsGroup from '../components/ReactionsGroup.vue'
|
||||
import DropdownMenu from '../components/DropdownMenu.vue'
|
||||
import LoginOverlay from '../components/LoginOverlay.vue'
|
||||
import { renderMarkdown } from '../utils/markdown'
|
||||
import { API_BASE_URL, toast } from '../main'
|
||||
import { getToken, authState } from '../utils/auth'
|
||||
@@ -114,7 +122,7 @@ hatch.register()
|
||||
|
||||
export default {
|
||||
name: 'PostPageView',
|
||||
components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ArticleCategory, ReactionsGroup, DropdownMenu },
|
||||
components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ArticleCategory, ReactionsGroup, DropdownMenu, LoginOverlay },
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const postId = route.params.id
|
||||
@@ -235,7 +243,12 @@ export default {
|
||||
headers: { Authorization: token ? `Bearer ${token}` : '' }
|
||||
})
|
||||
isWaitingFetchingPost.value = false;
|
||||
if (!res.ok) return
|
||||
if (!res.ok) {
|
||||
if (res.status === 404) {
|
||||
router.replace('/404')
|
||||
}
|
||||
return
|
||||
}
|
||||
const data = await res.json()
|
||||
postContent.value = data.content
|
||||
author.value = data.author
|
||||
@@ -779,4 +792,8 @@ export default {
|
||||
.copy-link:hover {
|
||||
background-color: #e2e2e2;
|
||||
}
|
||||
|
||||
.comment-editor-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -281,6 +281,8 @@ export default {
|
||||
const data = await res.json()
|
||||
user.value = data
|
||||
subscribed.value = !!data.subscribed
|
||||
} else if (res.status === 404) {
|
||||
router.replace('/404')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user