fix(web): 修复编辑模板名称输入组件的标签嵌套问题
- 移除 el-input 标签的多余闭合标签 - 确保模板名称输入框结构符合规范 - 改善表单项的渲染正确性和稳定性
This commit is contained in:
parent
c0578b1d24
commit
18e2e372a0
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
trigger: always_on
|
||||||
|
---
|
||||||
|
|
||||||
|
# 项目规则
|
||||||
|
|
||||||
|
## 需求文档
|
||||||
|
|
||||||
|
@docs/需求文档.md
|
||||||
|
|
@ -175,7 +175,7 @@
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-dialog v-model="editVisible" title="编辑模板" :width="editWidth">
|
<el-dialog v-model="editVisible" title="编辑模板" :width="editWidth">
|
||||||
<el-form ref="editFormRef" :model="edit" :rules="editRules" label-width="110px">
|
<el-form ref="editFormRef" :model="edit" :rules="editRules" label-width="110px">
|
||||||
<el-form-item label="模板名称" required show-message prop="name"><el-input v-model="edit.name" /></el-input></el-form-item>
|
<el-form-item label="模板名称" required show-message prop="name"><el-input v-model="edit.name" /></el-form-item>
|
||||||
<el-form-item label="数据源">
|
<el-form-item label="数据源">
|
||||||
<el-select v-model="edit.datasource" placeholder="选择" :teleported="false" style="width:160px">
|
<el-select v-model="edit.datasource" placeholder="选择" :teleported="false" style="width:160px">
|
||||||
<el-option v-for="opt in datasourceOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
<el-option v-for="opt in datasourceOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||||
|
|
|
||||||
111
web/main.js
111
web/main.js
|
|
@ -1,5 +1,23 @@
|
||||||
const { createApp, reactive } = Vue;
|
const { createApp, reactive } = Vue;
|
||||||
|
|
||||||
|
// ==================== 常量定义 ====================
|
||||||
|
const CONSTANTS = {
|
||||||
|
// 轮询间隔(毫秒)
|
||||||
|
JOBS_POLL_INTERVAL: 1000,
|
||||||
|
// 树形选择器渲染延迟(毫秒)
|
||||||
|
TREE_RENDER_DELAY: 100,
|
||||||
|
// 编辑模式树渲染延迟(毫秒)
|
||||||
|
TREE_EDIT_RENDER_DELAY: 300,
|
||||||
|
// 对话框最小宽度
|
||||||
|
DIALOG_MIN_WIDTH: 500,
|
||||||
|
// 对话框最大宽度
|
||||||
|
DIALOG_MAX_WIDTH: 1400,
|
||||||
|
// 默认对话框宽度
|
||||||
|
DIALOG_DEFAULT_WIDTH: 900,
|
||||||
|
// 默认编辑对话框宽度
|
||||||
|
DIALOG_EDIT_DEFAULT_WIDTH: 900
|
||||||
|
};
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
setup() {
|
setup() {
|
||||||
// ==================== 配置和工具函数 ====================
|
// ==================== 配置和工具函数 ====================
|
||||||
|
|
@ -70,8 +88,8 @@ const app = createApp({
|
||||||
editVisible: false,
|
editVisible: false,
|
||||||
exportVisible: false,
|
exportVisible: false,
|
||||||
exportSubmitting: false,
|
exportSubmitting: false,
|
||||||
createWidth: localStorage.getItem('tplDialogWidth') || '900px',
|
createWidth: localStorage.getItem('tplDialogWidth') || CONSTANTS.DIALOG_DEFAULT_WIDTH + 'px',
|
||||||
editWidth: localStorage.getItem('tplEditDialogWidth') || '900px',
|
editWidth: localStorage.getItem('tplEditDialogWidth') || CONSTANTS.DIALOG_EDIT_DEFAULT_WIDTH + 'px',
|
||||||
edit: {
|
edit: {
|
||||||
id: null,
|
id: null,
|
||||||
name: '',
|
name: '',
|
||||||
|
|
@ -120,10 +138,21 @@ const app = createApp({
|
||||||
const metaTableLabels = Vue.ref({});
|
const metaTableLabels = Vue.ref({});
|
||||||
const recommendedMeta = Vue.ref([]);
|
const recommendedMeta = Vue.ref([]);
|
||||||
|
|
||||||
const loadFieldsMeta = async (ds, type) => {
|
/**
|
||||||
|
* 加载字段元数据和推荐字段(合并为单次 API 调用)
|
||||||
|
* @param {string} ds - 数据源
|
||||||
|
* @param {number} type - 订单类型
|
||||||
|
* @returns {Promise<string[]>} 推荐字段列表
|
||||||
|
*/
|
||||||
|
const loadFieldsMetaAndRecommended = async (ds, type) => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(API_BASE + '/api/metadata/fields?datasource=' + encodeURIComponent(ds) + '&order_type=' + encodeURIComponent(String(type || 0)));
|
const res = await fetch(API_BASE + '/api/metadata/fields?datasource=' + encodeURIComponent(ds) + '&order_type=' + encodeURIComponent(String(type || 0)));
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载字段元数据失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
|
// 处理表字段映射
|
||||||
const tables = Array.isArray(data?.data?.tables) ? data.data.tables : (Array.isArray(data?.tables) ? data.tables : []);
|
const tables = Array.isArray(data?.data?.tables) ? data.data.tables : (Array.isArray(data?.tables) ? data.tables : []);
|
||||||
const m = {};
|
const m = {};
|
||||||
const tblLabels = {};
|
const tblLabels = {};
|
||||||
|
|
@ -135,25 +164,24 @@ const app = createApp({
|
||||||
});
|
});
|
||||||
metaFM.value = m;
|
metaFM.value = m;
|
||||||
metaTableLabels.value = tblLabels;
|
metaTableLabels.value = tblLabels;
|
||||||
} catch (_e) {
|
|
||||||
metaFM.value = {};
|
// 处理推荐字段
|
||||||
metaTableLabels.value = {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadRecommendedFields = async (ds, orderType) => {
|
|
||||||
try {
|
|
||||||
const res = await fetch(API_BASE + '/api/metadata/fields?datasource=' + encodeURIComponent(ds) + '&order_type=' + encodeURIComponent(String(orderType || 0)));
|
|
||||||
const data = await res.json();
|
|
||||||
const rec = Array.isArray(data?.data?.recommended) ? data.data.recommended : (Array.isArray(data?.recommended) ? data.recommended : []);
|
const rec = Array.isArray(data?.data?.recommended) ? data.data.recommended : (Array.isArray(data?.recommended) ? data.recommended : []);
|
||||||
recommendedMeta.value = rec;
|
recommendedMeta.value = rec;
|
||||||
return rec;
|
return rec;
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载字段元数据失败:', e);
|
||||||
|
metaFM.value = {};
|
||||||
|
metaTableLabels.value = {};
|
||||||
recommendedMeta.value = [];
|
recommendedMeta.value = [];
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 兼容旧接口
|
||||||
|
const loadFieldsMeta = (ds, type) => loadFieldsMetaAndRecommended(ds, type);
|
||||||
|
const loadRecommendedFields = (ds, orderType) => loadFieldsMetaAndRecommended(ds, orderType);
|
||||||
|
|
||||||
const getFieldsMap = (ds) => metaFM.value || {};
|
const getFieldsMap = (ds) => metaFM.value || {};
|
||||||
|
|
||||||
// ==================== 表标签映射 ====================
|
// ==================== 表标签映射 ====================
|
||||||
|
|
@ -340,8 +368,15 @@ const app = createApp({
|
||||||
const createFieldsTree = Vue.ref(null);
|
const createFieldsTree = Vue.ref(null);
|
||||||
const editFieldsTree = Vue.ref(null);
|
const editFieldsTree = Vue.ref(null);
|
||||||
|
|
||||||
// 树形选择器复选框变化处理
|
/**
|
||||||
const onTreeCheck = (kind, data) => {
|
* 树形选择器复选框变化处理
|
||||||
|
* @param {string} kind - 操作类型: 'create' | 'edit'
|
||||||
|
*/
|
||||||
|
const onTreeCheck = (kind) => {
|
||||||
|
if (kind !== 'create' && kind !== 'edit') {
|
||||||
|
console.warn('onTreeCheck: 无效的 kind 参数', kind);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const tree = kind === 'create' ? createFieldsTree.value : editFieldsTree.value;
|
const tree = kind === 'create' ? createFieldsTree.value : editFieldsTree.value;
|
||||||
if (!tree) return;
|
if (!tree) return;
|
||||||
|
|
||||||
|
|
@ -455,7 +490,7 @@ const app = createApp({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('设置树形选择器选中状态失败:', e);
|
console.warn('设置树形选择器选中状态失败:', e);
|
||||||
}
|
}
|
||||||
}, 100);
|
}, CONSTANTS.TREE_RENDER_DELAY);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -476,9 +511,7 @@ const app = createApp({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 兼容级联选择器的函数(已废弃,保留以避免错误)
|
// 废弃的函数已移除,如需调用请检查代码
|
||||||
const onCascaderVisible = () => {};
|
|
||||||
const onFieldsSelChange = () => {};
|
|
||||||
|
|
||||||
// ==================== 表单验证规则 ====================
|
// ==================== 表单验证规则 ====================
|
||||||
const createRules = {
|
const createRules = {
|
||||||
|
|
@ -545,10 +578,14 @@ const app = createApp({
|
||||||
const loadCreators = async () => {
|
const loadCreators = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(API_BASE + '/api/creators');
|
const res = await fetch(API_BASE + '/api/creators');
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载创建者列表失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
||||||
creatorOptions.value = arr.map(it => ({ label: it.name || String(it.id), value: Number(it.id) }));
|
creatorOptions.value = arr.map(it => ({ label: it.name || String(it.id), value: Number(it.id) }));
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载创建者列表失败:', e);
|
||||||
creatorOptions.value = [];
|
creatorOptions.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -558,10 +595,14 @@ const app = createApp({
|
||||||
const uid = getUserId();
|
const uid = getUserId();
|
||||||
const url = API_BASE + '/api/ymt/users?limit=2000' + (uid ? ('&q=' + encodeURIComponent(uid)) : '');
|
const url = API_BASE + '/api/ymt/users?limit=2000' + (uid ? ('&q=' + encodeURIComponent(uid)) : '');
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载易码通用户列表失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
||||||
ymtCreatorOptions.value = arr.map(it => ({ label: it.name || String(it.id), value: Number(it.id) }));
|
ymtCreatorOptions.value = arr.map(it => ({ label: it.name || String(it.id), value: Number(it.id) }));
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载易码通用户列表失败:', e);
|
||||||
ymtCreatorOptions.value = [];
|
ymtCreatorOptions.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -574,10 +615,14 @@ const app = createApp({
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await fetch(API_BASE + '/api/resellers?creator=' + ids.join(','));
|
const res = await fetch(API_BASE + '/api/resellers?creator=' + ids.join(','));
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载分销商列表失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
||||||
resellerOptions.value = arr.map(it => ({ label: (it.name || '') + (it.name ? '' : ''), value: Number(it.id) }));
|
resellerOptions.value = arr.map(it => ({ label: (it.name || '') + (it.name ? '' : ''), value: Number(it.id) }));
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载分销商列表失败:', e);
|
||||||
resellerOptions.value = [];
|
resellerOptions.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -593,10 +638,14 @@ const app = createApp({
|
||||||
qs.set('user_id', String(uid));
|
qs.set('user_id', String(uid));
|
||||||
qs.set('limit', '2000');
|
qs.set('limit', '2000');
|
||||||
const res = await fetch(API_BASE + '/api/ymt/merchants?' + qs.toString());
|
const res = await fetch(API_BASE + '/api/ymt/merchants?' + qs.toString());
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载客户列表失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
||||||
ymtMerchantOptions.value = arr.map(it => ({ label: `${it.id} - ${it.name || ''}`, value: Number(it.id) }));
|
ymtMerchantOptions.value = arr.map(it => ({ label: `${it.id} - ${it.name || ''}`, value: Number(it.id) }));
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载客户列表失败:', e);
|
||||||
ymtMerchantOptions.value = [];
|
ymtMerchantOptions.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -612,10 +661,14 @@ const app = createApp({
|
||||||
qs.set('merchant_id', String(mid));
|
qs.set('merchant_id', String(mid));
|
||||||
qs.set('limit', '2000');
|
qs.set('limit', '2000');
|
||||||
const res = await fetch(API_BASE + '/api/ymt/activities?' + qs.toString());
|
const res = await fetch(API_BASE + '/api/ymt/activities?' + qs.toString());
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载活动列表失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
||||||
ymtActivityOptions.value = arr.map(it => ({ label: `${it.id} - ${it.name || ''}`, value: Number(it.id) }));
|
ymtActivityOptions.value = arr.map(it => ({ label: `${it.id} - ${it.name || ''}`, value: Number(it.id) }));
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载活动列表失败:', e);
|
||||||
ymtActivityOptions.value = [];
|
ymtActivityOptions.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -630,10 +683,14 @@ const app = createApp({
|
||||||
const qs = new URLSearchParams();
|
const qs = new URLSearchParams();
|
||||||
qs.set('reseller', String(rid));
|
qs.set('reseller', String(rid));
|
||||||
const res = await fetch(API_BASE + '/api/plans?' + qs.toString());
|
const res = await fetch(API_BASE + '/api/plans?' + qs.toString());
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`加载计划列表失败: ${res.status}`);
|
||||||
|
}
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
const arr = Array.isArray(data?.data) ? data.data : (Array.isArray(data) ? data : []);
|
||||||
planOptions.value = arr.map(it => ({ label: `${it.id} - ${it.title || ''}`, value: Number(it.id) }));
|
planOptions.value = arr.map(it => ({ label: `${it.id} - ${it.title || ''}`, value: Number(it.id) }));
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
|
console.error('加载计划列表失败:', e);
|
||||||
planOptions.value = [];
|
planOptions.value = [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -925,7 +982,7 @@ const app = createApp({
|
||||||
await Vue.nextTick();
|
await Vue.nextTick();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setTreeChecked('edit', paths);
|
setTreeChecked('edit', paths);
|
||||||
}, 300);
|
}, CONSTANTS.TREE_EDIT_RENDER_DELAY);
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
state.edit.name = row.name;
|
state.edit.name = row.name;
|
||||||
state.edit.datasource = row.datasource || 'marketing';
|
state.edit.datasource = row.datasource || 'marketing';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue