Skip to content

Commit

Permalink
feat: 评论功能基本开发完成
Browse files Browse the repository at this point in the history
  • Loading branch information
wsvaio committed Dec 8, 2023
1 parent e364e83 commit f3c9f7d
Show file tree
Hide file tree
Showing 18 changed files with 1,224 additions and 132 deletions.
2 changes: 1 addition & 1 deletion .stylelintcache
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"C:\\Users\\admin\\Desktop\\mynewblog\\app.vue":"1","C:\\Users\\admin\\Desktop\\mynewblog\\components\\about-card\\index.vue":"2","C:\\Users\\admin\\Desktop\\mynewblog\\components\\catalog-card\\index.vue":"3","C:\\Users\\admin\\Desktop\\mynewblog\\components\\banner\\index.vue":"4","C:\\Users\\admin\\Desktop\\mynewblog\\components\\background\\index.vue":"5","C:\\Users\\admin\\Desktop\\mynewblog\\components\\article-card\\index.vue":"6","C:\\Users\\admin\\Desktop\\mynewblog\\components\\layout-header\\index.vue":"7","C:\\Users\\admin\\Desktop\\mynewblog\\components\\catalog-card\\deep-ul.vue":"8","C:\\Users\\admin\\Desktop\\mynewblog\\components\\music\\index.vue":"9","C:\\Users\\admin\\Desktop\\mynewblog\\components\\isbuilding\\index.vue":"10","C:\\Users\\admin\\Desktop\\mynewblog\\components\\markdown-editor\\index.vue":"11","C:\\Users\\admin\\Desktop\\mynewblog\\components\\music-icon-lines\\index.vue":"12","C:\\Users\\admin\\Desktop\\mynewblog\\components\\typewriter\\index.vue":"13","C:\\Users\\admin\\Desktop\\mynewblog\\components\\layout-footer\\index.vue":"14","C:\\Users\\admin\\Desktop\\mynewblog\\components\\markdown-preview\\index.vue":"15","C:\\Users\\admin\\Desktop\\mynewblog\\components\\tiangou-card\\index.vue":"16","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\index.vue":"17","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\about\\index.vue":"18","C:\\Users\\admin\\Desktop\\mynewblog\\components\\sclsday-card\\index.vue":"19","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\article\\[id].vue":"20","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\guestbook\\index.vue":"21","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\index\\index.vue":"22","C:\\Users\\admin\\Desktop\\mynewblog\\components\\theme-switch\\index.vue":"23","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\article\\index.vue":"24","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\tag\\index.vue":"25","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\tag\\[id].vue":"26","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\type\\index.vue":"27","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\type\\[id].vue":"28","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\header\\index.vue":"29","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\main\\index.vue":"30","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\footer\\index.vue":"31","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\banner\\index.vue":"32","C:\\Users\\admin\\Desktop\\mynewblog\\components\\comments\\index.vue":"33","C:\\Users\\admin\\Desktop\\mynewblog\\assets\\css\\main.less":"34","C:\\Users\\admin\\Desktop\\mynewblog\\components\\vtextarea\\index.vue":"35","C:\\Users\\admin\\Desktop\\mynewblog\\components\\vinput\\index.vue":"36","C:\\Users\\admin\\Desktop\\mynewblog\\components\\comments\\comment.vue":"37","C:\\Users\\admin\\Desktop\\mynewblog\\components\\comments\\comment-on.vue":"38"},{"size":298,"mtime":1701653713231,"hashOfConfig":"39"},{"size":1809,"mtime":1701653713234,"hashOfConfig":"39"},{"size":2506,"mtime":1701660369014,"hashOfConfig":"39"},{"size":927,"mtime":1701048764345,"hashOfConfig":"39"},{"size":338,"mtime":1701153527723,"hashOfConfig":"39"},{"size":3767,"mtime":1701048764345,"hashOfConfig":"39"},{"size":2855,"mtime":1701500458066,"hashOfConfig":"39"},{"size":391,"mtime":1701485422769,"hashOfConfig":"39"},{"size":862,"mtime":1701653713240,"hashOfConfig":"39"},{"size":559,"mtime":1701219435874,"hashOfConfig":"39"},{"size":246,"mtime":1700702915225,"hashOfConfig":"39"},{"size":3294,"mtime":1701134724740,"hashOfConfig":"39"},{"size":1728,"mtime":1701073967910,"hashOfConfig":"39"},{"size":5009,"mtime":1701219435881,"hashOfConfig":"39"},{"size":384,"mtime":1701654939994,"hashOfConfig":"39"},{"size":457,"mtime":1701072636287,"hashOfConfig":"39"},{"size":445,"mtime":1701654440043,"hashOfConfig":"39"},{"size":178,"mtime":1701048764346,"hashOfConfig":"39"},{"size":380,"mtime":1701059387094,"hashOfConfig":"39"},{"size":1193,"mtime":1701940333636,"hashOfConfig":"39"},{"size":191,"mtime":1700615139995,"hashOfConfig":"39"},{"size":1607,"mtime":1701654669013,"hashOfConfig":"39"},{"size":885,"mtime":1700636723925,"hashOfConfig":"39"},{"size":552,"mtime":1701654046674,"hashOfConfig":"39"},{"size":547,"mtime":1701654027632,"hashOfConfig":"39"},{"size":252,"mtime":1701048764348,"hashOfConfig":"39"},{"size":542,"mtime":1701653713260,"hashOfConfig":"39"},{"size":502,"mtime":1701653713259,"hashOfConfig":"39"},{"size":2069,"mtime":1701134724754,"hashOfConfig":"39"},{"size":1343,"mtime":1701654444474,"hashOfConfig":"39"},{"size":5009,"mtime":1701134724750,"hashOfConfig":"39"},{"size":1383,"mtime":1701653713243,"hashOfConfig":"39"},{"size":901,"mtime":1701934423347,"hashOfConfig":"39"},{"size":1072,"mtime":1701769833058,"hashOfConfig":"39"},{"size":1508,"mtime":1701769833060,"hashOfConfig":"39"},{"size":947,"mtime":1701769833060,"hashOfConfig":"39"},{"size":1677,"mtime":1701942672202,"hashOfConfig":"39"},{"size":2592,"mtime":1701937845227,"hashOfConfig":"39"},"1r8dhsw"]
[{"C:\\Users\\admin\\Desktop\\mynewblog\\app.vue":"1","C:\\Users\\admin\\Desktop\\mynewblog\\components\\about-card\\index.vue":"2","C:\\Users\\admin\\Desktop\\mynewblog\\components\\catalog-card\\index.vue":"3","C:\\Users\\admin\\Desktop\\mynewblog\\components\\banner\\index.vue":"4","C:\\Users\\admin\\Desktop\\mynewblog\\components\\background\\index.vue":"5","C:\\Users\\admin\\Desktop\\mynewblog\\components\\article-card\\index.vue":"6","C:\\Users\\admin\\Desktop\\mynewblog\\components\\layout-header\\index.vue":"7","C:\\Users\\admin\\Desktop\\mynewblog\\components\\catalog-card\\deep-ul.vue":"8","C:\\Users\\admin\\Desktop\\mynewblog\\components\\music\\index.vue":"9","C:\\Users\\admin\\Desktop\\mynewblog\\components\\isbuilding\\index.vue":"10","C:\\Users\\admin\\Desktop\\mynewblog\\components\\markdown-editor\\index.vue":"11","C:\\Users\\admin\\Desktop\\mynewblog\\components\\music-icon-lines\\index.vue":"12","C:\\Users\\admin\\Desktop\\mynewblog\\components\\typewriter\\index.vue":"13","C:\\Users\\admin\\Desktop\\mynewblog\\components\\layout-footer\\index.vue":"14","C:\\Users\\admin\\Desktop\\mynewblog\\components\\markdown-preview\\index.vue":"15","C:\\Users\\admin\\Desktop\\mynewblog\\components\\tiangou-card\\index.vue":"16","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\index.vue":"17","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\about\\index.vue":"18","C:\\Users\\admin\\Desktop\\mynewblog\\components\\sclsday-card\\index.vue":"19","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\article\\[id].vue":"20","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\guestbook\\index.vue":"21","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\index\\index.vue":"22","C:\\Users\\admin\\Desktop\\mynewblog\\components\\theme-switch\\index.vue":"23","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\article\\index.vue":"24","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\tag\\index.vue":"25","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\tag\\[id].vue":"26","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\type\\index.vue":"27","C:\\Users\\admin\\Desktop\\mynewblog\\pages\\type\\[id].vue":"28","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\header\\index.vue":"29","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\main\\index.vue":"30","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\footer\\index.vue":"31","C:\\Users\\admin\\Desktop\\mynewblog\\layouts\\default\\views\\banner\\index.vue":"32","C:\\Users\\admin\\Desktop\\mynewblog\\components\\comments\\index.vue":"33","C:\\Users\\admin\\Desktop\\mynewblog\\assets\\css\\main.less":"34","C:\\Users\\admin\\Desktop\\mynewblog\\components\\vtextarea\\index.vue":"35","C:\\Users\\admin\\Desktop\\mynewblog\\components\\vinput\\index.vue":"36","C:\\Users\\admin\\Desktop\\mynewblog\\components\\comments\\comment.vue":"37","C:\\Users\\admin\\Desktop\\mynewblog\\components\\comments\\comment-on.vue":"38","C:\\Users\\admin\\Desktop\\mynewblog\\components\\vbutton\\index.vue":"39","C:\\Users\\admin\\Desktop\\mynewblog\\components\\awesome-button\\index.vue":"40","C:\\Users\\admin\\Desktop\\mynewblog\\components\\avatar-input\\index.vue":"41","C:\\Users\\admin\\Desktop\\mynewblog\\components\\popup\\index.vue":"42"},{"size":298,"mtime":1701653713231,"hashOfConfig":"43"},{"size":1809,"mtime":1701653713234,"hashOfConfig":"43"},{"size":2506,"mtime":1701660369014,"hashOfConfig":"43"},{"size":927,"mtime":1701048764345,"hashOfConfig":"43"},{"size":338,"mtime":1701153527723,"hashOfConfig":"43"},{"size":3767,"mtime":1701048764345,"hashOfConfig":"43"},{"size":2855,"mtime":1701500458066,"hashOfConfig":"43"},{"size":391,"mtime":1701485422769,"hashOfConfig":"43"},{"size":862,"mtime":1701653713240,"hashOfConfig":"43"},{"size":559,"mtime":1701219435874,"hashOfConfig":"43"},{"size":246,"mtime":1700702915225,"hashOfConfig":"43"},{"size":3294,"mtime":1701134724740,"hashOfConfig":"43"},{"size":1728,"mtime":1701073967910,"hashOfConfig":"43"},{"size":5009,"mtime":1701219435881,"hashOfConfig":"43"},{"size":384,"mtime":1701654939994,"hashOfConfig":"43"},{"size":457,"mtime":1701072636287,"hashOfConfig":"43"},{"size":445,"mtime":1701654440043,"hashOfConfig":"43"},{"size":178,"mtime":1701048764346,"hashOfConfig":"43"},{"size":380,"mtime":1701059387094,"hashOfConfig":"43"},{"size":1194,"mtime":1702018825878,"hashOfConfig":"43"},{"size":191,"mtime":1700615139995,"hashOfConfig":"43"},{"size":1607,"mtime":1701654669013,"hashOfConfig":"43"},{"size":885,"mtime":1700636723925,"hashOfConfig":"43"},{"size":552,"mtime":1701654046674,"hashOfConfig":"43"},{"size":547,"mtime":1701654027632,"hashOfConfig":"43"},{"size":252,"mtime":1701048764348,"hashOfConfig":"43"},{"size":542,"mtime":1701653713260,"hashOfConfig":"43"},{"size":502,"mtime":1701653713259,"hashOfConfig":"43"},{"size":2069,"mtime":1701134724754,"hashOfConfig":"43"},{"size":1343,"mtime":1701654444474,"hashOfConfig":"43"},{"size":5009,"mtime":1701134724750,"hashOfConfig":"43"},{"size":1383,"mtime":1701653713243,"hashOfConfig":"43"},{"size":901,"mtime":1701942810803,"hashOfConfig":"43"},{"size":1072,"mtime":1701769833058,"hashOfConfig":"43"},{"size":2043,"mtime":1702024845093,"hashOfConfig":"43"},{"size":1651,"mtime":1702016572883,"hashOfConfig":"43"},{"size":2004,"mtime":1702022139370,"hashOfConfig":"43"},{"size":2425,"mtime":1702023853405,"hashOfConfig":"43"},{"size":587,"mtime":1702002221674,"hashOfConfig":"43"},{"size":2111,"mtime":1702020108890,"hashOfConfig":"43"},{"size":1725,"mtime":1702018366624,"hashOfConfig":"43"},{"size":624,"mtime":1702023952988,"hashOfConfig":"43"},"1r8dhsw"]
Binary file added assets/img/hmbb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img/textarea.webp
Binary file not shown.
Binary file added assets/img/textarea1.webp
Binary file not shown.
78 changes: 78 additions & 0 deletions components/avatar-input/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script setup lang="ts">
import { saveAs } from "@wsvaio/utils";
import IAvatar from "@/assets/img/avatar.png";
const user = useUserStore();
let modelValue = $(defineModel<string>());
const { data: avatar, execute: executeAvatar } = $(
useLazyFetch<{ content: string }>("/api/common/pp", {
onResponse(data) {
if (data.response.status == 200) modelValue = avatar?.content || "";
},
immediate: false,
})
);
</script>

