Initial project
This commit is contained in:
90
src/components/LoginMenu.vue
Normal file
90
src/components/LoginMenu.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="login-menu">
|
||||
<t-dropdown
|
||||
v-if="userStore.loginUser.user"
|
||||
class="logged"
|
||||
trigger="click"
|
||||
>
|
||||
<t-button class="button" variant="text">
|
||||
<template #icon>
|
||||
<t-avatar
|
||||
class="icon"
|
||||
:class="(<any>ImageType)[userStore.loginUser.user.profile.avatarType]"
|
||||
:image="UserAPI.getAvatarURL(userStore.loginUser.user.profile)"
|
||||
size="small"
|
||||
shape="round"
|
||||
/>
|
||||
</template>
|
||||
<span v-text="userStore.loginUser.user.name"></span>
|
||||
<template #suffix>
|
||||
<icon name="ARROW_1_S" />
|
||||
</template>
|
||||
</t-button>
|
||||
<t-dropdown-menu>
|
||||
<t-dropdown-item @click="toDeveloperConfig">
|
||||
<template #prefixIcon>
|
||||
<icon name="TOOL" />
|
||||
</template>
|
||||
<span>开发配置</span>
|
||||
</t-dropdown-item>
|
||||
<t-dropdown-item @click="userStore.logout()">
|
||||
<template #prefixIcon>
|
||||
<icon />
|
||||
</template>
|
||||
<span>退出登录</span>
|
||||
</t-dropdown-item>
|
||||
</t-dropdown-menu>
|
||||
</t-dropdown>
|
||||
<t-button
|
||||
v-if="!userStore.isLogged()"
|
||||
variant="text"
|
||||
@click="visitableAuth = true"
|
||||
>
|
||||
<template #icon>
|
||||
<icon class="icon" name="USER" />
|
||||
</template>
|
||||
<span>登录</span>
|
||||
</t-button>
|
||||
</div>
|
||||
<t-dialog
|
||||
v-model:visible="visitableAuth"
|
||||
attach="body"
|
||||
:close-btn="false"
|
||||
:header="false"
|
||||
:footer="false"
|
||||
>
|
||||
<authorize-form
|
||||
v-if="visitableAuth"
|
||||
@login-success="visitableAuth = false"
|
||||
@register-success="visitableAuth = false"
|
||||
/>
|
||||
</t-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Icon, ImageType, SettingKey, SettingMapper, UserAPI, userStore } from "timi-web";
|
||||
import { AuthorizeForm } from "timi-tdesign-pc";
|
||||
|
||||
const visitableAuth = ref<boolean>(false);
|
||||
|
||||
const toDeveloperConfig = () => {
|
||||
const loginUserId = userStore.loginUser.token?.id;
|
||||
if (loginUserId) {
|
||||
window.open(`https://${SettingMapper.getValue(SettingKey.DOMAIN_SPACE)}/${loginUserId}/developer`, "_blank");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.login-menu {
|
||||
|
||||
.icon {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.logged {
|
||||
|
||||
&.button {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
124
src/components/PushLogTimeline.vue
Normal file
124
src/components/PushLogTimeline.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<t-timeline class="push-log-timeline" mode="alternate">
|
||||
<t-timeline-item
|
||||
class="action"
|
||||
v-for="(action, actionIndex) in items"
|
||||
:key="actionIndex"
|
||||
dot-color="primary"
|
||||
>
|
||||
<template #label>
|
||||
<div class="time" v-text="Time.toPassedDate(action.operatedAt)" v-popup="Time.toDateTime(action.operatedAt)"></div>
|
||||
</template>
|
||||
<t-card class="item">
|
||||
<div>
|
||||
<div class="user">
|
||||
<icon class="avatar" name="USER"/>
|
||||
<span v-text="action.operator.name"></span>
|
||||
</div>
|
||||
<div v-if="showRepo" class="repository">
|
||||
<span>仓库:</span>
|
||||
<router-link class="link selectable" :to="`/${action.repoName}/TODO_BRANCH`">
|
||||
{{ action.repoName }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div v-if="showBranch" class="branch">
|
||||
<span>分支:</span>
|
||||
<span class="selectable" v-text="action.refName"></span>
|
||||
</div>
|
||||
<template v-if="action.commitList">
|
||||
<h4 v-if="showRepo || showBranch">提交记录:</h4>
|
||||
<div class="commits">
|
||||
<div
|
||||
class="commit"
|
||||
v-for="(commit, commitIndex) in action.commitList"
|
||||
:key="commitIndex"
|
||||
>
|
||||
<t-button
|
||||
class="sha1"
|
||||
size="small"
|
||||
theme="default"
|
||||
:content="`#${commit.sha.substring(0, 8)}`"
|
||||
@click="MessagePlugin.warning('文件比对功能暂不可用')"
|
||||
></t-button>
|
||||
<div class="msg selectable" v-text="commit.message"></div>
|
||||
<div
|
||||
class="light-gray"
|
||||
v-text="Time.toPassedDate(commit.committedAt)"
|
||||
v-popup="Time.toDateTime(commit.committedAt)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else>初始化分支</div>
|
||||
</div>
|
||||
</t-card>
|
||||
</t-timeline-item>
|
||||
</t-timeline>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Icon, Time } from "timi-web";
|
||||
import { MessagePlugin } from "tdesign-vue-next";
|
||||
import { ActionLogView } from "@/types/Common.ts";
|
||||
|
||||
defineOptions({
|
||||
name: "PushLogTimeline"
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
items?: ActionLogView[];
|
||||
showRepo?: boolean;
|
||||
showBranch?: boolean;
|
||||
}>(), {
|
||||
items: () => [],
|
||||
showRepo: true,
|
||||
showBranch: true
|
||||
});
|
||||
const { items } = toRefs(props);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.push-log-timeline {
|
||||
|
||||
:deep(.action) {
|
||||
|
||||
.t-timeline-item__label {
|
||||
width: 90px;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
|
||||
.avatar {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.repository {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.commits {
|
||||
|
||||
.commit {
|
||||
display: flex;
|
||||
|
||||
&:nth-last-child(n + 2) {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.sha1 {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.msg {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
568
src/components/TimelineComment.vue
Normal file
568
src/components/TimelineComment.vue
Normal file
@ -0,0 +1,568 @@
|
||||
<template>
|
||||
<div class="comment-time-line">
|
||||
<t-timeline
|
||||
class="comments"
|
||||
layout="vertical"
|
||||
mode="alternate"
|
||||
>
|
||||
<t-timeline-item
|
||||
class="comment"
|
||||
:class="{'system': comment.systemComment}"
|
||||
v-for="(comment, index) in comments"
|
||||
:key="index"
|
||||
dot-color="primary"
|
||||
>
|
||||
<template #label>
|
||||
<div class="time">
|
||||
<t-popup
|
||||
:delay="0"
|
||||
:content="Time.toDateTime(comment.systemComment ? comment.systemComment.time : comment.createdAt)"
|
||||
placement="left"
|
||||
:show-arrow="true"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<span
|
||||
v-text="Time.toPassedDateTime(comment.systemComment ? comment.systemComment.time : comment.createdAt)"></span>
|
||||
</t-popup>
|
||||
</div>
|
||||
</template>
|
||||
<t-card class="shadow">
|
||||
<div
|
||||
v-if="comment.systemComment"
|
||||
class="system-comment-line"
|
||||
:class="comment.systemComment.colorClass"
|
||||
></div>
|
||||
<template #title>
|
||||
<div class="comment-user">
|
||||
<template v-if="comment.user">
|
||||
<t-avatar
|
||||
class="icon"
|
||||
:class="(<any>ImageType)[comment.user.profile.avatarType]"
|
||||
:image="UserAPI.getAvatarURL(comment.user.profile)"
|
||||
size="small"
|
||||
shape="round"
|
||||
/>
|
||||
<h4 class="name" v-text="comment.user.name"></h4>
|
||||
</template>
|
||||
<template v-else>
|
||||
<icon v-if="!comment.systemComment" class="icon" name="USER"/>
|
||||
<h4 v-if="comment.systemComment" class="name">系统消息</h4>
|
||||
<h4 v-else class="name" v-text="comment.nick"></h4>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="comment.systemComment" v-text="comment.systemComment.msg"></div>
|
||||
<markdown-view v-else :content="comment.content"/>
|
||||
<div v-if="!comment.systemComment" class="reply-btn">
|
||||
<t-button
|
||||
v-if="props.canCreate"
|
||||
size="small"
|
||||
variant="text"
|
||||
:content="`回复(${comment.repliesLength})`"
|
||||
@click="doCommentReply(comment)"
|
||||
></t-button>
|
||||
<span v-else v-text="`回复(${comment.repliesLength})`"></span>
|
||||
</div>
|
||||
<div class="reply-pane"
|
||||
v-if="!comment.systemComment && (replyTo === comment || 0 < comment.repliesLength)">
|
||||
<t-list
|
||||
class="replies"
|
||||
:split="true"
|
||||
v-if="comment.replies"
|
||||
>
|
||||
<t-list-item
|
||||
class="reply"
|
||||
v-for="reply in comment.replies"
|
||||
:key="reply.id"
|
||||
>
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
<div class="user">
|
||||
<template v-if="reply.sender">
|
||||
<t-avatar
|
||||
class="user-icon"
|
||||
:class="(<any>ImageType)[reply.sender.profile.avatarType]"
|
||||
:image="UserAPI.getAvatarURL(reply.sender.profile)"
|
||||
size="small"
|
||||
shape="round"
|
||||
/>
|
||||
<div class="sender" v-text="reply.sender.name"></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<icon name="USER"/>
|
||||
<div class="sender" v-text="reply.senderNick"></div>
|
||||
</template>
|
||||
<template
|
||||
v-if="reply.receiverId !== comment.userId || reply.receiverNick !== comment.nick">
|
||||
<div class="reply-to gray">回复</div>
|
||||
<div v-if="reply.receiver" v-text="reply.receiver.name"></div>
|
||||
<div v-else v-text="reply.receiverNick"></div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="time light-gray" v-text="Time.toPassedDateTime(reply.createdAt)"></div>
|
||||
</div>
|
||||
<markdown-view :content="reply.content"/>
|
||||
<div v-if="props.canCreate" class="reply-ctrl">
|
||||
<t-button
|
||||
class="button"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="doCommentReply(comment, reply)"
|
||||
>回复
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</t-list-item>
|
||||
</t-list>
|
||||
<t-pagination
|
||||
class="reply-pagination"
|
||||
v-if="6 < comment.repliesLength"
|
||||
:total="comment.repliesLength"
|
||||
v-model="comment.repliesCurrentPage"
|
||||
:pageSize="6"
|
||||
:showPageSize="false"
|
||||
:onCurrentChange="(current: number) => doFetchRepliesEvent(comment, current)"
|
||||
/>
|
||||
<t-form
|
||||
class="form"
|
||||
v-if="replyTo === comment"
|
||||
:data="replyRequest"
|
||||
@submit="doCommentReplySubmit(comment)"
|
||||
>
|
||||
<t-form-item label="昵称" name="nick">
|
||||
<t-input
|
||||
v-if="userStore.isLogged()"
|
||||
class="nick"
|
||||
:value="userStore.loginUser.user?.name"
|
||||
disabled
|
||||
/>
|
||||
<t-input v-else class="nick" v-model="replyRequest.senderNick" placeholder=""/>
|
||||
<div
|
||||
v-if="replyToName"
|
||||
class="reply-to gray"
|
||||
v-text="`回复 ${replyToName}`"
|
||||
></div>
|
||||
</t-form-item>
|
||||
<t-form-item label="内容" name="content">
|
||||
<markdown-editor
|
||||
:minRows="4"
|
||||
:maxRows="16"
|
||||
v-model:data="replyRequest.content"
|
||||
/>
|
||||
</t-form-item>
|
||||
<t-form-item label="验证码" name="captcha">
|
||||
<t-input-adornment>
|
||||
<t-input class="captcha" v-model="replyCaptcha" placeholder=""/>
|
||||
<template #append>
|
||||
<captcha :width="90" :height="28" :api="CommonAPI.getCaptchaAPI()"
|
||||
:from="CaptchaFrom.COMMENT_REPLY"/>
|
||||
</template>
|
||||
</t-input-adornment>
|
||||
</t-form-item>
|
||||
<t-form-item>
|
||||
<t-space>
|
||||
<t-button type="submit">提交</t-button>
|
||||
<t-button theme="default" variant="outline" @click="replyTo = undefined">取消
|
||||
</t-button>
|
||||
</t-space>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
</t-card>
|
||||
</t-timeline-item>
|
||||
</t-timeline>
|
||||
<div class="more">
|
||||
<p v-if="isFinished" class="gray" @click="doFetchEvent">没有更多评论</p>
|
||||
<t-button v-if="!isFetching && !isFinished" @click="doFetchEvent">加载更多</t-button>
|
||||
</div>
|
||||
<t-form v-if="props.canCreate" class="comment-form" :data="commentRequest" @submit="doCommentSubmit">
|
||||
<t-form-item label="昵称" name="name">
|
||||
<t-input
|
||||
v-if="userStore.isLogged()"
|
||||
class="nick"
|
||||
:value="userStore.loginUser?.user?.name"
|
||||
disabled
|
||||
/>
|
||||
<t-input v-else class="nick" v-model="commentRequest.nick" placeholder=""/>
|
||||
</t-form-item>
|
||||
<t-form-item label="内容" name="course">
|
||||
<markdown-editor :minRows="8" v-model:data="commentRequest.content"/>
|
||||
</t-form-item>
|
||||
<t-form-item label="验证码" name="captcha">
|
||||
<t-input-adornment>
|
||||
<t-input class="captcha" v-model="commentCaptcha" placeholder=""/>
|
||||
<template #append>
|
||||
<captcha :width="90" :height="28" :api="CommonAPI.getCaptchaAPI()"
|
||||
:from="CaptchaFrom.COMMENT"/>
|
||||
</template>
|
||||
</t-input-adornment>
|
||||
</t-form-item>
|
||||
<t-form-item>
|
||||
<t-button type="submit">提交</t-button>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MessagePlugin } from "tdesign-vue-next";
|
||||
import {
|
||||
Captcha,
|
||||
CaptchaFrom,
|
||||
Comment,
|
||||
CommentAPI,
|
||||
CommentBizType,
|
||||
CommentReply,
|
||||
CommentReplyBizType,
|
||||
CommentReplyView,
|
||||
CommentView,
|
||||
CommonAPI,
|
||||
Icon,
|
||||
ImageType,
|
||||
MarkdownEditor,
|
||||
MarkdownView,
|
||||
OrderType,
|
||||
Page,
|
||||
Time,
|
||||
UserAPI,
|
||||
userStore
|
||||
} from "timi-web";
|
||||
|
||||
export interface Props {
|
||||
bizType: CommentBizType,
|
||||
bizId: number;
|
||||
canCreate?: boolean;
|
||||
systemComments?: SystemComment[];
|
||||
}
|
||||
|
||||
export type SystemComment = {
|
||||
colorClass?: string;
|
||||
time: number;
|
||||
msg?: string;
|
||||
inserted: boolean;
|
||||
}
|
||||
|
||||
type CommentItem = {
|
||||
systemComment?: SystemComment;
|
||||
repliesCurrentPage: number;
|
||||
} & CommentView;
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
canCreate: true
|
||||
});
|
||||
|
||||
const comments = reactive<CommentItem[]>([]);
|
||||
const commentCaptcha = ref();
|
||||
|
||||
const commentRequest = reactive<Comment>({
|
||||
bizType: props.bizType,
|
||||
bizId: props.bizId,
|
||||
nick: "",
|
||||
content: ""
|
||||
});
|
||||
const replyCaptcha = ref();
|
||||
const replyRequest = reactive<CommentReply>({
|
||||
commentId: -1,
|
||||
replyId: undefined,
|
||||
senderNick: "",
|
||||
content: ""
|
||||
});
|
||||
const replyTo = ref<CommentItem>();
|
||||
const replyToName = ref();
|
||||
const isFinished = ref(false);
|
||||
const isFetching = ref(true);
|
||||
|
||||
const page = ref<Page>({
|
||||
index: 0,
|
||||
size: 12
|
||||
});
|
||||
|
||||
watch(() => commentRequest.nick, () => {
|
||||
if (!userStore.isLogged()) {
|
||||
replyRequest.senderNick = commentRequest.nick;
|
||||
}
|
||||
});
|
||||
watch(() => replyRequest.senderNick, () => {
|
||||
if (!userStore.isLogged()) {
|
||||
commentRequest.nick = replyRequest.senderNick;
|
||||
}
|
||||
});
|
||||
watch(replyTo, () => {
|
||||
if (!replyTo.value) {
|
||||
replyRequest.content = "";
|
||||
replyCaptcha.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
const insertSystemComment = (item: SystemComment): boolean => {
|
||||
let insertAt = null;
|
||||
{
|
||||
// 查找插入位置
|
||||
for (let i = comments.length - 1; -1 < i; i--) {
|
||||
const element = comments[i];
|
||||
if (element.createdAt && item.time < element.createdAt) {
|
||||
insertAt = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((insertAt === null && isFinished.value) || comments.length === 0) {
|
||||
insertAt = comments.length;
|
||||
}
|
||||
}
|
||||
if (insertAt !== null) {
|
||||
// 可插入
|
||||
item.colorClass = item.colorClass ?? "bg-blue";
|
||||
comments.splice(insertAt, 0, {
|
||||
systemComment: item
|
||||
} as CommentItem);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const doFetchEvent = async () => {
|
||||
isFetching.value = true;
|
||||
|
||||
const fetchResult = await CommentAPI.page({
|
||||
bizType: props.bizType,
|
||||
bizId: props.bizId,
|
||||
orderMap: {
|
||||
"createdAt": OrderType.ASC
|
||||
},
|
||||
...page.value
|
||||
});
|
||||
const list = fetchResult.list;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
comments.push({
|
||||
repliesCurrentPage: 1,
|
||||
...list[i]
|
||||
});
|
||||
}
|
||||
page.value.index++;
|
||||
if (fetchResult.list.length < page.value.size) {
|
||||
isFinished.value = true;
|
||||
}
|
||||
|
||||
isFetching.value = false;
|
||||
|
||||
if (props.systemComments) {
|
||||
for (let i = 0; i < props.systemComments.length; i++) {
|
||||
const item = props.systemComments[i];
|
||||
if (!item.inserted) {
|
||||
item.inserted = insertSystemComment(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const doFetchRepliesEvent = async (comment: CommentItem, toPage: number) => {
|
||||
if (comment.id) {
|
||||
const result = await CommentAPI.pageReply({
|
||||
bizType: CommentReplyBizType.COMMENT,
|
||||
bizId: comment.id,
|
||||
index: toPage - 1,
|
||||
size: 6
|
||||
});
|
||||
comment.replies = result.list;
|
||||
}
|
||||
};
|
||||
|
||||
const doCommentReply = async (comment: CommentItem, reply?: CommentReplyView) => {
|
||||
replyTo.value = comment;
|
||||
replyRequest.commentId = comment.id!;
|
||||
|
||||
if (reply) {
|
||||
replyRequest.replyId = reply.id;
|
||||
replyToName.value = reply.sender ? reply.sender.name : reply.senderNick;
|
||||
} else {
|
||||
replyRequest.replyId = undefined;
|
||||
replyToName.value = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const doCommentSubmit = () => {
|
||||
CommentAPI.create({
|
||||
from: CaptchaFrom.GIT_ISSUE,
|
||||
captcha: commentCaptcha.value,
|
||||
data: commentRequest
|
||||
}).then(() => {
|
||||
if (isFinished.value) {
|
||||
isFinished.value = false;
|
||||
page.value.index--;
|
||||
const size = comments.length % page.value.size;
|
||||
comments.splice(comments.length - size, size);
|
||||
doFetchEvent();
|
||||
}
|
||||
commentRequest.content = "";
|
||||
commentCaptcha.value = "";
|
||||
|
||||
MessagePlugin.success("提交成功");
|
||||
}).catch(msg => {
|
||||
MessagePlugin.error("提交失败:" + msg);
|
||||
});
|
||||
};
|
||||
const doCommentReplySubmit = async (comment: CommentItem) => {
|
||||
CommentAPI.createReply({
|
||||
from: CaptchaFrom.COMMENT_REPLY,
|
||||
captcha: replyCaptcha.value,
|
||||
data: replyRequest
|
||||
}).then(() => {
|
||||
if (0 < comment.repliesLength) {
|
||||
const size = comment.replies.length % 6;
|
||||
comment.replies.splice(comment.replies.length - size, size);
|
||||
doFetchRepliesEvent(comment, comment.repliesCurrentPage = Math.ceil(comment.repliesLength / 6));
|
||||
} else {
|
||||
doFetchRepliesEvent(comment, comment.repliesCurrentPage = 1);
|
||||
}
|
||||
comment.repliesLength++;
|
||||
|
||||
replyTo.value = undefined;
|
||||
MessagePlugin.success("提交成功");
|
||||
}).catch((msg: string) => {
|
||||
MessagePlugin.error("提交失败:" + msg);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(doFetchEvent);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.comment-time-line {
|
||||
padding: 1rem 2rem;
|
||||
border-top: var(--tui-border);
|
||||
|
||||
.comments {
|
||||
|
||||
.comment {
|
||||
|
||||
.system-comment-line {
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
width: 4px;
|
||||
height: calc(100% + 2px);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.time {
|
||||
width: 90px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.comment-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-btn {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.reply-pane {
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
align-items: end;
|
||||
flex-direction: column;
|
||||
|
||||
.replies {
|
||||
width: 100%;
|
||||
background: #F8F8F8;
|
||||
padding-left: 2rem;
|
||||
|
||||
.reply {
|
||||
padding: .5rem;
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
|
||||
&:hover .reply-ctrl .button {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
|
||||
.sender {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.reply-to {
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.time {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-ctrl {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
.button {
|
||||
visibility: hidden;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-pagination {
|
||||
margin: .5rem 0;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
margin-top: 1rem;
|
||||
|
||||
.nick {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.reply-to {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.captcha {
|
||||
width: 6rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.comment-form {
|
||||
margin-top: 1rem;
|
||||
|
||||
.nick {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.captcha {
|
||||
width: 6rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user