socket to polling

This commit is contained in:
钱冠学
2024-09-14 16:05:35 +08:00
parent b524d6cdcf
commit 6a390f1ddf
6 changed files with 283 additions and 231 deletions

View File

@@ -130,7 +130,7 @@
</template>
<script setup>
import { ref, computed, onMounted, createVNode,nextTick, watch } from "vue";
import { ref, computed, onMounted, onUnmounted, createVNode,nextTick, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
import { message } from "ant-design-vue";
import useEmitter from "@/composables/useEmitter";
@@ -232,10 +232,12 @@ const showPublish = computed(() => {
});
const showLoading = computed(() => {
if (store.state.common.websocket.param !== undefined) {
return !store.state.common.websocket.param.is_cache;
}
return false;
const sn = route.query.sn;
const isSurvey = store.state.polling.surveyStatus?.[sn] === 0;
const questionStatus = store.state.polling.questionStatus?.[sn] || {};
const isQuestion = Object.keys(questionStatus).some((key) => questionStatus[key] === 0);
return isSurvey || isQuestion;
});
const toPage = (path, title) => {
console.log(path, title)
@@ -402,6 +404,10 @@ onMounted(() => {
}
fetchInfo();
});
onMounted(() => store.dispatch('polling/pollingSurveyState', { sn, polling: true }));
onUnmounted(() => store.dispatch('polling/stopPollingSurveyState', { sn }));
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,179 @@
import request from "@/utils/request";
export default {
namespaced: true,
state: {
surveyStatus: { "sn_example": 0 }, // 轮询结果keysnvalueis_cache 应该是这几个枚举值, 0设计中 1已生成设计 2未生成设计
surveyPollingStatus: { "sn_example": 10000 }, // 轮询状态标志keysnvaluetimestamp
questionStatus: { "sn_example": { "question_index_example": 0 } }, // 轮询结果
questionPollingStatus: { "sn_example": { "question_index_example": 10000 } }, // 轮询状态标志
},
getters: {},
mutations: {
updateSurveyStatus(state, status) {
state.surveyStatus = { ...state.surveyStatus, ...status };
},
updateSurveyPollingStatus(state, status) {
const surveys = { ...(state.surveyPollingStatus || {}) };
Object.keys(status || {}).forEach((key) => surveys[key] = status[key]);
state.surveyPollingStatus = surveys;
},
updateQuestionStatus(state, status) {
if(!status || !status.sn) {
return;
}
const surveys = { ...(state.questionStatus || {}) };
const survey = surveys[status.sn] = surveys[status.sn] || {};
Object.keys(status || {}).forEach((key) => survey[key] = status[key]);
state.questionStatus = surveys;
},
updateQuestionPollingStatus(state, status) {
if(!status || !status.sn) {
return;
}
const surveys = { ...(state.questionPollingStatus || {}) };
const survey = surveys[status.sn] = surveys[status.sn] || {};
Object.keys(status || {}).forEach((key) => survey[key] = status[key]);
state.questionPollingStatus = surveys;
},
},
actions: {
// 开始轮询问卷状态
async pollingSurveyState({ commit, dispatch, state }, info) {
// info 的结构:
// sn: 问卷 sn
// polling: 是否轮询true 轮询false 只查询一次
// count: 最大轮询次数,暂时未实现
// delay: 轮询间隔时间(毫秒)
// id: 一个标志,用于防止重复调用此方法的
// init: 是否初始设置轮询结果为 0
if(!info.sn) {
return;
}
const id = info.id || new Date().getTime();
const oldId = state.surveyPollingStatus[info.sn];
console.log("looping.sync survey", oldId, id, info);
if(oldId && oldId !== id) {
return;
}
commit("updateSurveyPollingStatus", { [info.sn]: id });
if(info.init) {
commit("updateSurveyStatus", { [info.sn]: 0 });
}
const data = await request(`/console/surveys/${ info.sn }/sync`);
// is_cache 应该是这几个枚举值, 0设计中 1已生成设计 2未生成设计
const isCache = data?.data?.is_cache || 0;
commit("updateSurveyStatus", { [info.sn]: isCache });
if(!info.polling) {
commit("updateSurveyPollingStatus", { [info.sn]: "" });
return;
}
switch(isCache) { // 暂时默认当成设计中处理
case 1: // 已生成设计,轮询停止
case 2: // 生成设计失败,轮询停止
commit("updateSurveyPollingStatus", { [info.sn]: "" });
break;
case 0: // 设计生成中,继续轮询
default:
const delay = info.delay || 1000; // 默认 1000 毫秒请求一次
const dispatcher = {
type: "pollingSurveyState",
sn: info.sn,
polling: info.polling,
count: info.count - 1,
delay: delay,
id,
};
setTimeout(() => dispatch(dispatcher), delay);
}
},
// 手动停止轮询问卷状态
stopPollingSurveyState({ commit }, info) {
commit("updateSurveyPollingStatus", { [info.sn]: "" });
},
// 开始轮询问题状态
async pollingQuestionState({ commit, dispatch, state }, info) {
// info 的结构:
// sn: 问卷 sn
// questionIndex: 问题的 question_index
// polling: 是否轮询true 轮询false 只查询一次
// count: 最大轮询次数,暂时未实现
// delay: 轮询间隔时间(毫秒)
// id: 一个标志,用于防止重复调用此方法的
// init: 是否初始设置轮询结果为 0
if(!info.sn) {
return;
}
const id = info.id || new Date().getTime();
const oldId = state.questionPollingStatus[info.sn]?.[info.questionIndex];
console.log("looping.sync question", oldId, id, info, state.questionPollingStatus);
if(oldId && oldId !== id) {
commit("updateQuestionPollingStatus", { sn: info.sn, [info.questionIndex]: "" });
return;
}
commit("updateQuestionPollingStatus", { sn: info.sn, [info.questionIndex]: id });
if(info.init) {
commit("updateQuestionStatus", { sn: info.sn, [info.questionIndex]: 0 });
}
const data = await request(`/console/surveys/${ info.sn }/question_index/${ info.questionIndex }/sync`);
// is_cache 应该是这几个枚举值, 0设计中 1已生成设计 2未生成设计
const isCache = data?.data?.is_cache || 0;
commit("updateQuestionStatus", { sn: info.sn, [info.questionIndex]: isCache });
if(!info.polling) {
commit("updateQuestionPollingStatus", { sn: info.sn, [info.questionIndex]: "" });
return;
}
switch(isCache) { // 暂时默认当成设计中处理
case 1: // 已生成设计,轮询停止
case 2: // 生成设计失败,轮询停止
commit("updateQuestionPollingStatus", { sn: info.sn, [info.questionIndex]: "" });
break;
case 0: // 设计生成中,继续轮询
default:
const delay = info.delay || 1000; // 默认 1000 毫秒请求一次
const dispatcher = {
type: "pollingQuestionState",
sn: info.sn,
questionIndex: info.questionIndex,
polling: info.polling,
count: info.count - 1,
delay: delay,
id,
callback: info.callback,
};
setTimeout(() => dispatch(dispatcher), delay);
}
if(info.callback) {
info.callback(data);
}
},
startPollingQuestionState({ commit, dispatch }, info) {
commit("updateQuestionPollingStatus", { sn: info.sn, [info.questionIndex]: "" });
dispatch("pollingQuestionState", info);
},
// 手动停止轮询问题状态
stopPollingQuestionState({ commit }, info) {
commit("updateQuestionPollingStatus", { sn: info.sn, [info.questionIndex]: new Date().getTime() });
},
},
};

View File

@@ -51,13 +51,14 @@
</template>
<script>
import { computed, ref, watch, reactive, createVNode, onMounted, onBeforeUnmount } from 'vue'
import { computed, ref, watch, reactive, createVNode, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
import { designs } from '../../../api/api'
import { useRoute } from 'vue-router'
import { message, Modal } from 'ant-design-vue'
//import Mask from "../../../components/common/Mask.vue";
import { SocketObserver } from '@/utils/websocket.js'
import { useStore } from 'vuex'
import useEmitter from '@/composables/useEmitter'
export default {
props: {
info: {
@@ -71,6 +72,11 @@ export default {
},
setup(props, context) {
const store = useStore()
const emitter = useEmitter()
const comUnMounted = ref(false)
onMounted(() => comUnMounted.value = false)
onUnmounted(() => comUnMounted.value = true)
const infoConfig = computed(() => {
return reactive(JSON.parse(JSON.stringify(props?.info ?? {})))
@@ -82,59 +88,6 @@ export default {
const loading = ref(false)
console.log('infoConfig', infoConfig)
const ws = ref(store.state.common.websocket.socket)
const watchDesignVersion = () => {
if (!infoConfig.value.config.design_version) {
const socketObserver = new SocketObserver(1)
ws.value.add(socketObserver)
const params = {
sn: route.query.sn,
question_index: infoConfig.value.question_index
}
ws.value.send(params)
var index = 0
// debugger;
socketObserver.listen(function (evt) {
if (evt.data) {
let data = {}
try {
data = JSON.parse(evt.data)
} catch {}
if (data?.sn === route.query.sn && data?.question_index === infoConfig.value.question_index) {
if (data?.is_cache === 1) {
if (data.config) {
if (!Array.isArray(data.config)) {
infoConfig.value.config = data.config
console.log('infoConfig.value.config ', infoConfig.value.config)
loading.value = false
if (index <= 0) {
updateConfig()
}
index++
return
}
}
} else if (data?.is_cache === 0) {
if (data.config) {
loading.value = true
}
} else if (data?.is_cache === 2) {
if (data.config) {
loading.value = false
}
}
}
}
})
onMounted(() => {
ws.value = store?.state?.common?.websocket?.socket ?? undefined
})
} else {
loading.value = false
}
}
watchDesignVersion()
watch(
() => infoConfig.value.config,
(value) => {
@@ -160,6 +113,7 @@ export default {
})
return
} else {
console.log('=======', infoConfig.value)
context.emit('update', infoConfig.value)
}
// console.log('update', infoConfig);
@@ -218,6 +172,8 @@ export default {
loading.value = true
message.info(`该参数组合下建议您样本量不小于${Number.parseInt(500 / (infoConfig.value.config.concept * infoConfig.value.config.task))}!`)
store.commit('polling/updateQuestionStatus', { sn: route.query.sn, [infoConfig.value.question_index]: 0 });
setTimeout(async () => {
await designs({
sn: route.query.sn,
@@ -228,12 +184,9 @@ export default {
shown: infoConfig.value.config.concept
}).finally(() => {
setTimeout(() => {
const params = {
sn: route.query.sn,
question_index: infoConfig.value.question_index
if (!comUnMounted.value) {
pollingQuestionStatus({ init: true });
}
ws.value.send(params)
watchDesignVersion()
}, 1000)
})
}, 2000)
@@ -329,9 +282,48 @@ export default {
return true
}
// onBeforeUnmount(() => {
// ws.value.remove(socketObserver);
// });
async function pollingQuestionStatusCallback(data) {
if (data?.data?.is_cache === 1) {
loading.value = false;
emitter.emit('app-loading', false);
if (data.data.config) {
if (!Array.isArray(data.data.config)) {
infoConfig.value.config = data.data.config;
loading.value = false;
updateConfig(true);
}
}
} else if (data?.data?.is_cache === 0) {
if (data.data.config?.code === -1) {
if (loading.value) {
loading.value = false;
emitter.emit('app-loading', false);
message.error(data.data.config.msg);
}
return;
}
loading.value = true;
if (infoConfig.value?.config?.design_version === 0) {
emitter.emit('app-loading', {
visible: true,
description: '正在进行生成设计...请您稍候~',
});
}
} else {
loading.value = false;
emitter.emit('app-loading', false);
}
}
async function pollingQuestionStatus(options) {
store.dispatch('polling/startPollingQuestionState', {
sn: route.query.sn,
questionIndex: infoConfig.value.question_index,
polling: true,
delay: 2000,
callback: pollingQuestionStatusCallback,
...(options || {}),
});
}
return {
infoConfig,

View File

@@ -1,5 +1,4 @@
import * as cheerio from "cheerio";
import { SocketObserver } from "../../../../utils/websocket.js";
import { saveQuestion } from "../../api/api.js";
const Base64 = require('js-base64').Base64;
@@ -20,28 +19,6 @@ let observer = null;
export const saveQuesApi = (quesSaveParam, store, callback, errorCallback) => {
updatePagesByOneQuestionPerPage(store, quesSaveParam);
const ws = store.state.common.websocket;
if (observer) {
ws.socket.remove(observer)
}
if (ws.socket.ws.readyState && ws.socket.ws.readyState === 1) {
ws.param = {
is_cache: 0,
}
}
store.commit(
"common/A_COMMON_SET_WEBSOCKET",
ws
);
ws.socket.listenClose(() => {
ws.param = {
is_cache: 1,
}
store.commit(
"common/A_COMMON_SET_WEBSOCKET",
ws
);
})
const newFunc = debounce(() => {
if (
quesSaveParam.newQuestion.length === 0 &&
@@ -67,24 +44,17 @@ export const saveQuesApi = (quesSaveParam, store, callback, errorCallback) => {
if (res && callback) {
callback()
}
store.dispatch({
type: 'polling/pollingSurveyState',
sn: store.state.common.questionInfo?.survey?.sn,
polling: true,
});
}).catch((error) => {
if (errorCallback) {
errorCallback(error)
}
});
ws.socket.send(store.state.common.questionInfo?.survey?.sn || '');
observer = new SocketObserver();
ws.socket.add(observer);
observer.listen(function (evt) {
console.log('接受消息', evt.data);
if (evt.data !== 'ping') {
ws.param = JSON.parse(evt.data);
store.commit(
"common/A_COMMON_SET_WEBSOCKET",
ws
);
}
})
console.log("清除");
store.commit(
"common/A_COMMON_SET_QUESSAVEPARAM",

View File

@@ -1,4 +1,3 @@
\
<template>
<div>
<QuesBaseItem :info="info">
@@ -226,7 +225,7 @@
</template>
<script>
import { computed, reactive, ref, watch, onMounted } from "vue";
import { computed, reactive, ref, watch, onMounted, onUnmounted } from "vue";
import Logical from "../../components/Logical.vue";
import BulkProductsOptions from "../../components/BulkProductsOptions.vue";
import BulkPriceOptions from "../../components/BulkPriceOptions.vue";
@@ -237,15 +236,15 @@ import { message, Modal } from "ant-design-vue";
import * as cheerio from "cheerio";
import { useStore } from "vuex";
import { getOptionName } from "../../js/util.js";
import { SocketObserver } from "@/utils/websocket.js";
import { useRoute } from "vue-router";
import useEmitter from '@/composables/useEmitter'
export default {
name: "Address",
components: {
QuesBaseItem,
BulkPriceOptions,
BulkProductsOptions,
BulkPriceOptions,
Logical,
QuestionTinymceOption,
// eslint-disable-next-line vue/no-unused-components
@@ -257,6 +256,7 @@ export default {
questionType: { type: [String, Number], default: 17 },
},
setup(props, context) {
const emitter = useEmitter()
const store = useStore();
const route = useRoute();
const maxLength = ref(150);
@@ -670,110 +670,40 @@ export default {
emitInfo(isupdateV);
};
// const ws = ref(store.state.common.websocket.socket);
// const socketObserver = new SocketObserver(1);
// ws.value.add(socketObserver);
// const params = {
// sn: route.query.sn,
// question_index: copyInfo.value.question_index,
// };
// ws.value.send(params);
// socketObserver.listen(function (evt) {
// console.log("maxdiff", evt.data);
// if (evt.data) {
// let data = {};
// try {
// data = JSON.parse(evt.data);
// } catch {}
// if (data?.is_cache === 1) {
// if (data.config) {
// if (!Array.isArray(data.config)) {
// if (copyInfo.value.config.deleteVersion <= 0) {
// copyInfo.value.config = data.config;
// console.log("copyInfo.value.config ", copyInfo.value.config);
// loading.value = false;
// // initWebsocket.close();
// emitInfo(true);
// return;
// }else{
// loading.value = false;
// }
// }
// }
// } else if (data?.is_cache === 0) {
// if (data.config) {
// loading.value = true;
// }
// }
// }
// });
const sn = route.query.sn;
// 如果 maxdiff 还未生成设计,则立即查询 maxdiff 生成设计的状态,然后立即加载 loading
if (!copyInfo.value.isFirstAdded && copyInfo.value.config.design_version <= 0) {
store.dispatch('polling/startPollingQuestionState', {
sn,
questionIndex: copyInfo.value.question_index,
polling: true,
delay: 2000,
callback: (data) => {
if (data?.data?.is_cache === 0) {
emitter.emit('app-loading', {
visible: true,
description: '正在进行生成设计...请您稍候~',
});
} else {
emitter.emit('app-loading', false);
const ws = ref(store.state.common.websocket.socket);
const watchDesignVersion = () => {
if (!copyInfo.value.config.design_version) {
const socketObserver = new SocketObserver(1);
ws.value.add(socketObserver);
const params = {
sn: route.query.sn,
question_index: copyInfo.value.question_index,
};
ws.value.send(params);
socketObserver.listen(function (evt) {
console.log("maxdiff", evt.data);
if (evt.data) {
let data = {};
try {
data = JSON.parse(evt.data);
} catch {}
if (
data?.sn === route.query.sn &&
data?.question_index === copyInfo.value.question_index
) {
if (data?.is_cache === 1) {
if (data.config) {
if (!Array.isArray(data.config)) {
copyInfo.value.config = data.config;
console.log(
"copyInfo.value.config ",
copyInfo.value.config
);
loading.value = false;
// initWebsocket.close();
emitInfo();
return;
}
}
} else if (data?.code < 0) {
loading.value = false;
message.error(data?.msg);
} else if (data?.is_cache === 0) {
if (data.config) {
loading.value = true;
}
} else if (data?.is_cache === 2) {
if (data.config) {
loading.value = false;
if (data?.data?.is_cache === 1) {
if (data.data.config) {
if (!Array.isArray(data.data.config)) {
copyInfo.value.config = data.data.config;
context.emit('update', copyInfo.value);
}
}
}
}
});
onMounted(() => {
ws.value = store?.state?.common?.websocket?.socket ?? undefined;
});
}
};
watch(
() => copyInfo.value.config.design_version,
(value) => {
if (value === 0) {
watchDesignVersion();
}
}
);
},
});
}
onUnmounted(() => store.dispatch('polling/stopPollingQuestionState', {
sn,
questionIndex: copyInfo.value.question_index,
}));
return {
quiz,

View File

@@ -23,7 +23,6 @@ import { getCurrentInstance } from "vue";
import LeftMenuBar from "../../components/LeftMenuBar.vue";
import { useRoute, useRouter } from "vue-router";
import { useStore } from "vuex";
import { InitWebsocket, SocketObserver } from "../../utils/websocket.js";
import { onBeforeUnmount } from "@vue/runtime-core";
export default {
name: "SurveyIndex",
@@ -70,33 +69,9 @@ export default {
path: "/survey/planet/redpacket",
},
]);
// 首次进入页面就需要给服务器发送一条消息判断是否保存成功,防止用户修改了很多东西,然后再保存的过程中直接刷新页面点击投放按钮
const newWebsocket = new InitWebsocket();
const observer = new SocketObserver();
newWebsocket.add(observer);
observer.open(function (evt) {
evt.send(route.query.sn || "");
});
observer.listen(function (evt) {
if (evt.data !== "ping") {
store.commit("common/A_COMMON_SET_WEBSOCKET", {
param: JSON.parse(evt.data),
socket: newWebsocket,
});
}
});
store.commit("common/A_COMMON_SET_WEBSOCKET", {
param: {},
socket: newWebsocket
});
if (router.options.history.state.back === "/home/project") {
store.commit("common/M_COMMON_SET_SURVEY_STATUS", 0);
}
onBeforeUnmount(() => {
const ws = store.state.common.websocket;
ws.socket.close();
store.commit("common/A_COMMON_SET_WEBSOCKET", {});
});
const fold = ref(true);
const checkHandle = (e) => {
if (e.path) {