<template>
<div class="avatar-input">
<img
:src="modelValue"
h="96px"
w="96px"
grid="row-span-full"
alt="头像"
@error="(modelValue = IAvatar), executeAvatar()"
/>
<div class="control">
<button class="i-material-symbols-light-download" title="下载" @click.prevent="modelValue && saveAs(modelValue)" />
<button class="i-material-symbols-light-sync" title="刷新" @click.prevent="executeAvatar()" />
<button class="i-material-symbols-light-reset-image" title="重置" @click.prevent="user.refreshAvatar()" />
</div>
</div>
</template>

<style lang="less" scoped>
.avatar-input {
position: relative;
overflow: hidden;
transition: all 0.3s;
border: 1px solid var(--border-color7);
& > .control {
display: flex;
position: absolute;
z-index: 1;
top: 0;
// inset: 0;
right: 0;
align-items: center;
justify-content: center;
transition: all 0.3s;
opacity: 0;
background-color: var(--bg-color);
pointer-events: none;
& > button {
transition: all .3s;
font-size: 20px;
cursor: pointer;
&:hover {
color: var(--primary-color)
}
}
}
&:hover {
border-color: var(--primary-color);
& > .control {
opacity: 1;
pointer-events: all;
}
}
}
</style>
80 changes: 80 additions & 0 deletions components/awesome-button/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script setup lang="ts">
const buttonRef = $ref<HTMLButtonElement>();
const buttonRefStyle = reactive<Record<any, any>>({});
useEventListener($$(buttonRef), "mousemove", e => {
const boundingClientRect = buttonRef?.getBoundingClientRect();
if (!boundingClientRect || !buttonRef) return;
const x = e.clientX - boundingClientRect.left;
const y = e.clientY - boundingClientRect.top;
const xc = boundingClientRect.width / 2;
const yc = boundingClientRect.height / 2;
const dx = x - xc;
const dy = y - yc;
buttonRefStyle["--rx"] = `${dy / -1}deg`;
buttonRefStyle["--ry"] = `${dx / 10}deg`;
});
useEventListener($$(buttonRef), "mouseleave", () => {
buttonRefStyle["--ty"] = "0";
buttonRefStyle["--rx"] = "0";
buttonRefStyle["--ry"] = "0";
});
useEventListener($$(buttonRef), "mousedown", () => {
buttonRefStyle["--tz"] = "-25px";
});
useEventListener("mouseup", () => {
buttonRefStyle["--tz"] = "-12px";
});
</script>

