diff --git a/src/api/index.js b/src/api/index.js index 525753d..487fb66 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -19,8 +19,8 @@ const service = axios.create({ // baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换 // baseURL: 'http://www.tougao.com/', //测试本地 记得切换 // baseURL: 'http://192.168.110.110/tougao/public/index.php/', - // baseURL: '/api', //本地 - baseURL: '/', //正式 + baseURL: '/api', //本地 + // baseURL: '/', //正式 }); diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index 73edf4b..7cd987c 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -1243,6 +1243,7 @@ colTitle: 'Template title', , autoPromotionLogs: { detail: 'Auto Promotion Details', + pipelineHistory: 'PIPELINE HISTORY', factoryTaskSelectPlaceholder: 'Select promotion task', configured: 'Configured', editConfig: 'Edit auto promotion configuration', @@ -1310,6 +1311,7 @@ colTitle: 'Template title', taskLogState2: 'Failed', taskLogState3: 'Bounced', taskLogState4: 'Cancelled', + logColIndex: 'No.', logColExpert: 'Expert', logColSendTime: 'Sent at', logColPreparedAt: 'Prepared at', diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index ea13872..d05537f 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -1228,6 +1228,7 @@ const zh = { , autoPromotionLogs: { detail: '自动推广详情', + pipelineHistory: '流水线历史', factoryTaskSelectPlaceholder: '选择推广任务', configured: '已配置', editConfig: '修改期刊自动推广配置', @@ -1295,6 +1296,7 @@ const zh = { taskLogState2: '失败', taskLogState3: '退信', taskLogState4: '取消', + logColIndex: '序号', logColExpert: '专家信息', logColSendTime: '发送时间', logColPreparedAt: '预处理完成时间', diff --git a/src/components/page/autoPromotionLogs.vue b/src/components/page/autoPromotionLogs.vue index c5cf550..e7e14b2 100644 --- a/src/components/page/autoPromotionLogs.vue +++ b/src/components/page/autoPromotionLogs.vue @@ -19,19 +19,46 @@ - + + + + + +
+
+ {{ mapFactoryTaskTypeLabel(opt.task.type) }} + + {{ opt.task.start_promotion==1 ? $t('autoPromotion.running') : $t('autoPromotion.stopped') }} + + +
+
+ {{ mapFactoryExpertTypeLabel(opt.task.expert_type) }} + + + + {{ opt.task.ctime_text }} +
+
+
+
- - - - - {{ config.initialized ? $t('autoPromotionLogs.editConfig') : $t('autoPromotionLogs.startConfig') }} - - - - + + + {{ config.initialized ? $t('autoPromotionLogs.editConfig') : $t('autoPromotionLogs.startConfig') }} +
- -
@@ -135,7 +151,9 @@
{{ scope.row.task_name || '-' }}
- {{ $t('autoPromotionLogs.templateIdLabel') }}: {{ scope.row.template_id }} + {{ $t('autoPromotionLogs.templateIdLabel') }}: {{ scope.row.template_id }} {{ $t('autoPromotionLogs.styleIdLabel') }}: {{ scope.row.style_id }}
@@ -224,7 +242,11 @@ :icon="String(scope.row.state) === '5' ? 'el-icon-edit-outline' : 'el-icon-view'" @click="previewRow(scope.row)" > - {{ String(scope.row.state) === '5' ? $t('autoPromotionLogs.editAction') : $t('autoPromotionLogs.previewAction') }} + {{ + String(scope.row.state) === '5' + ? $t('autoPromotionLogs.editAction') + : $t('autoPromotionLogs.previewAction') + }}
@@ -427,6 +449,36 @@ export default { const opt = this.factoryTaskOptions.find((o) => String(o.value) === id); if (opt && typeof opt.running === 'boolean') return opt.running; return null; + }, + /** + * 顶部 select 前缀展示用:与 headerPromotionFactoryId 对应的 option(含 task)。 + * URL 回退插入的占位项可能没有 task,这里统一成可安全渲染的对象。 + */ + currentSelectedTask() { + const emptyTask = { type: '', ctime_text: '' }; + const id = String(this.headerPromotionFactoryId || '').trim(); + if (!id || !Array.isArray(this.factoryTaskOptions) || !this.factoryTaskOptions.length) { + return { value: '', label: '', running: false, task: { ...emptyTask } }; + } + const opt = this.factoryTaskOptions.find((o) => String(o.value) === id); + if (!opt) { + return { value: id, label: id, running: false, task: { ...emptyTask } }; + } + const raw = opt.task && typeof opt.task === 'object' ? opt.task : {}; + const ctimeText = + raw.ctime_text != null && String(raw.ctime_text).trim() !== '' + ? String(raw.ctime_text).trim() + : this.formatFactoryHeaderTaskCreateTime(raw); + return { + value: opt.value, + label: opt.label, + running: typeof opt.running === 'boolean' ? opt.running : this.isFactoryHeaderTaskRunning(raw), + task: { + ...raw, + type: raw.type != null ? String(raw.type) : '', + ctime_text: ctimeText || '' + } + }; } }, watch: { @@ -445,12 +497,33 @@ export default { this.initPage(); }, methods: { + mapFactoryTaskTypeLabel(type) { + const t = String(type || ''); + if (t === '1') return this.$t('autoPromotion.factoryScenarioSolicit'); + if (t === '2') return this.$t('autoPromotion.factoryScenarioPromoteCitation'); + if (t === '3') return this.$t('autoPromotion.factoryScenarioGeneralThanks'); + if (t === '4') return this.$t('autoPromotion.autoSolicit'); + return this.$t('autoPromotion.autoSolicit'); + }, + mapFactoryExpertTypeLabel(expertType) { + const t = String(expertType || '').trim(); + if (t === '1') return this.$t('autoPromotion.factoryExpertChief'); + if (t === '2') return this.$t('autoPromotion.factoryExpertBoard'); + if (t === '3') return this.$t('autoPromotion.factoryExpertYoungBoard'); + if (t === '4') return this.$t('autoPromotion.factoryExpertAuthor'); + if (t === '5') return this.$t('autoPromotion.factoryExpertDb'); + return '-'; + }, + getStatusType(status) { + + return status==1?'success':'info'; + }, handleTabClick(tab) { - // tab.name 对应的就是原来的 value ("0", "1" 等) - // 注意:el-tabs 的 v-model 绑定的是字符串 - this.query.state = tab.name; - this.handleStateChange(); // 触发你原有的搜索逻辑 - }, + // tab.name 对应的就是原来的 value ("0", "1" 等) + // 注意:el-tabs 的 v-model 绑定的是字符串 + this.query.state = tab.name; + this.handleStateChange(); // 触发你原有的搜索逻辑 + }, getPercent(row) { if (!row.total_count || row.total_count === 0) return 0; // 计算已发送占比 @@ -554,9 +627,7 @@ export default { this.hidePage = false; var journal_id = (this.$route.query && this.$route.query.journal_id) || ''; var pfid = - (this.$route.query && this.$route.query.promotion_factory_id) || - (this.$route.query && this.$route.query.taskId) || - ''; + (this.$route.query && this.$route.query.promotion_factory_id) || (this.$route.query && this.$route.query.taskId) || ''; this.routePromotionFactoryId = String(pfid || ''); this.headerPromotionFactoryId = this.routePromotionFactoryId; this.selectedJournalId = String(journal_id); @@ -662,7 +733,7 @@ export default { } if (!dt || isNaN(dt.getTime())) return String(raw).trim(); const pad = (v) => String(v).padStart(2, '0'); - return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())} ${pad(dt.getHours())}:${pad(dt.getMinutes())}:${pad(dt.getSeconds())}`; + return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())}`; }, isFactoryHeaderTaskRunning(task) { if (!task || typeof task !== 'object') return false; @@ -676,9 +747,12 @@ export default { }, /** 下拉仅展示「类型 - 创建日期」,运行状态单独用 el-tag */ buildFactoryHeaderOptionMainLabel(task, pidFallback) { + console.log("🚀 ~ buildFactoryHeaderOptionMainLabel ~ task:", task); + const typePart = this.getFactoryHeaderTaskTypeLabel(task) || String(pidFallback || '').trim() || '—'; + const expertTypePart = this.mapFactoryExpertTypeLabel(task.expert_type); const datePart = this.formatFactoryHeaderTaskCreateTime(task); - return datePart ? `${typePart} - ${datePart}` : typePart; + return datePart ? `${typePart} | ${expertTypePart}${task.expert_type==5 ? ` | ${task.country_scope_label} ` :''} | ${datePart}` : typePart; }, replacePromotionFactoryIdInUrl(promotionFactoryId) { try { @@ -704,18 +778,20 @@ export default { }); const payload = (res && res.data) || {}; const list = this.findArray(payload) || this.findArray(res) || []; - let opts = (Array.isArray(list) ? list : []).map((task, idx) => { - const pid = - task && task.promotion_factory_id != null - ? String(task.promotion_factory_id) - : task && task.id != null - ? String(task.id) - : ''; - if (!pid) return null; - const label = this.buildFactoryHeaderOptionMainLabel(task, pid); - const running = this.isFactoryHeaderTaskRunning(task); - return { value: pid, label, running }; - }).filter(Boolean); + let opts = (Array.isArray(list) ? list : []) + .map((task, idx) => { + const pid = + task && task.promotion_factory_id != null + ? String(task.promotion_factory_id) + : task && task.id != null + ? String(task.id) + : ''; + if (!pid) return null; + const label = this.buildFactoryHeaderOptionMainLabel(task, pid); + const running = this.isFactoryHeaderTaskRunning(task); + return { value: pid, label, running,task:task } + }) + .filter(Boolean); let cur = String(this.routePromotionFactoryId || this.headerPromotionFactoryId || '').trim(); const ids = new Set(opts.map((o) => o.value)); @@ -799,10 +875,14 @@ export default { selectedPayload.country_fetch_ids != null ? selectedPayload.country_fetch_ids : selectedPayload.country_ids != null - ? selectedPayload.country_ids - : ''; + ? selectedPayload.country_ids + : ''; if (typeof raw === 'string' && raw.trim()) { - return raw.split(',').map((s) => s.trim()).filter(Boolean).map(String); + return raw + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + .map(String); } return []; }, @@ -825,7 +905,7 @@ export default { let availableArr = this.findArray(availablePayload); if (!availableArr) availableArr = Array.isArray(availablePayload) ? availablePayload : []; this.availableFields = availableArr.map((item, idx) => { - const id = item.expert_fetch_id || item.fetch_id || item.id || item.field_id || (idx + 1); + const id = item.expert_fetch_id || item.fetch_id || item.id || item.field_id || idx + 1; const label = item.field || item.title || item.name || item.label || String(id); return { id: String(id), label }; }); @@ -898,10 +978,10 @@ export default { matched.promotion_factory_id != null ? String(matched.promotion_factory_id) : matched.id != null - ? String(matched.id) - : matched.task_id != null - ? String(matched.task_id) - : ''; + ? String(matched.id) + : matched.task_id != null + ? String(matched.task_id) + : ''; } } // promotion_factory/getDetail 必须使用地址栏 promotion_factory_id,避免列表首行 id 与路由不一致 @@ -1048,11 +1128,7 @@ export default { const runAt = item.run_at || item.run_time || item.plan_time || item.execute_time || item.send_date || ''; const state = String(item.state != null ? item.state : ''); const promotionFactoryId = - item.promotion_factory_id != null - ? item.promotion_factory_id - : item.id != null - ? item.id - : item.task_id; + item.promotion_factory_id != null ? item.promotion_factory_id : item.id != null ? item.id : item.task_id; return { id: item.id || item.task_id || `task_${idx + 1}`, promotion_factory_id: promotionFactoryId != null ? String(promotionFactoryId) : '', @@ -1206,16 +1282,16 @@ export default { return Object.prototype.hasOwnProperty.call(stateTextMap, key) ? stateTextMap[key] : '-'; }, getTaskStatusClass(state) { - const stateClassMap = { - 0: 'status-draft', // Draft - 浅灰色 - 5: 'status-preparing', // Preparing - 橘色 - 1: 'status-running', // Running - 深蓝色 - 2: 'status-paused', // Paused - 深灰色 - 3: 'status-completed', // Completed - 绿色 - 4: 'status-cancelled' // Cancelled - 浅红色 - }; - return stateClassMap[Number(state)] || ''; -} + const stateClassMap = { + 0: 'status-draft', // Draft - 浅灰色 + 5: 'status-preparing', // Preparing - 橘色 + 1: 'status-running', // Running - 深蓝色 + 2: 'status-paused', // Paused - 深灰色 + 3: 'status-completed', // Completed - 绿色 + 4: 'status-cancelled' // Cancelled - 浅红色 + }; + return stateClassMap[Number(state)] || ''; + } } }; @@ -1229,11 +1305,13 @@ export default { margin-bottom: 15px; } .config-bar { + width: 100%; display: flex; justify-content: space-between; align-items: center; } .config-bar .left { + width: calc(100% - 100px); display: flex; flex-wrap: wrap; align-items: center; @@ -1758,96 +1836,96 @@ export default { } .filter-header-row { - display: flex; - align-items: center; /* 垂直居中对齐 */ - gap: 16px; /* 胶囊和Search按钮之间的间距 */ - margin-bottom: 16px; - background-color: transparent; /* 容器透明,不厚重 */ + display: flex; + align-items: center; /* 垂直居中对齐 */ + gap: 16px; /* 胶囊和Search按钮之间的间距 */ + margin-bottom: 16px; + background-color: transparent; /* 容器透明,不厚重 */ } /* 2. 彻底重置 el-tabs 的原生样式 (最丑的地方) */ .tmr-capsule-group { - flex: 1; /* 占据左侧空间 */ + flex: 1; /* 占据左侧空间 */ } /* 强制隐藏默认灰色横线和卡片灰边 */ .tmr-capsule-group .el-tabs__header { - margin: 0 !important; - border-bottom: none !important; + margin: 0 !important; + border-bottom: none !important; } .tmr-capsule-group .el-tabs__nav { - border: none !important; + border: none !important; } /* 3. 重新定义每个 Tab 的样式 (让其变成按钮) */ .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item { - height: 32px !important; /* 紧凑高度 */ - line-height: 32px !important; - font-size: 13px; /* 紧凑字体 */ - border: none !important; /* 彻底隐藏原生卡片边框 */ - background-color: transparent; /* 默认状态下透明背景 */ - color: #515a6e; /* 默认状态深灰文字,更专业 */ - transition: all 0.2s ease-in-out; - padding: 0 16px !important; /* 适当内边距 */ - margin-right: 8px; /* 每个 Tab 之间的间距 */ - border-radius: 6px !important; /* 先统一圆角 */ - overflow: visible; /* 确保选中的阴影显示完全 */ + height: 32px !important; /* 紧凑高度 */ + line-height: 32px !important; + font-size: 13px; /* 紧凑字体 */ + border: none !important; /* 彻底隐藏原生卡片边框 */ + background-color: transparent; /* 默认状态下透明背景 */ + color: #515a6e; /* 默认状态深灰文字,更专业 */ + transition: all 0.2s ease-in-out; + padding: 0 16px !important; /* 适当内边距 */ + margin-right: 8px; /* 每个 Tab 之间的间距 */ + border-radius: 6px !important; /* 先统一圆角 */ + overflow: visible; /* 确保选中的阴影显示完全 */ } /* 首尾 Tab 的圆角处理 (形成整体感) */ .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item:first-child { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; } .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item:last-child { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; - margin-right: 0; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + margin-right: 0; } /* 中间 Tab 的处理 (去掉左右圆角,紧凑对齐) */ .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item:not(:first-child):not(:last-child) { - border-radius: 0; + border-radius: 0; } /* 4. **选中效果 (Active) - 胶囊浮动核心** */ .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item.is-active { - background-color: #ffffff !important; /* 白色底色 */ - color: #409eff !important; /* 主题蓝色文字 */ - font-weight: 500; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; /* **关键:增加轻微阴影,浮动感** */ - border-radius: 6px; /* 选中时恢复圆角 */ - position: relative; - z-index: 2; /* 确保选中态在最前面,不被覆盖线破坏 */ + background-color: #ffffff !important; /* 白色底色 */ + color: #409eff !important; /* 主题蓝色文字 */ + font-weight: 500; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; /* **关键:增加轻微阴影,浮动感** */ + border-radius: 6px; /* 选中时恢复圆角 */ + position: relative; + z-index: 2; /* 确保选中态在最前面,不被覆盖线破坏 */ } /* 5. 处理 Tab 之间的断层 (像素级微调) */ .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item { - position: relative; + position: relative; } /* 首尾 Tab 之间的像素级处理 */ .tmr-capsule-group .el-tabs--card > .el-tabs__header .el-tabs__item:not(:last-child)::after { - content: ''; - position: absolute; - right: -5px; /* 让 Tab 之间紧密无缝 */ - top: 0; - height: 100%; - width: 1px; - background: transparent; /* 移除灰线,不丑 */ + content: ''; + position: absolute; + right: -5px; /* 让 Tab 之间紧密无缝 */ + top: 0; + height: 100%; + width: 1px; + background: transparent; /* 移除灰线,不丑 */ } /* 6. 搜索按钮对齐微调 */ .filter-actions .el-button { - height: 32px; /* 与 Tab 高度一致 */ - padding: 0 16px; - border-radius: 6px; - transition: all 0.2s; + height: 32px; /* 与 Tab 高度一致 */ + padding: 0 16px; + border-radius: 6px; + transition: all 0.2s; } .filter-actions { - margin-left: auto; + margin-left: auto; } /* 基础 Badge 样式 */ .status-badge { @@ -1894,21 +1972,25 @@ export default { background-color: #f1f5f9; color: #64748b; } -.status-draft::before { background-color: #94a3b8; } +.status-draft::before { + background-color: #94a3b8; +} /* 5: Preparing - 橘色 */ .status-preparing { background-color: #fff7ed; color: #c2410c; } -.status-preparing::before { background-color: #f97316; } +.status-preparing::before { + background-color: #f97316; +} /* 1: Running - 深蓝色 */ .status-running { background-color: #eff6ff; color: #1d4ed8; } -.status-running::before { +.status-running::before { background-color: #3b82f6; box-shadow: 0 0 4px rgba(59, 130, 246, 0.5); /* 模拟运行中的发光感 */ } @@ -1919,19 +2001,104 @@ export default { color: #334155; border: 1px solid #e2e8f0; /* 停用状态加个微边框增加分量感 */ } -.status-paused::before { background-color: #475569; } +.status-paused::before { + background-color: #475569; +} /* 3: Completed - 绿色 */ .status-completed { background-color: #f0fdf4; color: #15803d; } -.status-completed::before { background-color: #22c55e; } +.status-completed::before { + background-color: #22c55e; +} /* 4: Cancelled - 浅红色 */ .status-cancelled { background-color: #fef2f2; color: #b91c1c; } -.status-cancelled::before { background-color: #ef4444; } +.status-cancelled::before { + background-color: #ef4444; +} +/* 基础 Select 宽度 */ +.custom-pipeline-select { + width: 580px; +} + +/* 分组标题(el-option-group,避免在 ul 内放 div) */ +.pipeline-popper .el-select-group__title { + padding: 10px 20px; + font-size: 12px; + font-weight: bold; + color: #909399; + letter-spacing: 1px; + border-bottom: 1px solid #f0f2f5; + line-height: 1.2; +} + +/* 单个选项容器 */ +.pipeline-popper .el-select-dropdown__item { + height: auto; /* 允许高度自适应 */ + padding: 12px 20px; + line-height: normal; + border-left: 3px solid transparent; /* 预留边框位置防止抖动 */ +} + +/* 选中状态 */ +.pipeline-popper .el-select-dropdown__item.selected { + background-color: #f5f7fa; + border-left: 3px solid #409eff; /* 选中时的左侧蓝条 */ + color: #606266; /* 覆盖 Element 默认高亮色 */ +} + +/* 选项内容布局 */ +.pipeline-popper .option-content { + display: flex; + flex-direction: column; + gap: 6px; +} + +/* 第一行:标题 + 状态标签 */ +.pipeline-popper .option-content .row-top { + display: flex; + justify-content: space-between; + align-items: center; +} + +.pipeline-popper .option-content .row-top .task-title { + font-weight: 600; + color: #303133; + font-size: 14px; +} + +.pipeline-popper .option-content .row-top .status-tag { + border-radius: 12px; + padding: 0 8px; + height: 20px; + line-height: 18px; +} + +/* 第二行:元数据信息 */ +.pipeline-popper .option-content .row-bottom { + display: flex; + align-items: center; + font-size: 11px; + color: #909399; +} + +.pipeline-popper .option-content .row-bottom .meta-item.database { + color: #5856d6; + font-weight: bold; + /* text-transform: uppercase; */ +} + +.pipeline-popper .option-content .row-bottom .separator { + margin: 0 6px; + color: #dcdfe6; +} + + + diff --git a/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue b/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue index 017a5c7..0a15e8f 100644 --- a/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue +++ b/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue @@ -59,20 +59,22 @@
+
{{ $t('autoPromotionLogs.logColIndex') }}
{{ $t('autoPromotionLogs.logColExpert') }}
{{ $t('autoPromotionLogs.logColSendTime') }}
{{ $t('autoPromotionLogs.logColPreparedAt') }}
{{ $t('autoPromotionLogs.logColStatus') }}
-
{{ $t('autoPromotionLogs.logColAction') }}
+
+
{{ (currentPage - 1) * pageSize + rowIndex + 1 }}
{{ item.expertName }} @@ -782,6 +784,14 @@ export default { color: #475569; } +.col-index { + flex: 0 0 52px; + width: 52px; + text-align: center; + color: #64748b; + font-variant-numeric: tabular-nums; +} + .col-info { flex: 1.8; }