3812 lines
146 KiB
JavaScript
3812 lines
146 KiB
JavaScript
// 导入 h 函数
|
||
const { createApp, ref, reactive, onMounted, onUnmounted,nextTick, watch, h, computed } = Vue;
|
||
const { ElMessage, ElMessageBox, ElTableV2, ElCheckbox,ElLoading } = ElementPlus;
|
||
// 获取 URL 参数中的 BJDID
|
||
function getUrlParameter(name) {
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
return urlParams.get(name) || '';
|
||
}
|
||
|
||
function getControlValue(controlId) {
|
||
const exact = document.getElementById(controlId);
|
||
if (exact && typeof exact.value !== 'undefined') {
|
||
return exact.value || '';
|
||
}
|
||
const bySuffix = document.querySelector(`[id$="${controlId}"]`);
|
||
if (bySuffix && typeof bySuffix.value !== 'undefined') {
|
||
return bySuffix.value || '';
|
||
}
|
||
return '';
|
||
}
|
||
|
||
// 获取 BJDID,优先从 URL 参数,其次从输入框
|
||
function getBJDID() {
|
||
// 优先从 URL 参数获取
|
||
const urlBJDID = getUrlParameter('BJDID');
|
||
if (urlBJDID) {
|
||
return urlBJDID;
|
||
}
|
||
|
||
// 如果 URL 中没有,则从输入框获取
|
||
return getControlValue('txtBJDID');
|
||
}
|
||
|
||
const BJDID = getBJDID();
|
||
|
||
function logMappingDebug(stage, payload) {
|
||
try {
|
||
console.log(`[BJCPJXDR][MappingDebug][${stage}]`, payload);
|
||
} catch (e) {}
|
||
}
|
||
|
||
function getDefaultPasteFieldOrder() {
|
||
const defaultOrder = ['包号', '报价产品', '技术规范书ID', '计量单位', '数量'];
|
||
const validFields = new Set(['包号', '报价产品', '技术规范书ID', '计量单位', '数量', '不导入']);
|
||
let rawValue = getControlValue('txtBJU059').trim();
|
||
|
||
if (!rawValue) return defaultOrder;
|
||
|
||
const parsed = rawValue
|
||
.replace(/,/g, ',')
|
||
.split(',')
|
||
.map(item => item.trim())
|
||
.filter(item => item !== '');
|
||
|
||
if (parsed.length === 0) return defaultOrder;
|
||
|
||
const normalized = parsed.map(item => (validFields.has(item) ? item : '不导入'));
|
||
while (normalized.length < defaultOrder.length) normalized.push('不导入');
|
||
const finalOrder = normalized.slice(0, defaultOrder.length);
|
||
logMappingDebug('getDefaultPasteFieldOrder', {
|
||
protocol: window.location.protocol,
|
||
rawValue,
|
||
parsed,
|
||
finalOrder
|
||
});
|
||
return finalOrder;
|
||
}
|
||
|
||
function applyDefaultColumnMapping(columnFieldMapping, detectedColumnCount) {
|
||
logMappingDebug('applyDefaultColumnMapping.before', {
|
||
detectedColumnCount,
|
||
mapping: JSON.parse(JSON.stringify(columnFieldMapping))
|
||
});
|
||
Object.keys(columnFieldMapping).forEach(key => delete columnFieldMapping[key]);
|
||
const fields = getDefaultPasteFieldOrder();
|
||
for (let i = 0; i < Math.min(detectedColumnCount, fields.length); i++) {
|
||
columnFieldMapping[i + 1] = fields[i] === '不导入' ? '' : fields[i];
|
||
}
|
||
logMappingDebug('applyDefaultColumnMapping.after', {
|
||
fields,
|
||
mapping: JSON.parse(JSON.stringify(columnFieldMapping))
|
||
});
|
||
}
|
||
|
||
function forceApplyDefaultColumnMapping(columnFieldMapping, detectedColumnCount) {
|
||
logMappingDebug('forceApplyDefaultColumnMapping.start', {
|
||
detectedColumnCount,
|
||
mapping: JSON.parse(JSON.stringify(columnFieldMapping))
|
||
});
|
||
const applyOnce = () => {
|
||
const fields = getDefaultPasteFieldOrder();
|
||
const maxCount = Math.min(detectedColumnCount, fields.length);
|
||
for (let i = 1; i <= maxCount; i++) {
|
||
const fieldName = fields[i - 1] === '不导入' ? '' : fields[i - 1];
|
||
columnFieldMapping[i] = fieldName;
|
||
columnFieldMapping[i.toString()] = fieldName;
|
||
}
|
||
logMappingDebug('forceApplyDefaultColumnMapping.applyOnce', {
|
||
fields,
|
||
maxCount,
|
||
mapping: JSON.parse(JSON.stringify(columnFieldMapping))
|
||
});
|
||
};
|
||
applyOnce();
|
||
setTimeout(applyOnce, 0);
|
||
setTimeout(applyOnce, 60);
|
||
}
|
||
const app = createApp({
|
||
setup() {
|
||
//进度条
|
||
const showCenterProgress = ref(false);
|
||
const centerProgressPercentage = ref(0);
|
||
const centerProgressStatus = ref('');
|
||
const centerProgressText = ref('');
|
||
let progressTimer = null;
|
||
// 手动粘贴对话框相关变量
|
||
const manualPasteDialogVisible = ref(false);
|
||
const manualPasteText = ref('');
|
||
|
||
// 列映射相关变量
|
||
const pasteStep = ref('input');
|
||
const detectedColumnCount = ref(0);
|
||
const detectedPreviewRows = ref([]);
|
||
const columnFieldMapping = reactive({});
|
||
const pasteFieldOptions = [
|
||
{ value: '', label: '不导入' },
|
||
{ value: '包号', label: '包号' },
|
||
{ value: '报价产品', label: '报价产品' },
|
||
{ value: '技术规范书ID', label: '技术规范书ID' },
|
||
{ value: '计量单位', label: '计量单位' },
|
||
{ value: '数量', label: '数量' }
|
||
];
|
||
|
||
const batchUpdateUseSelect = computed(() =>
|
||
batchUpdateType.value === 'standard' || batchUpdateType.value === 'voltage'
|
||
);
|
||
|
||
const visibleColumns = computed(() => {
|
||
const count = detectedColumnCount.value || 0;
|
||
return Array.from({ length: count }, (_, i) => i + 1);
|
||
});
|
||
let clipboardText = '';
|
||
|
||
// 状态变量
|
||
const loading = ref(false);
|
||
const FGF = ref('');
|
||
const ruleDialogVisible = ref(false);
|
||
const EXRuleDialogVisible = ref(false);
|
||
const syntaxDialogVisible = ref(false);
|
||
const filterDialogVisible = ref(false);
|
||
const YFGZDialogVisible = ref(false);
|
||
const iframeUrl = ref('');
|
||
const BJCPJXDR_XHYF = ref('BJCPJXDR_XHYF.html');
|
||
const tableData = ref([]);
|
||
const tableRef = ref(null);
|
||
const selectedProduct = ref('');
|
||
const currentRowModel = ref(''); // 当前行型号
|
||
|
||
// 批量调整相关变量
|
||
const batchUpdateDialogVisible = ref(false);
|
||
const batchUpdateType = ref('');
|
||
const batchUpdateValue = ref('');
|
||
const batchUpdateOptions = ref([]);
|
||
const batchUpdateFilterQuery = ref('');
|
||
const batchUpdateFilteredOptions = computed(() => {
|
||
const options = batchUpdateOptions.value || [];
|
||
const query = (batchUpdateFilterQuery.value || '').trim().toLowerCase();
|
||
if (!query) return options;
|
||
const normalizedQuery = query.replace(/\s+/g, '');
|
||
return options.filter(option => {
|
||
const label = (option.label || '').toString().toLowerCase();
|
||
const value = (option.value || '').toString().toLowerCase();
|
||
const normalizedLabel = label.replace(/\s+/g, '');
|
||
const normalizedValue = value.replace(/\s+/g, '');
|
||
return label.includes(query)
|
||
|| value.includes(query)
|
||
|| normalizedLabel.includes(normalizedQuery)
|
||
|| normalizedValue.includes(normalizedQuery);
|
||
});
|
||
});
|
||
const filterBatchUpdateOptions = (query) => {
|
||
batchUpdateFilterQuery.value = query || '';
|
||
};
|
||
const resolveBatchUpdateSelectValue = () => {
|
||
const raw = batchUpdateValue.value;
|
||
if (raw == null || raw === '') return '';
|
||
const text = String(raw).trim();
|
||
const options = batchUpdateOptions.value || [];
|
||
const byValue = options.find(item => String(item.value) === text);
|
||
if (byValue) return String(byValue.value);
|
||
const byLabel = options.find(item => String(item.label).trim() === text);
|
||
if (byLabel) return String(byLabel.value);
|
||
return '';
|
||
};
|
||
const hasSelectedRows = ref(false);
|
||
const selectedRowsCount = ref(0);
|
||
const batchUpdateTitle = ref('');
|
||
|
||
|
||
const currentRowVoltage = ref(''); // 当前行电压
|
||
const currentRowStandard = ref(''); // 当前行标准
|
||
const currentRowSpec = ref(''); // 当前行规格
|
||
const currentRowXLJXGZXH = ref('');
|
||
const ruleEditor = ref(null);
|
||
const selection = ref({ start: 0, end: 0, text: '' });
|
||
const resultReceived = ref(false);
|
||
// 规则配置相关变量
|
||
const ruleString = ref('| 电力电缆,AC220kV,YJLW,1600,1,03,ZC,Z');
|
||
// 查询条件变量
|
||
const productQuery = ref(''); // 报价产品(模糊查询)
|
||
const modelQuery = ref(''); // 线缆型号(模糊查询)
|
||
const specQuery = ref(''); // 线缆规格(模糊查询)
|
||
const standardQuery = ref(''); // 线缆标准(精确查询)
|
||
const voltageQuery = ref(''); // 电压等级(精确查询)
|
||
const missingElementQuery = ref([]); // 缺失要素(多选)
|
||
const missingElementOptions = [
|
||
{ value: 'XLXH', label: '线缆型号' },
|
||
{ value: 'XLGG', label: '线缆规格' },
|
||
{ value: 'XLBZ', label: '线缆标准' },
|
||
{ value: 'DYDJ', label: '电压等级' }
|
||
];
|
||
|
||
const buildListQueryParams = () => ({
|
||
XLJXDJ: 'XSBJ',
|
||
XLJXDH: BJDID,
|
||
sXLJXGZXH: filterRule.value || '0',
|
||
sDCZT: filterStatus.value || ' ',
|
||
sBJCP: productQuery.value,
|
||
sXLXH: modelQuery.value,
|
||
sXLGG: specQuery.value,
|
||
sXLBZ: standardQuery.value,
|
||
sDYDJID: voltageQuery.value,
|
||
sQSYS: (missingElementQuery.value && missingElementQuery.value.length > 0)
|
||
? missingElementQuery.value.join(',')
|
||
: ''
|
||
});
|
||
|
||
// 下拉选项变量
|
||
const standardOptions = ref([]); // 线缆标准下拉选项
|
||
const voltageOptions = ref([]); // 电压等级下拉选项
|
||
const standardNameMap = ref({}); // 标准编码->名称映射
|
||
// 线缆型号标记相关变量
|
||
const modelTags = ref([]);
|
||
const modelNextChar = ref(97); // 'a'的ASCII码
|
||
const modelTagOrder = {
|
||
'前缀码': 1,
|
||
'型号码': 2,
|
||
'后缀码': 3,
|
||
'型号': 4
|
||
};
|
||
const modelActualValue = ref('');
|
||
|
||
// 线缆规格标记相关变量
|
||
const specTags = ref([]);
|
||
const specNextChar = ref(97); // 'a'的ASCII码
|
||
const specTagOrder = {
|
||
'芯数': 1,
|
||
'截面': 2,
|
||
'线组数': 3,
|
||
'线对数': 4,
|
||
'规格':5
|
||
};
|
||
const specActualValue = ref('');
|
||
|
||
|
||
const voltageTags = ref([]);
|
||
const voltageNextChar = ref(97); // 'a'的ASCII码
|
||
const voltageTagOrder = {
|
||
'相电压': 1,
|
||
'线电压': 2,
|
||
'电压单位': 3,
|
||
'电压等级': 4
|
||
};
|
||
const voltageActualValue = ref('');
|
||
|
||
const standardTags = ref([]);
|
||
const standardNextChar = ref(97); // 'a'的ASCII码
|
||
const standardTagOrder = {
|
||
'标准名': 1
|
||
};
|
||
const standardActualValue = ref('');
|
||
|
||
// 配置项的值
|
||
const modelValue = ref('');
|
||
const specValue = ref('');
|
||
const voltageValue = ref('');
|
||
const standardValue = ref('');
|
||
const colorValue = ref('');
|
||
|
||
// 筛选条件变量
|
||
const filterRule = ref('0');
|
||
const ExRule = ref('0');
|
||
const filterStatus = ref('');
|
||
const filterEquivalent=ref(false);
|
||
const isLoading = ref(false);
|
||
// 解析规则选项
|
||
const ruleOptions = ref([
|
||
{ value: '0', label: '未解析' }
|
||
]);
|
||
|
||
// 通用解析规则选项
|
||
const commonRuleOptions = ref([
|
||
{ value: '0', label: '请选择通用规则' }
|
||
]);
|
||
|
||
// 删除规则函数
|
||
const deleteRule = async (ruleId) => {
|
||
try {
|
||
const result = await ElMessageBox.confirm(
|
||
'确定要删除此解析规则吗?此操作不可恢复。',
|
||
'确认删除',
|
||
{
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}
|
||
);
|
||
|
||
if (result) {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 调用删除服务
|
||
const deleteResult = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'DeleteXLJXGZ',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005,
|
||
'XLJXDH': BJDID,
|
||
'sXLJXGZXH': ruleId
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"DeleteXLJXGZ",
|
||
50000
|
||
);
|
||
});
|
||
|
||
if (deleteResult && deleteResult.success) {
|
||
ElMessage.success('解析规则删除成功');
|
||
// 重新加载规则列表
|
||
await loadRuleOptions();
|
||
// 如果删除的是当前选中的规则,重置为默认值
|
||
if (filterRule.value === ruleId) {
|
||
filterRule.value = '0';
|
||
}
|
||
} else {
|
||
ElMessage.error(`删除失败: ${deleteResult?.message || '未知错误'}`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
if (error !== 'cancel') {
|
||
console.error('删除规则失败:', error);
|
||
ElMessage.error(`删除规则失败: ${error.message || '服务异常'}`);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 删除通用规则函数
|
||
const deleteCommonRule = async (ruleId) => {
|
||
try {
|
||
const result = await ElMessageBox.confirm(
|
||
'确定要删除此通用解析规则吗?此操作不可恢复。',
|
||
'确认删除',
|
||
{
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}
|
||
);
|
||
|
||
if (result) {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 调用删除通用规则服务
|
||
const deleteResult = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'DeleteXLJXGZ',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005,
|
||
'XLJXDH':' ',
|
||
'sXLJXGZXH': ruleId
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"DeleteXLJXGZ",
|
||
50000
|
||
);
|
||
});
|
||
|
||
if (deleteResult && deleteResult.success) {
|
||
ElMessage.success('通用解析规则删除成功');
|
||
// 重新加载通用规则列表
|
||
await loadCommonRuleOptions();
|
||
} else {
|
||
ElMessage.error(`删除失败: ${deleteResult?.message || '未知错误'}`);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
if (error !== 'cancel') {
|
||
console.error('删除通用规则失败:', error);
|
||
ElMessage.error(`删除通用规则失败: ${error.message || '服务异常'}`);
|
||
}
|
||
}
|
||
};
|
||
|
||
const statusOptions = ref([
|
||
{ value: 'N', label: '未导入' },
|
||
{ value: 'Y', label: '已导入' }
|
||
]);
|
||
//todo
|
||
// 正确声明对象路径
|
||
const cable_model_syntax = ref("/GTHINKING/Components/N_XLBJDX/cable_model_syntax.html");
|
||
// ==== Excel导入相关变量 ====
|
||
const importDialogVisible = ref(false);
|
||
const fileInput = ref(null);
|
||
const excelFile = ref(null);
|
||
const excelColumns = ref([]);
|
||
const previewData = ref([]);
|
||
const columnMapping = reactive({
|
||
'序号': null,
|
||
'包号': null,
|
||
'报价产品': null,
|
||
'技术规范书ID': null,
|
||
'计量单位': null, // 新增
|
||
'数量': null,
|
||
'型号': null,
|
||
'电压等级': null,
|
||
'标准': null,
|
||
'规格': null,
|
||
'等效': null,
|
||
});
|
||
const tableColumns = ref(['包号','报价产品', '技术规范书ID','计量单位', '数量', '型号', '电压等级', '标准', '规格', '等效']);
|
||
const importing = ref(false);
|
||
const importProgress = ref(0);
|
||
const totalRows = ref(0);
|
||
const importedCount = ref(0);
|
||
const importStatus = ref(null);
|
||
const errorMessage = ref('');
|
||
const successMessage = ref('');
|
||
const canImport = ref(false);
|
||
// ==== el-table-v2 相关变量 ====
|
||
const TABLE_CELL_BORDER_STYLE = {
|
||
borderRight: '1px solid #dcdfe6',
|
||
borderBottom: '1px solid #dcdfe6',
|
||
boxSizing: 'border-box'
|
||
};
|
||
const withTableCellBorder = (column) => ({
|
||
...column,
|
||
style: { ...TABLE_CELL_BORDER_STYLE, ...(column.style || {}) }
|
||
});
|
||
|
||
// 表头拖拽排序 / 列宽拖拽:固定列不可排序,配置持久化到 localStorage
|
||
const PINNED_COLUMN_KEYS = new Set(['selection', 'index']);
|
||
const COLUMN_ORDER_STORAGE_KEY = 'BJCPJXDR_tableColumnOrder_v1';
|
||
const COLUMN_WIDTH_STORAGE_KEY = 'BJCPJXDR_tableColumnWidths_v1';
|
||
const MIN_COLUMN_WIDTH = 48;
|
||
const columnDragSourceKey = ref(null);
|
||
const columnDragOverKey = ref(null);
|
||
let resizingColumnKey = null;
|
||
let resizeStartX = 0;
|
||
let resizeStartWidth = 0;
|
||
let resizeRafId = 0;
|
||
let pendingResizeWidth = 0;
|
||
let resizeGuideEl = null;
|
||
let resizeColumnIndex = -1;
|
||
|
||
const refreshTableColumns = () => {
|
||
tableColumnsV2.value = tableColumnsV2.value.map(col => ({ ...col }));
|
||
};
|
||
|
||
const loadSavedColumnWidths = () => {
|
||
try {
|
||
const raw = localStorage.getItem(COLUMN_WIDTH_STORAGE_KEY);
|
||
if (!raw) return null;
|
||
const parsed = JSON.parse(raw);
|
||
return parsed && typeof parsed === 'object' ? parsed : null;
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const applySavedWidthsToColumns = (columns, widths) => {
|
||
if (!widths) return columns;
|
||
return columns.map(col => {
|
||
if (col.hidden) return col;
|
||
const saved = widths[col.key];
|
||
if (typeof saved === 'number' && saved >= MIN_COLUMN_WIDTH) {
|
||
return { ...col, width: Math.round(saved) };
|
||
}
|
||
return col;
|
||
});
|
||
};
|
||
|
||
const saveColumnWidths = () => {
|
||
const widths = {};
|
||
tableColumnsV2.value.forEach(col => {
|
||
if (!col.hidden) widths[col.key] = col.width;
|
||
});
|
||
try {
|
||
localStorage.setItem(COLUMN_WIDTH_STORAGE_KEY, JSON.stringify(widths));
|
||
} catch (e) {}
|
||
};
|
||
|
||
const setColumnWidth = (key, width) => {
|
||
const w = Math.max(MIN_COLUMN_WIDTH, Math.round(width));
|
||
const idx = tableColumnsV2.value.findIndex(col => col.key === key);
|
||
if (idx < 0) return;
|
||
const col = tableColumnsV2.value[idx];
|
||
if (col.width === w) return;
|
||
tableColumnsV2.value[idx] = { ...col, width: w };
|
||
tableColumnsV2.value = [...tableColumnsV2.value];
|
||
};
|
||
|
||
const ensureResizeGuide = () => {
|
||
if (!resizeGuideEl) {
|
||
resizeGuideEl = document.createElement('div');
|
||
resizeGuideEl.className = 'bjcpjxdr-col-resize-guide';
|
||
document.body.appendChild(resizeGuideEl);
|
||
}
|
||
return resizeGuideEl;
|
||
};
|
||
|
||
const getHeaderGuideHost = () => document.querySelector('.table-container .el-table-v2__header-wrapper')
|
||
|| document.querySelector('.el-table-v2__header-wrapper')
|
||
|| document.querySelector('.el-table-v2__header');
|
||
|
||
const getHeaderCellByResizeIndex = () => {
|
||
if (resizeColumnIndex < 0) return null;
|
||
const host = getHeaderGuideHost();
|
||
if (!host) return null;
|
||
const cells = host.querySelectorAll('.el-table-v2__header-cell');
|
||
return cells[resizeColumnIndex] || null;
|
||
};
|
||
|
||
const updateResizeGuide = () => {
|
||
const guide = ensureResizeGuide();
|
||
const headerHost = getHeaderGuideHost();
|
||
if (headerHost) {
|
||
const hRect = headerHost.getBoundingClientRect();
|
||
guide.style.top = `${hRect.top}px`;
|
||
guide.style.height = `${hRect.height}px`;
|
||
}
|
||
const headerCell = getHeaderCellByResizeIndex();
|
||
if (headerCell) {
|
||
const cellRect = headerCell.getBoundingClientRect();
|
||
guide.style.left = `${cellRect.right - 1}px`;
|
||
}
|
||
guide.style.display = 'block';
|
||
};
|
||
|
||
const hideResizeGuide = () => {
|
||
if (resizeGuideEl) resizeGuideEl.style.display = 'none';
|
||
};
|
||
|
||
const applyColumnResizeByPointer = (clientX) => {
|
||
if (!resizingColumnKey) return;
|
||
pendingResizeWidth = Math.max(
|
||
MIN_COLUMN_WIDTH,
|
||
Math.round(resizeStartWidth + (clientX - resizeStartX))
|
||
);
|
||
if (resizeRafId) return;
|
||
resizeRafId = requestAnimationFrame(() => {
|
||
resizeRafId = 0;
|
||
setColumnWidth(resizingColumnKey, pendingResizeWidth);
|
||
updateResizeGuide();
|
||
});
|
||
};
|
||
|
||
const onColumnResizeMove = (e) => {
|
||
if (!resizingColumnKey) return;
|
||
applyColumnResizeByPointer(e.clientX);
|
||
};
|
||
|
||
const stopColumnResize = () => {
|
||
if (!resizingColumnKey) return;
|
||
const key = resizingColumnKey;
|
||
if (resizeRafId) {
|
||
cancelAnimationFrame(resizeRafId);
|
||
resizeRafId = 0;
|
||
}
|
||
setColumnWidth(key, pendingResizeWidth);
|
||
resizingColumnKey = null;
|
||
resizeColumnIndex = -1;
|
||
document.removeEventListener('mousemove', onColumnResizeMove);
|
||
document.removeEventListener('mouseup', stopColumnResize);
|
||
document.body.classList.remove('bjcpjxdr-col-resizing');
|
||
hideResizeGuide();
|
||
saveColumnWidths();
|
||
};
|
||
|
||
const startColumnResize = (columnKey, e) => {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
const headerCell = e.target.closest('.el-table-v2__header-cell');
|
||
const domWidth = headerCell ? headerCell.getBoundingClientRect().width : 0;
|
||
const col = tableColumnsV2.value.find(c => c.key === columnKey);
|
||
const visibleCols = tableColumnsV2.value.filter(c => !c.hidden);
|
||
resizeColumnIndex = visibleCols.findIndex(c => c.key === columnKey);
|
||
resizingColumnKey = columnKey;
|
||
resizeStartX = e.clientX;
|
||
resizeStartWidth = domWidth > 0 ? domWidth : (col ? col.width : MIN_COLUMN_WIDTH);
|
||
pendingResizeWidth = resizeStartWidth;
|
||
document.body.classList.add('bjcpjxdr-col-resizing');
|
||
updateResizeGuide();
|
||
document.addEventListener('mousemove', onColumnResizeMove);
|
||
document.addEventListener('mouseup', stopColumnResize);
|
||
};
|
||
|
||
const createColumnResizeHandle = (columnKey) => h('span', {
|
||
class: 'bjcpjxdr-col-resize-handle',
|
||
title: '拖动调整列宽',
|
||
onMousedown: (e) => startColumnResize(columnKey, e)
|
||
});
|
||
|
||
const loadSavedTableColumnOrder = () => {
|
||
try {
|
||
const raw = localStorage.getItem(COLUMN_ORDER_STORAGE_KEY);
|
||
if (!raw) return null;
|
||
const parsed = JSON.parse(raw);
|
||
return Array.isArray(parsed) ? parsed : null;
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const applySavedOrderToColumns = (columns, savedKeys) => {
|
||
if (!savedKeys || !savedKeys.length) return columns;
|
||
const pinnedStart = columns.filter(c => PINNED_COLUMN_KEYS.has(c.key));
|
||
const hidden = columns.filter(c => c.hidden);
|
||
const draggable = columns.filter(c => !PINNED_COLUMN_KEYS.has(c.key) && !c.hidden);
|
||
const map = new Map(draggable.map(c => [c.key, c]));
|
||
const ordered = [];
|
||
savedKeys.forEach(k => {
|
||
if (map.has(k)) {
|
||
ordered.push(map.get(k));
|
||
map.delete(k);
|
||
}
|
||
});
|
||
map.forEach(c => ordered.push(c));
|
||
return [...pinnedStart, ...ordered, ...hidden];
|
||
};
|
||
|
||
const reorderTableColumns = (fromKey, toKey) => {
|
||
if (!fromKey || !toKey || fromKey === toKey) return;
|
||
if (PINNED_COLUMN_KEYS.has(fromKey) || PINNED_COLUMN_KEYS.has(toKey)) return;
|
||
|
||
const cols = tableColumnsV2.value;
|
||
const draggable = cols.filter(c => !PINNED_COLUMN_KEYS.has(c.key) && !c.hidden);
|
||
const keys = draggable.map(c => c.key);
|
||
const fromIdx = keys.indexOf(fromKey);
|
||
const toIdx = keys.indexOf(toKey);
|
||
if (fromIdx < 0 || toIdx < 0) return;
|
||
|
||
keys.splice(fromIdx, 1);
|
||
keys.splice(toIdx, 0, fromKey);
|
||
|
||
const pinnedStart = cols.filter(c => PINNED_COLUMN_KEYS.has(c.key));
|
||
const hidden = cols.filter(c => c.hidden);
|
||
const map = new Map(draggable.map(c => [c.key, c]));
|
||
const reordered = keys.map(k => map.get(k)).filter(Boolean);
|
||
|
||
tableColumnsV2.value = [...pinnedStart, ...reordered, ...hidden];
|
||
try {
|
||
localStorage.setItem(COLUMN_ORDER_STORAGE_KEY, JSON.stringify(keys));
|
||
} catch (e) {}
|
||
};
|
||
|
||
const withDraggableHeader = (column) => {
|
||
if (column.hidden) return column;
|
||
if (column.key === 'selection') {
|
||
return {
|
||
...column,
|
||
headerCellRenderer: () => h('div', { class: 'bjcpjxdr-header-cell bjcpjxdr-header-pinned' }, [
|
||
h('input', {
|
||
type: 'checkbox',
|
||
onChange: (e) => {
|
||
if (e.target.checked) {
|
||
tableData.value.forEach(row => { row.selected = true; });
|
||
} else {
|
||
tableData.value.forEach(row => { row.selected = false; });
|
||
}
|
||
updateSelectedRows();
|
||
},
|
||
checked: hasSelectedRows.value && selectedRowsCount.value === tableData.value.length
|
||
}),
|
||
createColumnResizeHandle(column.key)
|
||
])
|
||
};
|
||
}
|
||
if (PINNED_COLUMN_KEYS.has(column.key)) {
|
||
return {
|
||
...column,
|
||
headerCellRenderer: () => h('div', { class: 'bjcpjxdr-header-cell bjcpjxdr-header-pinned' }, [
|
||
h('span', column.title),
|
||
createColumnResizeHandle(column.key)
|
||
])
|
||
};
|
||
}
|
||
return {
|
||
...column,
|
||
headerCellRenderer: () => {
|
||
const key = column.key;
|
||
const isDragging = columnDragSourceKey.value === key;
|
||
const isOver = columnDragOverKey.value === key;
|
||
return h('div', {
|
||
class: [
|
||
'bjcpjxdr-header-cell',
|
||
'bjcpjxdr-header-draggable',
|
||
isDragging ? 'is-dragging' : '',
|
||
isOver ? 'is-drag-over' : ''
|
||
],
|
||
draggable: true,
|
||
title: '按住拖动可调整列顺序',
|
||
onDragstart: (e) => {
|
||
columnDragSourceKey.value = key;
|
||
e.dataTransfer.effectAllowed = 'move';
|
||
try { e.dataTransfer.setData('text/plain', key); } catch (err) {}
|
||
},
|
||
onDragover: (e) => {
|
||
e.preventDefault();
|
||
if (columnDragSourceKey.value && columnDragSourceKey.value !== key
|
||
&& columnDragOverKey.value !== key) {
|
||
columnDragOverKey.value = key;
|
||
refreshTableColumns();
|
||
}
|
||
},
|
||
onDragleave: () => {
|
||
if (columnDragOverKey.value === key) {
|
||
columnDragOverKey.value = null;
|
||
refreshTableColumns();
|
||
}
|
||
},
|
||
onDrop: (e) => {
|
||
e.preventDefault();
|
||
const fromKey = columnDragSourceKey.value
|
||
|| (e.dataTransfer && e.dataTransfer.getData('text/plain'));
|
||
reorderTableColumns(fromKey, key);
|
||
columnDragSourceKey.value = null;
|
||
columnDragOverKey.value = null;
|
||
refreshTableColumns();
|
||
},
|
||
onDragend: () => {
|
||
columnDragSourceKey.value = null;
|
||
columnDragOverKey.value = null;
|
||
refreshTableColumns();
|
||
}
|
||
}, [
|
||
h('span', { class: 'bjcpjxdr-header-grip', 'aria-hidden': 'true' }, '⋮⋮'),
|
||
h('span', { class: 'bjcpjxdr-header-title' }, column.title),
|
||
createColumnResizeHandle(column.key)
|
||
]);
|
||
}
|
||
};
|
||
};
|
||
|
||
const createBaseTableColumns = () => ([
|
||
{
|
||
key: 'selection',
|
||
dataKey: 'selection',
|
||
title: '选择',
|
||
width: 60,
|
||
align: 'center',
|
||
cellRenderer: ({ rowData }) => {
|
||
return h('input', {
|
||
type: 'checkbox',
|
||
checked: rowData.selected || false,
|
||
onChange: (e) => {
|
||
rowData.selected = e.target.checked;
|
||
updateSelectedRows();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
{
|
||
key: 'index',
|
||
dataKey: 'index',
|
||
title: '序号',
|
||
width: 50,
|
||
align: 'center'
|
||
},
|
||
{
|
||
key: 'packageNumber',
|
||
dataKey: 'packageNumber',
|
||
title: '包号',
|
||
width: 80,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'product',
|
||
dataKey: 'product',
|
||
title: '报价产品',
|
||
width: 300,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'techSpecId',
|
||
dataKey: 'techSpecId',
|
||
title: '技术规范ID',
|
||
width: 100,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'unit',
|
||
dataKey: 'unit',
|
||
title: '计量单位',
|
||
width: 80,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'quantity',
|
||
dataKey: 'quantity',
|
||
title: '数量',
|
||
width: 80,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'model',
|
||
dataKey: 'model',
|
||
title: '型号',
|
||
width: 150,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'voltage',
|
||
dataKey: 'voltage',
|
||
title: '电压等级',
|
||
width: 100,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'standard',
|
||
dataKey: 'standard',
|
||
title: '标准',
|
||
width: 80,
|
||
align: 'left',
|
||
cellRenderer: ({ rowData }) => {
|
||
const name = standardNameMap.value[rowData.standard];
|
||
return name || rowData.standard || '';
|
||
}
|
||
},
|
||
{
|
||
key: 'spec',
|
||
dataKey: 'spec',
|
||
title: '规格',
|
||
width: 120,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'dx',
|
||
dataKey: 'dx',
|
||
title: '等效',
|
||
width: 200,
|
||
align: 'left'
|
||
},
|
||
{
|
||
key: 'XLJXGZXH',
|
||
dataKey: 'XLJXGZXH',
|
||
title: 'XLJXGZXH',
|
||
width: 0,
|
||
align: 'left',
|
||
hidden: true
|
||
}
|
||
]);
|
||
|
||
const buildTableColumnsV2 = () => {
|
||
const savedOrder = loadSavedTableColumnOrder();
|
||
const ordered = applySavedOrderToColumns(createBaseTableColumns(), savedOrder);
|
||
const withWidths = applySavedWidthsToColumns(ordered, loadSavedColumnWidths());
|
||
return withWidths.map(withDraggableHeader).map(withTableCellBorder);
|
||
};
|
||
|
||
const tableColumnsV2 = ref(buildTableColumnsV2());
|
||
|
||
const getRowKey = row => {
|
||
// 方案1:直接使用唯一字段
|
||
return row.id
|
||
|
||
}
|
||
|
||
const tableWidth = ref(window.innerWidth - 30);
|
||
const tableHeight = ref(window.innerHeight - 50);
|
||
|
||
// 启动进度条显示
|
||
const startCenterProgress = (text) => {
|
||
showCenterProgress.value = true;
|
||
centerProgressPercentage.value = 0;
|
||
centerProgressStatus.value = '';
|
||
centerProgressText.value = text || '处理中...';
|
||
|
||
// 模拟进度增长
|
||
progressTimer = setInterval(() => {
|
||
if (centerProgressPercentage.value < 75) {
|
||
centerProgressPercentage.value += 5;
|
||
}
|
||
}, 300);
|
||
};
|
||
|
||
// 完成进度条显示
|
||
const completeCenterProgress = (success = true, message = '') => {
|
||
if (progressTimer) clearInterval(progressTimer);
|
||
|
||
// 设置完成状态
|
||
centerProgressPercentage.value = 100;
|
||
centerProgressStatus.value = success ? 'success' : 'exception';
|
||
centerProgressText.value = message || (success ? '完成!' : '失败!');
|
||
|
||
// 2秒后自动关闭
|
||
setTimeout(() => {
|
||
showCenterProgress.value = false;
|
||
}, 2000);
|
||
};
|
||
// 显示手动粘贴对话框
|
||
const showManualPasteDialog = () => {
|
||
manualPasteText.value = '';
|
||
manualPasteDialogVisible.value = true;
|
||
};
|
||
|
||
// 处理手动粘贴确认(第一步:粘贴数据)
|
||
const handleManualPasteConfirm = () => {
|
||
logMappingDebug('handleManualPasteConfirm.click', {
|
||
protocol: window.location.protocol,
|
||
textLength: manualPasteText.value ? manualPasteText.value.length : 0
|
||
});
|
||
if (!manualPasteText.value.trim()) {
|
||
ElMessage.warning('请输入要粘贴的数据');
|
||
return;
|
||
}
|
||
|
||
const text = manualPasteText.value.trim();
|
||
const rows = text.split('\n').filter(row => row.trim() !== '');
|
||
|
||
if (rows.length === 0) {
|
||
ElMessage.warning('粘贴的数据为空');
|
||
return;
|
||
}
|
||
|
||
const firstRow = rows[0].split('\t');
|
||
detectedColumnCount.value = firstRow.length;
|
||
logMappingDebug('handleManualPasteConfirm.detected', {
|
||
rows: rows.length,
|
||
detectedColumnCount: detectedColumnCount.value,
|
||
firstRow
|
||
});
|
||
|
||
detectedPreviewRows.value = rows.slice(0, 5).map((row, idx) => {
|
||
const cols = row.split('\t');
|
||
const rowData = { rowIndex: idx + 1 };
|
||
for (let i = 0; i < cols.length; i++) {
|
||
rowData['col' + i] = cols[i];
|
||
}
|
||
return rowData;
|
||
});
|
||
|
||
// 点击“下一步”时强制覆盖列映射(HTTP场景)
|
||
applyDefaultColumnMapping(columnFieldMapping, detectedColumnCount.value);
|
||
pasteStep.value = 'mapping';
|
||
nextTick(() => forceApplyDefaultColumnMapping(columnFieldMapping, detectedColumnCount.value));
|
||
logMappingDebug('handleManualPasteConfirm.toMapping', {
|
||
pasteStep: pasteStep.value,
|
||
mapping: JSON.parse(JSON.stringify(columnFieldMapping))
|
||
});
|
||
};
|
||
|
||
// 处理映射确认(第二步:确认映射并导入)
|
||
const handleMappingConfirm = () => {
|
||
const hasProduct = Object.values(columnFieldMapping).includes('报价产品');
|
||
if (!hasProduct) {
|
||
ElMessage.warning('请设置"报价产品"列的对应位置(必填)');
|
||
return;
|
||
}
|
||
|
||
const fieldMapping = {};
|
||
Object.entries(columnFieldMapping).forEach(([col, field]) => {
|
||
if (field) {
|
||
fieldMapping[field] = col;
|
||
}
|
||
});
|
||
|
||
manualPasteDialogVisible.value = false;
|
||
pasteStep.value = 'input';
|
||
const text = manualPasteText.value;
|
||
manualPasteText.value = '';
|
||
|
||
processPastedTextWithMapping(fieldMapping, text);
|
||
};
|
||
|
||
// 返回上一步
|
||
const handleMappingBack = () => {
|
||
pasteStep.value = 'input';
|
||
};
|
||
|
||
// 处理手动粘贴取消
|
||
const handleManualPasteCancel = () => {
|
||
manualPasteDialogVisible.value = false;
|
||
manualPasteText.value = '';
|
||
pasteStep.value = 'input';
|
||
};
|
||
|
||
// 提取粘贴文本处理逻辑为独立函数
|
||
const processPastedText = async (text) => {
|
||
startCenterProgress('正在处理数据...');
|
||
|
||
// 解析剪贴板数据为表格
|
||
const rows = text.split('\n');
|
||
const itemsToInsert = [];
|
||
const parsedData = [];
|
||
|
||
// 为所有记录生成相同的XLJXDH
|
||
const commonXLJXDH = BJDID;
|
||
|
||
for (let i = 0; i < rows.length; i++) {
|
||
const row = rows[i].split('\t');
|
||
if (row.length > 0 && row[0].trim() !== '') {
|
||
const sequenceNumber = (i + 1).toString();
|
||
const packageNumber = row[0].trim();
|
||
const product= row.length > 1 ? row[1].trim() : ''; // 第二列为计量单位
|
||
const techSpecId= row.length > 2 ? row[2].trim() : ''; // 第三列为数量
|
||
const unit= row.length > 3 ? row[3].trim() : ''; // 第四列为包号
|
||
const quantity = row.length > 4 ? row[4].trim() : ''; // 第五列为技术规范ID
|
||
|
||
// 创建XLJXMX对象
|
||
itemsToInsert.push({
|
||
XLJXDJ: "XSBJ",
|
||
XLJXDH: commonXLJXDH,
|
||
XLJXMXH: sequenceNumber,
|
||
XLBSC: product,
|
||
XLJXGZXH: "0",
|
||
JXXLXHID: "",
|
||
XLXHID: "",
|
||
JXXLGGID: "",
|
||
XLGGID: "",
|
||
JXDYDJID: "",
|
||
DYDJID: "",
|
||
XLBZID: "",
|
||
JXXLBZID: "",
|
||
XLYSID: "",
|
||
JXXLYSID: "",
|
||
WHR: "",
|
||
WHRID: "",
|
||
WHSJ: "",
|
||
DCZT: "N",
|
||
JLDW: unit, // 计量单位
|
||
SL: quantity, // 数量
|
||
FBH: packageNumber, // 包号
|
||
JSGFID: techSpecId // 技术规范ID
|
||
});
|
||
|
||
parsedData.push({
|
||
index: sequenceNumber,
|
||
product: product,
|
||
unit: unit,
|
||
quantity: quantity,
|
||
packageNumber: packageNumber, // 包号
|
||
techSpecId: techSpecId, // 技术规范ID
|
||
model: '',
|
||
voltage: '',
|
||
standard: '',
|
||
spec: '',
|
||
status: 'N',
|
||
checked: false
|
||
});
|
||
}
|
||
}
|
||
|
||
// 批量插入数据
|
||
if (itemsToInsert.length > 0) {
|
||
loading.value = true;
|
||
const result = await bulkInsertXLJXMX(itemsToInsert);
|
||
|
||
if (result && result.success) {
|
||
await loadTableData();
|
||
ElMessage.success(`已粘贴并插入 ${parsedData.length} 条数据`);
|
||
await commonParse();
|
||
} else {
|
||
completeCenterProgress(false, '粘贴失败');
|
||
ElMessage.error(`插入失败: ${result?.msg || result?.message || '未知错误'}`);
|
||
}
|
||
loading.value = false;
|
||
}
|
||
|
||
completeCenterProgress(true, '数据处理完成!');
|
||
};
|
||
|
||
// 使用动态列映射处理粘贴文本
|
||
const processPastedTextWithMapping = async (fieldMapping, text) => {
|
||
startCenterProgress('正在处理数据...');
|
||
|
||
const rows = text.split('\n');
|
||
const itemsToInsert = [];
|
||
const parsedData = [];
|
||
const commonXLJXDH = BJDID;
|
||
|
||
const getMappedValue = (row, fieldName) => {
|
||
const colIndex = fieldMapping[fieldName];
|
||
if (colIndex) {
|
||
const idx = parseInt(colIndex) - 1;
|
||
return row.length > idx ? row[idx].trim() : '';
|
||
}
|
||
return '';
|
||
};
|
||
|
||
for (let i = 0; i < rows.length; i++) {
|
||
const row = rows[i].split('\t');
|
||
if (row.length > 0 && row[0].trim() !== '') {
|
||
const sequenceNumber = (i + 1).toString();
|
||
const packageNumber = getMappedValue(row, '包号');
|
||
const product = getMappedValue(row, '报价产品');
|
||
const techSpecId = getMappedValue(row, '技术规范书ID');
|
||
const unit = getMappedValue(row, '计量单位');
|
||
const quantity = getMappedValue(row, '数量');
|
||
|
||
itemsToInsert.push({
|
||
XLJXDJ: "XSBJ",
|
||
XLJXDH: commonXLJXDH,
|
||
XLJXMXH: sequenceNumber,
|
||
XLBSC: product,
|
||
XLJXGZXH: "0",
|
||
JXXLXHID: "",
|
||
XLXHID: "",
|
||
JXXLGGID: "",
|
||
XLGGID: "",
|
||
JXDYDJID: "",
|
||
DYDJID: "",
|
||
XLBZID: "",
|
||
JXXLBZID: "",
|
||
XLYSID: "",
|
||
JXXLYSID: "",
|
||
WHR: "",
|
||
WHRID: "",
|
||
WHSJ: "",
|
||
DCZT: "N",
|
||
JLDW: unit,
|
||
SL: quantity,
|
||
FBH: packageNumber,
|
||
JSGFID: techSpecId
|
||
});
|
||
|
||
parsedData.push({
|
||
index: sequenceNumber,
|
||
product: product,
|
||
unit: unit,
|
||
quantity: quantity,
|
||
packageNumber: packageNumber,
|
||
techSpecId: techSpecId,
|
||
model: '',
|
||
voltage: '',
|
||
standard: '',
|
||
spec: '',
|
||
status: 'N',
|
||
checked: false
|
||
});
|
||
}
|
||
}
|
||
|
||
if (itemsToInsert.length > 0) {
|
||
loading.value = true;
|
||
const result = await bulkInsertXLJXMX(itemsToInsert);
|
||
if (result && result.success) {
|
||
await loadTableData();
|
||
ElMessage.success(`已粘贴并插入 ${parsedData.length} 条数据`);
|
||
await commonParse();
|
||
} else {
|
||
completeCenterProgress(false, '粘贴失败');
|
||
ElMessage.error(`插入失败: ${result?.msg || result?.message || '未知错误'}`);
|
||
}
|
||
loading.value = false;
|
||
}
|
||
|
||
completeCenterProgress(true, '数据处理完成!');
|
||
};
|
||
|
||
// ==== 通用函数 ====
|
||
|
||
// 检查字符是否为字母数字
|
||
const isAlphanumeric = (char) => {
|
||
const code = char.charCodeAt(0);
|
||
return (code >= 48 && code <= 57) || // 0-9
|
||
(code >= 65 && code <= 90) || // A-Z
|
||
(code >= 97 && code <= 122); // a-z
|
||
};
|
||
const isnumeric = (char) => {
|
||
const code = char.charCodeAt(0);
|
||
return (code >= 48 && code <= 57) ; // 0-9
|
||
};
|
||
const isnumericDot = (char) => {
|
||
const code = char.charCodeAt(0);
|
||
return (code >= 48 && code <= 57)||char==='.' ; // 0-9
|
||
};
|
||
const isAlpha= (char) => {
|
||
const code = char.charCodeAt(0);
|
||
return (code >= 65 && code <= 90) || // A-Z
|
||
(code >= 97 && code <= 122); // a-z
|
||
};
|
||
|
||
function validateString(input) {
|
||
// 如果输入为空或不是字符串,直接返回 false
|
||
if (typeof input !== 'string' || input.length === 0) {
|
||
return false;
|
||
}
|
||
|
||
// 正则表达式:匹配中文字符(\u4e00-\u9fa5)或字母数字
|
||
const regex = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/;
|
||
|
||
// 遍历字符串中的每个字符
|
||
for (let i = 0; i < input.length; i++) {
|
||
const char = input[i];
|
||
// 如果字符不符合要求,返回 false
|
||
if (!regex.test(char)) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 所有字符都符合要求
|
||
return true;
|
||
}
|
||
|
||
// 通用标记添加函数
|
||
const addTag = (tagName,RuleStr,context) => {
|
||
// 解构上下文对象
|
||
const { tags, nextChar, tagOrder, value } = context;
|
||
const sBSC=GetBSC(RuleStr);
|
||
let sCPH='',sCXH='';
|
||
// 获取当前字符
|
||
if (tagName === 'XLXH') {
|
||
sCPH=ProdCPH_XLXH(sBSC);
|
||
sCXH=ProdCXH_XLXH(sBSC);
|
||
}
|
||
else if (tagName === 'DYDJ') {
|
||
sCPH=ProdCPH_DYDJ(sBSC);
|
||
sCXH=ProdCXH_DYDJ(sBSC);
|
||
}
|
||
else if (tagName === 'XLBZ') {
|
||
sCPH=ProdCPH_XLBZ(sBSC);
|
||
sCXH=ProdCXH_XLBZ(sBSC);
|
||
}
|
||
else if (tagName === 'XLGG') {
|
||
sCPH=ProdCPH_XLGG(sBSC);
|
||
sCXH=ProdCXH_XLGG(sBSC);
|
||
}
|
||
// 生成标记字符串
|
||
value.value =GetMRYFC(tagName,sCPH,sCXH);
|
||
};
|
||
|
||
// 创建上下文对象
|
||
const modelContext = {
|
||
tags: modelTags,
|
||
nextChar: modelNextChar,
|
||
tagOrder: modelTagOrder,
|
||
value: modelValue
|
||
};
|
||
|
||
const specContext = {
|
||
tags: specTags,
|
||
nextChar: specNextChar,
|
||
tagOrder: specTagOrder,
|
||
value: specValue
|
||
};
|
||
|
||
const voltageContext = {
|
||
tags: voltageTags,
|
||
nextChar: voltageNextChar,
|
||
tagOrder: voltageTagOrder,
|
||
value: voltageValue
|
||
};
|
||
|
||
const standardContext = {
|
||
tags: standardTags,
|
||
nextChar: standardNextChar,
|
||
tagOrder: standardTagOrder,
|
||
value: standardValue
|
||
};
|
||
|
||
// 原函数改为调用通用函数
|
||
const addModelTag = RuleStr => addTag('XLXH',RuleStr,modelContext);
|
||
const addSpecTag = RuleStr => addTag('XLGG',RuleStr, specContext);
|
||
const addvoltageTag = RuleStr => addTag('DYDJ',RuleStr, voltageContext);
|
||
const addstandardTag = RuleStr => addTag('XLBZ',RuleStr, standardContext);
|
||
function GetMRYFC(sYFLX, sCPH, sCXH) {
|
||
debugger
|
||
sYFLX = (sYFLX || '').trim();
|
||
sCPH = (sCPH || '').trim();
|
||
sCXH = (sCXH || '').trim();
|
||
let sYFC = '', tmp;
|
||
|
||
// 型号拆分处理(XLXH)
|
||
if (sYFLX === 'XLXH') {
|
||
const processCode = (char, typeName) => {
|
||
let s = sCXH, res = '';
|
||
while (s.includes(char)) {
|
||
const pos = s.indexOf(char);
|
||
res += `[${s.substr(pos, 2).replace(new RegExp(char, 'g'), typeName)}]`;
|
||
s = s.substring(pos + 2);
|
||
}
|
||
return res;
|
||
};
|
||
if(FGF.value==='空')
|
||
return processCode('B', '前缀码') + processCode('A', '型号码') + processCode('C', '后缀码');
|
||
else
|
||
{
|
||
if(processCode('A', '型号码')!=''&&processCode('B', '前缀码')!='')
|
||
return processCode('B', '前缀码') +FGF.value+ processCode('A', '型号码') + processCode('C', '后缀码');
|
||
else
|
||
return processCode('B', '前缀码') + processCode('A', '型号码') + processCode('C', '后缀码');
|
||
}
|
||
}
|
||
|
||
// 规格处理(XLGG)
|
||
else if (sYFLX === 'XLGG') {
|
||
const getCode = (char, typeName) => {
|
||
const pos = sCXH.indexOf(char);
|
||
return pos >= 0 ? `[${sCXH.substr(pos, 2).replace(char, typeName)}]` : '';
|
||
};
|
||
|
||
const getNextCode = (startChar, str, typeName) => {
|
||
const pos = str.indexOf(startChar);
|
||
return pos >= 0 ? `[${str.substr(pos, 2).replace(startChar, typeName)}]` : '';
|
||
};
|
||
|
||
switch (sCPH) {
|
||
case 'A1':
|
||
if (sCXH.includes('A')) sYFC = '[截面a]';
|
||
break;
|
||
case 'A2':
|
||
if (sCXH.includes('A')) sYFC = '[截面a]/[截面b]';
|
||
break;
|
||
case 'A1B1':
|
||
if (sCXH.includes('A') && sCXH.includes('B')) {
|
||
sYFC = `${getCode('B', '芯数')}*${getCode('A', '截面')}`;
|
||
}
|
||
break;
|
||
case 'A1B2':
|
||
const a1Pos = sCXH.indexOf('A');
|
||
if (a1Pos >= 0) {
|
||
tmp = sCXH.substring(a1Pos + 2);
|
||
if (sCXH.includes('B') && tmp.includes('A')) {
|
||
sYFC = `${getCode('B', '芯数')}*${getCode('A', '截面')}`
|
||
+ `+${getCode('B', '芯数')}*${getNextCode('A', tmp, '截面')}`;
|
||
}
|
||
}
|
||
break;
|
||
case 'A2B2':
|
||
const aPos = sCXH.indexOf('A');
|
||
const bPos = sCXH.indexOf('B');
|
||
if (aPos >= 0 && bPos >= 0) {
|
||
const bStr = sCXH.substring(bPos + 2);
|
||
tmp = sCXH.substring(aPos + 2);
|
||
if (bStr.includes('B') && tmp.includes('A')) {
|
||
sYFC = `${getCode('B', '芯数')}*${getCode('A', '截面')}`
|
||
+ `+${getNextCode('B', bStr, '芯数')}*${getNextCode('A', tmp, '截面')}`;
|
||
}
|
||
}
|
||
break;
|
||
case 'A1B1C1':
|
||
if (['A','B','C'].every(c => sCXH.includes(c))) {
|
||
sYFC = `${getCode('C', '线对数')}*${getCode('B', '芯数')}*${getCode('A', '截面')}`;
|
||
}
|
||
break;
|
||
case 'A1B1D1':
|
||
if (['A','B','D'].every(c => sCXH.includes(c))) {
|
||
sYFC = `${getCode('D', '线组数')}*${getCode('B', '芯数')}*${getCode('A', '截面')}`;
|
||
}
|
||
break;
|
||
}
|
||
|
||
// 默认处理规则
|
||
if (!sYFC && sCXH) {
|
||
let result = sCXH
|
||
.replace(/A/g, '][截面')
|
||
.replace(/B/g, '][芯数')
|
||
.replace(/C/g, '][线对数')
|
||
.replace(/D/g, '][线组数');
|
||
sYFC =result.substring(1) + ']';
|
||
}
|
||
return sYFC;
|
||
}
|
||
|
||
// 电压等级(DYDJ)处理
|
||
else if (sYFLX === 'DYDJ') {
|
||
const g = (c, n) => sCXH.includes(c) ? `[${sCXH.substr(sCXH.indexOf(c), 2).replace(c, n)}]` : '';
|
||
switch (sCPH) {
|
||
case 'A1': sYFC = g('A', '相电压'); break;
|
||
case 'B1': sYFC = g('B', '线电压'); break;
|
||
case 'A1B1':
|
||
if (sCXH.includes('A') && sCXH.includes('B')) {
|
||
sYFC = `${g('A', '相电压')}/${g('B', '线电压')}`;
|
||
}
|
||
break;
|
||
case 'B1C1':
|
||
if (sCXH.includes('B') && sCXH.includes('C')) {
|
||
sYFC = `${g('B', '线电压')}${g('C', '电压单位')}`;
|
||
}
|
||
break;
|
||
case 'A1B1C1':
|
||
if (sCXH.includes('A') && sCXH.includes('B') && sCXH.includes('C')) {
|
||
sYFC = `${g('A', '相电压')}/${g('B', '线电压')}${g('C', '电压单位')}`;
|
||
}
|
||
break;
|
||
}
|
||
if (!sYFC && sCXH) {
|
||
let result = sCXH
|
||
.replace(/A/g, '][相电压')
|
||
.replace(/B/g, '][线电压')
|
||
.replace(/C/g, '][电压单位')
|
||
sYFC =result.substring(1) + ']';
|
||
}
|
||
return sYFC;
|
||
}
|
||
|
||
// 标准(XLBZ)处理
|
||
else if (sYFLX === 'XLBZ') {
|
||
if (sCPH === 'A1' && sCXH.includes('A')) {
|
||
sYFC = '[标准名a]';
|
||
}
|
||
if (!sYFC && sCXH) {
|
||
sYFC = `[${sCXH.replace(/A/g, '标准名').replace(/A/g, '][标准名').substring(2)}]`;
|
||
}
|
||
return sYFC;
|
||
}
|
||
|
||
// 颜色(XLYS)处理
|
||
else if (sYFLX === 'XLYS') {
|
||
if (sCPH === 'A1' && sCXH.includes('A')) {
|
||
sYFC = '[颜色名a]';
|
||
} else if (sCPH === 'A2' && sCXH.includes('A')) {
|
||
sYFC = '[颜色名a][颜色名b]';
|
||
}
|
||
if (!sYFC && sCXH) {
|
||
sYFC = `[${sCXH.replace(/A/g, '][颜色名').substring(2)}]`;
|
||
}
|
||
return sYFC;
|
||
}
|
||
|
||
return '';
|
||
}
|
||
|
||
function GetBSC(inputText) {
|
||
// 实现 GetOptionIdList 的功能:提取文本中所有 [XX=YY] 中的 XX
|
||
const getOptionIdList = (str) => {
|
||
const matches = []; // 存储提取结果的数组
|
||
// 使用正则表达式匹配 [XX=YY] 模式,并提取 XX
|
||
const regex = /\[([^\]=]+)=[^\]]+\]/g;
|
||
let match;
|
||
// 循环获取所有匹配项
|
||
while ((match = regex.exec(str)) !== null) {
|
||
matches.push(match[1]); // 将捕获组1(XX)添加到结果数组
|
||
}
|
||
return matches;
|
||
};
|
||
|
||
const str = inputText;
|
||
const BLZList = getOptionIdList(str);
|
||
let sBSC = '';
|
||
// 拼接成 [XX][YY] 格式的字符串
|
||
for (let i = 0; i < BLZList.length; i++) {
|
||
sBSC += '[' + BLZList[i] + ']';
|
||
}
|
||
return sBSC;
|
||
}
|
||
|
||
function ProdCPH_XLXH(aJXGZC) {
|
||
const SBLZsXLXH = ['型号码', '前缀码', '后缀码'];
|
||
return prodCPH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCXH_XLXH(aJXGZC) {
|
||
const SBLZsXLXH = ['型号码', '前缀码', '后缀码'];
|
||
return ProdCXH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCPH_XLGG(aJXGZC) {
|
||
const SBLZsXLXH = ['截面', '芯数', '线对数', '线组数'];
|
||
return prodCPH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCXH_XLGG(aJXGZC) {
|
||
const SBLZsXLXH = ['截面', '芯数', '线对数', '线组数'];
|
||
return ProdCXH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCPH_DYDJ(aJXGZC) {
|
||
const SBLZsXLXH = ['相电压', '线电压', '电压单位'];
|
||
return prodCPH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCXH_DYDJ(aJXGZC) {
|
||
const SBLZsXLXH = ['相电压', '线电压', '电压单位'];
|
||
return ProdCXH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCPH_XLBZ(aJXGZC) {
|
||
const SBLZsXLXH = ['标准名'];
|
||
return prodCPH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function ProdCXH_XLBZ(aJXGZC) {
|
||
const SBLZsXLXH = ['标准名'];
|
||
return ProdCXH(aJXGZC, SBLZsXLXH);
|
||
}
|
||
|
||
function prodCPH(aJXGZC, aBJCTag) {
|
||
// 初始化计数数组
|
||
const tokenCount = new Array(64).fill(0);
|
||
const BJCTagLen = [];
|
||
|
||
// 存储每个标签的长度
|
||
for (let i = 0; i < aBJCTag.length; i++) {
|
||
BJCTagLen[i] = aBJCTag[i].length;
|
||
}
|
||
|
||
let index = 0;
|
||
const inputLength = aJXGZC.length;
|
||
let result = '';
|
||
|
||
while (index < inputLength) {
|
||
// 查找下一个左括号 '['
|
||
while (index < inputLength && aJXGZC[index] !== '[') {
|
||
index++;
|
||
}
|
||
|
||
if (index >= inputLength) break;
|
||
index++; // 跳过 '['
|
||
|
||
const startIndex = index;
|
||
|
||
// 查找下一个右括号 ']'
|
||
while (index < inputLength && aJXGZC[index] !== ']') {
|
||
index++;
|
||
}
|
||
|
||
if (index >= inputLength) break;
|
||
|
||
const tagContent = aJXGZC.substring(startIndex, index);
|
||
const tagLen = tagContent.length;
|
||
let found = false;
|
||
|
||
// 处理单字符标签 (A-Z)
|
||
if (tagLen === 1) {
|
||
const charCode = tagContent.charCodeAt(0);
|
||
if (charCode >= 65 && charCode <= 90) { // A-Z
|
||
const idx = charCode - 65;
|
||
if (idx < aBJCTag.length) {
|
||
tokenCount[idx]++;
|
||
found = true;
|
||
}
|
||
}
|
||
}
|
||
// 处理多字符标签
|
||
else {
|
||
for (let i = 0; i < aBJCTag.length; i++) {
|
||
const tag = aBJCTag[i];
|
||
// 检查标签长度匹配
|
||
if (tagLen === BJCTagLen[i]) {
|
||
// 不区分大小写的比较
|
||
if (tagContent.toLowerCase() === tag.toLowerCase()) {
|
||
tokenCount[i]++;
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
index++; // 跳过 ']'
|
||
}
|
||
|
||
// 构造结果字符串
|
||
for (let i = 0; i < aBJCTag.length; i++) {
|
||
if (tokenCount[i] > 0) {
|
||
result += String.fromCharCode(65 + i) + tokenCount[i].toString();
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
function ProdCXH(aJXGZC, aBJCTag) {
|
||
|
||
// 获取选项ID列表 (如 ['型号码', '前缀码', ...])
|
||
const getOptionIdList = (str) => {
|
||
const matches = [];
|
||
const regex = /\[([^\]]+)\]/g;
|
||
let match;
|
||
while ((match = regex.exec(str)) !== null) {
|
||
matches.push(match[1]);
|
||
}
|
||
return matches;
|
||
};
|
||
|
||
let result = '';
|
||
const BLZList = getOptionIdList(aJXGZC);
|
||
let BLZOrder = 97; // 'a'的ASCII码
|
||
|
||
// 遍历每个保留字标识符
|
||
for (let i = 0; i < BLZList.length; i++) {
|
||
const curBLZ = BLZList[i];
|
||
let found = false;
|
||
|
||
// 检查当前保留字是否在指定标签列表中
|
||
for (let j = 0; j < aBJCTag.length; j++) {
|
||
if (aBJCTag[j] === curBLZ) {
|
||
// 生成编码:字母(A,B,C...) + 顺序号(a,b,c...)
|
||
const letter = String.fromCharCode(65 + j);
|
||
const orderChar = String.fromCharCode(BLZOrder);
|
||
result += letter + orderChar;
|
||
BLZOrder++; // 顺序号递增
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
const openSyntaxPage = () => {
|
||
// 构建URL参数
|
||
const title = "线缆型号语法";
|
||
const ruleString1 = encodeURIComponent(ruleString.value);
|
||
const reservedWords = "型号码,前缀码,后缀码";
|
||
const connectors = ",-,/,(,)";
|
||
const syntaxString = encodeURIComponent(modelValue.value);
|
||
// 生成唯一ID用于标识这个窗口
|
||
const windowId = Date.now().toString();
|
||
// 打开新窗口
|
||
const url = `cable_model_syntax.html?title=${title}&ruleString=${ruleString1}&reservedWords=${reservedWords}&connectors=${connectors}&syntaxString=${syntaxString}&windowId=${windowId}`;
|
||
const syntaxWindow = window.open(url, '_blank', 'width=950,height=750,location=no');
|
||
window.syntaxWindows = window.syntaxWindows || {};
|
||
window.syntaxWindows[windowId] = syntaxWindow;
|
||
// 设置轮询检查窗口状态
|
||
const checkWindowClosed = setInterval(() => {
|
||
if (!window.syntaxWindows[windowId] || window.syntaxWindows[windowId].closed) {
|
||
clearInterval(checkWindowClosed);
|
||
handleSyntaxWindowClosed(windowId);
|
||
}
|
||
}, 500);
|
||
};
|
||
|
||
|
||
const closePage = () => {
|
||
let topDocument;
|
||
try {
|
||
topDocument = top.document;
|
||
} catch (e) {
|
||
topDocument = document; // 跨域时回退到当前文档
|
||
}
|
||
try {
|
||
// 获取元素引用
|
||
const divZDY = topDocument.getElementById("div_ZDY");
|
||
const divZDY_100860140 =
|
||
topDocument.getElementById("divZDY_100860140");
|
||
|
||
// 确保主容器存在
|
||
if (!divZDY) return;
|
||
|
||
// 清除divZDY_102849000(如果存在)
|
||
if (divZDY_100860140) {
|
||
divZDY.removeChild(divZDY_100860140);
|
||
}
|
||
while (divZDY.firstChild) {
|
||
divZDY.removeChild(divZDY.firstChild);
|
||
}
|
||
|
||
// 隐藏主容器
|
||
divZDY.style.display = "none";
|
||
} catch (error) {
|
||
console.error("隐藏自定义元素时出错:", error);
|
||
}
|
||
// 刷新报价单
|
||
const frameElement = topDocument.getElementById('XLBJDInfo_frame');
|
||
if (!frameElement) {
|
||
console.log('未找到 XLBJDInfo_frame');
|
||
} else {
|
||
const iframeDoc = frameElement.contentWindow.document;
|
||
if (!iframeDoc) {
|
||
console.log('跨域访问被阻止');
|
||
} else {
|
||
const reloadButton = iframeDoc.getElementById('ctl00_CPHButton_btnReLoad');
|
||
if (reloadButton) {
|
||
reloadButton.click();
|
||
} else {
|
||
console.log('未找到 btnReLoad 控件');
|
||
}
|
||
}
|
||
}
|
||
|
||
};
|
||
|
||
// 处理窗口关闭
|
||
const handleSyntaxWindowClosed = (windowId) => {
|
||
// 从localStorage获取返回值
|
||
const resultKey = `syntaxResult_${windowId}`;
|
||
const result = localStorage.getItem(resultKey);
|
||
|
||
if (result) {
|
||
try {
|
||
const parsedResult = JSON.parse(result);
|
||
processSyntaxResult(parsedResult);
|
||
} catch (e) {
|
||
console.error('解析返回值失败:', e);
|
||
ElMessage.error('解析语法规则返回值失败');
|
||
} finally {
|
||
// 清理存储
|
||
localStorage.removeItem(resultKey);
|
||
delete window.syntaxWindows[windowId];
|
||
}
|
||
} else {
|
||
ElMessage.warning('语法规则窗口已关闭,但未返回任何值');
|
||
}
|
||
};
|
||
|
||
// 处理返回值
|
||
const processSyntaxResult = (result) => {
|
||
if (result && result.modelValue) {
|
||
|
||
// 更新实际值
|
||
if (result.modelValue) {
|
||
modelValue.value = result.modelValue;
|
||
modelActualValue.value = result.modelActualValue;
|
||
}
|
||
|
||
ElMessage.success('语法规则已更新');
|
||
} else {
|
||
ElMessage.warning('未返回有效的语法规则');
|
||
}
|
||
};
|
||
|
||
// 重置线缆型号标记
|
||
const resetTags = () => {
|
||
modelTags.value = [];
|
||
modelNextChar.value = 97;
|
||
modelValue.value = '';
|
||
modelActualValue.value = '';
|
||
specTags.value = [];
|
||
specNextChar.value = 97;
|
||
specValue.value = '';
|
||
specActualValue.value = '';
|
||
voltageTags.value = [];
|
||
voltageNextChar.value = 97;
|
||
voltageValue.value = '';
|
||
voltageActualValue.value = '';
|
||
standardTags.value = [];
|
||
standardNextChar.value = 97;
|
||
standardValue.value = '';
|
||
standardActualValue.value = '';
|
||
|
||
specValue.value = '';
|
||
voltageValue.value = '';
|
||
standardValue.value = '';
|
||
colorValue.value = '';
|
||
};
|
||
|
||
|
||
const parseRuleValue = async () => {
|
||
|
||
if (!ruleString.value) {
|
||
modelActualValue.value = '';
|
||
specActualValue.value = '';
|
||
voltageActualValue.value = '';
|
||
standardActualValue.value = '';
|
||
return;
|
||
}
|
||
|
||
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 创建 XLJXGZ 对象
|
||
const ruleConfig = {
|
||
XLJXDJ: "XSBJ",
|
||
XLJXDH: BJDID, // 使用固定的解析规则代号
|
||
XLJXGZXH: "0", // 默认序号
|
||
XLJXGZC: ruleString.value,
|
||
XLXHYFC: modelValue.value,
|
||
XLGGYFC: specValue.value,
|
||
DYDJYFC: voltageValue.value,
|
||
XLBZYFC: standardValue.value,
|
||
XLYSYFC: colorValue.value || "",
|
||
WHR: "",
|
||
WHRID: "",
|
||
WHSJ: new Date().toISOString()
|
||
};
|
||
|
||
// 调用 XLYSTZJX 服务
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'XLYSTZJX',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005, // 替换为实际RefUmId
|
||
'item': ruleConfig,
|
||
'sXLBSC': ruleString.value.replace(/\[([^\]]+)=([^\]]+)\]/g, '$2') // 添加规则串参数
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"XLYSTZJX",
|
||
50000 // 5秒超时
|
||
);
|
||
});
|
||
|
||
|
||
|
||
// 更新实际值
|
||
if (result) {
|
||
modelActualValue.value = result.XLXH || '';
|
||
specActualValue.value = result.XLGG || '';
|
||
voltageActualValue.value = result.DYDJ || '';
|
||
standardActualValue.value = result.XLBZ || '';
|
||
} else {
|
||
ElMessage.warning('解析服务返回空结果');
|
||
}
|
||
|
||
} catch (error) {
|
||
|
||
console.error('规则解析失败:', error);
|
||
ElMessage.error(`规则解析失败: ${error.message || '服务异常'}`);
|
||
|
||
// 设置默认值
|
||
modelActualValue.value = '';
|
||
specActualValue.value = '';
|
||
voltageActualValue.value = '';
|
||
standardActualValue.value = '';
|
||
}
|
||
|
||
|
||
};
|
||
|
||
// 原始函数改为调用通用函数
|
||
const parseModelValue = parseRuleValue;
|
||
const parseSpecValue = parseRuleValue;
|
||
const parsevoltageValue = parseRuleValue;
|
||
const parsestandardValue = parseRuleValue;
|
||
|
||
// 通用标记函数
|
||
const createMarker = (tagName, validatorFunc, errorMessage) => {
|
||
return () => {
|
||
if (!ruleEditor.value) return;
|
||
|
||
const textarea = ruleEditor.value;
|
||
const text = ruleString.value;
|
||
let start = textarea.selectionStart;
|
||
let end = textarea.selectionEnd;
|
||
|
||
// 检查选中区域是否在已有标记内部
|
||
function isInsideMark(startPos, endPos) {
|
||
let inMark = false;
|
||
let lastOpen = -1;
|
||
|
||
for (let i = 0; i <= endPos; i++) {
|
||
if (i === text.length) break;
|
||
|
||
if (text[i] === '[') {
|
||
lastOpen = i;
|
||
inMark = true;
|
||
} else if (text[i] === ']' && lastOpen !== -1) {
|
||
inMark = false;
|
||
}
|
||
|
||
// 检查当前位置是否在标记范围内
|
||
if (i >= startPos && i < endPos && inMark) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 检查结束位置是否在未闭合的标记内
|
||
return inMark && (lastOpen <= startPos);
|
||
}
|
||
|
||
// 自动扩展选择范围的通用逻辑
|
||
if (end - start <= 0) {
|
||
// 向左扩展
|
||
let left = start;
|
||
while (left > 0 && validatorFunc(text.charAt(left - 1))) left--;
|
||
|
||
// 向右扩展
|
||
let right = start;
|
||
while (right < text.length && validatorFunc(text.charAt(right))) right++;
|
||
|
||
if (left < right) {
|
||
textarea.setSelectionRange(left, right);
|
||
start = left;
|
||
end = right;
|
||
} else {
|
||
ElMessage.warning('请先选中要标记的文本');
|
||
return;
|
||
}
|
||
}
|
||
|
||
const selectedText = text.substring(start, end);
|
||
if (!selectedText) {
|
||
ElMessage.warning('请先选中要标记的文本');
|
||
return;
|
||
}
|
||
|
||
// 精确判断是否在已有标记内部
|
||
if (isInsideMark(start, end)) {
|
||
ElMessage.error('选中区域位于已有标记内部,无法再次标记!');
|
||
return;
|
||
}
|
||
|
||
// 统一使用传入的验证函数
|
||
for (let i = 0; i < selectedText.length; i++) {
|
||
if (!validatorFunc(selectedText[i])) {
|
||
ElMessage.error(errorMessage);
|
||
return;
|
||
}
|
||
}
|
||
|
||
const markedText = `[${tagName}=${selectedText}]`;
|
||
ruleString.value = text.substring(0, start) + markedText + text.substring(end);
|
||
// 统一处理标记分组
|
||
const groups = {
|
||
// 型号分组
|
||
型号: {
|
||
tags: ['前缀码', '型号码', '后缀码', '型号'],
|
||
add: addModelTag,
|
||
parse: parseModelValue
|
||
},
|
||
// 规格分组
|
||
规格: {
|
||
tags: ['芯数', '截面', '线组数', '线对数', '规格'],
|
||
add: addSpecTag,
|
||
parse: parseSpecValue
|
||
},
|
||
// 电压分组
|
||
电压: {
|
||
tags: ['相电压', '线电压', '电压单位', '电压等级'],
|
||
add: addvoltageTag,
|
||
parse: parsevoltageValue
|
||
},
|
||
// 标准分组
|
||
标准: {
|
||
tags: ['标准名'],
|
||
add: addstandardTag,
|
||
parse: parsestandardValue
|
||
}
|
||
};
|
||
|
||
// 找到对应的分组并执行
|
||
Object.values(groups).forEach(group => {
|
||
if (group.tags.includes(tagName)) {
|
||
group.add(ruleString.value);
|
||
group.parse();
|
||
}
|
||
});
|
||
|
||
// 重置选择和设置光标位置
|
||
selection.value = { start: 0, end: 0, text: '' };
|
||
nextTick(() => {
|
||
const newStart = start + markedText.length;
|
||
ruleEditor.value.setSelectionRange(newStart, newStart);
|
||
ruleEditor.value.focus();
|
||
});
|
||
};
|
||
};
|
||
|
||
// ==== 创建具体标记函数 ====
|
||
|
||
// 型号相关标记函数
|
||
const markPrefixCode = createMarker(
|
||
'前缀码',
|
||
isAlphanumeric,
|
||
'前缀码只能包含字母和数字'
|
||
);
|
||
|
||
const markModelCode = createMarker(
|
||
'型号码',
|
||
isAlphanumeric,
|
||
'型号码只能包含字母和数字'
|
||
);
|
||
|
||
const markSuffixCode = createMarker(
|
||
'后缀码',
|
||
isAlphanumeric,
|
||
'后缀码只能包含字母和数字'
|
||
);
|
||
|
||
// 标准名标记函数
|
||
const markStandardName = createMarker(
|
||
'标准名',
|
||
validateString, // 使用统一的验证函数
|
||
'标准名包含无效字符' // 统一错误消息
|
||
);
|
||
|
||
// 电压相关标记函数
|
||
const markVoltagePhase = createMarker(
|
||
'相电压',
|
||
isnumericDot,
|
||
'相电压应为数字'
|
||
);
|
||
|
||
const markVoltageLine = createMarker(
|
||
'线电压',
|
||
isnumericDot,
|
||
'线电压应为数字'
|
||
);
|
||
|
||
const markVoltageUnit = createMarker(
|
||
'电压单位',
|
||
isAlpha,
|
||
'电压单位只能为字母'
|
||
);
|
||
|
||
const markVoltageLevel = createMarker(
|
||
'电压等级',
|
||
isnumeric,
|
||
'电压等级应为数字'
|
||
);
|
||
|
||
// 规格相关标记函数
|
||
const markWireCount = createMarker(
|
||
'芯数',
|
||
isnumeric,
|
||
'芯数必须为数字'
|
||
);
|
||
|
||
const markSection = createMarker(
|
||
'截面',
|
||
isnumericDot,
|
||
'截面必须为数字'
|
||
);
|
||
|
||
const markWireGroup = createMarker(
|
||
'线组数',
|
||
isnumeric,
|
||
'线组数必须为数字'
|
||
);
|
||
|
||
const markWirePair = createMarker(
|
||
'线对数',
|
||
isnumeric,
|
||
'线对数必须为数字'
|
||
);
|
||
|
||
const markSpec = createMarker(
|
||
'规格',
|
||
isnumeric,
|
||
'规格必须为数字'
|
||
);
|
||
|
||
// 批量插入函数
|
||
const bulkInsertXLJXMX = async (items) => {
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 分批处理(每批500条)
|
||
const batchSize = 100000;
|
||
const totalBatches = Math.ceil(items.length / batchSize);
|
||
let successCount = 0;
|
||
let errorMessages = [];
|
||
|
||
for (let i = 0; i < totalBatches; i++) {
|
||
const start = i * batchSize;
|
||
const end = Math.min(start + batchSize, items.length);
|
||
const batch = items.slice(start, end);
|
||
|
||
try {
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'InsertXLJXMXList',
|
||
false,
|
||
{
|
||
'-RefUmId': 102851007,
|
||
'items': batch
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"InsertXLJXMXList",
|
||
300000 // 30秒超时
|
||
);
|
||
});
|
||
|
||
if (result && result.success) {
|
||
successCount += batch.length;
|
||
} else {
|
||
errorMessages.push(`批次${i+1}插入失败: ${result?.message || '未知错误'}`);
|
||
}
|
||
} catch (error) {
|
||
errorMessages.push(`批次${i+1}插入失败: ${error.message || '服务异常'}`);
|
||
}
|
||
|
||
// 更新进度(仅针对导入Excel)
|
||
if (importing.value) {
|
||
importProgress.value = Math.round(((i + 1) / totalBatches) * 100);
|
||
}
|
||
}
|
||
|
||
// 返回合并结果
|
||
if (errorMessages.length === 0) {
|
||
return { success: true, message: `成功插入${successCount}条数据` };
|
||
} else {
|
||
return {
|
||
success: false,
|
||
message: `成功插入${successCount}条,失败${items.length - successCount}条。错误详情:${errorMessages.slice(0, 5).join('')}${errorMessages.length > 5 ? `...等${errorMessages.length}条错误` : ''}`
|
||
};
|
||
}
|
||
} catch (error) {
|
||
console.error('批量插入失败:', error);
|
||
return {
|
||
success: false,
|
||
message: `批量插入失败: ${error.message || '服务异常'}`
|
||
};
|
||
}
|
||
};
|
||
|
||
// ==== Excel导入相关函数 ====
|
||
const showImportDialog = () => {
|
||
importDialogVisible.value = true;
|
||
resetImport();
|
||
};
|
||
|
||
const closeImportDialog = () => {
|
||
importDialogVisible.value = false;
|
||
resetImport();
|
||
};
|
||
|
||
const resetImport = () => {
|
||
excelFile.value = null;
|
||
excelColumns.value = [];
|
||
previewData.value = [];
|
||
importProgress.value = 0;
|
||
importedCount.value = 0;
|
||
totalRows.value = 0;
|
||
importStatus.value = null;
|
||
errorMessage.value = '';
|
||
successMessage.value = '';
|
||
importing.value = false;
|
||
canImport.value = false;
|
||
|
||
// 重置映射关系
|
||
tableColumns.value.forEach(col => {
|
||
columnMapping[col] = null;
|
||
});
|
||
};
|
||
|
||
const triggerFileInput = () => {
|
||
if (fileInput.value) {
|
||
fileInput.value.click();
|
||
}
|
||
};
|
||
|
||
const handleFileChange = (e) => {
|
||
const file = e.target.files ? e.target.files[0] : null;
|
||
if (!file) return;
|
||
|
||
// 检查文件类型
|
||
if (!/\.(xlsx|xls)$/i.test(file.name)) {
|
||
errorMessage.value = '请选择Excel文件(.xlsx或.xls格式)';
|
||
return;
|
||
}
|
||
|
||
// 检查文件大小 (最大100MB)
|
||
if (file.size > 100 * 1024 * 1024) {
|
||
errorMessage.value = '文件过大,请选择小于100MB的Excel文件';
|
||
return;
|
||
}
|
||
|
||
excelFile.value = file;
|
||
errorMessage.value = '';
|
||
|
||
// 开始解析Excel
|
||
readExcelFile(file);
|
||
};
|
||
|
||
const readExcelFile = (file) => {
|
||
importing.value = true;
|
||
errorMessage.value = '';
|
||
importStatus.value = null;
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
try {
|
||
const data = new Uint8Array(e.target.result);
|
||
const workbook = XLSX.read(data, { type: 'array' });
|
||
|
||
// 获取第一个工作表
|
||
const firstSheetName = workbook.SheetNames[0];
|
||
const worksheet = workbook.Sheets[firstSheetName];
|
||
|
||
// 将工作表转换为JSON
|
||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||
|
||
if (jsonData.length === 0) {
|
||
errorMessage.value = 'Excel文件中没有数据';
|
||
importing.value = false;
|
||
return;
|
||
}
|
||
|
||
// 获取列名(假设第一行是列名)
|
||
excelColumns.value = jsonData[0].map(col => col || '未命名列');
|
||
|
||
// 提取预览数据(2-11行)
|
||
previewData.value = [];
|
||
for (let i = 1; i < Math.min(jsonData.length, 11); i++) {
|
||
const rowObj = {};
|
||
jsonData[i].forEach((cell, index) => {
|
||
rowObj[excelColumns.value[index]] = cell;
|
||
});
|
||
previewData.value.push(rowObj);
|
||
}
|
||
|
||
canImport.value = true;
|
||
importing.value = false;
|
||
totalRows.value = jsonData.length - 1;
|
||
ElMessage.success(`成功解析${totalRows.value}条数据`);
|
||
} catch (error) {
|
||
console.error('Excel解析错误:', error);
|
||
errorMessage.value = `Excel文件解析失败: ${error.message}`;
|
||
importing.value = false;
|
||
}
|
||
};
|
||
|
||
reader.onerror = (error) => {
|
||
console.error('文件读取错误:', error);
|
||
errorMessage.value = `文件读取失败: ${error.target.error.message}`;
|
||
importing.value = false;
|
||
};
|
||
|
||
reader.readAsArrayBuffer(file);
|
||
};
|
||
|
||
const startImport = async () => {
|
||
// 验证映射配置
|
||
if (!columnMapping['报价产品']) {
|
||
errorMessage.value = '请至少映射"报价产品"列';
|
||
return;
|
||
}
|
||
|
||
importing.value = true;
|
||
importProgress.value = 0;
|
||
importedCount.value = 0;
|
||
importStatus.value = null;
|
||
errorMessage.value = '';
|
||
successMessage.value = '';
|
||
|
||
// 重新读取文件以获取完整数据
|
||
const reader = new FileReader();
|
||
reader.onload = async function(e) {
|
||
try {
|
||
const data = new Uint8Array(e.target.result);
|
||
const workbook = XLSX.read(data, { type: 'array' });
|
||
const firstSheetName = workbook.SheetNames[0];
|
||
const worksheet = workbook.Sheets[firstSheetName];
|
||
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||
|
||
// 跳过标题行
|
||
const rows = jsonData.slice(1);
|
||
const itemsToInsert = [];
|
||
const newTableData = [];
|
||
|
||
// 为所有记录生成相同的XLJXDH
|
||
const commonXLJXDH =BJDID;
|
||
|
||
// 处理每一行数据
|
||
for (let i = 0; i < rows.length; i++) {
|
||
const row = rows[i];
|
||
const rowData = {};
|
||
|
||
// 映射Excel列到系统字段
|
||
tableColumns.value.forEach(col => {
|
||
const excelCol = columnMapping[col];
|
||
if (excelCol) {
|
||
const colIndex = excelColumns.value.indexOf(excelCol);
|
||
if (colIndex !== -1) {
|
||
rowData[col.toLowerCase()] = row[colIndex] || '';
|
||
}
|
||
}
|
||
});
|
||
|
||
// 创建数据库对象
|
||
const product = rowData['报价产品'] || '';
|
||
const sequenceNumber = (i + 1).toString(); // 生成序号
|
||
itemsToInsert.push({
|
||
XLJXDJ: "XSBJ",
|
||
XLJXDH: BJDID, // 使用相同的代号
|
||
XLJXMXH: sequenceNumber,
|
||
XLBSC: product,
|
||
XLJXGZXH: "0",
|
||
JXXLXHID: rowData['型号'] || "",
|
||
XLXHID: "",
|
||
JXXLGGID: rowData['规格'] || "",
|
||
XLGGID: "",
|
||
JXDYDJID: rowData['电压等级'] || "",
|
||
DYDJID: "",
|
||
XLBZID: rowData['标准'] || "",
|
||
JXXLBZID: "",
|
||
XLYSID: "",
|
||
JXXLYSID: "",
|
||
WHR: "",
|
||
WHRID: "",
|
||
WHSJ: "",
|
||
DCZT: "N" ,
|
||
JLDW: rowData['计量单位'] || "",
|
||
SL: rowData['数量'] || ""
|
||
});
|
||
|
||
newTableData.push({
|
||
index: sequenceNumber, // 添加序号字段
|
||
product,
|
||
unit: rowData['计量单位'] || '',
|
||
quantity: rowData['数量'] || '',
|
||
model: rowData['型号'] || '',
|
||
voltage: rowData['电压等级'] || '',
|
||
standard: rowData['标准'] || '',
|
||
spec: rowData['规格'] || '',
|
||
status: 'N',
|
||
checked: false
|
||
});
|
||
|
||
// 更新进度
|
||
importedCount.value = i + 1;
|
||
importProgress.value = Math.round(((i + 1) / rows.length) * 100);
|
||
}
|
||
|
||
// 批量插入数据
|
||
if (itemsToInsert.length > 0) {
|
||
const result = await bulkInsertXLJXMX(itemsToInsert);
|
||
|
||
if (result && result.success) {
|
||
// 重新加载以同步服务端分配的解析序号(与 DRHH 关联)
|
||
await loadTableData();
|
||
|
||
importing.value = false;
|
||
successMessage.value = `成功导入${itemsToInsert.length}条产品数据`;
|
||
ElMessage.success(`成功导入${itemsToInsert.length}条产品数据`);
|
||
} else {
|
||
importing.value = false;
|
||
errorMessage.value = `导入失败: ${result?.message || '未知错误'}`;
|
||
ElMessage.error(`导入失败: ${result?.message || '未知错误'}`);
|
||
}
|
||
} else {
|
||
importing.value = false;
|
||
ElMessage.warning('Excel中没有有效数据');
|
||
}
|
||
} catch (error) {
|
||
console.error('导入错误:', error);
|
||
errorMessage.value = `导入失败: ${error.message}`;
|
||
importing.value = false;
|
||
importStatus.value = 'exception';
|
||
}
|
||
};
|
||
|
||
reader.readAsArrayBuffer(excelFile.value);
|
||
};
|
||
|
||
// 辅助函数:格式化文件大小
|
||
const formatFileSize = (bytes) => {
|
||
if (bytes < 1024) return bytes + ' bytes';
|
||
else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + ' KB';
|
||
else return (bytes / 1048576).toFixed(2) + ' MB';
|
||
};
|
||
|
||
// ==== 其他功能函数 ====
|
||
|
||
// 保存文本选择
|
||
const saveSelection = () => {
|
||
if (!ruleEditor.value) return;
|
||
|
||
const textarea = ruleEditor.value;
|
||
const start = textarea.selectionStart;
|
||
const end = textarea.selectionEnd;
|
||
const text = textarea.value.substring(start, end);
|
||
|
||
selection.value = { start, end, text };
|
||
};
|
||
|
||
// 取消所有标记
|
||
const unmarkAll = () => {
|
||
// 移除所有标记格式 [tag=value]
|
||
ruleString.value = ruleString.value
|
||
.replace(/\[([^\]]+)=([^\]]+)\]/g, '$2');
|
||
|
||
// 清空所有配置项的值
|
||
resetTags();
|
||
|
||
|
||
// ElMessage.success('已取消所有标记');
|
||
};
|
||
|
||
// 粘贴数据
|
||
const pasteData = async () => {
|
||
const isHttps = window.location.protocol === 'https:';
|
||
logMappingDebug('pasteData.enter', { isHttps, protocol: window.location.protocol });
|
||
|
||
if (!isHttps) {
|
||
showManualPasteDialog();
|
||
logMappingDebug('pasteData.http.showManualPasteDialog', {});
|
||
return;
|
||
}
|
||
|
||
// HTTPS环境,使用列映射功能
|
||
try {
|
||
const text = await navigator.clipboard.readText();
|
||
if (!text) {
|
||
ElMessage.warning('剪贴板中没有数据');
|
||
return;
|
||
}
|
||
|
||
clipboardText = text;
|
||
const rows = text.split('\n').filter(row => row.trim() !== '');
|
||
|
||
if (rows.length === 0) {
|
||
ElMessage.warning('剪贴板中的数据为空');
|
||
return;
|
||
}
|
||
|
||
const firstRow = rows[0].split('\t');
|
||
detectedColumnCount.value = firstRow.length;
|
||
|
||
detectedPreviewRows.value = rows.slice(0, 5).map((row, idx) => {
|
||
const cols = row.split('\t');
|
||
const rowData = { rowIndex: idx + 1 };
|
||
for (let i = 0; i < cols.length; i++) {
|
||
rowData['col' + i] = cols[i];
|
||
}
|
||
return rowData;
|
||
});
|
||
|
||
applyDefaultColumnMapping(columnFieldMapping, detectedColumnCount.value);
|
||
|
||
manualPasteText.value = text;
|
||
pasteStep.value = 'mapping';
|
||
manualPasteDialogVisible.value = true;
|
||
nextTick(() => forceApplyDefaultColumnMapping(columnFieldMapping, detectedColumnCount.value));
|
||
|
||
} catch (err) {
|
||
if (err.name === 'NotAllowedError' || err.name === 'NotSupportedError') {
|
||
ElMessage.warning('剪贴板访问被拒绝,请使用手动粘贴');
|
||
showManualPasteDialog();
|
||
} else {
|
||
completeCenterProgress(false, `粘贴失败: ${err.message}`);
|
||
console.error('粘贴失败:', err);
|
||
ElMessage.error('粘贴数据失败: ' + (err.message || '服务异常'));
|
||
loading.value = false;
|
||
}
|
||
}
|
||
};
|
||
|
||
const GetFGF = async () => {
|
||
try {
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetXHFGF',
|
||
false,
|
||
{
|
||
'-RefUmId': 102851007
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetXHFGF",
|
||
50000
|
||
);
|
||
});
|
||
|
||
FGF.value = result || [];
|
||
} catch (error) {
|
||
ElMessage.error('获取失败: ' + (error.message || '服务异常'));
|
||
} finally {
|
||
}
|
||
};
|
||
|
||
|
||
|
||
const normalizeBatchSpec = (value) => (value || '').replace(/×/g, '*');
|
||
|
||
// 加载表格数据
|
||
const loadTableData = async (options = {}) => {
|
||
const silent = options && options.silent === true;
|
||
loading.value = true;
|
||
try {
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const params = buildListQueryParams();
|
||
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetXLJXMXList',
|
||
false,
|
||
{
|
||
'-RefUmId': 102851007,
|
||
...params
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetXLJXMXList",
|
||
50000
|
||
);
|
||
});
|
||
|
||
tableData.value = (result || []).map(item => ({
|
||
index: item.XLJXMXH, // 序号字段
|
||
product: item.XLBSC,
|
||
unit: item.JLDW || '', // 新增
|
||
quantity: item.SL || '', // 新增
|
||
packageNumber:item.FBH, // 包号
|
||
techSpecId:item.JSGFID, // 技术规范ID
|
||
|
||
model: item.JXXLXHID || item.XLXHID,
|
||
XLXHID: item.XLXHID || '',
|
||
voltage: item.JXDYDJID || item.DYDJID,
|
||
standard: item.JXXLBZID || item.XLBZID,
|
||
spec: item.JXXLGGID || item.XLGGID,
|
||
XLGGID: item.XLGGID || '',
|
||
status: item.DCZT,
|
||
dx:item.DX,
|
||
XLJXGZXH:item.XLJXGZXH,
|
||
selected: false,
|
||
current: false // 添加当前行状态标识
|
||
}));
|
||
if (filterEquivalent.value) {
|
||
tableData.value=tableData.value.filter(item => item.dx !== ""&&item.dx != null);
|
||
}
|
||
|
||
// 更新下拉选项
|
||
updateFilterOptions();
|
||
|
||
if (!silent) {
|
||
ElMessage.success(`加载${tableData.value.length}条数据`);
|
||
}
|
||
} catch (error) {
|
||
ElMessage.error('数据加载失败: ' + (error.message || '服务异常'));
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
const deleteData = async () => {
|
||
loading.value = true;
|
||
try {
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const params = buildListQueryParams();
|
||
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'DeleteXLJXMX',
|
||
false,
|
||
{
|
||
'-RefUmId': 102851007,
|
||
...params
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"DeleteXLJXMX",
|
||
50000
|
||
);
|
||
});
|
||
|
||
loadTableData();
|
||
ElMessage.success(`删除成功!`);
|
||
} catch (error) {
|
||
ElMessage.error('数据删除失败: ' + (error.message || '服务异常'));
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
const exportExcel = () => {
|
||
if (!tableData.value || tableData.value.length === 0) {
|
||
ElMessage.warning('当前列表没有可导出的数据')
|
||
return
|
||
}
|
||
|
||
// Excel 表头(和页面一致)
|
||
const header = [
|
||
'序号',
|
||
'包号',
|
||
'报价产品',
|
||
'技术规范ID',
|
||
'计量单位',
|
||
'数量',
|
||
'型号',
|
||
'电压等级',
|
||
'标准',
|
||
'规格',
|
||
'等效'
|
||
]
|
||
|
||
// 数据行(严格对应 tableData 字段)
|
||
const rows = tableData.value.map((row, idx) => [
|
||
idx + 1, // 序号(按页面顺序)
|
||
row.packageNumber ?? '',
|
||
row.product ?? '',
|
||
row.techSpecId ?? '',
|
||
row.unit ?? '',
|
||
row.quantity ?? '',
|
||
row.model ?? '',
|
||
row.voltage ?? '',
|
||
row.standard ?? '',
|
||
row.spec ?? '',
|
||
row.dx ?? ''
|
||
])
|
||
|
||
const ws = XLSX.utils.aoa_to_sheet([header, ...rows])
|
||
|
||
// 列宽
|
||
ws['!cols'] = [
|
||
{ wch: 6 }, // 序号
|
||
{ wch: 10 }, // 包号
|
||
{ wch: 20 }, // 报价产品
|
||
{ wch: 18 }, // 技术规范ID
|
||
{ wch: 10 }, // 计量单位
|
||
{ wch: 10 }, // 数量
|
||
{ wch: 14 }, // 型号
|
||
{ wch: 14 }, // 电压等级
|
||
{ wch: 14 }, // 标准
|
||
{ wch: 14 }, // 规格
|
||
{ wch: 10 } // 等效
|
||
]
|
||
|
||
const wb = XLSX.utils.book_new()
|
||
XLSX.utils.book_append_sheet(wb, ws, '报价产品明细')
|
||
|
||
XLSX.writeFile(
|
||
wb,
|
||
`报价产品明细_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||
)
|
||
}
|
||
|
||
|
||
// 更新下拉选项
|
||
const updateFilterOptions = () => {
|
||
// 从表格数据中提取唯一的电压等级值
|
||
const voltageSet = new Set();
|
||
const standardSet = new Set();
|
||
|
||
tableData.value.forEach(item => {
|
||
if (item.voltage && item.voltage.trim()) {
|
||
voltageSet.add(item.voltage.trim());
|
||
}
|
||
if (item.standard && item.standard.trim()) {
|
||
standardSet.add(item.standard.trim());
|
||
}
|
||
});
|
||
|
||
// 转换为数组并排序,添加空选项
|
||
voltageOptions.value = [
|
||
{ value: '', label: '全部' },
|
||
{ value: '空', label: '空' }
|
||
].concat(Array.from(voltageSet).sort().map(value => ({
|
||
value: value,
|
||
label: value
|
||
})));
|
||
|
||
standardOptions.value = [
|
||
{ value: '', label: '全部' }
|
||
,
|
||
{ value: '空', label: '空' }
|
||
].concat(Array.from(standardSet).sort().map(value => ({
|
||
value: value,
|
||
label: value
|
||
})));
|
||
};
|
||
|
||
// 更新选中行状态
|
||
const updateSelectedRows = () => {
|
||
const selectedRows = tableData.value.filter(row => row.selected);
|
||
selectedRowsCount.value = selectedRows.length;
|
||
hasSelectedRows.value = selectedRowsCount.value > 0;
|
||
};
|
||
|
||
// 加载批量调整线缆标准选项
|
||
const loadBatchStandardOptions = async () => {
|
||
try {
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'XLBZList',
|
||
false,
|
||
{},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"XLBZList_CallBack",
|
||
50000
|
||
);
|
||
});
|
||
|
||
if (result) {
|
||
batchUpdateOptions.value = result.map(item => ({
|
||
value: item.XLBZID,
|
||
label: item.XLBZMC
|
||
}));
|
||
}
|
||
} catch (error) {
|
||
console.error('加载线缆标准失败:', error);
|
||
ElMessage.error('加载线缆标准失败');
|
||
}
|
||
};
|
||
|
||
// 加载批量调整电压等级选项
|
||
const loadBatchVoltageOptions = async () => {
|
||
try {
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetDYDJList',
|
||
false,
|
||
{},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetDYDJList_CallBack",
|
||
50000
|
||
);
|
||
});
|
||
|
||
if (result) {
|
||
batchUpdateOptions.value = result.map(item => ({
|
||
value: item,
|
||
label: item
|
||
}));
|
||
}
|
||
} catch (error) {
|
||
console.error('加载电压等级失败:', error);
|
||
ElMessage.error('加载电压等级失败');
|
||
}
|
||
};
|
||
|
||
// 加载标准编码名称映射
|
||
const loadStandardNameMap = async () => {
|
||
try {
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'XLBZList',
|
||
false,
|
||
{},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"LoadStandardNameMap_CallBack",
|
||
50000
|
||
);
|
||
});
|
||
|
||
if (result) {
|
||
const map = {};
|
||
result.forEach(item => {
|
||
map[item.XLBZID] = item.XLBZMC;
|
||
});
|
||
standardNameMap.value = map;
|
||
}
|
||
} catch (error) {
|
||
console.error('加载标准名称映射失败:', error);
|
||
}
|
||
};
|
||
|
||
// 显示批量调整对话框
|
||
const showBatchUpdateDialog = async (type) => {
|
||
batchUpdateType.value = type;
|
||
batchUpdateValue.value = '';
|
||
batchUpdateOptions.value = [];
|
||
batchUpdateFilterQuery.value = '';
|
||
updateSelectedRows();
|
||
|
||
if (type === 'standard') {
|
||
batchUpdateTitle.value = '批量调整线缆标准';
|
||
await loadBatchStandardOptions();
|
||
} else if (type === 'voltage') {
|
||
batchUpdateTitle.value = '批量调整电压等级';
|
||
await loadBatchVoltageOptions();
|
||
} else if (type === 'model') {
|
||
batchUpdateTitle.value = '批量调整线缆型号';
|
||
} else if (type === 'spec') {
|
||
batchUpdateTitle.value = '批量调整线缆规格';
|
||
}
|
||
|
||
batchUpdateDialogVisible.value = true;
|
||
};
|
||
|
||
// 关闭批量调整对话框
|
||
const closeBatchUpdateDialog = () => {
|
||
batchUpdateDialogVisible.value = false;
|
||
batchUpdateValue.value = '';
|
||
batchUpdateFilterQuery.value = '';
|
||
};
|
||
|
||
const buildBatchUpdateItem = (row) => ({
|
||
XLJXDJ: "XSBJ",
|
||
XLJXDH: BJDID,
|
||
XLJXMXH: (row.index != null ? row.index : '').toString()
|
||
});
|
||
|
||
// 确认批量调整
|
||
const confirmBatchUpdate = async () => {
|
||
if (!batchUpdateValue.value.trim()) {
|
||
ElMessage.warning(batchUpdateUseSelect.value ? '请选择调整值' : '请输入调整值');
|
||
return;
|
||
}
|
||
|
||
const selectedRows = tableData.value.filter(row => row.selected);
|
||
if (selectedRows.length === 0) {
|
||
ElMessage.warning('请先选择要调整的记录');
|
||
return;
|
||
}
|
||
|
||
let updateValue = '';
|
||
if (batchUpdateUseSelect.value) {
|
||
updateValue = resolveBatchUpdateSelectValue();
|
||
if (!updateValue) {
|
||
ElMessage.warning(
|
||
batchUpdateType.value === 'standard'
|
||
? '请从下拉列表中选择有效的线缆标准'
|
||
: '请从下拉列表中选择有效的电压等级'
|
||
);
|
||
return;
|
||
}
|
||
} else {
|
||
updateValue = batchUpdateType.value === 'spec'
|
||
? normalizeBatchSpec(batchUpdateValue.value.trim())
|
||
: batchUpdateValue.value.trim();
|
||
}
|
||
|
||
try {
|
||
startCenterProgress('正在批量调整数据...');
|
||
const items = selectedRows.map(row => buildBatchUpdateItem(row));
|
||
|
||
// 调用后端批量更新服务
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'BatchUpdateXLJXMX',
|
||
false,
|
||
{
|
||
'-RefUmId': 102851007,
|
||
items: items,
|
||
updateType: batchUpdateType.value,
|
||
updateValue: updateValue
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"BatchUpdateXLJXMX",
|
||
50000
|
||
);
|
||
});
|
||
|
||
if (result && result.success) {
|
||
tableData.value = tableData.value.map(row => {
|
||
if (!row.selected) {
|
||
return row;
|
||
}
|
||
if (batchUpdateType.value === 'standard') {
|
||
return { ...row, standard: updateValue };
|
||
}
|
||
if (batchUpdateType.value === 'voltage') {
|
||
return { ...row, voltage: updateValue };
|
||
}
|
||
if (batchUpdateType.value === 'model') {
|
||
return { ...row, model: updateValue, XLXHID: updateValue };
|
||
}
|
||
if (batchUpdateType.value === 'spec') {
|
||
return { ...row, spec: updateValue, XLGGID: updateValue };
|
||
}
|
||
return row;
|
||
});
|
||
|
||
closeBatchUpdateDialog();
|
||
completeCenterProgress(true, '批量调整成功!');
|
||
ElMessage.success(result.msg || `已成功调整 ${selectedRows.length} 条记录`);
|
||
await loadTableData({ silent: true });
|
||
updateSelectedRows();
|
||
} else {
|
||
completeCenterProgress(false, result?.msg || '批量调整失败');
|
||
ElMessage.error(result?.msg || '批量调整失败');
|
||
}
|
||
} catch (error) {
|
||
completeCenterProgress(false, '批量调整失败');
|
||
ElMessage.error('批量调整失败: ' + (error.message || '服务异常'));
|
||
}
|
||
};
|
||
|
||
|
||
|
||
const InsertBJDMX = async () => {
|
||
try {
|
||
if (!tableData.value || tableData.value.length === 0) {
|
||
ElMessage.warning('没有可导入的数据');
|
||
return;
|
||
}
|
||
if (!filterRule.value || filterRule.value=== '0') {
|
||
ElMessage.warning('当前数据未解析,请先解析数据');
|
||
return;
|
||
}
|
||
startCenterProgress('正在导入数据...');
|
||
|
||
if (!BJDID) return; // 用户取消输入
|
||
|
||
// 转换数据
|
||
const items = tableData.value.map((row, index) => ({
|
||
XLJXDJ: "XSBJ",
|
||
XLJXDH: BJDID,
|
||
XLJXMXH: row.index.toString(),
|
||
XLBSC: row.product,
|
||
XLJXGZXH: "0",
|
||
JXXLXHID: row.model,
|
||
XLXHID: "",
|
||
JXXLGGID: row.spec,
|
||
XLGGID: "",
|
||
JXDYDJID: row.voltage,
|
||
DYDJID: "",
|
||
XLBZID: row.standard,
|
||
JXXLBZID: row.standard,
|
||
XLYSID: "",
|
||
JXXLYSID: "",
|
||
WHR: "",
|
||
WHRID: "",
|
||
WHSJ: new Date().toISOString(),
|
||
DCZT: row.status,
|
||
JLDW: row.unit || "",
|
||
SL: row.quantity || "",
|
||
FBH: row.packageNumber || "",
|
||
JSGFID: row.techSpecId || ""
|
||
}));
|
||
|
||
// 设置服务路径(确保正确)
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
isLoading.value = true;
|
||
// 调用服务(使用正确的服务方法名)
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'InsertBJDMX', // 确保这是正确的服务方法名
|
||
false,
|
||
{
|
||
'-RefUmId': 102851007, // 根据实际服务调整
|
||
'items': items,
|
||
'sBJDID': BJDID
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"InsertBJDMX",
|
||
300000
|
||
);
|
||
});
|
||
|
||
if (result && result.success) {
|
||
ElMessage.success(result.msg || `成功导入${items.length}条报价单明细`);
|
||
completeCenterProgress(true, result.msg || '数据导入完成!');
|
||
setTimeout(() => {
|
||
closePage();
|
||
}, 1000); // 延时 2000 毫秒(2 秒)
|
||
|
||
} else {
|
||
const errorMsg = result?.msg || result?.message || '服务返回未知错误';
|
||
ElMessage.error(`导入失败: ${errorMsg}`);
|
||
completeCenterProgress(false, '导入失败');
|
||
}
|
||
isLoading.value = false;
|
||
} catch (error) {
|
||
// 处理用户取消输入的情况
|
||
if (error.toString().includes('cancel')) {
|
||
return;
|
||
}
|
||
isLoading.value = false;
|
||
console.error('导入报价单明细失败:', error);
|
||
const errorMsg = error.message || '服务异常';
|
||
completeCenterProgress(false, '导入失败');
|
||
ElMessage.error(`导入失败: ${errorMsg}`);
|
||
}
|
||
};
|
||
|
||
const loadRuleOptions = async () => {
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 调用服务获取解析规则列表
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetXLJXGZList',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005, // 替换为实际RefUmId
|
||
'XLJXDJ': 'XSBJ', // 固定值
|
||
'XLJXDH': BJDID // 可为空
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetXLJXGZList",
|
||
50000 // 5秒超时
|
||
);
|
||
});
|
||
|
||
if (result && Array.isArray(result)) {
|
||
// 去重处理:基于XLJXGZXH
|
||
const uniqueRules = [];
|
||
const seen = new Set();
|
||
uniqueRules.push({
|
||
value: '0',
|
||
label: '未解析'
|
||
});
|
||
uniqueRules.push({
|
||
value: '-1',
|
||
label: '已解析'
|
||
});
|
||
uniqueRules.push({
|
||
value: '-2',
|
||
label: '全部'
|
||
});
|
||
result.forEach(rule => {
|
||
if (!seen.has(rule.XLJXGZXH)) {
|
||
seen.add(rule.XLJXGZXH);
|
||
uniqueRules.push({
|
||
value: rule.XLJXGZXH,
|
||
label: rule.XLJXGZC
|
||
});
|
||
}
|
||
});
|
||
|
||
ruleOptions.value = uniqueRules;
|
||
ElMessage.success(`加载${uniqueRules.length}条解析规则`);
|
||
} else {
|
||
ruleOptions.value = [];
|
||
ElMessage.warning('未获取到解析规则');
|
||
}
|
||
} catch (error) {
|
||
console.error('加载解析规则失败:', error);
|
||
ElMessage.error(`加载解析规则失败: ${error.message || '服务异常'}`);
|
||
ruleOptions.value = [];
|
||
}
|
||
};
|
||
|
||
// 加载通用解析规则选项
|
||
const loadCommonRuleOptions = async () => {
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 调用服务获取通用解析规则列表
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetXLJXGZList',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005, // 替换为实际RefUmId
|
||
'XLJXDJ': 'XSBJ',
|
||
'XLJXDH':' ' // 固定值
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetXLJXGZList",
|
||
50000 // 5秒超时
|
||
);
|
||
});
|
||
|
||
if (result && Array.isArray(result)) {
|
||
// 去重处理:基于XLJXGZXH
|
||
const uniqueRules = [];
|
||
const seen = new Set();
|
||
uniqueRules.push({
|
||
value: '0',
|
||
label: '请选择通用规则'
|
||
});
|
||
result.forEach(rule => {
|
||
if (!seen.has(rule.XLJXGZXH)) {
|
||
seen.add(rule.XLJXGZXH);
|
||
uniqueRules.push({
|
||
value: rule.XLJXGZXH,
|
||
label: rule.XLJXGZC
|
||
});
|
||
}
|
||
});
|
||
|
||
commonRuleOptions.value = uniqueRules;
|
||
ElMessage.success(`加载${uniqueRules.length}条通用解析规则`);
|
||
} else {
|
||
commonRuleOptions.value = [];
|
||
ElMessage.warning('未获取到通用解析规则');
|
||
}
|
||
} catch (error) {
|
||
console.error('加载通用解析规则失败:', error);
|
||
ElMessage.error(`加载通用解析规则失败: ${error.message || '服务异常'}`);
|
||
commonRuleOptions.value = [];
|
||
}
|
||
};
|
||
|
||
// 应用通用解析规则
|
||
|
||
const showXlxhDialog = () => {
|
||
// 构建带参数的 URL
|
||
|
||
const params = new URLSearchParams({
|
||
title: '线缆型号语法',
|
||
ruleString1 : ruleString.value,
|
||
reservedWords : "型号码,前缀码,后缀码",
|
||
connectors : ",-,/,(,)",
|
||
syntaxString : modelValue.value
|
||
});
|
||
|
||
// 设置 iframe 的 URL 并添加参数
|
||
iframeUrl.value = `${BJCPJXDR_XHYF.value}?${params.toString()}`;
|
||
|
||
YFGZDialogVisible.value = true;
|
||
resultReceived.value = false;
|
||
};
|
||
const showXLGGDialog = () => {
|
||
// 构建带参数的 URL
|
||
|
||
const params = new URLSearchParams({
|
||
title: '线缆规格语法',
|
||
ruleString1 : ruleString.value,
|
||
reservedWords : "截面,芯数,线对数,线组数",
|
||
connectors : "*,×,+,/,(,)",
|
||
syntaxString : specValue.value
|
||
});
|
||
|
||
// 设置 iframe 的 URL 并添加参数
|
||
iframeUrl.value = `${BJCPJXDR_XHYF.value}?${params.toString()}`;
|
||
|
||
YFGZDialogVisible.value = true;
|
||
resultReceived.value = false;
|
||
};
|
||
const showXlBZDialog = () => {
|
||
// 构建带参数的 URL
|
||
|
||
const params = new URLSearchParams({
|
||
title: '线缆标准语法',
|
||
ruleString1 : ruleString.value,
|
||
reservedWords : "标准名",
|
||
connectors : ",-,/,(,)",
|
||
syntaxString : standardValue.value
|
||
});
|
||
|
||
// 设置 iframe 的 URL 并添加参数
|
||
iframeUrl.value = `${BJCPJXDR_XHYF.value}?${params.toString()}`;
|
||
|
||
YFGZDialogVisible.value = true;
|
||
resultReceived.value = false;
|
||
};
|
||
|
||
const showDYDJDialog = () => {
|
||
// 构建带参数的 URL
|
||
|
||
const params = new URLSearchParams({
|
||
title: '电压等级语法',
|
||
ruleString1 : ruleString.value,
|
||
reservedWords : "相电压,线电压,电压单位",
|
||
connectors : "/,V,kV",
|
||
syntaxString : voltageValue.value
|
||
});
|
||
|
||
// 设置 iframe 的 URL 并添加参数
|
||
iframeUrl.value = `${BJCPJXDR_XHYF.value}?${params.toString()}`;
|
||
|
||
YFGZDialogVisible.value = true;
|
||
resultReceived.value = false;
|
||
};
|
||
|
||
const handleMessage = (event) => {
|
||
|
||
if (event.data && event.data.type === 'SAVE_SYNTAX_RESULT') {
|
||
// 从子页面接收结果
|
||
const result = event.data.payload;
|
||
|
||
const { type, Value, ActualValue } = result;
|
||
|
||
switch(type) {
|
||
case '线缆型号语法':
|
||
modelValue.value = Value;
|
||
modelActualValue.value = ActualValue;
|
||
break;
|
||
case '线缆规格语法':
|
||
specValue.value = Value;
|
||
specActualValue.value = ActualValue;
|
||
break;
|
||
case '电压等级语法':
|
||
voltageValue.value = Value;
|
||
voltageActualValue.value = ActualValue;
|
||
break;
|
||
case '线缆标准语法':
|
||
standardValue.value = Value;
|
||
standardActualValue.value = ActualValue;
|
||
break;
|
||
default:
|
||
console.warn(`未知语法类型: ${type}`);
|
||
return;
|
||
}
|
||
|
||
ElMessage.success(`${type}已更新`);
|
||
|
||
// 关闭对话框
|
||
YFGZDialogVisible.value = false;
|
||
resultReceived.value = true;
|
||
}
|
||
};
|
||
onMounted(() => {
|
||
window.addEventListener('message', handleMessage);
|
||
});
|
||
|
||
// 清理监听器
|
||
onUnmounted(() => {
|
||
window.removeEventListener('message', handleMessage);
|
||
});
|
||
// 显示规则配置对话框
|
||
const showRuleDialog = async () => {
|
||
if (!selectedProduct.value) {
|
||
ElMessage.warning('请先在表格中选择一条数据');
|
||
return;
|
||
}
|
||
GetFGF();
|
||
// 1. 创建请求参数
|
||
const requestItem = {
|
||
XLBSC: selectedProduct.value,
|
||
XLJXDJ:'XSBJ',
|
||
XLJXDH:BJDID,
|
||
XLJXGZXH:filterRule.value<=0?currentRowXLJXGZXH.value:filterRule.value,
|
||
XLXHID: currentRowModel.value, // 当前行型号值
|
||
DYDJID: currentRowVoltage.value, // 当前行电压值
|
||
XLBZID: currentRowStandard.value, // 当前行标准值
|
||
XLGGID: currentRowSpec.value // 当前行规格值
|
||
};
|
||
|
||
|
||
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 调用服务获取解析规则列表
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetJXPZ',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005, // 替换为实际RefUmId
|
||
'item': requestItem
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetJXPZ",
|
||
50000 // 5秒超时
|
||
);
|
||
});
|
||
|
||
if (result) {
|
||
ruleString.value = result.XLJXGZC;
|
||
modelValue.value = result.XLXHYFC;
|
||
modelActualValue.value = result.XLXH;
|
||
specValue.value = result.XLGGYFC;
|
||
specActualValue.value = result.XLGG;
|
||
voltageValue.value = result.DYDJYFC;
|
||
voltageActualValue.value = result.DYDJ;
|
||
standardValue.value = result.XLBZYFC;
|
||
standardActualValue.value = result.XLBZ;
|
||
ruleDialogVisible.value = true;
|
||
|
||
} else {
|
||
|
||
}
|
||
} catch (error) {
|
||
}
|
||
|
||
// 重置选择状态
|
||
nextTick(() => {
|
||
selection.value = { start: 0, end: 0, text: '' };
|
||
if (ruleEditor.value) {
|
||
ruleEditor.value.focus();
|
||
}
|
||
});
|
||
|
||
//parseRuleValue();
|
||
|
||
|
||
|
||
};
|
||
|
||
// 关闭规则配置对话框
|
||
const closeRuleDialog = () => {
|
||
ruleDialogVisible.value = false;
|
||
unmarkAll();
|
||
};
|
||
|
||
//todo
|
||
// todo
|
||
const showSyntaxDialog = () => {
|
||
|
||
syntaxDialogVisible.value = true;
|
||
|
||
};
|
||
|
||
// todo
|
||
const closeSyntaxDialog = () => {
|
||
|
||
syntaxDialogVisible.value = false;
|
||
|
||
};
|
||
|
||
// 保存规则配置
|
||
const saveRuleConfig = async (IsCommon) => {
|
||
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 生成唯一的规则代号(使用时间戳)
|
||
const XLJXDH = BJDID;
|
||
|
||
// 创建XLJXGZ对象
|
||
const ruleConfig = {
|
||
XLJXDJ: "XSBJ", // 固定值
|
||
XLJXDH: XLJXDH,
|
||
XLJXGZXH: filterRule.value<=0?currentRowXLJXGZXH.value:filterRule.value, // 默认序号
|
||
XLJXGZC: ruleString.value,
|
||
XLXHYFC: modelValue.value,
|
||
XLGGYFC: specValue.value,
|
||
DYDJYFC: voltageValue.value,
|
||
XLBZYFC: standardValue.value,
|
||
XLYSYFC: colorValue.value || "", // 颜色标记串(可选)
|
||
WHR: "",
|
||
WHRID: "", // 维护人ID(可选)
|
||
WHSJ: new Date().toISOString() // 当前时间
|
||
};
|
||
isLoading.value = true;
|
||
// 调用服务
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'InsertXLJXGZ',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005, // 替换为实际RefUmId
|
||
'item': ruleConfig,
|
||
'IsCommon':IsCommon
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"InsertXLJXGZ",
|
||
50000 // 5秒超时
|
||
);
|
||
});
|
||
|
||
if (result && result.success) {
|
||
ElMessage.success('解析规则已保存');
|
||
ruleDialogVisible.value = false;
|
||
loadRuleOptions();
|
||
filterRule.value=result.msg;
|
||
filterRule.label=ruleOptions.value[result.msg];
|
||
loadTableData();
|
||
unmarkAll();
|
||
|
||
} else {
|
||
ElMessage.error(`保存失败: ${result?.message || '未知错误'}`);
|
||
}
|
||
isLoading.value = false;
|
||
} catch (error) {
|
||
isLoading.value = false;
|
||
console.error('保存规则失败:', error);
|
||
ElMessage.error(`保存失败: ${error.message || '服务异常'}`);
|
||
}
|
||
};
|
||
|
||
const commonParse = async () => {
|
||
try {
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 生成唯一的规则代号(使用时间戳)
|
||
const XLJXDH = BJDID;
|
||
// 调用服务
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'commonParse',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005, // 替换为实际RefUmId
|
||
'BJDID': XLJXDH
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"commonParse",
|
||
500000 // 5秒超时
|
||
);
|
||
});
|
||
|
||
if (result && result.success) {
|
||
ElMessage.success('通用解析规则已执行');
|
||
loadRuleOptions();
|
||
filterRule.value=result.msg;
|
||
loadTableData();
|
||
|
||
|
||
} else {
|
||
ElMessage.error(`通用解析失败: ${result?.message || '未知错误'}`);
|
||
}
|
||
} catch (error) {
|
||
console.error('通用解析失败:', error);
|
||
ElMessage.error(`通用解析失败: ${error.message || '服务异常'}`);
|
||
}
|
||
};
|
||
|
||
// 解析数据
|
||
const parseData = () => {
|
||
if (tableData.value.length === 0) {
|
||
ElMessage.warning('请先粘贴数据');
|
||
return;
|
||
}
|
||
|
||
loading.value = true;
|
||
|
||
// 模拟解析过程
|
||
setTimeout(() => {
|
||
const parsedData = tableData.value.map(item => {
|
||
// 简单模拟解析逻辑
|
||
const parts = item.product.split(',');
|
||
|
||
return {
|
||
...item,
|
||
model: parts.length > 2 ? parts[2] : '',
|
||
voltage: parts.length > 1 ? parts[1] : '',
|
||
spec: parts.length > 3 ? parts[3] : '',
|
||
status: 'N'
|
||
};
|
||
});
|
||
|
||
tableData.value = parsedData;
|
||
loading.value = false;
|
||
ElMessage.success('解析完成');
|
||
}, 1000);
|
||
};
|
||
|
||
// 显示筛选对话框
|
||
const showFilterDialog = () => {
|
||
loadRuleOptions();
|
||
filterDialogVisible.value = true;
|
||
};
|
||
|
||
const showEXRuleDialog = () => {
|
||
loadRuleOptions();
|
||
EXRuleDialogVisible.value = true;
|
||
};
|
||
|
||
// 关闭筛选对话框
|
||
const closeFilterDialog = () => {
|
||
filterDialogVisible.value = false;
|
||
};
|
||
|
||
// 关闭筛选对话框
|
||
const closeEXRuleDialog = () => {
|
||
EXRuleDialogVisible.value = false;
|
||
};
|
||
|
||
// 应用筛选条件
|
||
const applyFilter = () => {
|
||
// 应用筛选条件后重新加载表格数据
|
||
loadTableData();
|
||
ElMessage.success('筛选条件已应用');
|
||
|
||
};
|
||
|
||
const applyRule = async () => {
|
||
try {
|
||
|
||
// 设置服务路径
|
||
GThinking.Web.AjaxServiceProxy.servicePath = 'S_XLXSBJ/102373012.ashx';
|
||
|
||
// 1. 获取规则数据
|
||
const item = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'GetXLJXGZ',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005,
|
||
'XLJXDJ': 'XSBJ',
|
||
'XLJXDH': BJDID,
|
||
'XLJXGZXH': ExRule.value
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"GetXLJXGZ",
|
||
50000
|
||
);
|
||
});
|
||
|
||
// 2. 更新规则属性
|
||
item.XLJXGZXH = filterRule.value;
|
||
|
||
// 3. 更新规则
|
||
const result = await new Promise((resolve, reject) => {
|
||
GThinking.Web.AjaxServiceProxy.invoke(
|
||
'InsertXLJXGZ',
|
||
false,
|
||
{
|
||
'-RefUmId': 102855005,
|
||
'item': item,
|
||
'IsCommon':'N'
|
||
},
|
||
(data) => resolve(data),
|
||
(error) => reject(error),
|
||
"InsertXLJXGZ",
|
||
50000
|
||
);
|
||
});
|
||
|
||
// 4. 处理结果
|
||
if (result && result.success) {
|
||
ElMessage.success('解析规则已保存');
|
||
ruleDialogVisible.value = false;
|
||
ExRule.value = '0';
|
||
filterRule.value = result.msg || filterRule.value;
|
||
|
||
// 5. 刷新表格数据
|
||
await loadTableData();
|
||
unmarkAll();
|
||
} else {
|
||
const errorMsg = result?.message || '未知错误';
|
||
ElMessage.error(`保存失败: ${errorMsg}`);
|
||
}
|
||
} catch (error) {
|
||
console.error('保存规则失败:', error);
|
||
const errorMsg = error.message || '服务异常';
|
||
ElMessage.error(`保存失败: ${errorMsg}`);
|
||
} finally {
|
||
// 6. 确保关闭对话框并刷新数据
|
||
EXRuleDialogVisible.value = false;
|
||
}
|
||
};
|
||
|
||
// ===== el-table-v2 相关方法 =====
|
||
|
||
// 处理行点击
|
||
// const handleRowClick = (params) => {
|
||
// const { rowData, rowIndex } = params;
|
||
|
||
// // 使用 map 创建新数组确保响应性
|
||
// tableData.value = tableData.value.map((row, idx) => ({
|
||
// ...row,
|
||
// current: idx === rowIndex
|
||
// }));
|
||
|
||
// // 设置选中的产品
|
||
// selectedProduct.value = rowData.product;
|
||
// currentRowModel.value = rowData.model || '';
|
||
// currentRowVoltage.value = rowData.voltage || '';
|
||
// currentRowStandard.value = rowData.standard || '';
|
||
// currentRowSpec.value = rowData.spec || '';
|
||
// };
|
||
|
||
const handleTableClick = (e) => {
|
||
const rowElement = e.target.closest('.el-table-v2__row');
|
||
if (!rowElement) return;
|
||
|
||
const rowIndex = Array.from(rowElement.parentNode.children).indexOf(rowElement);
|
||
const rowData = tableData.value[rowIndex];
|
||
|
||
if (rowData) {
|
||
// 清除所有行的选中状态
|
||
tableData.value = tableData.value.map(row => ({
|
||
...row,
|
||
current: false
|
||
}));
|
||
|
||
// 设置当前行选中
|
||
tableData.value[rowIndex] = {
|
||
...tableData.value[rowIndex],
|
||
current: true
|
||
};
|
||
|
||
// 设置选中的产品
|
||
selectedProduct.value = rowData.product;
|
||
currentRowModel.value = rowData.model || '';
|
||
currentRowVoltage.value = rowData.voltage || '';
|
||
currentRowStandard.value = rowData.standard || '';
|
||
currentRowSpec.value = rowData.spec || '';
|
||
currentRowXLJXGZXH.value = rowData.XLJXGZXH || '0';
|
||
}
|
||
};
|
||
|
||
|
||
const getRowClass = ({ rowData }) => {
|
||
return rowData.current ? 'current-row' : '';
|
||
};
|
||
|
||
// 处理窗口大小变化
|
||
const handleResize = () => {
|
||
tableWidth.value = window.innerWidth - 30;
|
||
tableHeight.value = window.innerHeight - 150;
|
||
};
|
||
const handleRowClick = (row)=>{
|
||
//console.log('选中的行ID:', row.id)
|
||
//根据选中行id获取rowdata
|
||
// 获取rowIndex
|
||
const rowIndex = row.rowIndex;
|
||
const rowData = tableData.value[rowIndex];
|
||
if (rowData) {
|
||
// 清除所有行的选中状态
|
||
tableData.value = tableData.value.map(row => ({
|
||
...row,
|
||
current: false
|
||
}));
|
||
|
||
// 设置当前行选中
|
||
tableData.value[rowIndex] = {
|
||
...tableData.value[rowIndex],
|
||
current: true
|
||
};
|
||
|
||
// 设置选中的产品
|
||
selectedProduct.value = rowData.product;
|
||
currentRowModel.value = rowData.model || '';
|
||
currentRowVoltage.value = rowData.voltage || '';
|
||
currentRowStandard.value = rowData.standard || '';
|
||
currentRowSpec.value = rowData.spec || '';
|
||
currentRowXLJXGZXH.value = rowData.XLJXGZXH || '0';
|
||
|
||
}
|
||
|
||
}
|
||
// 监听规则串变化
|
||
watch(ruleString, () => {
|
||
parseModelValue });
|
||
|
||
// 监听窗口大小变化
|
||
window.addEventListener('resize', handleResize);
|
||
|
||
// 页面加载时自动加载数据
|
||
onMounted(() => {
|
||
loadTableData();
|
||
loadStandardNameMap();
|
||
handleResize();
|
||
const tableElement = document.querySelector('.el-table-v2');
|
||
if (tableElement) {
|
||
//tableElement.addEventListener('click', handleTableClick);
|
||
}
|
||
});
|
||
const handleEnterKey = (e) => {
|
||
// 检查是否在查询条件输入框中
|
||
const isInQueryInput = e.target.matches('input[placeholder*="报价产品"], input[placeholder*="线缆型号"], input[placeholder*="线缆规格"], input[placeholder*="电压等级"], input[placeholder*="线缆标准"]');
|
||
|
||
if (isInQueryInput && e.key === 'Enter') {
|
||
e.preventDefault(); // 阻止默认行为
|
||
loadTableData(); // 执行查询
|
||
}
|
||
};
|
||
|
||
onMounted(() => {
|
||
// 添加全局键盘事件监听
|
||
document.addEventListener('keydown', handleEnterKey);
|
||
window.addEventListener('message', handleMessage);
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
// 清理事件监听
|
||
document.removeEventListener('keydown', handleEnterKey);
|
||
window.removeEventListener('message', handleMessage);
|
||
stopColumnResize();
|
||
if (resizeGuideEl && resizeGuideEl.parentNode) {
|
||
resizeGuideEl.parentNode.removeChild(resizeGuideEl);
|
||
resizeGuideEl = null;
|
||
}
|
||
});
|
||
|
||
// 返回所有变量和方法
|
||
return {
|
||
// 状态变量
|
||
|
||
loading,
|
||
ruleDialogVisible,
|
||
EXRuleDialogVisible,
|
||
filterDialogVisible,
|
||
tableData,
|
||
// 批量调整相关变量
|
||
batchUpdateDialogVisible,
|
||
batchUpdateType,
|
||
batchUpdateValue,
|
||
hasSelectedRows,
|
||
selectedRowsCount,
|
||
batchUpdateTitle,
|
||
selectedProduct,
|
||
ruleEditor,
|
||
ruleString,
|
||
modelValue,
|
||
specValue,
|
||
voltageValue,
|
||
standardValue,
|
||
colorValue,
|
||
filterRule,
|
||
filterStatus,
|
||
ruleOptions,
|
||
modelActualValue,
|
||
specActualValue,
|
||
voltageActualValue,
|
||
standardActualValue,
|
||
// Excel导入相关
|
||
importDialogVisible,
|
||
fileInput,
|
||
excelFile,
|
||
excelColumns,
|
||
previewData,
|
||
columnMapping,
|
||
tableColumns,
|
||
importing,
|
||
importProgress,
|
||
totalRows,
|
||
importedCount,
|
||
importStatus,
|
||
errorMessage,
|
||
successMessage,
|
||
canImport,
|
||
// el-table-v2相关
|
||
tableColumnsV2,
|
||
tableWidth,
|
||
tableHeight,
|
||
// 方法
|
||
pasteData,
|
||
showRuleDialog,
|
||
closeRuleDialog,
|
||
saveRuleConfig,
|
||
showFilterDialog,
|
||
showEXRuleDialog,
|
||
closeFilterDialog,
|
||
applyFilter,
|
||
applyRule,
|
||
markPrefixCode,
|
||
markModelCode,
|
||
markSuffixCode,
|
||
markVoltagePhase,
|
||
markVoltageLine,
|
||
markVoltageUnit,
|
||
markVoltageLevel,
|
||
markWireCount,
|
||
markSection,
|
||
markWireGroup,
|
||
markWirePair,
|
||
markSpec,
|
||
markStandardName,
|
||
saveSelection,
|
||
parseData,
|
||
handleResize,
|
||
handleRowClick,
|
||
unmarkAll,
|
||
openSyntaxPage,
|
||
// Excel导入方法
|
||
showImportDialog,
|
||
closeImportDialog,
|
||
triggerFileInput,
|
||
handleFileChange,
|
||
startImport,
|
||
resetImport,
|
||
formatFileSize,
|
||
// el-table-v2方法
|
||
getRowClass,
|
||
InsertBJDMX,
|
||
closePage,
|
||
statusOptions,
|
||
closeEXRuleDialog,
|
||
ExRule,
|
||
showXlxhDialog,
|
||
showXlBZDialog,
|
||
showDYDJDialog,
|
||
showXLGGDialog,
|
||
YFGZDialogVisible,
|
||
iframeUrl,
|
||
loadRuleOptions,
|
||
loadTableData,
|
||
getRowKey,
|
||
deleteData,
|
||
exportExcel,
|
||
isLoading,
|
||
showCenterProgress,
|
||
centerProgressPercentage,
|
||
centerProgressStatus,
|
||
centerProgressText,
|
||
filterEquivalent,
|
||
handleManualPasteCancel,
|
||
handleManualPasteConfirm,
|
||
handleMappingConfirm,
|
||
handleMappingBack,
|
||
manualPasteText,
|
||
manualPasteDialogVisible,
|
||
pasteStep,
|
||
detectedColumnCount,
|
||
detectedPreviewRows,
|
||
columnFieldMapping,
|
||
pasteFieldOptions,
|
||
visibleColumns,
|
||
productQuery,
|
||
modelQuery,
|
||
specQuery,
|
||
voltageQuery,
|
||
standardQuery,
|
||
missingElementQuery,
|
||
missingElementOptions,
|
||
deleteRule,
|
||
// 新增通用解析规则相关
|
||
commonRuleOptions,
|
||
loadCommonRuleOptions,
|
||
deleteCommonRule,
|
||
// 批量调整相关方法
|
||
updateSelectedRows,
|
||
showBatchUpdateDialog,
|
||
closeBatchUpdateDialog,
|
||
confirmBatchUpdate,
|
||
batchUpdateOptions,
|
||
batchUpdateFilteredOptions,
|
||
filterBatchUpdateOptions,
|
||
loadBatchStandardOptions,
|
||
loadBatchVoltageOptions,
|
||
voltageOptions,
|
||
standardOptions,
|
||
standardNameMap,
|
||
loadStandardNameMap
|
||
};
|
||
}
|
||
});
|
||
|
||
app.use(ElementPlus);
|
||
|
||
// 全局注册所有图标组件
|
||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||
app.component(key, component);
|
||
}
|
||
|
||
app.mount('#app');
|