diff --git a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java index 4ae0335ec..4c2ec561d 100644 --- a/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java +++ b/stdiet-admin/src/main/java/com/stdiet/web/controller/custom/WebSocketController.java @@ -2,8 +2,8 @@ package com.stdiet.web.controller.custom; import com.alibaba.fastjson.JSONObject; import com.stdiet.common.core.controller.BaseController; -import com.stdiet.custom.utils.WsUtils; import com.stdiet.custom.server.WebSocketServer; +import com.stdiet.custom.utils.WsUtils; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Controller; @@ -38,16 +38,16 @@ public class WebSocketController extends BaseController { } -// @Scheduled(fixedRate = 30000) -// public void boardCast() { -// try { -// JSONObject heartBeat = new JSONObject(); -// heartBeat.put("type", WsUtils.WS_TYPE_HEART_BEAT); -// heartBeat.put("msg", "ping"); -// -// WebSocketServer.sendInfo(heartBeat.toJSONString(), null); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } + @Scheduled(fixedRate = 1800000) + public void boardCast() { + try { + JSONObject heartBeat = new JSONObject(); + heartBeat.put("type", WsUtils.WS_TYPE_SYSTEM_MESSAGE_CLEAN); + heartBeat.put("msg", "clean"); + + WebSocketServer.sendInfo(heartBeat.toJSONString(), null); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java index a1b2286bf..fb76c3526 100644 --- a/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java +++ b/stdiet-custom/src/main/java/com/stdiet/custom/utils/WsUtils.java @@ -6,6 +6,8 @@ public class WsUtils { public static final String WS_TYPE_SYSTEM_MESSAGE = "WS_TYPE_SYSTEM_MESSAGE"; + public static final String WS_TYPE_SYSTEM_MESSAGE_CLEAN = "WS_TYPE_SYSTEM_MESSAGE_CLEAN"; + public static final String WS_TYPE_MESSAGE_COUNT = "WS_TYPE_MESSAGE_COUNT"; public static final String WS_TYPE_NEW_CUSTOMER_REPLY = "WS_TYPE_NEW_CUSTOMER_REPLY"; diff --git a/stdiet-ui/src/components/RecipesPlanDrawer/index.vue b/stdiet-ui/src/components/RecipesPlanDrawer/index.vue index 60e7b60b1..c024ae6e4 100644 --- a/stdiet-ui/src/components/RecipesPlanDrawer/index.vue +++ b/stdiet-ui/src/components/RecipesPlanDrawer/index.vue @@ -192,7 +192,7 @@ export default { }, methods: { showDrawer(data) { - // console.log(data); + console.log(data); this.data = data; if (!this.data) { return; diff --git a/stdiet-ui/src/store/modules/message.js b/stdiet-ui/src/store/modules/message.js index e3643e1c5..168bf4cbb 100644 --- a/stdiet-ui/src/store/modules/message.js +++ b/stdiet-ui/src/store/modules/message.js @@ -1,5 +1,9 @@ import { getCustomerPhysicalSignsByCusId } from "@/api/custom/customer"; import { dealHealthy } from "@/utils/healthyData"; +import { + listRecipesPlanByCusId, + updateRecipesPlan +} from "@/api/custom/recipesPlan"; import { fetchTopicList, @@ -16,7 +20,10 @@ const oriState = { healthyData: {}, healthDataLoading: false, healthyDataType: 0, - avoidFoodIds: [] + avoidFoodIds: [], + // + planList: [], + planListLoading: false }; const mutations = { @@ -71,12 +78,17 @@ const actions = { }, async fetchTopicDetailActions({ commit, dispatch, state }, payload) { const { topicId, id, uid } = payload; - const { healthyData } = state; + const { healthyData, planList } = state; commit("save", { selTopicId: topicId }); // 客户信息 if (healthyData.customerId !== parseInt(uid)) { dispatch("getHealthyData", { cusId: uid, callback: payload.callback }); } + // 食谱计划 + if (!planList.length || planList[0].cusId !== parseInt(uid)) { + dispatch("getRecipesPlanActions", { cusId: uid }); + } + // const result = await fetchTopicDetail({ topicId, id }); if (result.code === 200) { @@ -130,13 +142,39 @@ const actions = { obj => obj.id ); } - } else { - throw new Error(healthyDataResult.msg); } commit("save", { healthDataLoading: false, ...newState }); + }, + async getRecipesPlanActions({ commit }, payload) { + commit("save", { planListLoading: true, planList: [] }); + const result = await listRecipesPlanByCusId(payload.cusId); + let planList = []; + if (result.code === 200) { + planList = result.data; + } + commit("save", { + planList, + planListLoading: false + }); + }, + async updateRecipesPlanActions({ commit, state }, payload) { + const { id, sendFlag, callback } = payload; + const { planList } = state; + const result = await updateRecipesPlan({ id, sendFlag }); + if (result.code === 200) { + callback && callback("success", result.msg); + const newPlanList = JSON.parse(JSON.stringify(planList)); + const tarPlan = newPlanList.find(obj => obj.id === id); + if (tarPlan) { + tarPlan.sendFlag = sendFlag; + } + commit("save", { + planList: newPlanList + }); + } } }; diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue index 94a390e71..e60aaa6f4 100644 --- a/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue +++ b/stdiet-ui/src/views/custom/message/messageBrowser/Comment.vue @@ -31,6 +31,7 @@ export default { dietician: "主任营养师", after_sale: "售后营养师", dietician_assistant: "营养师助理", + manager: "总经理", }, }; }, diff --git a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue index dac7a0d1b..f90b80429 100644 --- a/stdiet-ui/src/views/custom/message/messageBrowser/index.vue +++ b/stdiet-ui/src/views/custom/message/messageBrowser/index.vue @@ -353,6 +353,13 @@ export default { .topic_detail_title { display: flex; cursor: pointer; + + & > :nth-child(1) { + flex: 0 0 40px; + } + & > :nth-child(2) { + flex: 1 0 0; + } } .comment_reply_item { diff --git a/stdiet-ui/src/views/custom/message/userInfo/index.vue b/stdiet-ui/src/views/custom/message/userInfo/index.vue index ba447aeb3..304299003 100644 --- a/stdiet-ui/src/views/custom/message/userInfo/index.vue +++ b/stdiet-ui/src/views/custom/message/userInfo/index.vue @@ -1,38 +1,61 @@ <template> - <div v-loading="healthDataLoading"> - <HealthyView - dev - :data="healthyDataType === 0 ? healthyData : {}" - v-show="healthyDataType === 0" - /> - <BodySignView - dev - :data="healthyDataType === 1 ? healthyData : {}" - v-show="healthyDataType === 1" - /> - </div> + <el-tabs v-model="activeName"> + <el-tab-pane label="客户信息" name="health"> + <div + v-loading="healthDataLoading" + :style="{ height: getTabContentHeight(), overflow: 'auto' }" + > + <HealthyView + dev + :data="healthyDataType === 0 ? healthyData : {}" + v-show="healthyDataType === 0" + /> + <BodySignView + dev + :data="healthyDataType === 1 ? healthyData : {}" + v-show="healthyDataType === 1" + /> + </div> + </el-tab-pane> + <el-tab-pane label="食谱计划" name="plan"> + <div :style="{ height: getTabContentHeight(), overflow: 'auto' }"> + <RecipesPlan /> + </div> + </el-tab-pane> + </el-tabs> </template> <script> import { createNamespacedHelpers } from "vuex"; import HealthyView from "@/components/HealthyView"; import BodySignView from "@/components/BodySignView"; -const { - mapActions, - mapState, - mapMutations, - mapGetters, -} = createNamespacedHelpers("message"); +import RecipesPlan from "./recipesPlan"; +const { mapActions, mapState, mapMutations, mapGetters } = + createNamespacedHelpers("message"); export default { name: "SignUserInfo", components: { HealthyView, BodySignView, + RecipesPlan, }, data() { - return {}; + return { + activeName: "health", + }; + }, + methods: { + getTabContentHeight() { + const tabPanelElm = document.querySelector(".el-tabs"); + if (tabPanelElm) { + return `${tabPanelElm.clientHeight - 68}px`; + } + return ""; + }, }, computed: { ...mapState(["healthyData", "healthyDataType", "healthDataLoading"]), }, }; </script> +<style lang="scss" scoped> +</style> diff --git a/stdiet-ui/src/views/custom/message/userInfo/recipesPlan.vue b/stdiet-ui/src/views/custom/message/userInfo/recipesPlan.vue new file mode 100644 index 000000000..1a4bfc80b --- /dev/null +++ b/stdiet-ui/src/views/custom/message/userInfo/recipesPlan.vue @@ -0,0 +1,220 @@ +<template> + <div class="recipes_plan_wrapper"> + <div class="header"> + <section> + <el-button + v-if="cusOutId" + type="primary" + icon="el-icon-share" + size="mini" + class="copyBtn" + :data-clipboard-text="copyValue" + @click="handleOnRecipesLinkClick" + >食谱链接 + </el-button> + <el-popover + placement="top" + trigger="click" + v-if="cusOutId" + style="margin: 0 12px" + > + <VueQr :text="copyValue" :logoSrc="logo" :size="256" /> + <el-button + slot="reference" + size="mini" + icon="el-icon-picture-outline" + type="primary" + @click="handleCopy(scope.row.path)" + >二维码</el-button + > + </el-popover> + <!-- <el-button icon="el-icon-view" size="mini" @click="handleInnerOpen" + >查看暂停记录 + </el-button> --> + </section> + <section> + <el-button + icon="el-icon-refresh" + size="mini" + @click="getRecipesPlanActions({ cusId })" + circle + /> + </section> + </div> + + <el-table :data="planList" v-loading="planListLoading"> + <el-table-column label="审核状态" align="center"> + <template slot-scope="scope"> + <el-tag :type="getReviewType(scope.row.reviewStatus)"> + {{ getReviewStatusName(scope.row.reviewStatus) }} + </el-tag> + </template> + </el-table-column> + <el-table-column label="计划" align="center" width="130"> + <template slot-scope="scope"> + {{ `第${scope.row.startNumDay} 至 ${scope.row.endNumDay}天` }} + </template> + </el-table-column> + <el-table-column label="日期" align="center" width="200"> + <template slot-scope="scope"> + {{ `${scope.row.startDate} 至 ${scope.row.endDate}` }} + </template> + </el-table-column> + <el-table-column label="订阅情况" align="center"> + <template slot-scope="scope"> + <el-tag :type="scope.row.subscribed ? 'success' : 'danger'"> + {{ scope.row.subscribed ? "已订阅" : "未订阅" }} + </el-tag> + </template> + </el-table-column> + <el-table-column label="发送" align="center"> + <template slot-scope="scope"> + <el-switch + v-model="!!scope.row.sendFlag" + @change="(val) => handleOnSendChange(val, scope.row)" + /> + </template> + </el-table-column> + <el-table-column label="操作" align="center"> + <template slot-scope="scope"> + <el-button + type="text" + :icon=" + scope.row.recipesId ? 'el-icon-edit' : 'el-icon-edit-outline' + " + @click="handleOnRecipesEditClick(scope.row)" + > + {{ `${scope.row.recipesId ? "编辑" : "制作"}` }} + </el-button> + </template> + </el-table-column> + </el-table> + + <!-- 暂停记录抽屉 --> + <!-- <PlanPauseDrawer ref="planPauseRef" /> --> + </div> +</template> +<script> +import Clipboard from "clipboard"; +import { createNamespacedHelpers } from "vuex"; +const { mapActions, mapState, mapMutations, mapGetters } = + createNamespacedHelpers("message"); +// import PlanPauseDrawer from "./PlanPauseDrawer"; +import VueQr from "vue-qr"; +const logo = require("@/assets/logo/logo_b.png"); +export default { + name: "RecipesPlanDrawer", + components: { + // PlanPauseDrawer, + VueQr, + }, + data() { + return { + logo, + title: "", + cusOutId: "", + copyValue: "", + cusId: "", + }; + }, + watch: { + planList(val, newVal) { + console.log({ val, newVal }); + this.cusOutId = val.reduce((str, cur) => { + if (!str && cur.recipesId && cur.reviewStatus === 2) { + str = cur.outId; + this.cusId = cur.cusId; + } + return str; + }, ""); + if (this.cusOutId) { + this.copyValue = + window.location.origin.replace("manage", "sign") + + "/recipes/detail/" + + this.cusOutId; + } + }, + }, + computed: { + ...mapState(["planList", "planListLoading", "healthyData"]), + }, + methods: { + getReviewStatusName(status) { + switch (status) { + case 1: + return "未审核"; + case 2: + return "已审核"; + case 3: + return "制作中"; + case 0: + default: + return "未制作"; + } + }, + getReviewType(status) { + switch (status) { + case 1: + return "danger"; + case 2: + return "success"; + case 3: + return ""; + case 0: + default: + return "info"; + } + }, + // handleInnerOpen() { + // this.$refs["planPauseRef"].showDrawer(this.data); + // this.innerVisible = true; + // this.innerTitle = `「${this.data.name}」暂停记录`; + // }, + handleOnRecipesLinkClick() { + new Clipboard(".copyBtn"); + this.$message({ + message: "拷贝成功", + type: "success", + }); + }, + handleOnRecipesEditClick(data) { + window.open( + "/recipes/build/" + this.healthyData.name + "/" + data.id, + "_blank" + ); + }, + handleOnSendChange(val, data) { + console.log({ val, data }); + const { id } = data; + if (data.reviewStatus === 2) { + this.updateRecipesPlanActions({ + id, + sendFlag: val ? 1 : 0, + callback: (type, msg) => { + this.$message[type](msg); + }, + }); + } else { + this.$message.error("未审核的食谱不能发送"); + } + }, + ...mapActions(["getRecipesPlanActions", "updateRecipesPlanActions"]), + }, +}; +</script> +<style lang="scss" scoped> +/deep/ :focus { + outline: 0; +} + +.recipes_plan_wrapper { + // height: calc(100vh - 77px); + + .header { + margin-bottom: 8px; + display: flex; + align-items: center; + justify-content: space-between; + } +} +</style>