From 0e08a1fef80e15bd434a2b17e603711e782878d5 Mon Sep 17 00:00:00 2001 From: huangdeliang <huangdeliang@skieer.com> Date: Mon, 31 May 2021 19:34:04 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=90=AD=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 6148 -> 6148 bytes .../mapper/custom/SysServicesTopicMapper.xml | 22 +- stdiet-ui/src/api/custom/message.js | 4 +- stdiet-ui/src/store/getters.js | 6 +- stdiet-ui/src/store/modules/message.js | 60 +++- stdiet-ui/src/views/custom/message/index.vue | 2 +- .../views/custom/message/messageBrowser.vue | 45 --- .../custom/message/messageBrowser/Comment.vue | 94 ++++++ .../custom/message/messageBrowser/index.vue | 276 ++++++++++++++++++ 9 files changed, 439 insertions(+), 70 deletions(-) delete mode 100644 stdiet-ui/src/views/custom/message/messageBrowser.vue create mode 100644 stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue create mode 100644 stdiet-ui/src/views/custom/message/messageBrowser/index.vue diff --git a/.DS_Store b/.DS_Store index 20c255b4c0208f1c6c70c5b04b3dd52fbaef6a68..d343bcd39fd6f5467ffeb3453751438201595a32 100644 GIT binary patch delta 61 zcmZoMXfc@J&&aVcU^gQp$7UX;WsLHi48;s33@Hqm45>g`m!Xs)vn;qMFDE}Qoq>UY Paq~uIGnUQl9Dn%%?J^Ju delta 32 ocmZoMXfc@J&&a+pU^gQp`(_@dWsIAzFdMT>Y*5<F&heKY0H@*#KL7v# diff --git a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml index 947d75729..688494714 100644 --- a/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml +++ b/stdiet-custom/src/main/resources/mapper/custom/SysServicesTopicMapper.xml @@ -9,11 +9,13 @@ <result column="topic_id" property="topicId"/> <result column="topic_type" property="topicType"/> <result column="content" property="content"/> - <result column="name" property="name"/> + <result column="uid" property="uid"/> + <result column="role" property="role"/> <result column="read" property="read"/> <result column="img" property="img" typeHandler="com.stdiet.custom.typehandler.ArrayJsonHandler"/> <result column="create_time" property="createTime"/> <result column="update_time" property="updateTime"/> + <association column="{uid=uid,role=role}" property="name" select="selectUserInfo"/> </resultMap> @@ -21,21 +23,9 @@ <select id="selectSysServicesTopicByUserIdAndRole" parameterType="SysServicesTopic" resultMap="SysServicesTopicResult"> SELECT * FROM ( - SELECT * FROM sys_services_topic_status WHERE role = #{role} AND uid = #{uid} + SELECT topic_id, id, `read`, create_time, update_time, 'customer' AS role FROM sys_services_topic_status WHERE role = #{role} AND uid = #{uid} ) AS status LEFT JOIN sys_services_topic USING(topic_id) - <choose> - <when test="role == 'customer'"> - LEFT JOIN ( - SELECT name, id AS uid FROM sys_customer WHERE id = #{uid} - ) AS customer ON status.uid = customer.uid - </when> - <otherwise> - LEFT JOIN ( - SELECT user_id AS uid, nick_name AS name FROM sys_user WHERE user_id = #{uid} - ) AS user ON status.uid = user.uid - </otherwise> - </choose> ORDER BY `read` ASC, update_time DESC </select> @@ -92,10 +82,12 @@ <select id="selectServicesTopicCommentByTopicId" resultMap="ServicesTopicCommentResult"> select * from sys_services_topic_comment where topic_id = #{topic_id} + order by create_time asc </select> <select id="selectServicesTopicCommentReplyByCommentId" resultMap="ServicesTopicCommentReplyResult"> - select * from sys_services_topic_reply where comment_id = #{id} order by create_time asc + select * from sys_services_topic_reply where comment_id = #{id} + order by create_time asc </select> diff --git a/stdiet-ui/src/api/custom/message.js b/stdiet-ui/src/api/custom/message.js index f453c905f..7b81d2223 100644 --- a/stdiet-ui/src/api/custom/message.js +++ b/stdiet-ui/src/api/custom/message.js @@ -20,7 +20,7 @@ export function postTopicReply(data) { return request({ url: "/services/topic/reply", method: "post", - body: data + data }); } @@ -28,6 +28,6 @@ export function postTopicComment(data) { return request({ url: "/services/topic/comment", method: "post", - body: data + data }); } diff --git a/stdiet-ui/src/store/getters.js b/stdiet-ui/src/store/getters.js index 8550c09d2..c74df90cb 100644 --- a/stdiet-ui/src/store/getters.js +++ b/stdiet-ui/src/store/getters.js @@ -8,9 +8,11 @@ const getters = { avatar: state => state.user.avatar, name: state => state.user.name, introduction: state => state.user.introduction, - roles: state => state.user.roles, + // roles: state => state.user.roles, + roles: state => ["dietician"], permissions: state => state.user.permissions, - userId: state => state.user.userId, + // userId: state => state.user.userId, + userId: state => 131, userRemark: state => state.user.remark, permission_routes: state => state.permission.routes, // diff --git a/stdiet-ui/src/store/modules/message.js b/stdiet-ui/src/store/modules/message.js index b65fc812f..bd01aa685 100644 --- a/stdiet-ui/src/store/modules/message.js +++ b/stdiet-ui/src/store/modules/message.js @@ -6,11 +6,15 @@ import { } from "@/api/custom/message"; const oriState = { - topicList: undefined, - detailData: undefined + topicList: [], + detailData: {}, + selTopicId: "" }; const mutations = { + updateSelTopicId(state, payload) { + state.selTopicId = payload.selTopicId; + }, save(state, payload) { Object.keys(payload).forEach(key => { state[key] = payload[key]; @@ -24,13 +28,59 @@ const mutations = { }; const actions = { - async init({ rootGetters, commit }, payload) { + async init({ rootGetters, commit, dispatch }, payload) { const { roles: [role], userId } = rootGetters; - const result = await fetchTopicList({ role: "dietician", uid: 131 }); - console.log({ result }); + const result = await fetchTopicList({ role, uid: userId }); + if (result.code === 200) { + const [defTopic] = result.rows; + + dispatch("fetchTopicDetailActions", { + topicId: defTopic.topicId, + id: defTopic.id + }); + + commit("save", { + topicList: result.rows + }); + } + }, + async fetchTopicDetailActions({ commit }, payload) { + const { topicId, id } = payload; + commit("save", { selTopicId: topicId }); + const result = await fetchTopicDetail({ topicId, id }); + if (result.code === 200) { + commit("save", { detailData: result.data[0] }); + } + }, + async postTopicReplyActions( + { commit, rootGetters, dispatch, state }, + payload + ) { + const { + roles: [role], + userId + } = rootGetters; + const { detailData, topicList } = state; + const params = { ...payload, fromRole: role, fromUid: userId }; + + const result = payload.commentId + ? await postTopicReply(params) + : await postTopicComment(params); + if (result.code === 200) { + const tarTopic = topicList.find( + obj => obj.topicId === detailData.topicId + ); + if (tarTopic) { + dispatch("fetchTopicDetailActions", { + topicId: tarTopic.topicId, + id: tarTopic.id + }); + } + } + return result; } }; diff --git a/stdiet-ui/src/views/custom/message/index.vue b/stdiet-ui/src/views/custom/message/index.vue index 0df449440..a2239526f 100644 --- a/stdiet-ui/src/views/custom/message/index.vue +++ b/stdiet-ui/src/views/custom/message/index.vue @@ -5,7 +5,7 @@ </div> </template> <script> -import MessageBrowser from "./messageBrowser"; +import MessageBrowser from "./messageBrowser/index"; export default { data() { return {}; diff --git a/stdiet-ui/src/views/custom/message/messageBrowser.vue b/stdiet-ui/src/views/custom/message/messageBrowser.vue deleted file mode 100644 index bd861a855..000000000 --- a/stdiet-ui/src/views/custom/message/messageBrowser.vue +++ /dev/null @@ -1,45 +0,0 @@ -<template> - <div class="message_browser_wrapper"> - <div class="topic_list"> - - </div> - <div class="topic_detail"></div> - </div> -</template> -<script> -import { createNamespacedHelpers } from "vuex"; -const { - mapActions, - mapState, - mapMutations, - mapGetters, -} = createNamespacedHelpers("message"); -export default { - data() { - return {}; - }, - created() { - this.init(); - }, - computed: { - ...mapState([]), - }, - methods: { - ...mapActions(["init"]), - ...mapMutations(["clean"]), - }, -}; -</script> -<style lang="scss" scoped> -.message_browser_wrapper { - display: flex; - .topic_list { - flex: 2; - } - - .topic_detail { - flex: 3; - background: gray; - } -} -</style> diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue new file mode 100644 index 000000000..db43c3cd8 --- /dev/null +++ b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue @@ -0,0 +1,94 @@ +<template> + <div class="topic_comment_item"> + <div class="comment_avatar"> + <el-avatar size="medium">{{ data.fromName.substr(-1) }}</el-avatar> + </div> + <div class="comment_content"> + <div class="content_title"> + {{ getContentTitle(data) }} + </div> + <div class="content_type">{{ data.content }}</div> + <div class="content_time"> + {{ formatDate(data.createTime) }} + <div + v-if="data.fromUid !== userId.toString()" + class="reply_btn" + @click="handOnClick(data)" + > + 回复 + </div> + </div> + </div> + </div> +</template> +<script> +import dayjs from "dayjs"; +import { mapGetters } from "vuex"; + +export default { + data() { + return { + roleDict: { + customer: "客户", + dietician: "主营养师", + after_sale: "售后营养师", + dietician_assistant: "营养师助理", + }, + }; + }, + props: ["data"], + computed: { + ...mapGetters(["userId"]), + }, + methods: { + formatDate(date) { + return dayjs(date).format("MM-DD HH:mm"); + }, + handOnClick(data) { + this.$emit("click", data); + }, + getContentTitle(data) { + return `${this.roleDict[data.fromRole]} - ${data.fromName}${ + data.commentId + ? ` to ${this.roleDict[data.toRole]} - ${data.toName}` + : "" + }`; + }, + }, +}; +</script> +<style lang="scss" scoped> +.topic_comment_item { + margin: 12px; + display: flex; + + .comment_avatar { + flex: 0 0 36px; + } + + .comment_content { + flex: 1 0 0; + margin-left: 8px; + + .content_title { + font-size: 14px; + color: #909399; + } + + .content_type { + padding: 4px 0; + } + + .content_time { + display: flex; + font-size: 14px; + color: #8c8c8c; + + .reply_btn { + margin-left: 16px; + cursor: pointer; + } + } + } +} +</style> diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue new file mode 100644 index 000000000..3e0ba46ea --- /dev/null +++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue @@ -0,0 +1,276 @@ +<template> + <div class="message_browser_wrapper"> + <div class="topic_list"> + <div + v-for="topic in topicList" + :key="topic.topicId" + :class="`topic_item ${ + selTopicId === topic.topicId ? 'topic_item_sel' : '' + }`" + @click="handleOnTopicClick(topic)" + > + <div class="topic_status topic_status_read" /> + <div class="topic_item_content"> + <div class="topic_content">{{ topic.content }}</div> + <div class="topic_user_name">by {{ topic.name }}</div> + </div> + <div class="topic_info"> + <el-tag size="small">{{ topicTypeDict[topic.topicType] }}</el-tag> + <div class="topic_time">{{ formatDate(topic.createTime) }}</div> + </div> + </div> + </div> + <div class="topic_detail"> + <div class="topic_detail_list"> + <div class="topic_detail_title"> + <div>{{ detailData.content }}</div> + <div class="content_time" :style="{ marginTop: '4px' }"> + {{ formatDate(detailData.createTime) }} + <div class="reply_btn" @click="handleOnReplyTopic(detailData)"> + 回复 + </div> + </div> + </div> + <div v-for="comment in detailData.comments" :key="comment.id"> + <Comment :data="comment" @click="handleOnReplyComment" /> + <div v-if="!!comment.replys"> + <div + v-for="reply in comment.replys" + :key="reply.id" + class="comment_reply_item" + > + <Comment :data="reply" @click="handleOnReplyReply" /> + </div> + </div> + </div> + </div> + <div class="topic_detail_reply"> + <div + :style="{ marginBottom: '8px', fontSize: '12px', color: '#8c8c8c' }" + > + 回复:{{ replyTarget }} + </div> + <el-input type="textarea" :rows="3" v-model="replyContent" /> + <div class="send_btn_zone"> + <el-button + type="primary" + @click="handleOnReply" + :disabled="!this.replyContent" + size="mini" + >发送</el-button + > + </div> + </div> + </div> + </div> +</template> +<script> +import { createNamespacedHelpers } from "vuex"; +import Comment from "./Comment"; +import dayjs from "dayjs"; +const { + mapActions, + mapState, + mapMutations, + mapGetters, +} = createNamespacedHelpers("message"); +export default { + data() { + return { + topicTypeDict: { + 0: "建议", + 1: "食谱", + 2: "咨询", + }, + replyTarget: "", + replyContent: "", + replyObj: {}, + }; + }, + components: { Comment }, + created() { + this.init(); + }, + computed: { + ...mapState(["topicList", "selTopicId", "detailData"]), + }, + methods: { + formatDate(date) { + return dayjs(date).format("MM-DD HH:mm"); + }, + handleOnTopicClick(data) { + this.replyTarget = ""; + this.replyContent = ""; + this.replyObj = {}; + this.fetchTopicDetailActions({ topicId: data.topicId, id: data.id }); + }, + handleOnReplyTopic(data) { + this.replyTarget = "主题"; + this.replyObj = { + toRole: data.role, + toUid: data.uid, + topicId: data.topicId, + img: [], + }; + }, + handleOnReplyComment(data) { + this.replyTarget = data.fromName; + this.replyObj = { + toRole: data.fromRole, + toUid: data.fromUid, + commentId: data.id, + img: [], + }; + }, + handleOnReplyReply(data) { + this.replyTarget = data.fromName; + this.replyObj = { + toRole: data.fromRole, + toUid: data.fromUid, + commentId: data.commentId, + replyId: data.id, + img: [], + }; + }, + handleOnReply() { + if (this.replyTarget) { + this.postTopicReplyActions({ + ...this.replyObj, + content: this.replyContent, + }).then((res) => { + this.$message.success(res.msg); + this.replyContent = ""; + this.replyTarget = ""; + this.replyObj = {}; + }); + } else { + this.$message.error("请选择回复对象"); + } + }, + ...mapActions(["init", "fetchTopicDetailActions", "postTopicReplyActions"]), + ...mapMutations(["clean"]), + }, +}; +</script> +<style lang="scss" scoped> +.message_browser_wrapper { + display: flex; + .topic_list { + flex: 2; + + .topic_item { + display: flex; + padding: 8px 16px; + cursor: pointer; + + &:hover { + background: #dedede; + } + + .topic_status { + flex: 0 0 20px; + display: flex; + justify-content: center; + padding: 7px 0; + + &::before { + content: ""; + width: 8px; + display: block; + height: 8px; + border-radius: 50%; + } + } + + .topic_status_read::before { + background: #909399; + } + + .topic_status_unread::before { + background: #d96969; + } + + .topic_item_content { + flex: 1 0 0; + + .topic_content { + width: 260px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + line-height: 1.5; + } + + .topic_user_name { + color: #8c8c8c; + font-size: 14px; + margin-top: 8px; + } + } + + .topic_info { + flex: 0 0 80px; + text-align: center; + + .topic_time { + font-size: 14px; + margin-top: 8px; + color: #8c8c8c; + } + } + } + + .topic_item_sel { + background: #dedede; + } + } + + .topic_detail { + flex: 3; + background: #fafafa; + padding: 16px; + + .topic_detail_list { + height: calc(100vh - 300px); + overflow: auto; + + .content_time { + display: flex; + font-size: 14px; + color: #8c8c8c; + + .reply_btn { + margin-left: 16px; + cursor: pointer; + } + } + + .topic_detail_title { + } + + .comment_reply_item { + margin-left: 24px; + + .topic_comment_item { + margin: 8px; + + /deep/.el-avatar { + transform: scale(0.8); + } + } + } + } + + .topic_detail_reply { + height: 160px; + background: white; + padding: 16px; + + .send_btn_zone { + margin-top: 8px; + text-align: right; + } + } + } +} +</style> From d86bdedf790b61e9ec8c8fb2788dd30ce9225d47 Mon Sep 17 00:00:00 2001 From: huangdeliang <huangdeliang@skieer.com> Date: Mon, 31 May 2021 19:39:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stdiet-ui/src/store/getters.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdiet-ui/src/store/getters.js b/stdiet-ui/src/store/getters.js index c74df90cb..ccedcde6d 100644 --- a/stdiet-ui/src/store/getters.js +++ b/stdiet-ui/src/store/getters.js @@ -8,11 +8,11 @@ const getters = { avatar: state => state.user.avatar, name: state => state.user.name, introduction: state => state.user.introduction, - // roles: state => state.user.roles, - roles: state => ["dietician"], + roles: state => state.user.roles, + // roles: state => ["dietician"], permissions: state => state.user.permissions, - // userId: state => state.user.userId, - userId: state => 131, + userId: state => state.user.userId, + // userId: state => 131, userRemark: state => state.user.remark, permission_routes: state => state.permission.routes, //