|
@@ -0,0 +1,345 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <el-card shadow="never" :class="total > 0 && 'reply-card'">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ <el-button type="primary" link @click="showInput = !showInput">留言
|
|
|
|
|
+ <template #icon><el-icon size="16"><tjm-icon-mdi-comment-text-outline /></el-icon></template>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-show="showInput" class="reply-form">
|
|
|
|
|
+ <el-input v-model="messageContent" type="textarea" autosize clearable show-word-limit maxlength="140" placeholder="发布你的留言"></el-input>
|
|
|
|
|
+ <el-button :loading="isSaving" type="primary" :disabled="!messageContent" @click="submit">发布</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form v-loading="loading">
|
|
|
|
|
+ <template v-for="(item, index) in replyData" :key="item.id">
|
|
|
|
|
+ <div class="reply-item">
|
|
|
|
|
+ <el-form-item :label="`${item.createName}:`">{{ item.messageContent }}</el-form-item>
|
|
|
|
|
+ <el-form-item class="reply-date-item" :label="formatDate(item.createTime)">
|
|
|
|
|
+ <div class="handle-button-group">
|
|
|
|
|
+ <el-tooltip content="删除" placement="top">
|
|
|
|
|
+ <el-button v-if="item.createId == loginUser" icon="delete" circle @click="reply_del(item)"></el-button>
|
|
|
|
|
+ </el-tooltip>
|
|
|
|
|
+ <el-tooltip content="留言" placement="top">
|
|
|
|
|
+ <el-button circle @click="reply_add(item)">
|
|
|
|
|
+ <template #icon><tjm-icon-mdi-comment-text-outline /></template>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-tooltip>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="reply-item__children">
|
|
|
|
|
+ <div class="reply-item" v-for="child in replyChildren[index]" :key="child.id">
|
|
|
|
|
+ <el-form-item>
|
|
|
|
|
+ <template #label>{{ child.createName }}:
|
|
|
|
|
+ <template v-if="child.replyId"><span>回复@</span>{{ child.replyName }}:</template>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ {{ child.messageContent }}
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item class="reply-date-item" :label="formatDate(child.createTime)">
|
|
|
|
|
+ <div class="handle-button-group">
|
|
|
|
|
+ <el-tooltip content="删除" placement="top">
|
|
|
|
|
+ <el-button v-if="child.createId == loginUser" icon="delete" circle @click="reply_del(child)"></el-button>
|
|
|
|
|
+ </el-tooltip>
|
|
|
|
|
+ <el-tooltip content="留言" placement="top">
|
|
|
|
|
+ <el-button circle @click="reply_add(child)">
|
|
|
|
|
+ <template #icon><tjm-icon-mdi-comment-text-outline /></template>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-tooltip>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-button v-if="item.childrenCount > replyChildrenCount" class="text-reverse" type="primary" link>共{{ item.childrenCount }}条回复
|
|
|
|
|
+ <template #icon><el-icon size="22"><tjm-icon-mdi-menu-down /></el-icon></template>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-if="total > replyCount">
|
|
|
|
|
+ <el-divider></el-divider>
|
|
|
|
|
+ <el-button class="text-reverse" type="primary" link>查看全部{{ allTotal }}条留言
|
|
|
|
|
+ <template #icon><arrow-right /></template>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template v-else>
|
|
|
|
|
+ <el-divider v-if="total">已加载全部留言</el-divider>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <reply-detail v-if="dialog.detail" ref="replyDetail" :refId="refId" :refType="refType" @success="reloadChildrenTree" @closed="dialog.detail = false"></reply-detail>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+import moment from "moment";
|
|
|
|
|
+import API from "@/api/policy/message";
|
|
|
|
|
+import replyDetail from "./dialog.vue";
|
|
|
|
|
+import { useUserStore } from "@/store/user";
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ components: {
|
|
|
|
|
+ replyDetail
|
|
|
|
|
+ },
|
|
|
|
|
+ props: {
|
|
|
|
|
+ refId: { type: String, default: "" },
|
|
|
|
|
+ refType: { type: String, default: "policy_share" },
|
|
|
|
|
+ replyCount: { type: Number, default: 2 },
|
|
|
|
|
+ replyChildrenCount: { type: Number, default: 2 }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ loginUser: useUserStore().userInfo.id,
|
|
|
|
|
+ params: {
|
|
|
|
|
+ page: 1,
|
|
|
|
|
+ size: this.replyCount,
|
|
|
|
|
+ parentId: 0,
|
|
|
|
|
+ refId: this.refId,
|
|
|
|
|
+ refType: this.refType
|
|
|
|
|
+ },
|
|
|
|
|
+ total: 0,
|
|
|
|
|
+ replyData: [],
|
|
|
|
|
+ replyChildren: [],
|
|
|
|
|
+
|
|
|
|
|
+ showInput: false,
|
|
|
|
|
+ isSaving: false,
|
|
|
|
|
+ messageContent: null,
|
|
|
|
|
+
|
|
|
|
|
+ dialog: {
|
|
|
|
|
+ detail: false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ computed: {
|
|
|
|
|
+ allTotal() {
|
|
|
|
|
+ return this.total + (this.replyChildren.length && this.replyChildren.map(c => c.length).reduce((p, v) => p + v) || 0);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ mounted() {
|
|
|
|
|
+ this.reloadTree();
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ formatDate(value) {
|
|
|
|
|
+ return value && moment(value).format("YY-MM-DD HH:mm") || "";
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ reloadTree() {
|
|
|
|
|
+ this.loading = true;
|
|
|
|
|
+ API.get(this.params).then(res => {
|
|
|
|
|
+ this.loading = false;
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ this.replyData = res.data.records;
|
|
|
|
|
+ this.total = res.data.total;
|
|
|
|
|
+ this.reloadChildrenTree();
|
|
|
|
|
+ } else ElMessage.error(res.msg);
|
|
|
|
|
+ }).catch(() => this.loading = false);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ reloadChildrenTree(parentId) {
|
|
|
|
|
+ let promiseArray = [];
|
|
|
|
|
+ if (parentId) {
|
|
|
|
|
+ const reply_i = this.replyData.findIndex(c => c.id == parentId);
|
|
|
|
|
+ this.replyData[reply_i].childrenCount++;
|
|
|
|
|
+
|
|
|
|
|
+ promiseArray = this.replyData.map(_ => []);
|
|
|
|
|
+ promiseArray[reply_i] = API.get({ ...this.params, size: this.replyChildrenCount, parentId });
|
|
|
|
|
+ } else this.replyData.forEach(item => promiseArray.push(API.get({ ...this.params, size: this.replyChildrenCount, parentId: item.id })));
|
|
|
|
|
+
|
|
|
|
|
+ Promise.all(promiseArray).then(res => {
|
|
|
|
|
+ this.replyChildren = res.map(r => r.code == 200 && r.data.records || []);
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ this.replyChildren = [];
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ submit() {
|
|
|
|
|
+ if (this.refId && this.refType) {
|
|
|
|
|
+ const data = {
|
|
|
|
|
+ parentId: 0,
|
|
|
|
|
+ refId: this.refId,
|
|
|
|
|
+ refType: this.refType,
|
|
|
|
|
+ messageContent: this.messageContent
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.isSaving = true;
|
|
|
|
|
+ API.add(data).then(res => {
|
|
|
|
|
+ this.isSaving = false;
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ ElMessage.success("留言成功");
|
|
|
|
|
+ this.messageContent = null;
|
|
|
|
|
+ this.reloadTree();
|
|
|
|
|
+ } else ElMessage.error(res.msg);
|
|
|
|
|
+ }).catch(() => this.isSaving = false);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ reply_add(data) {
|
|
|
|
|
+ this.dialog.detail = true;
|
|
|
|
|
+ nextTick(() => this.$refs.replyDetail.open().setData(data));
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ reply_del(data) {
|
|
|
|
|
+ ElMessageBox.confirm("是否确认删除?", "删除警告", {
|
|
|
|
|
+ type: "warning",
|
|
|
|
|
+ confirmButtonText: "确定",
|
|
|
|
|
+ cancelButtonText: "取消"
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ API.del({ ids: data.id }).then(res => {
|
|
|
|
|
+ if (res.code == 200) {
|
|
|
|
|
+ ElMessage.success("操作成功");
|
|
|
|
|
+ this.reloadTree();
|
|
|
|
|
+ } else ElMessage.error(res.msg);
|
|
|
|
|
+ });
|
|
|
|
|
+ }).catch(() => ElMessage.success("已取消"));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.text-reverse {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row-reverse;
|
|
|
|
|
+
|
|
|
|
|
+ :deep([class*='el-icon'] + span) {
|
|
|
|
|
+ margin-left: 0;
|
|
|
|
|
+ margin-right: 6px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-card {
|
|
|
|
|
+ margin-bottom: 25px;
|
|
|
|
|
+ border-top: none;
|
|
|
|
|
+ border-radius: 0 0 var(--el-card-border-radius) var(--el-card-border-radius);
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-card__header) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ border-bottom: 0;
|
|
|
|
|
+ padding: calc(var(--el-card-padding) / 2) var(--el-card-padding);
|
|
|
|
|
+
|
|
|
|
|
+ .reply-form {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: flex-end;
|
|
|
|
|
+ width: 80%;
|
|
|
|
|
+ margin-top: calc(var(--el-card-padding) / 2);
|
|
|
|
|
+ padding-bottom: calc(var(--el-card-padding) / 2);
|
|
|
|
|
+
|
|
|
|
|
+ .el-button {
|
|
|
|
|
+ margin-top: calc(var(--el-card-padding) / 2);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-card__body) {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+
|
|
|
|
|
+ .el-divider {
|
|
|
|
|
+ margin: calc(var(--el-card-padding) / 2) 0;
|
|
|
|
|
+ border-color: var(--el-border-color-light);
|
|
|
|
|
+
|
|
|
|
|
+ .el-divider__text {
|
|
|
|
|
+ color: var(--el-text-color-disabled);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.reply-card :deep(.el-card__body) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ padding: calc(var(--el-card-padding) / 2) var(--el-card-padding);
|
|
|
|
|
+ padding-top: 0;
|
|
|
|
|
+
|
|
|
|
|
+ .el-form {
|
|
|
|
|
+ flex-basis: 100%;
|
|
|
|
|
+ padding: calc(var(--el-card-padding) / 2) 10% 0;
|
|
|
|
|
+ border-top: 1px solid var(--el-card-border-color);
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item {
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ padding: 0 6px;
|
|
|
|
|
+ border-radius: 4px 4px 0 0;
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item__label {
|
|
|
|
|
+ height: fit-content;
|
|
|
|
|
+ line-height: 22px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: var(--el-color-primary);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item__content {
|
|
|
|
|
+ height: fit-content;
|
|
|
|
|
+ line-height: 22px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reply-date-item {
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ border-radius: 0 0 4px 4px;
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item__label {
|
|
|
|
|
+ color: var(--el-text-color-placeholder);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item__content {
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+
|
|
|
|
|
+ .handle-button-group {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+
|
|
|
|
|
+ .el-button {
|
|
|
|
|
+ width: 20px;
|
|
|
|
|
+ height: 20px;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ background-color: transparent;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ background-color: var(--el-color-primary-light-8);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reply-item:hover .el-form-item {
|
|
|
|
|
+ background: var(--el-color-primary-light-9);
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item__content .handle-button-group {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reply-item__children {
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+ margin-left: 6px;
|
|
|
|
|
+ padding-left: 1px;
|
|
|
|
|
+ border-left: 1px solid var(--el-card-border-color);
|
|
|
|
|
+
|
|
|
|
|
+ .el-form-item__label span {
|
|
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .reply-item:last-child .reply-date-item {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .text-reverse {
|
|
|
|
|
+ margin-left: 3px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|