Initial project

This commit is contained in:
Timi
2025-07-08 16:28:40 +08:00
parent 03cf3638d7
commit fb1438c393
44 changed files with 13002 additions and 129 deletions

View File

@ -0,0 +1,17 @@
import { Comment } from "timi-web";
/**
* emits
*/
export interface Emits {
(event: "submit"): Promise<void>;
}
export const useHandler = (emit: Emits) => {
const onSubmit = async (comment: Comment) => await emit("submit");
return {
onSubmit,
};
};

View File

@ -0,0 +1,5 @@
import view from "./index.vue";
import { Toolkit } from "timi-web";
export const CommentForm = Toolkit.withInstall(view);
export default CommentForm;

View File

@ -0,0 +1,114 @@
<template>
<t-form class="tui-comment-form">
<t-form-item label="昵称" name="nick">
<t-input
class="nick"
v-model="formData.data.nick"
:disabled="userStore.isLogged()"
placeholder="昵称"
/>
</t-form-item>
<t-form-item label="评论" name="content">
<markdown-editor v-model:data="formData.data.content" />
</t-form-item>
<t-form-item label="验证码" name="captcha">
<t-input class="captcha" v-model="formData.captcha" placeholder="" />
<captcha
ref="captchaRef"
class="captcha-img"
:api="CommonAPI.getCaptchaAPI()"
:width="90"
:height="28"
:from="CaptchaFrom.COMMENT"
/>
<t-button
type="submit"
:loading="doSubmitting"
:disabled="doSubmitting"
@click="doSubmit"
>提交</t-button>
</t-form-item>
</t-form>
</template>
<script lang="ts" setup>
import {
Captcha,
CaptchaData,
CaptchaFrom,
Comment,
CommentAPI,
CommentBizType,
CommonAPI,
MarkdownEditor,
userStore
} from "timi-web";
import { Emits, useHandler } from "~/components/comment/form/emits";
defineOptions({
name: "CommentForm"
});
const emits = defineEmits<Emits>();
const { onSubmit } = useHandler(emits);
const props = withDefaults(defineProps<{
bizType: CommentBizType,
bizId: number,
}>(), {});
const { bizType, bizId } = toRefs(props);
const captchaRef = ref<{ update: () => void}>();
const formData = reactive<CaptchaData<Comment>>({
from: CaptchaFrom.COMMENT,
captcha: "",
data: {
bizType: bizType.value,
bizId: bizId.value,
nick: "",
content: ""
}
});
const doSubmitting = ref(false);
async function doSubmit() {
doSubmitting.value = true;
const loginUser = userStore.loginUser;
if (userStore.isLogged() && loginUser.user) {
formData.data.nick = loginUser.user.name;
formData.data.userId = loginUser.user.id;
}
await CommentAPI.create(formData);
formData.captcha = "";
formData.data.content = "";
captchaRef.value?.update();
doSubmitting.value = false;
await onSubmit(formData.data);
}
// 登录用户
const updateNick = () => formData.data.nick = userStore.loginUser?.user?.name || "";
watch(userStore.loginUser, updateNick);
onMounted(updateNick);
</script>
<style lang="less" scoped>
.tui-comment-form {
padding: 1rem;
.nick {
width: 12rem;
}
.captcha {
width: 6rem;
}
.captcha-img {
margin: 0 1rem;
}
}
</style>

View File

@ -0,0 +1,20 @@
import { CommentReply } from "timi-web";
/**
* emits
*/
export interface Emits {
(event: "cancel"): Promise<void>;
(event: "submit", reply: CommentReply): Promise<void>;
}
export const useHandler = (emit: Emits) => {
const onCancel = async () => await emit("cancel");
const onSubmit = async (reply: CommentReply) => await emit("submit", reply);
return {
onCancel,
onSubmit
};
};

View File

