From 62dd79a2fcd5b17a72479b313596927c3a912e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=A1=E5=A4=8D=E4=B9=A0?= <2353956224@qq.com> Date: Wed, 7 Jan 2026 18:57:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E6=92=AD=E5=9C=BA=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/LiveRecordDialog.vue | 304 +++++++++++----------------- src/utils/axios.js | 4 +- src/utils/pythonBridge.js | 163 ++++++++------- src/views/hosts/hostsList.vue | 65 +++++- 4 files changed, 265 insertions(+), 271 deletions(-) diff --git a/src/components/LiveRecordDialog.vue b/src/components/LiveRecordDialog.vue index 41a3fd8..6252b5a 100644 --- a/src/components/LiveRecordDialog.vue +++ b/src/components/LiveRecordDialog.vue @@ -1,240 +1,166 @@ - - - - - - - - - - - - - - - - + + + + 只看异常 + + + 总条数:{{ filteredRows.length }} + 点赞合计:{{ totalLikes }} + 无点赞:{{ zeroLikeCount }} + + - 只看异常 + + + + + - 重置 - - - - 总条数:{{ filteredRows.length }} - 点赞合计:{{ totalLikes }} - like=0:{{ zeroLikeCount }} - - - - - - - - - - - - - - - - - - - 0 - {{ row.likeCount }} - - - - - - - - 复制 - 选中 - - - - - - - - 关闭 + + + 0 + {{ row.likeCount }} - + + + + + + + 关闭 + + - \ No newline at end of file + diff --git a/src/utils/axios.js b/src/utils/axios.js index 38f0829..f4f8b1c 100644 --- a/src/utils/axios.js +++ b/src/utils/axios.js @@ -17,8 +17,8 @@ let baseURL = '' if (process.env.NODE_ENV === 'development') { // 生产环境 // baseURL = "https://api.tkpage.yolozs.com" - baseURL = "http://192.168.2.21:8101" - // baseURL = "https://crawlclient.api.yolozs.com" + // baseURL = "http://192.168.2.21:8101" + baseURL = "https://crawlclient.api.yolozs.com" } else { // 测试环境 // baseURL = "http://120.26.251.180:8085/" diff --git a/src/utils/pythonBridge.js b/src/utils/pythonBridge.js index 33ef1ee..edec806 100644 --- a/src/utils/pythonBridge.js +++ b/src/utils/pythonBridge.js @@ -1,121 +1,134 @@ // pythonBridge.js import { ref, onMounted } from 'vue'; + const bridge = ref(null); + +// 统一安全调用,确保 Qt 响应有回调可执行 +const callBridge = (method, ...args) => { + if (!bridge.value || typeof bridge.value[method] !== 'function') return; + const last = args[args.length - 1]; + const hasCallback = typeof last === 'function'; + const callback = hasCallback ? args.pop() : () => { }; + bridge.value[method](...args, callback); +}; + +// 防御:若 Qt 返回了未知 id,忽略以免 execCallbacks 报错 +const patchQWebChannel = () => { + if (!window.QWebChannel || QWebChannel.__patchedIgnoreMissing) return; + const originalHandleResponse = QWebChannel.prototype.handleResponse; + QWebChannel.__patchedIgnoreMissing = true; + QWebChannel.prototype.handleResponse = function (message) { + const cb = this.execCallbacks && this.execCallbacks[message.id]; + if (message.id && typeof cb !== 'function') { + console.warn('忽略未知的 WebChannel 响应', message); + return; + } + return originalHandleResponse.call(this, message); + }; +}; + // 初始化 QWebChannel const initBridge = () => { - if (/localhost/.test(window.location.href)) return + if (/localhost/.test(window.location.href)) return; + patchQWebChannel(); new QWebChannel(qt.webChannelTransport, (channel) => { + // 兜底:任何缺失的回调都返回空函数,避免 execCallbacks 报错 + channel.execCallbacks = new Proxy(channel.execCallbacks || {}, { + get(target, prop) { + const val = target[prop]; + if (typeof val === 'function') return val; + // 返回空函数,确保 handleResponse 可调用 + return () => {}; + }, + set(target, prop, value) { + target[prop] = value; + return true; + }, + }); + bridge.value = channel.objects.bridge; }); }; + export function usePythonBridge() { - - - - // 调用 Python 方法 const fetchDataConfig = (data) => { - return new Promise((resolve, reject) => { - if (bridge.value) { - bridge.value.fetchDataConfig(data, function (result) { - resolve(result); - }); - } - - + return new Promise((resolve) => { + if (!bridge.value) return resolve(null); + callBridge('fetchDataConfig', data, (result) => { + resolve(result); + }); }); }; // 查询获取主播的数据 const fetchDataCount = () => { - return new Promise((resolve, reject) => { - if (bridge.value) { - bridge.value.fetchDataCount(function (result) { - resolve(result); - }); - } + return new Promise((resolve) => { + if (!bridge.value) return resolve(null); + callBridge('fetchDataCount', (result) => { + resolve(result); + }); }); }; - // 打开tk后台 + // 打开 tk 后台 const loginTikTok = () => { - if (bridge.value) { - bridge.value.loginTikTok(function (result) { - - }); - } - + callBridge('loginTikTok'); }; - // 登录tk后台 + // 登录 tk 后台 const loginBackStage = (data) => { - if (bridge.value) { - if (data.index == 0) { - bridge.value.loginBackStage(JSON.stringify(data)); - } else if (data.index == 1) { - bridge.value.loginBackStageCopy(JSON.stringify(data)); - } - + if (data.index == 0) { + callBridge('loginBackStage', JSON.stringify(data)); + } else if (data.index == 1) { + callBridge('loginBackStageCopy', JSON.stringify(data)); } }; - //跳转到主播页面 + // 跳转到主播页面 const givePyAnchorId = (id) => { - - if (bridge.value) { - bridge.value.givePyAnchorId(id, function (result) { - - }); - } + callBridge('givePyAnchorId', id); }; - //查询登录状态 + // 查询登录状态 const backStageloginStatus = () => { - return new Promise((resolve, reject) => { - if (bridge.value) { - bridge.value.backStageloginStatus(function (result) { - resolve(result); - }); - } + return new Promise((resolve) => { + if (!bridge.value) return resolve(null); + callBridge('backStageloginStatus', (result) => { + resolve(result); + }); }); - }; - //查询登录状态 + + // 查询登录状态(副账号) const backStageloginStatusCopy = () => { - return new Promise((resolve, reject) => { - if (bridge.value) { - bridge.value.backStageloginStatusCopy(function (result) { - resolve(result); - }); - } + return new Promise((resolve) => { + if (!bridge.value) return resolve(null); + callBridge('backStageloginStatusCopy', (result) => { + resolve(result); + }); }); - }; - //导出表格 + // 导出表格 const exportToExcel = (data) => { - if (bridge.value) { - bridge.value.exportToExcel(JSON.stringify(data)); - } - + callBridge('exportToExcel', JSON.stringify(data)); }; + const stopScript = () => { - if (bridge.value) { - bridge.value.stopScript(); - } - + callBridge('stopScript'); }; - //获取版本号 + // 获取版本号 const getVersion = () => { - return new Promise((resolve, reject) => { - if (bridge.value) { - bridge.value.currentVersion(function (result) { - resolve(result); - }); - } + return new Promise((resolve) => { + if (!bridge.value) return resolve(null); + callBridge('currentVersion', (result) => { + resolve(result); + }); }); }; + // 在组件挂载时初始化桥接 onMounted(initBridge); @@ -129,6 +142,6 @@ export function usePythonBridge() { backStageloginStatusCopy, exportToExcel, stopScript, - getVersion + getVersion, }; -} \ No newline at end of file +} diff --git a/src/views/hosts/hostsList.vue b/src/views/hosts/hostsList.vue index 167fd7f..2629bb9 100644 --- a/src/views/hosts/hostsList.vue +++ b/src/views/hosts/hostsList.vue @@ -46,12 +46,20 @@ - + {{ scope.row.invitationType == 1 ? $t('hostList.invitationType1') : $t('hostList.invitationType2') }} + + + + 查看场次 + + + + @@ -139,6 +147,9 @@ + + @@ -154,6 +165,7 @@ import { ref, reactive, onMounted } from 'vue'; // import { ElMessage, ElMessageBox } from 'element-plus' // import { color } from 'echarts'; import { useI18n } from 'vue-i18n' +import LiveRecordDialog from '@/components/LiveRecordDialog.vue' const { t } = useI18n() @@ -224,6 +236,9 @@ let staffId = ref({}) let commentInfo = ref('') //备注信息主播 let commentHost = ref('') +let liveDetailDialogVisible = ref(false) +let liveDetailRecords = ref([]) +let liveDetailLoading = ref(false) //分页 let pageSize = ref(10) let page = ref(1) @@ -359,17 +374,29 @@ function handleClose(done) { } -function getliveHost() { +function getliveHost(hostId) { + liveDetailLoading.value = true liveHostDetail( { - "hostsId": "1296peahh", + "hostsId": hostId, "tenantId": userInfo.value.tenantId } ).then(res => { - console.log("直播间信息列表", JSON.stringify(res)) + const detailList = Array.isArray(res) ? res : (res?.records || []) + liveDetailRecords.value = detailList + liveDetailDialogVisible.value = true + }).catch(err => { + console.log('liveHostDetail error', err) + }).finally(() => { + liveDetailLoading.value = false }) } +function handleLiveSelect(row) { + console.log('selected live row', row) + liveDetailDialogVisible.value = false +} + //修改主播维护状态 // function handleSelectChange(event, data) { @@ -518,6 +545,8 @@ function openHTML(id) { box-sizing: border-box; // height: 100vh; padding: 40px; + background: linear-gradient(135deg, #f7fbff 0%, #f2f9f8 100%); + border-radius: 16px; /* 页面无法选中 */ // -webkit-user-select: none; @@ -527,12 +556,38 @@ function openHTML(id) { .hostTable { width: 100%; - padding: 40px 0; + max-width: 100%; + overflow: hidden; + padding: 16px; + background: #ffffff; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.04); + box-sizing: border-box; .hostIdText { text-decoration: underline; cursor: pointer; } + + .live-btn { + background: linear-gradient(90deg, #45a1ff, #5ad9ff); + border: none; + color: #fff; + } + + .live-btn:hover { + filter: brightness(1.05); + } + + :deep(.el-table) { + width: 100% !important; + table-layout: fixed; + } + + :deep(.el-table__header-wrapper), + :deep(.el-table__body-wrapper) { + overflow-x: auto; + } } .serch-button {