This commit is contained in:
2026-04-17 13:35:56 +08:00
parent 7458beb8b2
commit 0d913e90a7
7 changed files with 266 additions and 23 deletions

View File

@@ -19,8 +19,8 @@ const service = axios.create({
// baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换 // baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换
// baseURL: 'http://www.tougao.com/', //测试本地 记得切换 // baseURL: 'http://www.tougao.com/', //测试本地 记得切换
// baseURL: 'http://192.168.110.110/tougao/public/index.php/', // baseURL: 'http://192.168.110.110/tougao/public/index.php/',
baseURL: '/api', //本地 // baseURL: '/api', //本地
// baseURL: '/', //正式 baseURL: '/', //正式
}); });

View File

@@ -330,8 +330,8 @@ const en = {
exportFailed: 'Export failed' exportFailed: 'Export failed'
}, },
countryManagement: { countryManagement: {
title: 'Country maintenance', title: 'Country Management',
keywordPlaceholder: 'Chinese / English / code', keywordPlaceholder: 'Chinese / English / Code',
partitionLabel: 'Partition', partitionLabel: 'Partition',
partitionAll: 'All partitions', partitionAll: 'All partitions',
partition1: 'Partition 1', partition1: 'Partition 1',
@@ -1098,14 +1098,20 @@ colTitle: 'Template title',
changeTemplate: 'Change Template', changeTemplate: 'Change Template',
selectPromotionFields: 'Select Promotion Fields', selectPromotionFields: 'Select Promotion Fields',
choosePromotionFields: 'Choose Fields', choosePromotionFields: 'Choose Fields',
selectPromotionCountry: 'Select Country',
choosePromotionCountry: 'Choose Countries',
selectedCount: 'Selected {count}', selectedCount: 'Selected {count}',
selectAll: 'Select All', selectAll: 'Select All',
clearAll: 'Clear All', clearAll: 'Clear All',
selectPromotionFieldsTip: 'Multiple selection supported; leave empty for no field restriction.', selectPromotionFieldsTip: 'Multiple selection supported; leave empty for no field restriction.',
selectPromotionCountryTip: 'Multiple selection supported; leave empty for no country restriction. Uses the same API as fields until a dedicated country list is available.',
fieldSearchPlaceholder: 'Search promotion fields', fieldSearchPlaceholder: 'Search promotion fields',
countrySearchPlaceholder: 'Search countries',
noFieldMatch: 'No matching fields', noFieldMatch: 'No matching fields',
noCountryMatch: 'No matching countries',
confirm: 'Confirm', confirm: 'Confirm',
fieldsSaved: 'Promotion fields saved', fieldsSaved: 'Promotion fields saved',
countriesSaved: 'Promotion countries saved',
confirmAndEnable: 'Confirm and Enable', confirmAndEnable: 'Confirm and Enable',
onlySaveConfig: 'Save configuration only', onlySaveConfig: 'Save configuration only',
enableNowNextDay: 'Enable auto promotion now (starts next day)' enableNowNextDay: 'Enable auto promotion now (starts next day)'

View File

@@ -1083,14 +1083,20 @@ const zh = {
changeTemplate: '更换模版', changeTemplate: '更换模版',
selectPromotionFields: '选择推广领域', selectPromotionFields: '选择推广领域',
choosePromotionFields: '选择领域', choosePromotionFields: '选择领域',
selectPromotionCountry: '选择国家',
choosePromotionCountry: '选择国家',
selectedCount: '已选 {count} 项', selectedCount: '已选 {count} 项',
selectAll: '全选', selectAll: '全选',
clearAll: '取消全选', clearAll: '取消全选',
selectPromotionFieldsTip: '可多选;未选择则不限制推广领域。', selectPromotionFieldsTip: '可多选;未选择则不限制推广领域。',
selectPromotionCountryTip: '可多选;未选择则不限制国家。与领域接口一致,后续可对接独立国家数据。',
fieldSearchPlaceholder: '搜索推广领域', fieldSearchPlaceholder: '搜索推广领域',
countrySearchPlaceholder: '搜索国家',
noFieldMatch: '没有匹配的领域', noFieldMatch: '没有匹配的领域',
noCountryMatch: '没有匹配的国家',
confirm: '确定', confirm: '确定',
fieldsSaved: '推广领域已保存', fieldsSaved: '推广领域已保存',
countriesSaved: '推广国家已保存',
confirmAndEnable: '确认并开启', confirmAndEnable: '确认并开启',
onlySaveConfig: '仅保存配置', onlySaveConfig: '仅保存配置',
enableNowNextDay: '立即激活自动推广(次日开始自动推广)' enableNowNextDay: '立即激活自动推广(次日开始自动推广)'

View File

@@ -99,7 +99,9 @@
:config="wizardConfig" :config="wizardConfig"
:wizardStartDate.sync="wizardStartDate" :wizardStartDate.sync="wizardStartDate"
:selectedFieldIds.sync="selectedFieldIds" :selectedFieldIds.sync="selectedFieldIds"
:selectedCountryIds.sync="selectedCountryIds"
:availableFields="availableFields" :availableFields="availableFields"
:availableCountries="availableCountries"
:fieldsLoading="fieldsLoading" :fieldsLoading="fieldsLoading"
:fieldsSaving="fieldsSaving" :fieldsSaving="fieldsSaving"
:currentJournalName="wizardJournal ? wizardJournal.title : ''" :currentJournalName="wizardJournal ? wizardJournal.title : ''"
@@ -110,6 +112,7 @@
:title="`${$t('autoPromotion.journalManage')}: ${wizardJournal ? wizardJournal.title : ''}`" :title="`${$t('autoPromotion.journalManage')}: ${wizardJournal ? wizardJournal.title : ''}`"
@open-template-selector="openTemplateSelector" @open-template-selector="openTemplateSelector"
@confirm-fields="savePromotionFieldsNow" @confirm-fields="savePromotionFieldsNow"
@confirm-countries="savePromotionCountriesNow"
@cancel="showWizardDialog = false" @cancel="showWizardDialog = false"
@confirm="saveWizardConfig" @confirm="saveWizardConfig"
/> />
@@ -156,7 +159,9 @@ export default {
selectedTemplateName: '', selectedTemplateName: '',
selectedStyleName: '', selectedStyleName: '',
selectedFieldIds: [], selectedFieldIds: [],
selectedCountryIds: [],
availableFields: [], availableFields: [],
availableCountries: [],
fieldsLoading: false, fieldsLoading: false,
fieldsSaving: false, fieldsSaving: false,
templateDialogInitialStyleId: '', templateDialogInitialStyleId: '',
@@ -326,10 +331,33 @@ export default {
if (values.length && Array.isArray(values[0])) return values[0]; if (values.length && Array.isArray(values[0])) return values[0];
return null; return null;
}, },
/** 与 getJournalPromotionFields 返回体对齐;后端未返回时为空数组 */
parseCountryIdsFromPromotionPayload(selectedPayload) {
if (!selectedPayload || typeof selectedPayload !== 'object') return [];
const raw =
selectedPayload.country_fetch_ids != null
? selectedPayload.country_fetch_ids
: selectedPayload.country_ids != null
? selectedPayload.country_ids
: '';
if (typeof raw === 'string' && raw.trim()) {
return raw.split(',').map((s) => s.trim()).filter(Boolean).map(String);
}
return [];
},
journalPromotionFieldsPayload(journalId) {
return {
journal_id: String(journalId),
fetch_ids: (this.selectedFieldIds || []).join(','),
country_fetch_ids: (this.selectedCountryIds || []).join(',')
};
},
async loadPromotionFields(journalId) { async loadPromotionFields(journalId) {
this.fieldsLoading = true; this.fieldsLoading = true;
this.availableFields = []; this.availableFields = [];
this.availableCountries = [];
this.selectedFieldIds = []; this.selectedFieldIds = [];
this.selectedCountryIds = [];
try { try {
const availableRes = await this.$api.post('api/email_client/getAvailableFields', { journal_id: String(journalId) }); const availableRes = await this.$api.post('api/email_client/getAvailableFields', { journal_id: String(journalId) });
@@ -344,8 +372,10 @@ export default {
const label = item.field || item.title || item.name || item.label || String(id); const label = item.field || item.title || item.name || item.label || String(id);
return { id: String(id), label }; return { id: String(id), label };
}); });
this.availableCountries = this.availableFields.map((x) => ({ id: String(x.id), label: x.label }));
} catch (e) { } catch (e) {
this.availableFields = []; this.availableFields = [];
this.availableCountries = [];
} }
try { try {
@@ -364,10 +394,12 @@ export default {
} else if (typeof selectedPayload.fetch_ids === 'string') { } else if (typeof selectedPayload.fetch_ids === 'string') {
this.selectedFieldIds = selectedPayload.fetch_ids.split(',').map((s) => s.trim()).filter(Boolean); this.selectedFieldIds = selectedPayload.fetch_ids.split(',').map((s) => s.trim()).filter(Boolean);
} }
this.selectedCountryIds = this.parseCountryIdsFromPromotionPayload(selectedPayload);
console.log('[getJournalPromotionFields] parsed selected:', this.selectedFieldIds); console.log('[getJournalPromotionFields] parsed selected:', this.selectedFieldIds);
} catch (e) { } catch (e) {
console.error('[getJournalPromotionFields] error:', e); console.error('[getJournalPromotionFields] error:', e);
this.selectedFieldIds = []; this.selectedFieldIds = [];
this.selectedCountryIds = [];
} }
this.fieldsLoading = false; this.fieldsLoading = false;
@@ -376,10 +408,10 @@ export default {
if (!this.wizardJournal || !this.wizardJournal.journal_id) return; if (!this.wizardJournal || !this.wizardJournal.journal_id) return;
this.fieldsSaving = true; this.fieldsSaving = true;
try { try {
await this.$api.post('api/email_client/setJournalPromotionFields', { await this.$api.post(
journal_id: String(this.wizardJournal.journal_id), 'api/email_client/setJournalPromotionFields',
fetch_ids: (this.selectedFieldIds || []).join(',') this.journalPromotionFieldsPayload(this.wizardJournal.journal_id)
}); );
this.$message.success(this.$t('autoPromotion.fieldsSaved')); this.$message.success(this.$t('autoPromotion.fieldsSaved'));
} catch (e) { } catch (e) {
this.$message.error(this.$t('autoPromotion.saveFailed')); this.$message.error(this.$t('autoPromotion.saveFailed'));
@@ -387,6 +419,21 @@ export default {
this.fieldsSaving = false; this.fieldsSaving = false;
} }
}, },
async savePromotionCountriesNow() {
if (!this.wizardJournal || !this.wizardJournal.journal_id) return;
this.fieldsSaving = true;
try {
await this.$api.post(
'api/email_client/setJournalPromotionFields',
this.journalPromotionFieldsPayload(this.wizardJournal.journal_id)
);
this.$message.success(this.$t('autoPromotion.countriesSaved'));
} catch (e) {
this.$message.error(this.$t('autoPromotion.saveFailed'));
} finally {
this.fieldsSaving = false;
}
},
async handleSolicitAction(journal) { async handleSolicitAction(journal) {
if (!this.isSolicitConfigured(journal)) { if (!this.isSolicitConfigured(journal)) {
@@ -437,7 +484,9 @@ export default {
this.selectedStyleName = s.styleName || ''; this.selectedStyleName = s.styleName || '';
this.selectedTemplateThumbHtml = `<div style="zoom:0.18; pointer-events:none; user-select:none;">${s.html || ''}</div>`; this.selectedTemplateThumbHtml = `<div style="zoom:0.18; pointer-events:none; user-select:none;">${s.html || ''}</div>`;
this.selectedFieldIds = []; this.selectedFieldIds = [];
this.selectedCountryIds = [];
this.availableFields = []; this.availableFields = [];
this.availableCountries = [];
if (journal && journal.journal_id) { if (journal && journal.journal_id) {
await this.loadPromotionFields(journal.journal_id); await this.loadPromotionFields(journal.journal_id);
} }
@@ -473,10 +522,10 @@ export default {
start_promotion: this.wizardConfig.enabled ? '1' : '0', start_promotion: this.wizardConfig.enabled ? '1' : '0',
user_id: userId user_id: userId
}); });
await this.$api.post('api/email_client/setJournalPromotionFields', { await this.$api.post(
journal_id: String(this.wizardJournal.journal_id), 'api/email_client/setJournalPromotionFields',
fetch_ids: (this.selectedFieldIds || []).join(',') this.journalPromotionFieldsPayload(this.wizardJournal.journal_id)
}); );
await this.refreshJournalByDetail(this.wizardJournal); await this.refreshJournalByDetail(this.wizardJournal);
this.$message.success(this.$t('autoPromotion.configSaved')); this.$message.success(this.$t('autoPromotion.configSaved'));
this.showWizardDialog = false; this.showWizardDialog = false;

View File

@@ -41,7 +41,9 @@
:config="config" :config="config"
:wizardStartDate.sync="wizardStartDate" :wizardStartDate.sync="wizardStartDate"
:selectedFieldIds.sync="selectedFieldIds" :selectedFieldIds.sync="selectedFieldIds"
:selectedCountryIds.sync="selectedCountryIds"
:availableFields="availableFields" :availableFields="availableFields"
:availableCountries="availableCountries"
:fieldsLoading="fieldsLoading" :fieldsLoading="fieldsLoading"
:fieldsSaving="fieldsSaving" :fieldsSaving="fieldsSaving"
:currentJournalName="currentJournalName" :currentJournalName="currentJournalName"
@@ -52,6 +54,7 @@
:title="$t('autoPromotion.title')" :title="$t('autoPromotion.title')"
@open-template-selector="showTemplateDialog = true" @open-template-selector="showTemplateDialog = true"
@confirm-fields="savePromotionFieldsNow" @confirm-fields="savePromotionFieldsNow"
@confirm-countries="savePromotionCountriesNow"
@confirm="completeInitialization" @confirm="completeInitialization"
/> />
</el-card> </el-card>
@@ -210,7 +213,9 @@
:config="config" :config="config"
:wizardStartDate.sync="wizardStartDate" :wizardStartDate.sync="wizardStartDate"
:selectedFieldIds.sync="selectedFieldIds" :selectedFieldIds.sync="selectedFieldIds"
:selectedCountryIds.sync="selectedCountryIds"
:availableFields="availableFields" :availableFields="availableFields"
:availableCountries="availableCountries"
:fieldsLoading="fieldsLoading" :fieldsLoading="fieldsLoading"
:fieldsSaving="fieldsSaving" :fieldsSaving="fieldsSaving"
:currentJournalName="currentJournalName" :currentJournalName="currentJournalName"
@@ -221,6 +226,7 @@
:title="$t('autoPromotion.title')" :title="$t('autoPromotion.title')"
@open-template-selector="showTemplateDialog = true" @open-template-selector="showTemplateDialog = true"
@confirm-fields="savePromotionFieldsNow" @confirm-fields="savePromotionFieldsNow"
@confirm-countries="savePromotionCountriesNow"
@cancel="showWizardDialog = false" @cancel="showWizardDialog = false"
@confirm="completeInitialization" @confirm="completeInitialization"
/> />
@@ -339,7 +345,9 @@ export default {
templateDialogInitialTemplateId: '', templateDialogInitialTemplateId: '',
togglingTaskId: '', togglingTaskId: '',
selectedFieldIds: [], selectedFieldIds: [],
selectedCountryIds: [],
availableFields: [], availableFields: [],
availableCountries: [],
fieldsLoading: false, fieldsLoading: false,
fieldsSaving: false, fieldsSaving: false,
previewForm: { previewForm: {
@@ -552,10 +560,32 @@ export default {
if (values.length && Array.isArray(values[0])) return values[0]; if (values.length && Array.isArray(values[0])) return values[0];
return null; return null;
}, },
parseCountryIdsFromPromotionPayload(selectedPayload) {
if (!selectedPayload || typeof selectedPayload !== 'object') return [];
const raw =
selectedPayload.country_fetch_ids != null
? selectedPayload.country_fetch_ids
: selectedPayload.country_ids != null
? selectedPayload.country_ids
: '';
if (typeof raw === 'string' && raw.trim()) {
return raw.split(',').map((s) => s.trim()).filter(Boolean).map(String);
}
return [];
},
journalPromotionFieldsPayload(journalId) {
return {
journal_id: String(journalId),
fetch_ids: (this.selectedFieldIds || []).join(','),
country_fetch_ids: (this.selectedCountryIds || []).join(',')
};
},
async loadPromotionFields(journalId) { async loadPromotionFields(journalId) {
this.fieldsLoading = true; this.fieldsLoading = true;
this.availableFields = []; this.availableFields = [];
this.availableCountries = [];
this.selectedFieldIds = []; this.selectedFieldIds = [];
this.selectedCountryIds = [];
try { try {
const availableRes = await this.$api.post('api/email_client/getAvailableFields', { journal_id: String(journalId) }); const availableRes = await this.$api.post('api/email_client/getAvailableFields', { journal_id: String(journalId) });
const availablePayload = (availableRes && availableRes.data) || availableRes || {}; const availablePayload = (availableRes && availableRes.data) || availableRes || {};
@@ -566,8 +596,10 @@ export default {
const label = item.field || item.title || item.name || item.label || String(id); const label = item.field || item.title || item.name || item.label || String(id);
return { id: String(id), label }; return { id: String(id), label };
}); });
this.availableCountries = this.availableFields.map((x) => ({ id: String(x.id), label: x.label }));
} catch (e) { } catch (e) {
this.availableFields = []; this.availableFields = [];
this.availableCountries = [];
} }
try { try {
const selectedRes = await this.$api.post('api/email_client/getJournalPromotionFields', { journal_id: String(journalId) }); const selectedRes = await this.$api.post('api/email_client/getJournalPromotionFields', { journal_id: String(journalId) });
@@ -580,8 +612,10 @@ export default {
} else if (typeof selectedPayload.fetch_ids === 'string') { } else if (typeof selectedPayload.fetch_ids === 'string') {
this.selectedFieldIds = selectedPayload.fetch_ids.split(',').map((s) => s.trim()).filter(Boolean); this.selectedFieldIds = selectedPayload.fetch_ids.split(',').map((s) => s.trim()).filter(Boolean);
} }
this.selectedCountryIds = this.parseCountryIdsFromPromotionPayload(selectedPayload);
} catch (e) { } catch (e) {
this.selectedFieldIds = []; this.selectedFieldIds = [];
this.selectedCountryIds = [];
} }
this.fieldsLoading = false; this.fieldsLoading = false;
}, },
@@ -589,10 +623,10 @@ export default {
if (!this.selectedJournalId) return; if (!this.selectedJournalId) return;
this.fieldsSaving = true; this.fieldsSaving = true;
try { try {
await this.$api.post('api/email_client/setJournalPromotionFields', { await this.$api.post(
journal_id: String(this.selectedJournalId), 'api/email_client/setJournalPromotionFields',
fetch_ids: (this.selectedFieldIds || []).join(',') this.journalPromotionFieldsPayload(this.selectedJournalId)
}); );
this.$message.success(this.$t('autoPromotion.fieldsSaved')); this.$message.success(this.$t('autoPromotion.fieldsSaved'));
} catch (e) { } catch (e) {
this.$message.error(this.$t('autoPromotion.saveFailed')); this.$message.error(this.$t('autoPromotion.saveFailed'));
@@ -600,6 +634,21 @@ export default {
this.fieldsSaving = false; this.fieldsSaving = false;
} }
}, },
async savePromotionCountriesNow() {
if (!this.selectedJournalId) return;
this.fieldsSaving = true;
try {
await this.$api.post(
'api/email_client/setJournalPromotionFields',
this.journalPromotionFieldsPayload(this.selectedJournalId)
);
this.$message.success(this.$t('autoPromotion.countriesSaved'));
} catch (e) {
this.$message.error(this.$t('autoPromotion.saveFailed'));
} finally {
this.fieldsSaving = false;
}
},
async openWizardDialog() { async openWizardDialog() {
this.wizardStep = 0; this.wizardStep = 0;
if (this.config && this.config.start_date) { if (this.config && this.config.start_date) {
@@ -704,10 +753,10 @@ export default {
this.showWizardDialog = false; this.showWizardDialog = false;
this.fetchList(); this.fetchList();
await this.$api.post(API.saveConfig, payload); await this.$api.post(API.saveConfig, payload);
await this.$api.post('api/email_client/setJournalPromotionFields', { await this.$api.post(
journal_id: String(this.selectedJournalId || ''), 'api/email_client/setJournalPromotionFields',
fetch_ids: (this.selectedFieldIds || []).join(',') this.journalPromotionFieldsPayload(this.selectedJournalId || '')
}); );
this.$message.success(this.$t('autoPromotionLogs.configUpdated')); this.$message.success(this.$t('autoPromotionLogs.configUpdated'));
} finally { } finally {
this.saving = false; this.saving = false;

View File

@@ -17,11 +17,14 @@
:selectedTemplateName="selectedTemplateName" :selectedTemplateName="selectedTemplateName"
:selectedStyleName="selectedStyleName" :selectedStyleName="selectedStyleName"
:availableFields="availableFields" :availableFields="availableFields"
:availableCountries="availableCountries"
:fieldsLoading="fieldsLoading" :fieldsLoading="fieldsLoading"
:fieldsSaving="fieldsSaving" :fieldsSaving="fieldsSaving"
:selectedFieldIds.sync="selectedFieldIdsProxy" :selectedFieldIds.sync="selectedFieldIdsProxy"
:selectedCountryIds.sync="selectedCountryIdsProxy"
@open-template-selector="emitOpenTemplateSelector" @open-template-selector="emitOpenTemplateSelector"
@confirm-fields="emitConfirmFields" @confirm-fields="emitConfirmFields"
@confirm-countries="emitConfirmCountries"
@update:wizardStartDate="onWizardStartDateUpdate" @update:wizardStartDate="onWizardStartDateUpdate"
/> />
@@ -49,11 +52,14 @@
:selectedTemplateName="selectedTemplateName" :selectedTemplateName="selectedTemplateName"
:selectedStyleName="selectedStyleName" :selectedStyleName="selectedStyleName"
:availableFields="availableFields" :availableFields="availableFields"
:availableCountries="availableCountries"
:fieldsLoading="fieldsLoading" :fieldsLoading="fieldsLoading"
:fieldsSaving="fieldsSaving" :fieldsSaving="fieldsSaving"
:selectedFieldIds.sync="selectedFieldIdsProxy" :selectedFieldIds.sync="selectedFieldIdsProxy"
:selectedCountryIds.sync="selectedCountryIdsProxy"
@open-template-selector="emitOpenTemplateSelector" @open-template-selector="emitOpenTemplateSelector"
@confirm-fields="emitConfirmFields" @confirm-fields="emitConfirmFields"
@confirm-countries="emitConfirmCountries"
@update:wizardStartDate="onWizardStartDateUpdate" @update:wizardStartDate="onWizardStartDateUpdate"
/> />
<div class="dialog-footer"> <div class="dialog-footer">
@@ -89,9 +95,11 @@ export default {
selectedStyleName: { type: String, default: '' }, selectedStyleName: { type: String, default: '' },
saving: { type: Boolean, default: false }, saving: { type: Boolean, default: false },
availableFields: { type: Array, default: () => [] }, availableFields: { type: Array, default: () => [] },
availableCountries: { type: Array, default: () => [] },
fieldsLoading: { type: Boolean, default: false }, fieldsLoading: { type: Boolean, default: false },
fieldsSaving: { type: Boolean, default: false }, fieldsSaving: { type: Boolean, default: false },
selectedFieldIds: { type: Array, default: () => [] } selectedFieldIds: { type: Array, default: () => [] },
selectedCountryIds: { type: Array, default: () => [] }
}, },
computed: { computed: {
dialogVisible: { dialogVisible: {
@@ -118,6 +126,14 @@ export default {
this.$emit('update:selectedFieldIds', val); this.$emit('update:selectedFieldIds', val);
} }
}, },
selectedCountryIdsProxy: {
get() {
return this.selectedCountryIds;
},
set(val) {
this.$emit('update:selectedCountryIds', val);
}
},
canConfirm() { canConfirm() {
const id = this.config && this.config.defaultTemplateId != null ? String(this.config.defaultTemplateId) : ''; const id = this.config && this.config.defaultTemplateId != null ? String(this.config.defaultTemplateId) : '';
return id !== '' && id !== '0'; return id !== '' && id !== '0';
@@ -130,6 +146,9 @@ export default {
emitConfirmFields() { emitConfirmFields() {
this.$emit('confirm-fields'); this.$emit('confirm-fields');
}, },
emitConfirmCountries() {
this.$emit('confirm-countries');
},
onWizardStartDateUpdate(val) { onWizardStartDateUpdate(val) {
// 由内容组件回传日期,继续走父组件的 .sync 链路 // 由内容组件回传日期,继续走父组件的 .sync 链路
this.wizardStartDateProxy = val; this.wizardStartDateProxy = val;

View File

@@ -101,6 +101,33 @@
<el-divider></el-divider> <el-divider></el-divider>
<!-- <section class="form-section">
<h4 class="section-title">
<i class="el-icon-location-outline"></i> 3. {{ $t('autoPromotion.selectPromotionCountry') }}
<span class="selected-count">
{{ $t('autoPromotion.selectedCount', { count: selectedCountryIdsProxy.length }) }}
</span>
<el-button
size="small"
type="primary"
plain
icon="el-icon-edit-outline"
class="section-action-btn"
@click="countryDialogVisible = true"
>
{{ $t('autoPromotion.choosePromotionCountry') }}
</el-button>
</h4>
<div class="status-confirm-box">
<div v-if="selectedCountryTagRows.length" class="selected-tags">
<el-tag v-for="row in selectedCountryTagRows" :key="'c-' + row.id" size="mini" type="info" effect="plain">{{ row.text }}</el-tag>
</div>
<div class="field-tip">{{ $t('autoPromotion.selectPromotionCountryTip') }}</div>
</div>
</section>
<el-divider></el-divider> -->
<section class="form-section"> <section class="form-section">
<h4 class="section-title"> <h4 class="section-title">
<i class="el-icon-finished"></i> 3. {{ $t('autoPromotion.confirmAndEnable') }} <i class="el-icon-finished"></i> 3. {{ $t('autoPromotion.confirmAndEnable') }}
@@ -150,6 +177,41 @@
<el-button size="small" type="primary" :loading="fieldsSaving" @click="emitConfirmFields">{{ $t('autoPromotion.confirm') }}</el-button> <el-button size="small" type="primary" :loading="fieldsSaving" @click="emitConfirmFields">{{ $t('autoPromotion.confirm') }}</el-button>
</span> </span>
</el-dialog> </el-dialog>
<el-dialog
:title="$t('autoPromotion.selectPromotionCountry')"
:visible.sync="countryDialogVisible"
width="1200px"
append-to-body
:close-on-click-modal="false"
>
<div class="field-dialog-toolbar">
<el-input
v-model="countrySearchText"
size="small"
clearable
class="field-search-input"
prefix-icon="el-icon-search"
:placeholder="$t('autoPromotion.countrySearchPlaceholder')"
/>
<el-button size="mini" @click="selectAllCountries">{{ $t('autoPromotion.selectAll') }}</el-button>
<el-button size="mini" @click="clearAllCountries">{{ $t('autoPromotion.clearAll') }}</el-button>
</div>
<div class="field-dialog-body" v-loading="fieldsLoading">
<el-checkbox-group v-model="selectedCountryIdsProxy" class="field-check-group">
<el-checkbox v-for="c in sortedFilteredCountries" :key="'country-' + String(c.id)" :label="String(c.id)">
{{ c.label }}
</el-checkbox>
</el-checkbox-group>
<div v-if="!fieldsLoading && sortedFilteredCountries.length === 0" class="field-empty-tip">
{{ $t('autoPromotion.noCountryMatch') }}
</div>
</div>
<span slot="footer">
<el-button size="small" @click="countryDialogVisible = false">{{ $t('autoPromotion.cancel') }}</el-button>
<el-button size="small" type="primary" :loading="fieldsSaving" @click="emitConfirmCountries">{{ $t('autoPromotion.confirm') }}</el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
@@ -159,7 +221,9 @@ export default {
data() { data() {
return { return {
fieldSearchText: '', fieldSearchText: '',
fieldDialogVisible: false fieldDialogVisible: false,
countrySearchText: '',
countryDialogVisible: false
}; };
}, },
props: { props: {
@@ -170,9 +234,11 @@ export default {
selectedTemplateName: { type: String, default: '' }, selectedTemplateName: { type: String, default: '' },
selectedStyleName: { type: String, default: '' }, selectedStyleName: { type: String, default: '' },
availableFields: { type: Array, default: () => [] }, availableFields: { type: Array, default: () => [] },
availableCountries: { type: Array, default: () => [] },
fieldsLoading: { type: Boolean, default: false }, fieldsLoading: { type: Boolean, default: false },
fieldsSaving: { type: Boolean, default: false }, fieldsSaving: { type: Boolean, default: false },
selectedFieldIds: { type: Array, default: () => [] } selectedFieldIds: { type: Array, default: () => [] },
selectedCountryIds: { type: Array, default: () => [] }
}, },
computed: { computed: {
hasSelectedTemplate() { hasSelectedTemplate() {
@@ -190,6 +256,14 @@ export default {
this.$emit('update:selectedFieldIds', val); this.$emit('update:selectedFieldIds', val);
} }
}, },
selectedCountryIdsProxy: {
get() {
return this.selectedCountryIds;
},
set(val) {
this.$emit('update:selectedCountryIds', val);
}
},
sortedFilteredFields() { sortedFilteredFields() {
const kwRaw = String(this.fieldSearchText || ''); const kwRaw = String(this.fieldSearchText || '');
const normalize = (s) => const normalize = (s) =>
@@ -211,12 +285,42 @@ export default {
}); });
return list.slice().sort((a, b) => String(a.label || '').localeCompare(String(b.label || ''))); return list.slice().sort((a, b) => String(a.label || '').localeCompare(String(b.label || '')));
}, },
sortedFilteredCountries() {
const kwRaw = String(this.countrySearchText || '');
const normalize = (s) =>
String(s || '')
.trim()
.replace(/\s+/g, ' ')
.toLowerCase();
const tokens = kwRaw
? kwRaw
.split(/[\r\n,;]+/g)
.map((s) => normalize(s))
.filter(Boolean)
: [];
const list = (this.availableCountries || []).filter((item) => {
if (!tokens.length) return true;
const label = normalize(item.label || '');
return tokens.some((t) => t === label);
});
return list.slice().sort((a, b) => String(a.label || '').localeCompare(String(b.label || '')));
},
selectedFieldLabels() { selectedFieldLabels() {
const map = {}; const map = {};
(this.availableFields || []).forEach((i) => { map[String(i.id)] = i.label; }); (this.availableFields || []).forEach((i) => { map[String(i.id)] = i.label; });
return (this.selectedFieldIdsProxy || []) return (this.selectedFieldIdsProxy || [])
.map((id) => map[String(id)]) .map((id) => map[String(id)])
.filter(Boolean); .filter(Boolean);
},
selectedCountryTagRows() {
const map = {};
(this.availableCountries || []).forEach((i) => {
map[String(i.id)] = i.label;
});
return (this.selectedCountryIdsProxy || []).map((id) => ({
id: String(id),
text: map[String(id)] != null && map[String(id)] !== '' ? map[String(id)] : String(id)
}));
} }
}, },
methods: { methods: {
@@ -232,6 +336,16 @@ export default {
emitConfirmFields() { emitConfirmFields() {
this.$emit('confirm-fields'); this.$emit('confirm-fields');
this.fieldDialogVisible = false; this.fieldDialogVisible = false;
},
selectAllCountries() {
this.selectedCountryIdsProxy = (this.availableCountries || []).map((c) => String(c.id));
},
clearAllCountries() {
this.selectedCountryIdsProxy = [];
},
emitConfirmCountries() {
this.$emit('confirm-countries');
this.countryDialogVisible = false;
} }
} }
}; };