chore: format code

pull/36/head
CXM 3 years ago
parent 33631a9806
commit b2a66c3788

@ -1,26 +1,17 @@
<template>
<n-config-provider :theme="theme">
<n-message-provider>
<div
class="app-container"
:class="{ dark: theme?.name === 'dark' }"
>
<div class="app-container" :class="{ dark: theme?.name === 'dark' }">
<div has-sider class="main-wrap" position="static">
<!-- -->
<sidebar />
<div class="content-wrap">
<router-view class="app-wrap" v-slot="{ Component }">
<router-view v-slot="{ Component }" class="app-wrap">
<keep-alive>
<component
v-if="$route.meta.keepAlive"
:is="Component"
/>
<component :is="Component" v-if="$route.meta.keepAlive" />
</keep-alive>
<component
v-if="!$route.meta.keepAlive"
:is="Component"
/>
<component :is="Component" v-if="!$route.meta.keepAlive" />
</router-view>
</div>

@ -75,7 +75,6 @@
}
.dark {
.hash-link,
.user-link {
color: #63e2b7;

@ -41,9 +41,9 @@
</n-form-item-row>
<n-form-item-row label="密码" path="password">
<n-input
v-model:value="loginForm.password"
type="password"
show-password-on="mousedown"
v-model:value="loginForm.password"
placeholder="请输入账户密码"
@keyup.enter.prevent="handleLogin"
/>
@ -74,19 +74,19 @@
</n-form-item-row>
<n-form-item-row label="密码" path="password">
<n-input
v-model:value="registerForm.password"
type="password"
show-password-on="mousedown"
placeholder="密码不少于6位"
v-model:value="registerForm.password"
@keyup.enter.prevent="handleRegister"
/>
</n-form-item-row>
<n-form-item-row label="重复密码" path="repassword">
<n-input
v-model:value="registerForm.repassword"
type="password"
show-password-on="mousedown"
placeholder="请再次输入密码"
v-model:value="registerForm.repassword"
@keyup.enter.prevent="handleRegister"
/>
</n-form-item-row>

@ -7,28 +7,22 @@
<template #header>
<span class="nickname-wrap">
<router-link
@click.stop
class="username-link"
:to="{
name: 'user',
query: { username: comment.user.username },
}"
@click.stop
>
{{ comment.user.nickname }}
</router-link>
</span>
<span class="username-wrap">
@{{ comment.user.username }}
</span>
<span class="username-wrap"> @{{ comment.user.username }} </span>
</template>
<template #header-extra>
<div class="opt-wrap">
<span class="timestamp">
{{
comment.ip_loc
? comment.ip_loc + ' · '
: comment.ip_loc
}}
{{ comment.ip_loc ? comment.ip_loc + ' · ' : comment.ip_loc }}
{{ formatRelativeTime(comment.created_on) }}
</span>
@ -42,12 +36,7 @@
@positive-click="execDelAction"
>
<template #trigger>
<n-button
quaternary
circle
size="tiny"
class="del-btn"
>
<n-button quaternary circle size="tiny" class="del-btn">
<template #icon>
<n-icon>
<trash />
@ -59,7 +48,7 @@
</n-popconfirm>
</div>
</template>
<template #description v-if="comment.texts.length > 0">
<template v-if="comment.texts.length > 0" #description>
<span
v-for="content in comment.texts"
:key="content.id"
@ -84,8 +73,8 @@
</div>
<!-- -->
<compose-reply
ref="replyComposeRef"
v-if="store.state.userInfo.id > 0"
ref="replyComposeRef"
:comment-id="comment.id"
:at-userid="replyAtUserID"
:at-username="replyAtUsername"
@ -113,11 +102,14 @@ const replyAtUsername = ref('');
const replyComposeRef = ref();
const emit = defineEmits<{
(e: "reload"): void
(e: 'reload'): void;
}>();
const props = withDefaults(defineProps<{
comment: Item.CommentProps
}>(), {})
const props = withDefaults(
defineProps<{
comment: Item.CommentProps;
}>(),
{},
);
const comment = computed(() => {
let comment: Item.CommentComponentProps = Object.assign(
@ -125,7 +117,7 @@ const comment = computed(() => {
texts: [],
imgs: [],
},
props.comment
props.comment,
);
comment.contents.map((content: any) => {
if (+content.type === 1 || +content.type === 2) {

@ -1,13 +1,9 @@
<template>
<div>
<div class="compose-wrap" v-if="store.state.userInfo.id > 0">
<div v-if="store.state.userInfo.id > 0" class="compose-wrap">
<div class="compose-line">
<div class="compose-user">
<n-avatar
round
:size="30"
:src="store.state.userInfo.avatar"
/>
<n-avatar round :size="30" :src="store.state.userInfo.avatar" />
</div>
<n-mention
type="textarea"
@ -19,14 +15,14 @@
:loading="loading"
:value="content"
:disabled="props.lock === 1"
@update:value="changeContent"
@search="handleSearch"
@focus="focusComment"
:placeholder="
props.lock === 1
? ''
: '...'
"
@update:value="changeContent"
@search="handleSearch"
@focus="focusComment"
/>
</div>
@ -55,25 +51,21 @@
<n-upload-trigger #="{ handleClick }" abstract>
<n-button
:disabled="
(fileQueue.length > 0 &&
uploadType === 'public/video') ||
(fileQueue.length > 0 && uploadType === 'public/video') ||
fileQueue.length === 9
"
quaternary
circle
type="primary"
@click="
() => {
setUploadType('public/image');
handleClick();
}
"
quaternary
circle
type="primary"
>
<template #icon>
<n-icon
size="20"
color="var(--primary-color)"
>
<n-icon size="20" color="var(--primary-color)">
<image-outline />
</n-icon>
</template>
@ -108,11 +100,11 @@
</n-button>
<n-button
:loading="submitting"
@click="submitPost"
type="primary"
secondary
size="small"
round
@click="submitPost"
>
</n-button>
@ -125,7 +117,7 @@
</n-upload>
</div>
<div class="compose-wrap" v-else>
<div v-else class="compose-wrap">
<div class="login-wrap">
<span class="login-banner"> </span>
</div>
@ -153,7 +145,6 @@
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useStore } from 'vuex';
@ -170,15 +161,18 @@ import { parsePostTag } from '@/utils/content';
import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui';
const emit = defineEmits<{
(e: "post-success"): void
(e: 'post-success'): void;
}>();
const props = withDefaults(defineProps<{
lock: number,
postId: number,
}>(), {
const props = withDefaults(
defineProps<{
lock: number;
postId: number;
}>(),
{
lock: 0,
postId: 0
});
postId: 0,
},
);
const store = useStore();
@ -242,14 +236,17 @@ const beforeUpload = async (data: any) => {
if (
uploadType.value === 'public/image' &&
!['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].includes(
(data.file as any).file?.type
(data.file as any).file?.type,
)
) {
window.$message.warning(' png/jpg/gif ');
return false;
}
if (uploadType.value === 'image' && (data.file as any).file?.size > 10485760) {
if (
uploadType.value === 'image' &&
(data.file as any).file?.size > 10485760
) {
window.$message.warning('10MB');
return false;
}

@ -1,26 +1,24 @@
<template>
<div class="reply-compose-wrap">
<div class="reply-switch">
<span class="show" v-if="!showReply" @click="switchReply(true)">
<span v-if="!showReply" class="show" @click="switchReply(true)">
</span>
<span class="hide" v-if="showReply" @click="switchReply(false)">
<span v-if="showReply" class="hide" @click="switchReply(false)">
</span>
</div>
<div class="reply-input-wrap" v-if="showReply">
<div v-if="showReply" class="reply-input-wrap">
<n-input-group>
<n-input
ref="inputInstRef"
v-model:value="replyContent"
size="small"
:placeholder="
props.atUsername
? '@' + props.atUsername
: '..'
props.atUsername ? '@' + props.atUsername : '..'
"
maxlength="100"
v-model:value="replyContent"
show-count
clearable
/>
@ -43,18 +41,21 @@ import { ref } from 'vue';
import { createCommentReply } from '@/api/post';
import { InputInst } from 'naive-ui';
const props = withDefaults(defineProps<{
commentId: number,
atUserid: number,
atUsername: string,
}>(), {
const props = withDefaults(
defineProps<{
commentId: number;
atUserid: number;
atUsername: string;
}>(),
{
commentId: 0,
atUserid: 0,
atUsername: ""
});
atUsername: '',
},
);
const emit = defineEmits<{
(e: "reload"): void,
(e: "reset"): void
(e: 'reload'): void;
(e: 'reset'): void;
}>();
const inputInstRef = ref<InputInst>();
const showReply = ref(false);

@ -1,13 +1,9 @@
<template>
<div>
<div class="compose-wrap" v-if="store.state.userInfo.id > 0">
<div v-if="store.state.userInfo.id > 0" class="compose-wrap">
<div class="compose-line">
<div class="compose-user">
<n-avatar
round
:size="30"
:src="store.state.userInfo.avatar"
/>
<n-avatar round :size="30" :src="store.state.userInfo.avatar" />
</div>
<n-mention
type="textarea"
@ -18,9 +14,9 @@
:value="content"
:prefix="['@', '#']"
:options="optionsRef"
placeholder="说说您的新鲜事..."
@search="handleSearch"
@update:value="changeContent"
placeholder="说说您的新鲜事..."
/>
</div>
@ -48,25 +44,21 @@
<n-upload-trigger #="{ handleClick }" abstract>
<n-button
:disabled="
(fileQueue.length > 0 &&
uploadType === 'public/video') ||
(fileQueue.length > 0 && uploadType === 'public/video') ||
fileQueue.length === 9
"
quaternary
circle
type="primary"
@click="
() => {
setUploadType('public/image');
handleClick();
}
"
quaternary
circle
type="primary"
>
<template #icon>
<n-icon
size="20"
color="var(--primary-color)"
>
<n-icon size="20" color="var(--primary-color)">
<image-outline />
</n-icon>
</template>
@ -76,25 +68,21 @@
<n-upload-trigger #="{ handleClick }" abstract>
<n-button
:disabled="
(fileQueue.length > 0 &&
uploadType !== 'public/video') ||
(fileQueue.length > 0 && uploadType !== 'public/video') ||
fileQueue.length === 9
"
quaternary
circle
type="primary"
@click="
() => {
setUploadType('public/video');
handleClick();
}
"
quaternary
circle
type="primary"
>
<template #icon>
<n-icon
size="20"
color="var(--primary-color)"
>
<n-icon size="20" color="var(--primary-color)">
<videocam-outline />
</n-icon>
</template>
@ -104,37 +92,28 @@
<n-upload-trigger #="{ handleClick }" abstract>
<n-button
:disabled="
(fileQueue.length > 0 &&
uploadType === 'public/video') ||
(fileQueue.length > 0 && uploadType === 'public/video') ||
fileQueue.length === 9
"
quaternary
circle
type="primary"
@click="
() => {
setUploadType('attachment');
handleClick();
}
"
quaternary
circle
type="primary"
>
<template #icon>
<n-icon
size="20"
color="var(--primary-color)"
>
<n-icon size="20" color="var(--primary-color)">
<attach-outline />
</n-icon>
</template>
</n-button>
</n-upload-trigger>
<n-button
quaternary
circle
type="primary"
@click.stop="switchLink"
>
<n-button quaternary circle type="primary" @click.stop="switchLink">
<template #icon>
<n-icon size="20" color="var(--primary-color)">
<compass-outline />
@ -160,10 +139,10 @@
<n-button
:loading="submitting"
@click="submitPost"
type="primary"
secondary
round
@click="submitPost"
>
</n-button>
@ -173,8 +152,8 @@
<div class="attachment-list-wrap">
<n-upload-file-list />
<div
class="attachment-price-wrap"
v-if="attachmentContents.length > 0"
class="attachment-price-wrap"
>
<n-input-number
v-model:value="attachmentPrice"
@ -190,7 +169,7 @@
</div>
</n-upload>
<div class="link-wrap" v-if="showLinkSet">
<div v-if="showLinkSet" class="link-wrap">
<n-dynamic-input
v-model:value="links"
placeholder="请输入以http(s)://开头的链接"
@ -202,7 +181,7 @@
</div>
</div>
<div class="compose-wrap" v-else>
<div v-else class="compose-wrap">
<div class="login-wrap">
<span class="login-banner"> </span>
</div>
@ -230,7 +209,6 @@
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
@ -248,7 +226,7 @@ import { parsePostTag } from '@/utils/content';
import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui';
const emit = defineEmits<{
(e: 'post-success', post: Item.PostProps): void
(e: 'post-success', post: Item.PostProps): void;
}>();
const store = useStore();
@ -348,7 +326,7 @@ const beforeUpload = async (data: any) => {
if (
uploadType.value === 'public/image' &&
!['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].includes(
data.file.file?.type
data.file.file?.type,
)
) {
window.$message.warning(' png/jpg/gif ');
@ -369,10 +347,7 @@ const beforeUpload = async (data: any) => {
return false;
}
if (
uploadType.value === 'public/video' &&
data.file.file?.size > 104857600
) {
if (uploadType.value === 'public/video' && data.file.file?.size > 104857600) {
window.$message.warning('100MB');
return false;
}

@ -3,12 +3,12 @@
<template #header>
<div class="navbar">
<n-button
class="back-btn"
v-if="back"
@click="goBack"
class="back-btn"
quaternary
circle
size="small"
@click="goBack"
>
<template #icon>
<n-icon><chevron-left-round /></n-icon>
@ -19,9 +19,9 @@
<n-switch
:value="store.state.theme === 'dark'"
@update:value="switchTheme"
size="small"
class="theme-switch-wrap"
@update:value="switchTheme"
>
<template #icon>
<dark-mode-outlined />
@ -50,7 +50,7 @@ const props = withDefaults(
{
title: '',
back: false,
}
},
);
const switchTheme = (theme: boolean) => {
if (theme) {

@ -15,9 +15,8 @@
: defaultavatar
"
/>
<span class="nickname" v-if="message.sender_user.id > 0">
<span v-if="message.sender_user.id > 0" class="nickname">
<router-link
@click.stop
class="username-link"
:to="{
name: 'user',
@ -25,14 +24,13 @@
username: message.sender_user.username,
},
}"
@click.stop
>
{{ message.sender_user.nickname }}
</router-link>
<span class="username">
@{{ message.sender_user.username }}
</span>
<span class="username"> @{{ message.sender_user.username }} </span>
</span>
<span class="nickname" v-else> </span>
<span v-else class="nickname"> </span>
</div>
</template>
<template #header-extra>
@ -53,8 +51,8 @@
{{ message.breif }}
<span
v-if="message.type !== 4"
@click.stop="viewDetail(message)"
class="hash-link view-link"
@click.stop="viewDetail(message)"
>
<n-icon><share-outline /></n-icon>
</span>
@ -83,7 +81,7 @@ const props = withDefaults(
defineProps<{
message: Item.MessageProps;
}>(),
{}
{},
);
const viewDetail = (message: Item.MessageProps) => {
handleReadMessage(message);

@ -1,5 +1,5 @@
<template>
<div class="skeleton-item" v-for="i in new Array(num)" :key="i">
<div v-for="i in new Array(num)" :key="i" class="skeleton-item">
<div class="content">
<n-skeleton text :repeat="2" />
<n-skeleton text style="width: 60%" />
@ -8,11 +8,14 @@
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
num: number
}>(), {
num: 1
});
const props = withDefaults(
defineProps<{
num: number;
}>(),
{
num: 1,
},
);
</script>
<style lang="less" scoped>

@ -1,15 +1,15 @@
<template>
<div class="attachment-wrap">
<div
class="attach-item"
v-for="attachment in attachments"
:key="attachment.id"
class="attach-item"
>
<n-button
@click.stop="download(attachment)"
type="primary"
size="tiny"
dashed
@click.stop="download(attachment)"
>
<template #icon>
<n-icon>
@ -48,7 +48,7 @@ const props = withDefaults(
{
attachments: () => [],
price: 0,
}
},
);
const showDownloadModal = ref(false);
const downloadTip = ref<any>('');
@ -67,7 +67,7 @@ const download = (attachment: Item.PostItemProps) => {
{},
'' +
(props.price / 100).toFixed(2) +
'元'
'元',
),
]);
@ -78,11 +78,7 @@ const download = (attachment: Item.PostItemProps) => {
if (res.paid) {
downloadTip.value = () =>
h('div', {}, [
h(
'p',
{},
''
),
h('p', {}, ''),
]);
}
})

@ -6,12 +6,12 @@
</template>
<template #header>
<router-link
@click.stop
class="username-link"
:to="{
name: 'user',
query: { username: post.user.username },
}"
@click.stop
>
{{ post.user.nickname }}
</router-link>
@ -19,11 +19,11 @@
</template>
<template #header-extra>
<div
class="options"
v-if="
store.state.userInfo.is_admin ||
store.state.userInfo.id === post.user.id
"
class="options"
>
<n-dropdown
placement="bottom-end"
@ -60,9 +60,7 @@
preset="dialog"
title="提示"
:content="
'' +
(post.is_lock ? '' : '') +
''
'' + (post.is_lock ? '' : '') + ''
"
positive-text="确认"
negative-text="取消"
@ -75,9 +73,7 @@
preset="dialog"
title="提示"
:content="
'' +
(post.is_top ? '' : '') +
''
'' + (post.is_top ? '' : '') + ''
"
positive-text="确认"
negative-text="取消"
@ -119,10 +115,7 @@
<template #action>
<div class="opts-wrap">
<n-space justify="space-between">
<div
class="opt-item hover"
@click.stop="handlePostStar"
>
<div class="opt-item hover" @click.stop="handlePostStar">
<n-icon size="20" class="opt-item-icon">
<heart-outline v-if="!hasStarred" />
<heart v-if="hasStarred" color="red" />
@ -135,10 +128,7 @@
</n-icon>
{{ post.comment_count }}
</div>
<div
class="opt-item hover"
@click.stop="handlePostCollection"
>
<div class="opt-item hover" @click.stop="handlePostCollection">
<n-icon size="20" class="opt-item-icon">
<bookmark-outline v-if="!hasCollected" />
<bookmark v-if="hasCollected" color="#ff7600" />
@ -184,7 +174,7 @@ const props = withDefaults(
defineProps<{
post: Item.PostProps;
}>(),
{}
{},
);
const showDelModal = ref(false);
const showLockModal = ref(false);
@ -206,7 +196,7 @@ const post = computed({
attachments: [],
charge_attachments: [],
},
props.post
props.post,
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {
@ -306,7 +296,7 @@ const doClickText = (e: MouseEvent, id: number) => {
goPostDetail(id);
};
const handlePostAction = (
item: 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick'
item: 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick',
) => {
if (item === 'delete') {
showDelModal.value = true;

@ -5,12 +5,12 @@
<template v-for="img in props.imgs" :key="img.id">
<n-gi>
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x1"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -22,12 +22,12 @@
<template v-for="img in props.imgs" :key="img.id">
<n-gi>
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -39,12 +39,12 @@
<template v-for="img in props.imgs" :key="img.id">
<n-gi>
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x3"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -56,12 +56,12 @@
<template v-for="(img, idx) in props.imgs" :key="img.id">
<n-gi v-if="idx < 3">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -70,12 +70,12 @@
<template v-for="(img, idx) in props.imgs" :key="img.id">
<n-gi v-if="idx >= 3">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x1"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -87,12 +87,12 @@
<template v-for="(img, idx) in props.imgs" :key="img.id">
<n-gi v-if="idx < 3">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -101,12 +101,12 @@
<template v-for="(img, idx) in props.imgs" :key="img.id">
<n-gi v-if="idx >= 3">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -116,28 +116,28 @@
<n-image-group v-if="props.imgs.length === 7">
<n-grid :x-gap="4" :y-gap="4" :cols="4">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx < 4">
<n-gi v-if="idx < 4" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x3"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
</n-grid>
<n-grid :x-gap="4" :y-gap="4" :cols="3" style="margin-top: 4px">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx >= 4">
<n-gi v-if="idx >= 4" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -147,28 +147,28 @@
<n-image-group v-if="props.imgs.length === 8">
<n-grid :x-gap="4" :y-gap="4" :cols="4">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx < 4">
<n-gi v-if="idx < 4" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x3"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
</n-grid>
<n-grid :x-gap="4" :y-gap="4" :cols="4" style="margin-top: 4px">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx >= 4">
<n-gi v-if="idx >= 4" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x3"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -178,42 +178,42 @@
<n-image-group v-if="props.imgs.length === 9">
<n-grid :x-gap="4" :y-gap="4" :cols="3">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx < 3">
<n-gi v-if="idx < 3" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
</n-grid>
<n-grid :x-gap="4" :y-gap="4" :cols="3" style="margin-top: 4px">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx >= 3 && idx < 6">
<n-gi v-if="idx >= 3 && idx < 6" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
</n-grid>
<n-grid :x-gap="4" :y-gap="4" :cols="3" style="margin-top: 4px">
<template v-for="(img, idx) in props.imgs">
<n-gi :key="img.id" v-if="idx >= 6">
<n-gi v-if="idx >= 6" :key="img.id">
<n-image
@error="() => (img.content = defaultImg)"
@click.stop
class="post-img x2"
object-fit="cover"
:src="img.content + thumbnail"
:preview-src="img.content"
@error="() => (img.content = defaultImg)"
@click.stop
/>
</n-gi>
</template>
@ -229,11 +229,14 @@ const defaultImg =
'https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/404.png';
const thumbnail =
'?x-oss-process=image/resize,m_fill,w_300,h_300,limit_0/auto-orient,1/format,png';
const props = withDefaults(defineProps<{
imgs: Item.PostItemProps[],
}>(), {
imgs: () => []
});
const props = withDefaults(
defineProps<{
imgs: Item.PostItemProps[];
}>(),
{
imgs: () => [],
},
);
</script>
<style lang="less">

@ -7,12 +7,12 @@
<template #header>
<span class="nickname-wrap">
<router-link
@click.stop
class="username-link"
:to="{
name: 'user',
query: { username: post.user.username },
}"
@click.stop
>
{{ post.user.nickname }}
</router-link>
@ -34,7 +34,7 @@
{{ formatRelativeTime(post.created_on) }}
</span>
</template>
<template #description v-if="post.texts.length > 0">
<template v-if="post.texts.length > 0" #description>
<span
v-for="content in post.texts"
:key="content.id"
@ -96,9 +96,12 @@ import {
const route = useRoute();
const router = useRouter();
const store = useStore();
const props = withDefaults(defineProps<{
post: Item.PostProps,
}>(), {});
const props = withDefaults(
defineProps<{
post: Item.PostProps;
}>(),
{},
);
const post = computed(() => {
let post: Item.PostComponentProps = Object.assign(
@ -110,7 +113,7 @@ const post = computed(() => {
attachments: [],
charge_attachments: [],
},
props.post
props.post,
);
post.contents.map((content) => {
if (+content.type === 1 || +content.type === 2) {

@ -1,13 +1,8 @@
<template>
<div class="link-wrap">
<div class="link-item" v-for="link in props.links" :key="link.id">
<div v-for="link in props.links" :key="link.id" class="link-item">
<n-icon class="hash-link"><link-outline /></n-icon>
<a
:href="link.content"
class="hash-link"
target="_blank"
@click.stop
>
<a :href="link.content" class="hash-link" target="_blank" @click.stop>
<span class="link-txt">{{ link.content }}</span>
</a>
</div>
@ -16,11 +11,14 @@
<script setup lang="ts">
import { LinkOutline } from '@vicons/ionicons5';
const props = withDefaults(defineProps<{
links: Item.PostItemProps[]
}>(), {
links: () => []
});
const props = withDefaults(
defineProps<{
links: Item.PostItemProps[];
}>(),
{
links: () => [],
},
);
</script>
<style lang="less" scoped>

@ -1,5 +1,5 @@
<template>
<div class="skeleton-item" v-for="i in new Array(num)" :key="i">
<div v-for="i in new Array(num)" :key="i" class="skeleton-item">
<div class="user">
<n-skeleton circle size="small" />
</div>
@ -11,11 +11,14 @@
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
num: number,
}>(), {
num: 1
});
const props = withDefaults(
defineProps<{
num: number;
}>(),
{
num: 1,
},
);
</script>
<style lang="less" scoped>

@ -3,13 +3,13 @@
<n-grid :x-gap="4" :y-gap="4" :cols="full ? 1 : 5">
<n-gi :span="full ? 1 : 3">
<n-video
@click.stop
v-for="video in props.videos"
:key="video.id"
:src="video.content"
:colors="['#18a058', '#2aca75']"
:hoverable="true"
theme="gradient"
@click.stop
></n-video>
</n-gi>
</n-grid>
@ -28,6 +28,6 @@ const props = withDefaults(
{
videos: () => [],
full: false,
}
},
);
</script>

@ -16,21 +16,19 @@
</span>
<router-link
v-if="props.reply.at_user_id > 0"
class="user-link"
:to="{
name: 'user',
query: { username: props.reply.at_user.username },
}"
v-if="props.reply.at_user_id > 0"
>
{{ props.reply.at_user.username }}
</router-link>
</div>
<div class="timestamp">
{{
props.reply.ip_loc
? props.reply.ip_loc + ' · '
: props.reply.ip_loc
props.reply.ip_loc ? props.reply.ip_loc + ' · ' : props.reply.ip_loc
}}
{{ formatRelativeTime(props.reply.created_on) }}
<n-popconfirm
@ -58,7 +56,7 @@
<div class="base-wrap">
<div class="content">{{ props.reply.content }}</div>
<div class="reply-switch" v-if="store.state.userInfo.id > 0">
<div v-if="store.state.userInfo.id > 0" class="reply-switch">
<span class="show" @click="focusReply"> </span>
</div>
</div>
@ -71,13 +69,16 @@ import { Trash } from '@vicons/tabler';
import { formatRelativeTime } from '@/utils/formatTime';
import { deleteCommentReply } from '@/api/post';
const props = withDefaults(defineProps<{
reply: Item.ReplyProps,
}>(), {});
const props = withDefaults(
defineProps<{
reply: Item.ReplyProps;
}>(),
{},
);
const store = useStore();
const emit = defineEmits<{
(e: "focusReply", reply: Item.ReplyProps): void,
(e: "reload"): void
(e: 'focusReply', reply: Item.ReplyProps): void;
(e: 'reload'): void;
}>();
const focusReply = () => {
@ -100,7 +101,6 @@ const execDelAction = () => {
};
</script>
<style lang="less" scoped>
.reply-item {
display: flex;

@ -1,11 +1,11 @@
<template>
<div class="rightbar-wrap" v-if="!store.state.collapsedRight">
<div v-if="!store.state.collapsedRight" class="rightbar-wrap">
<div class="search-wrap">
<n-input
v-model:value="keyword"
round
clearable
placeholder="搜一搜..."
v-model:value="keyword"
@keyup.enter.prevent="handleSearch"
>
<template #prefix>
@ -15,7 +15,7 @@
</div>
<n-card title="热门话题" embedded :bordered="false" size="small">
<n-spin :show="loading">
<div class="hot-tag-item" v-for="tag in tags" :key="tag.id">
<div v-for="tag in tags" :key="tag.id" class="hot-tag-item">
<router-link
class="hash-link"
:to="{
@ -49,17 +49,10 @@
</div>
<div>
<n-space>
<a
href="https://www.rocs.me"
target="_blank"
class="hash-link"
<a href="https://www.rocs.me" target="_blank" class="hash-link"
>Roc's Me</a
>
<a
href="https://www.rocboss.com"
target="_blank"
class="hash-link"
>
<a href="https://www.rocboss.com" target="_blank" class="hash-link">
ROCBOSS
</a>
</n-space>

@ -21,7 +21,7 @@
@update:value="goRouter"
/>
<div class="user-wrap" v-if="store.state.userInfo.id > 0">
<div v-if="store.state.userInfo.id > 0" class="user-wrap">
<n-avatar
class="user-avatar"
round
@ -50,19 +50,14 @@
</div>
<div class="user-mini-wrap">
<n-button
class="logout"
quaternary
circle
@click="handleLogout"
>
<n-button class="logout" quaternary circle @click="handleLogout">
<template #icon>
<n-icon :size="24"><log-out-outline /></n-icon>
</template>
</n-button>
</div>
</div>
<div class="user-wrap" v-else>
<div v-else class="user-wrap">
<div class="login-wrap">
<n-button
strong
@ -92,7 +87,7 @@ import { h, ref, watch, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { NIcon, NBadge, useMessage } from 'naive-ui';
import type { RouteRecordName } from "vue-router";
import type { RouteRecordName } from 'vue-router';
import {
HomeOutline,
BookmarkOutline,
@ -243,9 +238,9 @@ const renderMenuIcon = (option: AnyObject) => {
? 'var(--n-item-icon-color-active)'
: 'var(--n-item-icon-color)',
},
{ default: option.icon }
{ default: option.icon },
),
}
},
);
}
return h(NIcon, null, { default: option.icon });

@ -1,7 +1,6 @@
<template>
<n-modal
:show="show"
@update:show="closeModal"
class="whisper-card"
preset="card"
size="small"
@ -11,6 +10,7 @@
:style="{
width: '360px',
}"
@update:show="closeModal"
>
<div class="whisper-wrap">
<n-alert :show-icon="false">
@ -23,13 +23,13 @@
</n-alert>
<div class="whisper-line">
<n-input
v-model:value="content"
type="textarea"
placeholder="请输入私信内容(请勿发送不和谐内容,否则将会被封号)"
:autosize="{
minRows: 5,
maxRows: 10,
}"
v-model:value="content"
maxlength="200"
show-count
/>
@ -60,7 +60,7 @@ const props = withDefaults(
}>(),
{
show: false,
}
},
);
const content = ref('');
const loading = ref(false);

@ -1,188 +1,182 @@
declare module NetParams {
declare namespace NetParams {
interface AuthUserLogin {
/** 用户名 */
username: string,
username: string;
/** 密码 */
password: string
password: string;
}
interface AuthUserRegister {
/** 用户名 */
username: string,
username: string;
/** 密码 */
password: string
password: string;
}
type AuthUserInfo = string
type AuthUserInfo = string;
interface AuthUpdateUserPassword {
/** 新密码 */
password: string,
password: string;
/** 旧密码 */
old_password: string
old_password: string;
}
interface UserGetCollections {
page: number,
page_size: number
page: number;
page_size: number;
}
interface UserPrecheckAttachment {
id: number
id: number;
}
interface UserGetAttachment {
id: number
id: number;
}
interface UserGetUnreadMsgCount {
}
interface UserGetUnreadMsgCount {}
interface UserGetMessages {
page: number,
page_size: number
page: number;
page_size: number;
}
interface UserGetUserPosts {
/** 用户名 */
username: string,
page: number,
page_size: number
username: string;
page: number;
page_size: number;
}
interface UserGetStars {
page: number,
page_size: number
page: number;
page_size: number;
}
interface UserGetUserProfile {
username: string
username: string;
}
interface UserGetBills {
page: number,
page_size: number
page: number;
page_size: number;
}
interface UserReqRecharge {
amount: number
amount: number;
}
interface UserGetRecharge {
id: number
id: number;
}
interface UserBindUserPhone {
phone: string,
captcha: string
phone: string;
captcha: string;
}
interface UserGetCaptcha {
}
interface UserGetCaptcha {}
interface UserWhisper {
user_id: number,
content: string
user_id: number;
content: string;
}
interface UserChangePassword {
/** 新密码 */
password: string,
password: string;
/** 旧密码 */
old_password: string
old_password: string;
}
interface UserChangeNickname {
/** 昵称 */
nickname: string
nickname: string;
}
interface PostGetPost {
id: number
id: number;
}
interface PostGetPosts {
query: string | null,
type: string,
page: number,
page_size: number
query: string | null;
type: string;
page: number;
page_size: number;
}
interface PostLockPost {
id: number
id: number;
}
interface PostStickPost {
id: number
id: number;
}
interface PostGetPostStar {
id: number
id: number;
}
interface PostPostStar {
id: number
id: number;
}
interface PostGetPostCollection {
id: number
id: number;
}
interface PostPostCollection {
id: number
id: number;
}
interface PostGetTags {
type: "hot" | string,
num: number
type: 'hot' | string;
num: number;
}
interface PostGetPostComments {
id: number
id: number;
}
interface PostCreatePost {
/** 帖子内容列表 */
contents: Partial<Item.PostItemProps>[],
contents: Partial<Item.PostItemProps>[];
/** 标签列表 */
tags: string[],
tags: string[];
/** 艾特用户列表 */
users: string[],
users: string[];
/** 附件价格 */
attachment_price: number
attachment_price: number;
}
interface PostDeletePost {
id: number
id: number;
}
interface PostCreateComment {
/** 内容ID */
post_id: number,
post_id: number;
/** 帖子内容列表 */
contents: Partial<Item.CommentItemProps>[],
contents: Partial<Item.CommentItemProps>[];
/** 艾特用户列表 */
users: string[]
users: string[];
}
interface PostDeleteComment {
id: number
id: number;
}
interface PostCreateCommentReply {
/** 艾特的用户UID */
at_user_id: number,
at_user_id: number;
/** 回复的评论ID */
comment_id: number,
comment_id: number;
/** 回复内容 */
content: string
content: string;
}
interface PostDeleteCommentReply {
id: number
id: number;
}
}

@ -1,158 +1,142 @@
declare module NetReq {
declare namespace NetReq {
interface AuthUserLogin {
token: string
token: string;
}
interface AuthUserRegister {
/** 用户UID */
id: number,
id: number;
/** 用户名 */
username: string
username: string;
}
type AuthUserInfo = Item.UserInfo
interface AuthUpdateUserPassword {
type AuthUserInfo = Item.UserInfo;
}
interface AuthUpdateUserPassword {}
interface UserGetCollections {
/** 帖子列表 */
list: Item.PostProps[],
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps
pager: Item.PagerProps;
}
type UserGetSuggestUsers = string[]
type UserGetSuggestTags = string[]
type UserGetSuggestUsers = string[];
type UserGetSuggestTags = string[];
interface UserPrecheckAttachment {
paid: number
paid: number;
}
type UserGetAttachment = string
type UserGetAttachment = string;
interface UserGetUnreadMsgCount {
count: number
count: number;
}
interface UserGetMessages {
/** 消息列表 */
list: Item.MessageProps[],
list: Item.MessageProps[];
/** 页码信息 */
pager: Item.PagerProps
pager: Item.PagerProps;
}
interface UserGetUserPosts {
/** 帖子列表 */
list: Item.PostProps[],
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps
pager: Item.PagerProps;
}
interface UserGetStars {
/** 帖子列表 */
list: Item.PostProps[],
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps
pager: Item.PagerProps;
}
type UserGetUserProfile = Item.UserInfo
type UserGetUserProfile = Item.UserInfo;
interface UserGetBills {
list: Item.BillProps[],
list: Item.BillProps[];
/** 页码信息 */
pager: Item.PagerProps
pager: Item.PagerProps;
}
interface UserReqRecharge {
id: number,
pay: string
id: number;
pay: string;
}
interface UserGetRecharge {
status: string
status: string;
}
interface UserBindUserPhone {
}
interface UserBindUserPhone {}
interface UserGetCaptcha {
id: string,
id: string;
/** 头像图片 base64 */
b64s: string
}
interface UserChangeNickname {
b64s: string;
}
interface UserChangePassword {
interface UserChangeNickname {}
}
interface UserChangePassword {}
type PostGetPost = Item.PostProps
type PostGetPost = Item.PostProps;
interface PostGetPosts {
/** 帖子列表 */
list: Item.PostProps[],
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps
pager: Item.PagerProps;
}
interface PostLockPost {
/** 锁定状态0为未锁定1为锁定 */
lock_status: 0 | 1
lock_status: 0 | 1;
}
interface PostStickPost {
/** 置顶状态0为未置顶1为置顶 */
top_status: 0 | 1
top_status: 0 | 1;
}
interface PostGetPostStar {
status: boolean
status: boolean;
}
interface PostPostStar {
status: boolean
status: boolean;
}
interface PostGetPostCollection {
status: boolean
status: boolean;
}
interface PostPostCollection {
status: boolean
status: boolean;
}
type PostGetTags = Item.TagProps[]
type PostGetTags = Item.TagProps[];
interface PostGetPostComments {
/** 评论列表 */
list: Item.CommentProps[],
list: Item.CommentProps[];
/** 页码信息 */
pager: Item.PagerProps
}
type PostCreatePost = Item.PostProps
interface PostDeletePost {
pager: Item.PagerProps;
}
type PostCreateComment = Item.CommentProps
interface PostDeleteComment {
type PostCreatePost = Item.PostProps;
}
interface PostDeletePost {}
type PostCreateCommentReply = Item.ReplyProps
type PostCreateComment = Item.CommentProps;
interface PostDeleteCommentReply{
interface PostDeleteComment {}
}
type PostCreateCommentReply = Item.ReplyProps;
interface PostDeleteCommentReply {}
}

@ -1,290 +1,288 @@
declare module Item {
declare namespace Item {
interface UserInfo {
/** 用户UID */
id: number,
id: number;
/** 用户名 */
username: string,
username: string;
/** 用户昵称 */
nickname: string,
nickname: string;
/** 用户头像 */
avatar: string,
avatar: string;
/** 用户手机号 */
phone?: string,
phone?: string;
/** 是否为管理员 */
is_admin: boolean,
is_admin: boolean;
/** 用户余额(分) */
balance?: number,
balance?: number;
/** 用户状态 */
status?: 0 | 1
status?: 0 | 1;
}
/** 评论内容 */
interface CommentItemProps {
/** 内容ID */
id: number,
id: number;
/** 评论ID */
comment_id: number,
comment_id: number;
/** 评论者UID */
user_id: number,
user_id: number;
/** 类别1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址 */
type: number,
type: number;
/** 内容 */
content: string,
content: string;
/** 排序,越小越靠前 */
sort: number,
sort: number;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
/** 评论数据 */
interface CommentProps {
id: number,
post_id: number,
id: number;
post_id: number;
/** 评论者UID */
user_id: number,
user_id: number;
/** 评论者用户信息 */
user: UserInfo,
user: UserInfo;
/** 评论内容 */
contents: CommentItemProps[],
contents: CommentItemProps[];
/** 回复列表 */
replies: ReplyProps[],
replies: ReplyProps[];
/** 评论者IP地址 */
ip?: string,
ip?: string;
/** 评论者城市地址 */
ip_loc: string,
ip_loc: string;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
interface CommentComponentProps extends CommentProps {
/** 文字评论列表 */
texts: CommentItemProps[],
texts: CommentItemProps[];
/** 图片评论列表 */
imgs: CommentItemProps[]
imgs: CommentItemProps[];
}
/** 回复内容 */
interface ReplyProps {
/** 内容ID */
id: number,
id: number;
/** 评论ID */
comment_id: number,
comment_id: number;
/** 回复人ID */
user_id: number,
user_id: number;
/** 回复人用户数据 */
user: UserInfo,
user: UserInfo;
/** 艾特人ID */
at_user_id: number,
at_user_id: number;
/** 艾特人用户数据 */
at_user: UserInfo,
at_user: UserInfo;
/** 内容 */
content: string,
content: string;
/** 回复人IP地址 */
ip?: string,
ip?: string;
/** 回复人城市地址 */
ip_loc: string,
ip_loc: string;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
/** 帖子内容 */
interface PostItemProps {
/** 内容ID */
id: number,
id: number;
/** 类型1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址7为附件资源8为收费资源 */
type: number,
type: number;
/** POST ID */
post_id: number,
post_id: number;
/** 内容 */
content: string,
content: string;
/** 排序,越小越靠前 */
sort: number,
sort: number;
/** 用户UID */
user_id?: number,
user_id?: number;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1,
is_del?: 0 | 1;
}
/** 帖子 */
interface PostProps {
id: number,
id: number;
/** 发帖人UID */
user_id: number,
user_id: number;
/** 发帖人用户数据 */
user: UserInfo,
user: UserInfo;
/** 附件价格(分) */
attachment_price: number,
attachment_price: number;
/** 发帖时IP地址 */
ip?: string,
ip?: string;
/** 发帖时城市地址 */
ip_loc: string,
ip_loc: string;
/** 最新回复时间 */
latest_replied_on: number,
latest_replied_on: number;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 点赞数 */
upvote_count: number,
upvote_count: number;
/** 评论数 */
comment_count: number,
comment_count: number;
/** 收藏数 */
collection_count: number,
collection_count: number;
/** 内容列表 */
contents: PostItemProps[],
contents: PostItemProps[];
/** 标签列表 */
tags: {[key: string]: number} | string,
tags: { [key: string]: number } | string;
/** 是否锁定 */
is_lock: number,
is_lock: number;
/** 是否置顶 */
is_top: number,
is_top: number;
/** 是否精华 */
is_essence: number,
is_essence: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
/** 组件用帖子 */
interface PostComponentProps extends PostProps {
/** 文字段落列表 */
texts: PostItemProps[],
texts: PostItemProps[];
/** 图片列表 */
imgs: PostItemProps[],
imgs: PostItemProps[];
/** 视频列表 */
videos: PostItemProps[],
videos: PostItemProps[];
/** 链接列表 */
links: PostItemProps[],
links: PostItemProps[];
/** 附件列表 */
attachments: PostItemProps[],
attachments: PostItemProps[];
/** 收费附件列表 */
charge_attachments: PostItemProps[]
charge_attachments: PostItemProps[];
}
interface MessageProps {
id: number,
id: number;
/** 类型1为动态2为评论3为回复4为私信99为系统通知 */
type: 1 | 2 | 3 | 4 | 99,
type: 1 | 2 | 3 | 4 | 99;
/** 摘要说明 */
breif: string,
breif: string;
/** 详细内容 */
content: string,
content: string;
/** 是否已读0为未读1为已读 */
is_read: 0 | 1,
is_read: 0 | 1;
/** 发送人UID */
sender_user_id: number,
sender_user_id: number;
/** 发送人用户数据 */
sender_user: UserInfo,
sender_user: UserInfo;
/** 接收方UID */
receiver_user_id: number,
receiver_user_id: number;
/** 帖子ID */
post_id: number,
post_id: number;
/** 帖子内容 */
post: PostProps,
post: PostProps;
/** 评论ID */
comment_id: number,
comment_id: number;
/** 评论内容 */
comment: CommentProps,
comment: CommentProps;
/** 回复ID */
reply_id: number,
reply_id: number;
/** 回复内容 */
replay: ReplyProps,
replay: ReplyProps;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
interface AttachmentProps {
id: number,
id: number;
/** 类别1为图片2为视频3为其他附件 */
type: 1 | 2 | 3,
type: 1 | 2 | 3;
/** 发布者用户UID */
user_id: number,
user_id: number;
/** 发布者用户数据 */
user: UserInfo,
user: UserInfo;
/** 文件大小 */
file_size: number,
file_size: number;
/** 图片宽度 */
img_width?: number,
img_width?: number;
/** 图片高度 */
img_height?: number,
img_height?: number;
/** 内容 */
content: string,
content: string;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
interface TagProps {
id: number,
id: number;
/** 创建者UID */
user_id: number,
user_id: number;
/** 创建者用户数据 */
user: UserInfo,
user: UserInfo;
/** 标签名 */
tag: string,
tag: string;
/** 引用数 */
quote_num: number,
quote_num: number;
/** 创建时间 */
created_on: number,
created_on: number;
/** 修改时间 */
modified_on?: number,
modified_on?: number;
/** 删除时间 */
deleted_on?: number,
deleted_on?: number;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1
is_del?: 0 | 1;
}
interface PagerProps {
/** 当前页码 */
page: number,
page: number;
/** 每页条数 */
page_size: number,
page_size: number;
/** 总条数 */
total_rows: number
total_rows: number;
}
interface BillProps {
id: number,
reason: string,
change_amount: number,
created_on: number
id: number;
reason: string;
change_amount: number;
created_on: number;
}
}

@ -2,8 +2,8 @@
export const parsePostTag = (content: string) => {
const tags: string[] = []
const users: string[] = []
var tagExp = /(#|)([^#@])+?\s+?/g // 这⾥中⽂#和英⽂#都会识别
var atExp = /@([a-zA-Z0-9])+?\s+?/g // 这⾥中⽂#和英⽂#都会识别
const tagExp = /(#|)([^#@])+?\s+?/g // 这⾥中⽂#和英⽂#都会识别
const atExp = /@([a-zA-Z0-9])+?\s+?/g // 这⾥中⽂#和英⽂#都会识别
content = content
.replace(/<[^>]*?>/gi, '')
.replace(/(.*?)<\/[^>]*?>/gi, '')

@ -1,8 +1,8 @@
// 滚动到顶部
export const scrollToTop = (scrollDuration: number) => {
var cosParameter = window.scrollY / 2;
var scrollCount = 0;
var oldTimestamp = performance.now();
const cosParameter = window.scrollY / 2;
let scrollCount = 0;
let oldTimestamp = performance.now();
function step(newTimestamp: number) {
scrollCount +=
Math.PI / (scrollDuration / (newTimestamp - oldTimestamp));

@ -4,12 +4,12 @@
<n-list class="main-content-wrap" bordered>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>
@ -18,7 +18,7 @@
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>

@ -4,12 +4,12 @@
<n-list class="main-content-wrap" bordered>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>
@ -22,7 +22,7 @@
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>
@ -126,7 +126,7 @@ watch(
loadPosts();
}, 0);
}
}
},
);
</script>

@ -8,7 +8,7 @@
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>
@ -18,12 +18,12 @@
</div>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>

@ -5,10 +5,10 @@
<n-list class="main-content-wrap" bordered>
<n-list-item>
<n-spin :show="loading">
<div class="detail-wrap" v-if="post.id > 0">
<div v-if="post.id > 0" class="detail-wrap">
<post-detail :post="post" @reload="loadPost" />
</div>
<div class="empty-wrap" v-else>
<div v-else class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>
</n-spin>
@ -26,18 +26,12 @@
<post-skeleton :num="5" />
</div>
<div v-else>
<div class="empty-wrap" v-if="comments.length === 0">
<n-empty
size="large"
description="暂无评论,快来抢沙发"
/>
<div v-if="comments.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无评论,快来抢沙发" />
</div>
<n-list-item v-for="comment in comments" :key="comment.id">
<comment-item
:comment="comment"
@reload="loadComments"
/>
<comment-item :comment="comment" @reload="loadComments" />
</n-list-item>
</div>
</div>
@ -76,7 +70,7 @@ const loadPost = () => {
loading.value = false;
});
};
const loadComments = (scrollToBottom: boolean = false) => {
const loadComments = (scrollToBottom = false) => {
if (comments.value.length === 0) {
commentLoading.value = true;
}

@ -3,9 +3,9 @@
<main-nav title="主页" />
<n-list
v-if="store.state.userInfo.id > 0"
class="main-content-wrap profile-wrap"
bordered
v-if="store.state.userInfo.id > 0"
>
<!-- -->
<div class="profile-baseinfo">
@ -21,12 +21,12 @@
</div>
</div>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>
@ -38,7 +38,7 @@
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>

@ -29,22 +29,22 @@
{{ store.state.userInfo.nickname }}
</div>
<n-input
ref="inputInstRef"
v-show="showNicknameEdit"
class="nickname-input"
ref="inputInstRef"
v-model:value="store.state.userInfo.nickname"
class="nickname-input"
type="text"
size="small"
placeholder="请输入昵称"
@blur="handleNicknameChange"
:maxlength="16"
@blur="handleNicknameChange"
/>
<n-button
v-if="!showNicknameEdit"
quaternary
round
type="success"
size="small"
v-if="!showNicknameEdit"
@click="handleNicknameShow"
>
<template #icon>
@ -64,17 +64,16 @@
<n-card title="手机号" size="small" class="setting-card">
<div
v-if="
store.state.userInfo.phone &&
store.state.userInfo.phone.length > 0
store.state.userInfo.phone && store.state.userInfo.phone.length > 0
"
>
{{ store.state.userInfo.phone }}
<n-button
v-if="!showPhoneBind"
quaternary
round
type="success"
v-if="!showPhoneBind"
@click="showPhoneBind = true"
>
@ -84,26 +83,22 @@
<n-alert title="手机绑定提示" type="warning">
~
<a
v-if="!showPhoneBind"
class="hash-link"
@click="showPhoneBind = true"
v-if="!showPhoneBind"
>
</a>
</n-alert>
</div>
<div class="phone-bind-wrap" v-if="showPhoneBind">
<n-form
ref="phoneFormRef"
:model="modelData"
:rules="bindRules"
>
<div v-if="showPhoneBind" class="phone-bind-wrap">
<n-form ref="phoneFormRef" :model="modelData" :rules="bindRules">
<n-form-item path="phone" label="手机号">
<n-input
:value="modelData.phone"
@update:value="(v) => (modelData.phone = v.trim())"
placeholder="请输入中国大陆手机号"
@update:value="(v) => (modelData.phone = v.trim())"
@keydown.enter.prevent
/>
</n-form-item>
@ -146,11 +141,7 @@
<n-row :gutter="[0, 24]">
<n-col :span="24">
<div class="form-submit-wrap">
<n-button
quaternary
round
@click="showPhoneBind = false"
>
<n-button quaternary round @click="showPhoneBind = false">
</n-button>
<n-button
@ -172,15 +163,15 @@
<n-card title="账户安全" size="small" class="setting-card">
<n-button
v-if="!showPasswordSetting"
quaternary
round
type="success"
v-if="!showPasswordSetting"
@click="showPasswordSetting = true"
>
</n-button>
<div class="phone-bind-wrap" v-if="showPasswordSetting">
<div v-if="showPasswordSetting" class="phone-bind-wrap">
<n-form ref="formRef" :model="modelData" :rules="passwordRules">
<n-form-item path="old_password" label="旧密码">
<n-input
@ -216,11 +207,7 @@
<n-row :gutter="[0, 24]">
<n-col :span="24">
<div class="form-submit-wrap">
<n-button
quaternary
round
@click="showPasswordSetting = false"
>
<n-button quaternary round @click="showPasswordSetting = false">
</n-button>
<n-button
@ -285,9 +272,9 @@ const modelData = reactive({
imgCaptcha: '',
phone: '',
phone_captcha: '',
password: "",
old_password: "",
reenteredPassword: "",
password: '',
old_password: '',
reenteredPassword: '',
});
const beforeUpload = async (data: any) => {

@ -4,12 +4,12 @@
<n-list class="main-content-wrap" bordered>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>
@ -18,7 +18,7 @@
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>

@ -10,11 +10,11 @@
<n-spin :show="loading">
<n-space>
<n-tag
v-for="tag in tags"
:key="tag.id"
class="tag-item"
type="success"
round
v-for="tag in tags"
:key="tag.id"
>
<router-link
class="hash-link"

@ -5,7 +5,7 @@
<n-list class="main-content-wrap profile-wrap" bordered>
<!-- -->
<n-spin :show="userLoading">
<div class="profile-baseinfo" v-if="user.id > 0">
<div v-if="user.id > 0" class="profile-baseinfo">
<div class="avatar">
<n-avatar size="large" :src="user.avatar" />
</div>
@ -32,19 +32,15 @@
</div>
<!-- -->
<whisper
:show="showWhisper"
:user="user"
@success="whisperSuccess"
/>
<whisper :show="showWhisper" :user="user" @success="whisperSuccess" />
</n-spin>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>
@ -56,7 +52,7 @@
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>
@ -151,7 +147,7 @@ watch(
username.value = route.query.username || '';
loadUser();
}
}
},
);
onMounted(() => {
loadUser();

@ -37,12 +37,12 @@
</div>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
<div v-if="totalPage > 0" class="pagination-wrap">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
@update:page="updatePage"
/>
</div>
</template>
@ -51,7 +51,7 @@
<post-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<div v-if="list.length === 0" class="empty-wrap">
<n-empty size="large" description="暂无数据" />
</div>
@ -85,18 +85,14 @@
aria-modal="true"
style="width: 100%; max-width: 330px"
>
<div class="amount-options" v-if="rechargeQrcode.length === 0">
<div v-if="rechargeQrcode.length === 0" class="amount-options">
<n-space align="baseline">
<n-button
v-for="amount in openAmounts"
:key="amount"
size="small"
secondary
:type="
selectedRechargeAmount === amount
? 'info'
: 'default'
"
:type="selectedRechargeAmount === amount ? 'info' : 'default'"
@click.stop="selectedRechargeAmount = amount"
>
{{ amount / 100 }}
@ -104,10 +100,7 @@
</n-space>
</div>
<div
v-if="
selectedRechargeAmount > 0 &&
rechargeQrcode.length === 0
"
v-if="selectedRechargeAmount > 0 && rechargeQrcode.length === 0"
style="margin-top: 10px"
>
<n-button
@ -124,7 +117,7 @@
</n-button>
</div>
<div class="qrcode-wrap" v-show="rechargeQrcode.length > 0">
<div v-show="rechargeQrcode.length > 0" class="qrcode-wrap">
<canvas id="qrcode-container"></canvas>
<div class="pay-tips">
使{{
@ -133,9 +126,7 @@
</div>
<div class="pay-sub-tips">
<n-badge :value="100" type="info" dot processing />
<span style="margin-left: 6px">
...
</span>
<span style="margin-left: 6px"> ... </span>
</div>
</div>
</n-card>
@ -222,14 +213,10 @@ const handleRecharge = (amount: any) => {
rechargeQrcode.value = res.pay;
// 生成二维码
QRCode.toCanvas(
document.querySelector('#qrcode-container'),
res.pay,
{
QRCode.toCanvas(document.querySelector('#qrcode-container'), res.pay, {
width: 150,
margin: 2,
}
);
});
const s = setInterval(() => {
getRecharge({

Loading…
Cancel
Save