+
Template :
- {{ taskCard.templateName || 'No Template Configured' }}
+ {{ taskCard.templateName || 'No Template Configured' }}
-
+
Promotion Fields :
{{ taskCard.fieldCountText }}
-
+
Country :
@@ -112,18 +112,29 @@
-
{{ $t('autoPromotion.noFactoryTask') }}
-
+
+
{{ $t('autoPromotion.factoryCreateNow') }}
@@ -196,6 +207,7 @@ export default {
saving: false,
promotionUpdating: false,
promotionUpdatingJournalId: '',
+ promotionUpdatingTaskId: '',
wizardJournal: null,
wizardStartDate: '',
wizardConfig: {
@@ -215,6 +227,7 @@ export default {
templateDialogInitialStyleId: '',
templateDialogInitialTemplateId: '',
templateNameMap: {},
+ factoryTemplateNameMap: {},
allJournals: [],
showFactoryTaskDialog: false,
factoryDialogInitialJournalId: '',
@@ -225,6 +238,9 @@ export default {
this.fetchPromotionJournals();
},
methods: {
+ savePromotionCountriesNow(){
+
+ },
// --- 逻辑部分完全保留你原有的实现 ---
openTemplateSelector() {
this.templateDialogInitialStyleId =
@@ -279,6 +295,38 @@ export default {
this.$set(taskCard, 'switchLoading', false);
}
},
+ getFactoryTaskActionText(taskCard) {
+ return taskCard && taskCard.enabled ? this.$t('autoPromotion.goManagePlan') : this.$t('autoPromotion.startPlan');
+ },
+ async handleFactoryTaskAction(journal, taskCard) {
+ if (!journal || !taskCard) return;
+ const taskId = taskCard.taskId != null ? String(taskCard.taskId) : '';
+ this.promotionUpdating = true;
+ this.promotionUpdatingJournalId = journal.journal_id;
+ this.promotionUpdatingTaskId = taskId;
+ try {
+ if (!taskCard.enabled && taskId) {
+ const res = await this.$api.post('api/promotion_factory/changePromotionAct', {
+ promotion_factory_id: taskId,
+ start_promotion: '1'
+ });
+ if (!res || Number(res.code) !== 0) {
+ this.$message.error((res && res.msg) || this.$t('autoPromotion.saveFailed'));
+ return;
+ }
+ this.$set(taskCard, 'enabled', true);
+ if (taskCard.rawTask) {
+ this.$set(taskCard.rawTask, 'state', '1');
+ this.$set(taskCard.rawTask, 'start_promotion', '1');
+ }
+ }
+ this.openDetail(journal, taskCard);
+ } finally {
+ this.promotionUpdating = false;
+ this.promotionUpdatingJournalId = '';
+ this.promotionUpdatingTaskId = '';
+ }
+ },
_parseJournalDetail(data) {
const journalInfo = data.journal || data || {};
const tpl = journalInfo.template || data.template || {};
@@ -356,6 +404,7 @@ export default {
async loadFactoryTaskSummaryByJournal(journal, userId) {
if (!journal || !journal.journal_id) return;
try {
+ await this.loadFactoryTemplateNamesByJournal(journal.journal_id);
const res = await this.$api.post('api/promotion_factory/getList', {
journal_id: String(journal.journal_id),
user_id: String(userId || ''),
@@ -375,7 +424,12 @@ export default {
const normalizedTasks = (Array.isArray(list) ? list : []).map((task, idx) => {
const type = task && task.type != null ? String(task.type) : '';
const fieldCount = this.getFactoryTaskFieldCount(task);
- const enabled = String(task && task.state != null ? task.state : '0') === '1';
+ const startPromotion =
+ task && task.start_promotion != null && String(task.start_promotion).trim() !== ''
+ ? String(task.start_promotion)
+ : null;
+ const state = task && task.state != null && String(task.state).trim() !== '' ? String(task.state) : null;
+ const enabled = String(startPromotion != null ? startPromotion : state != null ? state : '0') === '1';
return {
cardKey: `${journal.journal_id}_${task.promotion_factory_id || idx}`,
taskId: task.promotion_factory_id || '',
@@ -384,10 +438,11 @@ export default {
enabled: enabled,
switchLoading: false,
initialized: true,
- templateName: task.template_name || (task.template_id ? `Template #${task.template_id}` : 'No Template Configured'),
+ templateName: this.getFactoryTemplateName(task, journal.journal_id),
fieldCount: fieldCount,
fieldCountText: String(fieldCount),
countryScopeLabel: task.country_scope_label || '-',
+ createdAtText: this.formatTaskCreateTime(task),
totalCount: total,
showCount: idx === 0 && total > 0,
rawTask: task
@@ -411,6 +466,37 @@ export default {
});
}
},
+ async loadFactoryTemplateNamesByJournal(journalId) {
+ const key = String(journalId || '');
+ if (!key) return {};
+ if (this.factoryTemplateNameMap[key]) {
+ return this.factoryTemplateNameMap[key];
+ }
+ let map = {};
+ try {
+ const res = await this.$api.post('api/mail_template/listTemplatesAll', { journal_id: key });
+ const payload = (res && res.data) || {};
+ const list = this.findArray(payload) || this.findArray(res) || [];
+ map = (Array.isArray(list) ? list : []).reduce((acc, item) => {
+ const id = item && (item.template_id != null ? item.template_id : item.id);
+ if (id == null) return acc;
+ const name = item.title || item.name || '';
+ if (name) acc[String(id)] = name;
+ return acc;
+ }, {});
+ } catch (e) {
+ map = {};
+ }
+ this.$set(this.factoryTemplateNameMap, key, map);
+ return map;
+ },
+ getFactoryTemplateName(task, journalId) {
+ const tplId = task && task.template_id != null ? String(task.template_id) : '';
+ const journalMap = this.factoryTemplateNameMap[String(journalId || '')] || {};
+ if (tplId && journalMap[tplId]) return journalMap[tplId];
+ if (task && task.template_name) return task.template_name;
+ return tplId ? `Template #${tplId}` : 'No Template Configured';
+ },
getFactoryTaskFieldCount(task) {
if (!task) return 0;
if (task.fetch_fields && typeof task.fetch_fields === 'object') {
@@ -423,6 +509,23 @@ export default {
.filter(Boolean);
return fetchIds.length;
},
+ formatTaskCreateTime(task) {
+ if (!task || typeof task !== 'object') return '';
+ const raw = task.ctime || task.create_time || task.created_at || task.time || '';
+ if (raw == null || String(raw).trim() === '') return '';
+ const n = Number(raw);
+ let dt = null;
+ if (!isNaN(n) && n > 0) {
+ dt = new Date(n > 1e12 ? n : n * 1000);
+ } else {
+ dt = new Date(String(raw));
+ }
+ if (!dt || isNaN(dt.getTime())) return String(raw);
+ 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())}`;
+ },
mapFactoryTaskTypeLabel(type) {
const t = String(type || '');
if (t === '1') return this.$t('autoPromotion.factoryScenarioSolicit');
@@ -443,6 +546,7 @@ export default {
},
async refreshAll() {
this.templateNameMap = {};
+ this.factoryTemplateNameMap = {};
this.allJournals = [];
await this.fetchPromotionJournals();
},
@@ -512,34 +616,7 @@ export default {
country_fetch_ids: (this.selectedCountryIds || []).join(',')
};
},
- async handleSolicitAction(journal) {
- if (!this.isSolicitConfigured(journal)) {
- this.openWizardForJournal(journal);
- return;
- }
- if (!journal.solicit.enabled) {
- this.promotionUpdating = true;
- this.promotionUpdatingJournalId = journal.journal_id;
- try {
- await this.$api.post('api/email_client/setDefaultPromotion', {
- journal_id: String(journal.journal_id),
- default_template_id: String(journal.solicit.templateId),
- default_style_id: String(journal.solicit.styleId),
- start_promotion: '1',
- user_id: localStorage.getItem('U_id')
- });
- await this.refreshJournalByDetail(journal);
- this.openDetail(journal);
- } catch (e) {
- this.$message.error('Update failed');
- } finally {
- this.promotionUpdating = false;
- this.promotionUpdatingJournalId = '';
- }
- } else {
- this.openDetail(journal);
- }
- },
+
openWizardForJournal(journal) {
this.wizardJournal = journal;
const s = journal.solicit || {};
@@ -575,8 +652,15 @@ export default {
this.saving = false;
}
},
- openDetail(journal) {
- this.$router.push({ path: '/autoPromotionLogs', query: { journal_id: String(journal.journal_id) } });
+ openDetail(journal,taskCard) {
+ const taskId = taskCard && taskCard.taskId != null ? String(taskCard.taskId) : '';
+ this.$router.push({
+ path: '/autoPromotionLogs',
+ query: {
+ journal_id: String(journal.journal_id),
+ promotion_factory_id: taskId
+ }
+ });
},
async handleSwitch(journal, type, nextVal) {
if (!this.isSolicitConfigured(journal)) {
@@ -713,6 +797,13 @@ export default {
background: #fafafa;
}
+.no-task-create-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ cursor: pointer;
+}
+
/* 统计卡片基础样式 */
.stat-card {
background: #ffffff;
@@ -732,7 +823,7 @@ export default {
}
.stat-card.is-active {
- border-top: 3px solid #409eff;
+ border-top: 3px solid #67c23a;
}
.stat-card.is-stopped {
@@ -839,6 +930,12 @@ export default {
box-sizing: border-box;
}
+.meta-row {
+ display: flex;
+ align-items: center;
+ min-width: 0;
+}
+
.template-preview-box.is-empty {
color: #909399;
font-style: italic;
@@ -852,6 +949,11 @@ export default {
white-space: nowrap;
}
+.tpl-name-single-line {
+ flex: 1;
+ min-width: 0;
+}
+
/* 统计项 */
.stats-container {
display: grid;
@@ -896,24 +998,46 @@ export default {
/* 底部按钮区 */
.card-actions {
display: flex;
+ flex-direction: column;
gap: 8px;
}
+.task-create-time {
+ font-size: 11px;
+ color: #909399;
+ text-align: right;
+}
+
.action-main-btn {
flex: 1;
- height: 30px;
- background: #006699;
- color: white;
- border: none;
- border-radius: 6px;
- font-weight: 700;
+ height: 24px;
+ background: #eaf3ff;
+ color: #409eff;
+ border: 1px solid #d7e7ff;
+ border-radius: 4px;
+ font-weight: 600;
font-size: 12px;
+ line-height: 22px;
cursor: pointer;
- transition: background 0.2s;
+ transition: all 0.2s;
}
.action-main-btn:hover {
- background: #006699;
+ background: #dcecff;
+ border-color: #c4ddff;
+ color: #2f89ea;
+}
+
+.is-active .action-main-btn {
+ background: #3dbb6a;
+ color: #ffffff;
+ border-color: #3dbb6a;
+}
+
+.is-active .action-main-btn:hover {
+ background: #31a85b;
+ border-color: #31a85b;
+ color: #ffffff;
}
.action-main-btn.secondary {
diff --git a/src/components/page/autoPromotionLogs.vue b/src/components/page/autoPromotionLogs.vue
index d058d20..87cb326 100644
--- a/src/components/page/autoPromotionLogs.vue
+++ b/src/components/page/autoPromotionLogs.vue
@@ -363,6 +363,7 @@ export default {
showFactoryTaskDialog: false,
factoryDialogInitialJournalId: '',
factoryDialogInitialTask: null,
+ routePromotionFactoryId: '',
previewForm: {
id: '',
email: '',
@@ -485,6 +486,11 @@ export default {
async initPage() {
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.routePromotionFactoryId = String(pfid || '');
this.selectedJournalId = String(journal_id);
this.loading = true;
try {
@@ -675,7 +681,29 @@ export default {
},
openFactoryTaskDialogFromLogs() {
this.factoryDialogInitialJournalId = this.selectedJournalId ? String(this.selectedJournalId) : '';
- this.factoryDialogInitialTask = this.list && this.list.length ? { ...this.list[0] } : null;
+ const targetTaskId = String(this.routePromotionFactoryId || '').trim();
+ const first = this.list && this.list.length ? this.list[0] : null;
+ const matched = targetTaskId
+ ? (this.list || []).find((row) => {
+ const pid = row && row.promotion_factory_id != null ? String(row.promotion_factory_id) : '';
+ const rid = row && row.id != null ? String(row.id) : '';
+ const tid = row && row.task_id != null ? String(row.task_id) : '';
+ return pid === targetTaskId || rid === targetTaskId || tid === targetTaskId;
+ }) || first
+ : first;
+ this.factoryDialogInitialTask = matched
+ ? {
+ ...matched,
+ promotion_factory_id:
+ matched.promotion_factory_id != null
+ ? String(matched.promotion_factory_id)
+ : matched.id != null
+ ? String(matched.id)
+ : matched.task_id != null
+ ? String(matched.task_id)
+ : ''
+ }
+ : null;
this.showFactoryTaskDialog = true;
},
@@ -811,8 +839,15 @@ export default {
this.list = rawList.map((item, idx) => {
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;
return {
id: item.id || item.task_id || `task_${idx + 1}`,
+ promotion_factory_id: promotionFactoryId != null ? String(promotionFactoryId) : '',
task_id: String(item.task_id != null ? item.task_id : item.id || ''),
task_name: item.task_name || item.name || '',
scene: item.scene || '',
diff --git a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
index d4ad0bc..7df0c78 100644
--- a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
+++ b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
@@ -44,6 +44,7 @@
v-model="selectedJournalId"
filterable
clearable
+ :disabled="isEditMode"
style="width: 100%"
:placeholder="$t('autoPromotion.factoryJournalPlaceholder')"
@change="onJournalChange"
@@ -111,7 +112,7 @@
-
+
- {{ sendCountMaxHint }}
+ {{ sendCountMaxHint }}
@@ -280,6 +281,9 @@
const API_GET_ACCOUNTS = 'api/email_client/getAccounts';
const API_AVAILABLE_FIELDS = 'api/email_client/getAvailableFields';
const API_FACTORY_ADD = 'api/promotion_factory/add';
+ const API_FACTORY_EDIT = 'api/promotion_factory/edit';
+ const API_FACTORY_DETAIL = 'api/promotion_factory/getDetail';
+ const API_TEMPLATE_LIST_ALL = 'api/mail_template/listTemplatesAll';
const API_GET_COUNT_FOR_EMAIL_IDS = 'api/promotion_factory/getCountForPromotionEmailIds';
const PARTITION_TO_API = {
@@ -327,6 +331,8 @@
factoryConfig: { defaultTemplateId: '', defaultStyleId: '' },
factoryTemplateName: '',
factoryStyleName: '',
+ editingTaskId: '',
+ templateNameMap: {},
showFactoryTemplateDialog: false,
_emailIdsSendLimitTimer: null
};
@@ -393,6 +399,7 @@
},
sendCountMaxHint() {
if (this.sendCountMaxLoading) return '';
+ if (!this.selectedEmailIds || this.selectedEmailIds.length === 0) return '';
if (this.sendLimitFromApi) {
return this.$t('autoPromotion.factorySendMaxFromApi', { max: this.sendCountMax });
}
@@ -439,19 +446,96 @@
await this.loadJournalList();
if (this.journalList.length) {
const preferred = this.initialJournalId != null && this.initialJournalId !== '' ? String(this.initialJournalId) : '';
+ const initialTaskJournalId = this.getInitialTaskJournalId(this.initialTask);
const matched = preferred
? this.journalList.find(function (j) {
return String(j.journal_id) === preferred;
})
: null;
- this.selectedJournalId = matched ? matched.journal_id : this.journalList[0].journal_id;
- await this.onJournalChange(this.selectedJournalId);
- this.applyInitialTaskSeed();
+ const matchedByTask = initialTaskJournalId
+ ? this.journalList.find(function (j) {
+ return String(j.journal_id) === initialTaskJournalId;
+ })
+ : null;
+ this.selectedJournalId = matchedByTask
+ ? matchedByTask.journal_id
+ : matched
+ ? matched.journal_id
+ : this.journalList[0].journal_id;
+ if (this.isEditMode) {
+ await this.loadAccounts(this.selectedJournalId);
+ const initialSeed = await this.loadInitialTaskSeed();
+ await this.applyInitialTaskSeed(initialSeed);
+ await Promise.all([this.loadAvailableFields(this.selectedJournalId), this.refreshSendCountMax()]);
+ this.syncActiveStep();
+ } else {
+ await this.onJournalChange(this.selectedJournalId);
+ }
}
},
- applyInitialTaskSeed() {
+ getInitialTaskId(task) {
+ if (!task || typeof task !== 'object') return '';
+ const id = task.promotion_factory_id != null ? task.promotion_factory_id : task.id != null ? task.id : '';
+ return String(id || '').trim();
+ },
+ getInitialTaskJournalId(task) {
+ if (!task || typeof task !== 'object') return '';
+ const id = task.journal_id != null ? task.journal_id : task.j_id != null ? task.j_id : '';
+ return String(id || '').trim();
+ },
+ async loadInitialTaskSeed() {
const task = this.initialTask && typeof this.initialTask === 'object' ? this.initialTask : null;
+ if (!task) return null;
+ const taskId = this.getInitialTaskId(task);
+ if (!taskId) return task;
+ try {
+ const res = await this.$api.post(API_FACTORY_DETAIL, { promotion_factory_id: taskId });
+ if (!res || (res.code != null && Number(res.code) !== 0)) {
+ return task;
+ }
+ const payload = res.data != null ? res.data : res;
+ if (Array.isArray(payload)) return payload[0] || task;
+ if (payload && typeof payload === 'object') {
+ if (payload.detail && typeof payload.detail === 'object') return payload.detail;
+ if (payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)) return payload.data;
+ const fromArray = this.findArray(payload);
+ if (Array.isArray(fromArray) && fromArray.length) return fromArray[0];
+ return payload;
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ return task;
+ },
+ async loadTemplateNameMapByJournal(journalId) {
+ const key = String(journalId || '');
+ if (!key) return {};
+ if (this.templateNameMap[key]) return this.templateNameMap[key];
+ let map = {};
+ try {
+ const res = await this.$api.post(API_TEMPLATE_LIST_ALL, { journal_id: key });
+ const payload = (res && res.data) || {};
+ const list = this.findArray(payload) || this.findArray(res) || [];
+ map = (Array.isArray(list) ? list : []).reduce(function (acc, item) {
+ const id = item && (item.template_id != null ? item.template_id : item.id);
+ const name = item && (item.title || item.name || '');
+ if (id != null && String(name).trim() !== '') {
+ acc[String(id)] = String(name);
+ }
+ return acc;
+ }, {});
+ } catch (e) {
+ console.error(e);
+ map = {};
+ }
+ this.$set(this.templateNameMap, key, map);
+ return map;
+ },
+ async applyInitialTaskSeed(seedTask) {
+ const task = seedTask && typeof seedTask === 'object' ? seedTask : this.initialTask && typeof this.initialTask === 'object' ? this.initialTask : null;
if (!task) return;
+ const taskId = this.getInitialTaskId(task);
+ this.editingTaskId = taskId || this.editingTaskId;
if (task.type != null && String(task.type) !== '') {
this.factoryType = String(task.type);
}
@@ -491,7 +575,10 @@
this.factoryZoneCountryIds = zoneKeys;
if (task.template_id != null && String(task.template_id) !== '') {
this.factoryConfig.defaultTemplateId = String(task.template_id);
- this.factoryTemplateName = task.template_name || `Template #${task.template_id}`;
+ const tplId = String(task.template_id);
+ const fallbackTplName = task.template_name || '';
+ const templateMap = await this.loadTemplateNameMapByJournal(this.selectedJournalId);
+ this.factoryTemplateName = templateMap[tplId] || fallbackTplName || `Template #${task.template_id}`;
}
if (task.style_id != null && String(task.style_id) !== '') {
this.factoryConfig.defaultStyleId = String(task.style_id);
@@ -896,21 +983,31 @@
return;
}
const pc = this.buildPartitionsAndCountries();
+ const payload = {
+ journal_id: String(this.selectedJournalId),
+ type: String(this.factoryType),
+ expert_type: String(this.expertType || '').trim(),
+ email_ids: this.selectedEmailIds.join(','),
+ send_count: String(this.sendCount),
+ template_id: String(this.factoryConfig.defaultTemplateId),
+ style_id: String(this.factoryConfig.defaultStyleId),
+ fetch_ids: (this.factoryFieldIds || []).join(','),
+ target_partitions: pc.target_partitions,
+ target_country_ids: pc.target_country_ids,
+ start_promotion: '0'
+ };
+ const endpoint = this.isEditMode ? API_FACTORY_EDIT : API_FACTORY_ADD;
+ if (this.isEditMode) {
+ const taskId = this.editingTaskId || this.getInitialTaskId(this.initialTask || {});
+ if (!taskId) {
+ this.$message.error(this.$t('autoPromotion.factorySubmitFailed'));
+ return;
+ }
+ payload.promotion_factory_id = String(taskId);
+ }
this.submitting = true;
try {
- const res = await this.$api.post(API_FACTORY_ADD, {
- journal_id: String(this.selectedJournalId),
- type: String(this.factoryType),
- expert_type: String(this.expertType || '').trim(),
- email_ids: this.selectedEmailIds.join(','),
- send_count: String(this.sendCount),
- template_id: String(this.factoryConfig.defaultTemplateId),
- style_id: String(this.factoryConfig.defaultStyleId),
- fetch_ids: (this.factoryFieldIds || []).join(','),
- target_partitions: pc.target_partitions,
- target_country_ids: pc.target_country_ids,
- start_promotion: '0'
- });
+ const res = await this.$api.post(endpoint, payload);
if (res && Number(res.code) === 0) {
this.$message.success(this.$t('autoPromotion.factorySubmitSuccess'));
this.innerVisible = false;
@@ -1229,7 +1326,7 @@
display: flex;
flex-direction: row;
flex-wrap: wrap;
- align-items: center;
+ align-items: flex-start;
justify-content: flex-start;
gap: 6px 14px;
padding: 10px 16px 10px 18px;
@@ -1244,7 +1341,7 @@
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
- align-items: center;
+ align-items: flex-start;
gap: 8px;
min-width: 0;
}
@@ -1267,6 +1364,13 @@
min-width: 0;
}
+ .parameter-bar .param-inline-controls-send {
+ display: inline-flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 4px;
+ }
+
.parameter-bar .param-item-send {
flex: 0 0 auto;
}
@@ -1284,6 +1388,11 @@
line-height: 32px;
}
+ .parameter-bar .el-input-number--small .el-input__inner {
+ height: 32px;
+ line-height: 32px;
+ }
+
.max-tip {
font-size: 11px;
color: #e6a23c;
@@ -1291,6 +1400,10 @@
max-width: 260px;
}
+ .max-tip-under {
+ display: block;
+ }
+
.factory-required-star {
color: #f56c6c;
font-weight: 700;
@@ -1346,7 +1459,7 @@
}
.more-hint { font-size: 12px; color: #409eff; font-weight: bold; }
-/deep/.el-dialog__body{
+::v-deep .el-dialog__body{
padding-top:15px !important;
padding-bottom:15px !important;
}