fix(web): 修复编辑模板名称输入组件的标签嵌套问题

- 移除 el-input 标签的多余闭合标签
- 确保模板名称输入框结构符合规范
- 改善表单项的渲染正确性和稳定性
This commit is contained in:
zhouyonggao 2025-12-17 09:28:11 +08:00
parent c0578b1d24
commit 18e2e372a0
3 changed files with 94 additions and 28 deletions

9
.qoder/rules/index.md Normal file
View File

@ -0,0 +1,9 @@
---
trigger: always_on
---
# 项目规则
## 需求文档
@docs/需求文档.md

View File

@ -175,7 +175,7 @@
</el-dialog>
<el-dialog v-model="editVisible" title="编辑模板" :width="editWidth">
<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-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" />

View File

@ -1,5 +1,23 @@
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({
setup() {
// ==================== 配置和工具函数 ====================
@ -70,8 +88,8 @@ const app = createApp({
editVisible: false,
exportVisible: false,
exportSubmitting: false,
createWidth: localStorage.getItem('tplDialogWidth') || '900px',
editWidth: localStorage.getItem('tplEditDialogWidth') || '900px',
createWidth: localStorage.getItem('tplDialogWidth') || CONSTANTS.DIALOG_DEFAULT_WIDTH + 'px',
editWidth: localStorage.getItem('tplEditDialogWidth') || CONSTANTS.DIALOG_EDIT_DEFAULT_WIDTH + 'px',
edit: {
id: null,
name: '',
@ -120,10 +138,21 @@ const app = createApp({
const metaTableLabels = 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 {
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 tables = Array.isArray(data?.data?.tables) ? data.data.tables : (Array.isArray(data?.tables) ? data.tables : []);
const m = {};
const tblLabels = {};
@ -135,25 +164,24 @@ const app = createApp({
});
metaFM.value = m;
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 : []);
recommendedMeta.value = rec;
return rec;
} catch (_e) {
} catch (e) {
console.error('加载字段元数据失败:', e);
metaFM.value = {};
metaTableLabels.value = {};
recommendedMeta.value = [];
return [];
}
};
// 兼容旧接口
const loadFieldsMeta = (ds, type) => loadFieldsMetaAndRecommended(ds, type);
const loadRecommendedFields = (ds, orderType) => loadFieldsMetaAndRecommended(ds, orderType);
const getFieldsMap = (ds) => metaFM.value || {};
// ==================== 表标签映射 ====================
@ -340,8 +368,15 @@ const app = createApp({
const createFieldsTree = 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;
if (!tree) return;
@ -455,7 +490,7 @@ const app = createApp({
} catch (e) {
console.warn('设置树形选择器选中状态失败:', e);
}
}, 100);
}, CONSTANTS.TREE_RENDER_DELAY);
});
};
@ -476,9 +511,7 @@ const app = createApp({
}
});
// 兼容级联选择器的函数(已废弃,保留以避免错误)
const onCascaderVisible = () => {};
const onFieldsSelChange = () => {};
// 废弃的函数已移除,如需调用请检查代码
// ==================== 表单验证规则 ====================
const createRules = {
@ -545,10 +578,14 @@ const app = createApp({
const loadCreators = async () => {
try {
const res = await fetch(API_BASE + '/api/creators');
if (!res.ok) {
throw new Error(`加载创建者列表失败: ${res.status}`);
}
const data = await res.json();
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) }));
} catch (_e) {
} catch (e) {
console.error('加载创建者列表失败:', e);
creatorOptions.value = [];
}
};
@ -558,10 +595,14 @@ const app = createApp({
const uid = getUserId();
const url = API_BASE + '/api/ymt/users?limit=2000' + (uid ? ('&q=' + encodeURIComponent(uid)) : '');
const res = await fetch(url);
if (!res.ok) {
throw new Error(`加载易码通用户列表失败: ${res.status}`);
}
const data = await res.json();
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) }));
} catch (_e) {
} catch (e) {
console.error('加载易码通用户列表失败:', e);
ymtCreatorOptions.value = [];
}
};
@ -574,10 +615,14 @@ const app = createApp({
}
try {
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 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) }));
} catch (_e) {
} catch (e) {
console.error('加载分销商列表失败:', e);
resellerOptions.value = [];
}
};
@ -593,10 +638,14 @@ const app = createApp({
qs.set('user_id', String(uid));
qs.set('limit', '2000');
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 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) }));
} catch (_e) {
} catch (e) {
console.error('加载客户列表失败:', e);
ymtMerchantOptions.value = [];
}
};
@ -612,10 +661,14 @@ const app = createApp({
qs.set('merchant_id', String(mid));
qs.set('limit', '2000');
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 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) }));
} catch (_e) {
} catch (e) {
console.error('加载活动列表失败:', e);
ymtActivityOptions.value = [];
}
};
@ -630,10 +683,14 @@ const app = createApp({
const qs = new URLSearchParams();
qs.set('reseller', String(rid));
const res = await fetch(API_BASE + '/api/plans?' + qs.toString());
if (!res.ok) {
throw new Error(`加载计划列表失败: ${res.status}`);
}
const data = await res.json();
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) }));
} catch (_e) {
} catch (e) {
console.error('加载计划列表失败:', e);
planOptions.value = [];
}
};
@ -925,7 +982,7 @@ const app = createApp({
await Vue.nextTick();
setTimeout(() => {
setTreeChecked('edit', paths);
}, 300);
}, CONSTANTS.TREE_EDIT_RENDER_DELAY);
} catch (_e) {
state.edit.name = row.name;
state.edit.datasource = row.datasource || 'marketing';