@ -0,0 +1,5 @@
import view from "./index.vue";
import { Toolkit } from "timi-web";
export const CommentReplyForm = Toolkit.withInstall(view);
export default CommentReplyForm;

View File

@ -0,0 +1,168 @@
<template>
<t-form class="tui-comment-reply-form">
<t-form-item label="昵称" name="nick">
<t-input
ref="nickRef"
class="nick"
v-model="formData.data.senderNick"
placeholder="昵称"
:disabled="userStore.isLogged()"
/>
<span v-if="replyToNick" class="gray selectable clip-text" v-text="`&nbsp;回复 ${replyToNick}`"></span>
</t-form-item>
<t-form-item label="评论" name="content">
<markdown-editor ref="contentRef" v-model:data="formData.data.content" />
</t-form-item>
<t-form-item label="验证码" name="captcha">
<div class="captcha-box">
<div>
<t-input class="captcha" v-model="formData.captcha" placeholder="" />
<captcha
ref="captchaRef"
class="captcha-img"
:api="CommonAPI.getCaptchaAPI()"
:width="90"
:height="28"
:from="CaptchaFrom.COMMENT_REPLY"
/>
</div>
<div>
<t-button
class="submit"
type="submit"
:loading="doSubmitting"
:disabled="doSubmitting"
@click="doSubmit"
>提交</t-button>
<t-button theme="default" @click="onCancel">取消</t-button>
</div>
</div>
</t-form-item>
</t-form>
</template>
<script lang="ts" setup>
import {
Captcha,
CaptchaData,
CaptchaFrom,
CommentAPI,
CommentReply,
CommonAPI,
MarkdownEditor,
Scroller,
userStore
} from "timi-web";
import { Emits, useHandler } from "~/components/comment/form/reply/emits";
defineOptions({
name: "CommentReplyForm"
});
const emits = defineEmits<Emits>();
const { onCancel, onSubmit } = useHandler(emits);
const props = withDefaults(defineProps<{
commentId: number,
replyId?: number,
replyToNick?: string,
}>(), {});
const { commentId, replyId, replyToNick } = toRefs(props);
const doSubmitting = ref(false);
const nickRef = ref();
const contentRef = ref();
const formData = reactive<CaptchaData<CommentReply>>({
from: CaptchaFrom.COMMENT_REPLY,
captcha: "",
data: {
commentId: commentId.value,
replyId: replyId.value,
senderNick: "",
content: ""
}
});
/** 提交回复 */
async function doSubmit() {
doSubmitting.value = true;
formData.data.commentId = commentId.value;
formData.data.replyId = replyId.value;
const loginUser = userStore.loginUser;
if (userStore.isLogged() && loginUser.user) {
formData.data.senderNick = loginUser.user.name;
formData.data.senderId = loginUser.user.id;
}
await CommentAPI.createReply(formData);
formData.captcha = "";
await onSubmit(formData.data);
doSubmitting.value = false;
}
async function focus() {
await nextTick();
let focusEl: HTMLElement;
if (userStore.isLogged()) {
focusEl = nickRef.value[0].textArea;
} else {
focusEl = contentRef.value[0].inputRef;
}
focusEl.focus();
Scroller.toElement(focusEl);
}
// 登录用户
const updateNick = () => formData.data.senderNick = userStore.loginUser?.user?.name || "";
watch(userStore.loginUser, updateNick);
onMounted(updateNick);
defineExpose({
focus
});
</script>
<style lang="less" scoped>
.tui-comment-reply-form {
padding: 1rem 1rem 1rem 0;
.nick {
width: 12rem;
}
.captcha-box {
display: flex;
flex-wrap: wrap;
div {
display: flex;
}
.captcha {
width: 6rem;
}
.captcha-img {
margin: 0 1rem;
}
.submit {
margin-right: 1rem;
}
}
}
@media screen and (max-width: 650px) {
.tui-comment-reply-form {
.captcha-box {
> div:first-child {
margin-bottom: 1rem;
}
}
}
}
</style>