<template>
<button ref="buttonRef" class="awesome-button" :style="buttonRefStyle">
<div class="content"><slot /></div>
</button>
</template>

<style lang="less">
.awesome-button {
display: inline-block;
position: relative;
padding: .5em 1em;
border: none;
outline: none;
background-color: transparent;
color: white;
text-align: center;
text-decoration: none;
cursor: pointer;
user-select: none;
&::before {
content: "";
position: absolute;
inset: 0;
transform: translateY(var(--ty, 0)) rotateX(var(--rx, 0)) rotateY(var(--ry, 0)) translateZ(var(--tz, -12px));
transition: box-shadow 0.5s ease, transform 0.2s ease;
border-radius: 4px;
background: linear-gradient(135deg, #6e8efb, #a777e3);
box-shadow: 0 2px 5px rgb(0 0 0 / 20%);
will-change: transform;
}
&:hover::before {
box-shadow: 0 5px 15px rgb(0 0 0 / 30%);
}
& > .content {
display: inline-block;
position: relative;
transform: translateY(var(--ty, 0)) rotateX(var(--rx, 0)) rotateY(var(--ry, 0));
transition: transform 0.2s ease;
font-weight: bold;
letter-spacing: 0.01em;
will-change: transform;
}
}
</style>
121 changes: 54 additions & 67 deletions components/comments/comment-on.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,94 +9,81 @@ const emit = defineEmits<{
}>();
const user = useUserStore();
let content = $ref("");
const load = reactive({
name: "",
email: "",
content: "",
avatar: "",
site: "",
});
const { execute, error } = useLazyAsyncData(
() => {
if (commentId) {
return $fetch(`/api/comment/${commentId}/comment`, {
const { execute, error, pending, data } = useAsyncData(
async () => {
let result = commentId
? await $fetch(`/api/comment/${commentId}/comment`, {
method: "POST",
body: {
...load,
},
onResponse(data) {
if (data.response.status === 200) emit("submit");
...user,
content,
},
});
}
else {
return $fetch(`/api/article/${articleId}/comment`, {
})
: await $fetch(`/api/article/${articleId}/comment`, {
method: "POST",
body: {
...load,
},
onResponse(data) {
if (data.response.status === 200) {
emit("submit");
Object.assign(load, {
content: "",
});
}
...user,
content,
},
});
}
emit("submit");
content = "";
return result;
},
{
immediate: false,
}
);
const { data: avatar, execute: executeAvatar } = $(
useLazyFetch<{ content: string }>("/api/common/pp", {
onResponse(data) {
if (data.response.status == 200) load.avatar = avatar?.content || "";
},
})
);
const handleEmailInput = useDebounceFn(async () => {
if (!load.email) return;
const user = await $fetch(`/api/user/email/${load.email}`);
if (user) Object.assign(load, user);
}, 1000);
const handleEmailInput = useDebounceFn(user.refresh, 200);
</script>

<template>
<form class="comment-on" @submit.prevent="execute()">
<vtextarea v-model="load.content" placeholder="见到你很高兴!" :input="{ required: 'true' }" />
<div flex="~" items="center" font-italic my=".5em">
<nuxt-link to="https://www.baidu.com/s?wd=markdown" un-text="inherit" mr=".5em">
<div text="14px" class="i-cib-markdown" />
</nuxt-link>
<span>Markdown Supported while</span>
<div mx=".5em" class="i-bi-code-slash" text="14px" />
<span>Forbidden</span>
</div>

<vtextarea v-model="content" placeholder="见到你很高兴!" :textarea="{ required: 'true' }" />
<!-- <markdown-editor v-model="content" /> -->

<div grid="~ cols-[max-content_1fr_1fr_1fr]" gap="1em" mt="1em">
<img
:src="load?.avatar" w="64px" h="64px" border="1px solid [var(--border-color7)]"
@click="executeAvatar()"
<div grid="~ cols-[max-content_1fr_1fr_max-content] rows-2" gap="1em" mt="1em">
<avatar-input
v-model="user.avatar" h="96px" w="96px" grid="row-span-full"
:img="{ alt: '头像' }"
/>
<vinput
v-model="user.email"
placeholder="邮箱(可获取上次登录信息)"
:input="{ required: 'true', type: 'email' }"
grid="col-span-3"
@input="handleEmailInput"
/>
<vinput v-model="load.name" placeholder="昵称" :input="{ required: 'true' }" />
<vinput v-model="load.email" placeholder="邮箱(可自动获取信息)" :input="{ required: 'true' }" @input="handleEmailInput" />
<vinput v-model="load.site" placeholder="http(s)://主页" />
</div>

<button
w="full"
py="1em"
mt="1em"
grid="col-span-full"
bg="transparent"
transition="all"
border="1px solid [var(--border-color7)] hover:[var(--primary-color)]"
text="[var(--text-color)] hover:[var(--primary-color)]"
font-size="inherit"
cursor="pointer"
formaction="true"
>
发送
</button>
<vinput v-model="user.name" placeholder="昵称" :input="{ required: 'true' }" />

<vinput v-model="user.site" placeholder="http(s)://主页" />

<awesome-button
flex="~" justify="center" items="center" :disabled="pending && data"
w="5em"
>
<template v-if="pending && data">
<div class="i-eos-icons-loading" />
发送中
</template>
<template v-else>发送</template>
</awesome-button>
</div>

<!-- <p text="[var(--error-color)]">{{ (error as any)?.data?.message || error?.message }}</p> -->
<typewriter
Expand Down
Loading

0 comments on commit f3c9f7d

Please sign in to comment.