|
@@ -84,10 +84,14 @@ const EMPTY_BOOK = (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ai填值缓存,用于ai填值报错时,可以从当前报错处开始匹配,而不用重头开始匹配
|
|
|
+ let aiMatchCache = null;
|
|
|
+
|
|
|
// 清空
|
|
|
function clear() {
|
|
|
cache.length = 0;
|
|
|
workBookObj.sheet.setRowCount(0);
|
|
|
+ aiMatchCache = null;
|
|
|
}
|
|
|
|
|
|
let curRow = 0;
|
|
@@ -334,43 +338,69 @@ const EMPTY_BOOK = (() => {
|
|
|
return code.substring(0, 4);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ const getAIMatchData = (summaryGroupMap, noCodeSummary) => {
|
|
|
+ if (aiMatchCache && aiMatchCache.changedCells.length && aiMatchCache.curIndex > 0) {
|
|
|
+ return aiMatchCache;
|
|
|
+ }
|
|
|
+ const curPercent = 0;
|
|
|
+ const curIndex = 0;
|
|
|
+ const totalRows = workBookObj.sheet.getRowCount();
|
|
|
+ const changedCells = [];
|
|
|
+ const noMatchRows = []; // 没有匹配、ai没有命中的行,后续需要自动生成别名编码(最大的别名编码+1)
|
|
|
+ for (let i = 0; i < totalRows; i++) {
|
|
|
+ const rowData = getRowData(workBookObj.sheet, i, setting.header);
|
|
|
+ // const code = rowData.code || '';
|
|
|
+ const code = getFourCode(rowData.code);
|
|
|
+ const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
+ if (toMatchSummary.length) {
|
|
|
+ changedCells.push({ row: i });
|
|
|
+ } else {
|
|
|
+ noMatchRows.push(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ curPercent,
|
|
|
+ curIndex,
|
|
|
+ changedCells,
|
|
|
+ noMatchRows,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// ai填值
|
|
|
const aiMatch = async () => {
|
|
|
+ let percent = 0;
|
|
|
+ let curIndex = 0;
|
|
|
+ let noMatchRows = [];
|
|
|
+ let changedCells = [];
|
|
|
try {
|
|
|
// 获取信息价总表
|
|
|
const priceInfoSummary = await ajaxPost('/priceInfoSummary/getData', {}, 1000 * 60 * 5);
|
|
|
const summaryGroupMap = _.groupBy(priceInfoSummary, item => getFourCode(item.code));
|
|
|
const noCodeSummary = priceInfoSummary.filter(item => !item.code);
|
|
|
- const totalRows = workBookObj.sheet.getRowCount();
|
|
|
- const changedCells = [];
|
|
|
- const noMatchRows = []; // 没有匹配、ai没有命中的行,后续需要自动生成别名编码(最大的别名编码+1)
|
|
|
- for (let i = 0; i < totalRows; i++) {
|
|
|
- const rowData = getRowData(workBookObj.sheet, i, setting.header);
|
|
|
- // const code = rowData.code || '';
|
|
|
- const code = getFourCode(rowData.code);
|
|
|
- const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
- if (toMatchSummary.length) {
|
|
|
- changedCells.push({ row: i });
|
|
|
- } else {
|
|
|
- noMatchRows.push(i);
|
|
|
- }
|
|
|
- }
|
|
|
+
|
|
|
+ const aiMatchData = getAIMatchData(summaryGroupMap, noCodeSummary);
|
|
|
+ percent = aiMatchData.curPercent;
|
|
|
+ curIndex = aiMatchData.curIndex;
|
|
|
+ changedCells = aiMatchData.changedCells;
|
|
|
+ noMatchRows = aiMatchData.noMatchRows;
|
|
|
+
|
|
|
if (!changedCells.length) {
|
|
|
return;
|
|
|
}
|
|
|
const classCodeCol = setting.header.findIndex(h => h.dataCode === 'classCode');
|
|
|
const expStringCol = setting.header.findIndex(h => h.dataCode === 'expString');
|
|
|
// const chunks = _.chunk(changedCells, 20);
|
|
|
- const chunks = _.chunk(changedCells, 1); //
|
|
|
- let percent = 0;
|
|
|
+ const chunks = _.chunk(changedCells, 1); // 只能一条一条匹配改成,否则经常ai服务经常挂
|
|
|
$.bootstrapLoading.progressStart('AI填值', false);
|
|
|
- $("#progress_modal_body").text('正在进行AI填值,请稍后...');
|
|
|
+ $('#progress_modal_body').text(`正在进行AI填值,请稍后${curIndex + 1}/${chunks.length}...`);
|
|
|
await setTimeoutSync(500);
|
|
|
const matchResCache = {};
|
|
|
|
|
|
// 分块进行ai匹配
|
|
|
const step = 100 / (chunks.length || 1);
|
|
|
- for (let i = 0; i < chunks.length; i++) {
|
|
|
+ for (let i = curIndex; i < chunks.length; i++) {
|
|
|
+ curIndex = i;
|
|
|
const chunk = chunks[i];
|
|
|
const listA = [];
|
|
|
const listB = [];
|
|
@@ -378,7 +408,6 @@ const EMPTY_BOOK = (() => {
|
|
|
chunk.forEach(item => {
|
|
|
const rowData = getRowData(workBookObj.sheet, item.row, setting.header);
|
|
|
listA.push(`${rowData.name || ''} ${rowData.specs}`);
|
|
|
- // const code = rowData.code || '';
|
|
|
const code = getFourCode(rowData.code);
|
|
|
const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
summaryData.push(toMatchSummary);
|
|
@@ -387,7 +416,6 @@ const EMPTY_BOOK = (() => {
|
|
|
});
|
|
|
const test = listB.map(item => item.length);
|
|
|
console.log(test);
|
|
|
-
|
|
|
const matchRes = matchResCache[listA[0]] ? matchResCache[listA[0]] : await ajaxPost('/priceInfoSummary/aiMatch', { listA, listB }, 1000 * 60 * 5);
|
|
|
matchResCache[listA[0]] = matchRes;
|
|
|
// 填匹配值到表格,不实时保存,因为需要人工核查
|
|
@@ -430,7 +458,7 @@ const EMPTY_BOOK = (() => {
|
|
|
workBookObj.sheet.resumeEvent();
|
|
|
workBookObj.sheet.resumePaint();
|
|
|
percent += step;
|
|
|
- $('#progress_modal_body').text(`正在进行AI填值,请稍后${i + 1}/${chunks.length}...`)
|
|
|
+ $('#progress_modal_body').text(`正在进行AI填值,请稍后${i + 1}/${chunks.length}...`);
|
|
|
$("#progress_modal_bar").css('width', `${percent}%`);
|
|
|
await setTimeoutSync(100);
|
|
|
}
|
|
@@ -451,139 +479,147 @@ const EMPTY_BOOK = (() => {
|
|
|
}
|
|
|
workBookObj.sheet.resumeEvent();
|
|
|
workBookObj.sheet.resumePaint();
|
|
|
-
|
|
|
-
|
|
|
+ aiMatchCache = null;
|
|
|
+ $("#ai-match").text('AI填值');
|
|
|
} catch (error) {
|
|
|
console.log(error);
|
|
|
+ aiMatchCache = {
|
|
|
+ curPercent: percent,
|
|
|
+ curIndex,
|
|
|
+ noMatchRows,
|
|
|
+ changedCells,
|
|
|
+ }
|
|
|
+ $("#ai-match").text('继续AI填值');
|
|
|
alert(error);
|
|
|
}
|
|
|
await setTimeoutSync(500);
|
|
|
$.bootstrapLoading.progressEnd();
|
|
|
}
|
|
|
|
|
|
- /* const aiMatch = async () => {
|
|
|
- try {
|
|
|
- // 获取信息价总表
|
|
|
- const priceInfoSummary = await ajaxPost('/priceInfoSummary/getData', {}, 1000 * 60 * 5);
|
|
|
- const summaryGroupMap = _.groupBy(priceInfoSummary, item => getFourCode(item.code));
|
|
|
- const noCodeSummary = priceInfoSummary.filter(item => !item.code);
|
|
|
- const totalRows = workBookObj.sheet.getRowCount();
|
|
|
- const changedCells = [];
|
|
|
- const noMatchRows = []; // 没有匹配、ai没有命中的行,后续需要自动生成别名编码(最大的别名编码+1)
|
|
|
- for (let i = 0; i < totalRows; i++) {
|
|
|
- const rowData = getRowData(workBookObj.sheet, i, setting.header);
|
|
|
- // const code = rowData.code || '';
|
|
|
- const code = getFourCode(rowData.code);
|
|
|
- const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
- if (toMatchSummary.length) {
|
|
|
- changedCells.push({ row: i });
|
|
|
- } else {
|
|
|
- noMatchRows.push(i);
|
|
|
- }
|
|
|
- }
|
|
|
- if (!changedCells.length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const classCodeCol = setting.header.findIndex(h => h.dataCode === 'classCode');
|
|
|
- const expStringCol = setting.header.findIndex(h => h.dataCode === 'expString');
|
|
|
- // const chunks = _.chunk(changedCells, 20);
|
|
|
- const chunks = _.chunk(changedCells, 1);
|
|
|
- let percent = 0;
|
|
|
- $.bootstrapLoading.progressStart('AI填值', false);
|
|
|
- $("#progress_modal_body").text('正在进行AI填值,请稍后...');
|
|
|
- await setTimeoutSync(500);
|
|
|
- debugger;
|
|
|
-
|
|
|
- // 分块进行ai匹配
|
|
|
- const step = 100 / (chunks.length || 1);
|
|
|
- for (const chunk of chunks) {
|
|
|
- const listA = [];
|
|
|
- const listB = [];
|
|
|
- const summaryData = [];
|
|
|
- chunk.forEach(item => {
|
|
|
- const rowData = getRowData(workBookObj.sheet, item.row, setting.header);
|
|
|
- listA.push(`${rowData.name || ''} ${rowData.specs}`);
|
|
|
- // const code = rowData.code || '';
|
|
|
- const code = getFourCode(rowData.code);
|
|
|
- const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
- summaryData.push(toMatchSummary);
|
|
|
- const summaryKeys = toMatchSummary.map(summary => `${summary.name || ''} ${summary.specs || ''}`);
|
|
|
- listB.push(summaryKeys)
|
|
|
- });
|
|
|
- const test = listB.map(item => item.length);
|
|
|
- console.log(test);
|
|
|
-
|
|
|
- const matchRes = await ajaxPost('/priceInfoSummary/aiMatch', { listA, listB }, 1000 * 60 * 5);
|
|
|
- // 填匹配值到表格,不实时保存,因为需要人工核查
|
|
|
- workBookObj.sheet.suspendEvent();
|
|
|
- workBookObj.sheet.suspendPaint();
|
|
|
- matchRes.forEach((item, index) => {
|
|
|
- const firstMatch = item[0];
|
|
|
- const chunkItem = chunk[index];
|
|
|
- const summaryIndex = item[0].index;
|
|
|
- const summaryItem = summaryData[index][summaryIndex];
|
|
|
- const curUnit = cache[chunkItem.row]?.unit || '';
|
|
|
- const summaryItemUnit = summaryItem?.unit || '';
|
|
|
- // 相似度过低的、单位不一致的,不命中
|
|
|
- if (firstMatch.similarity < 70 || curUnit !== summaryItemUnit) {
|
|
|
- noMatchRows.push(chunkItem.row);
|
|
|
- return;
|
|
|
- };
|
|
|
- if (chunkItem && summaryItem) {
|
|
|
- workBookObj.sheet.setValue(chunkItem.row, classCodeCol, summaryItem.classCode);
|
|
|
- cache[chunkItem.row].classCode = summaryItem.classCode;
|
|
|
- const items = getItemsFromTableItem(cache[chunkItem.row]);
|
|
|
- items.forEach(item => {
|
|
|
- item.classCode = summaryItem.classCode;
|
|
|
- });
|
|
|
- // 如果实际行存在珠海地区的,才填计算式
|
|
|
- const tableItems = getItemsFromTableItem(cache[chunkItem.row]);
|
|
|
- const needExpString = tableItems.some(tItem => {
|
|
|
- const area = AREA_BOOK.cache.find(areaItem => areaItem.ID === tItem.areaID)
|
|
|
- return area && area.name && /珠海/.test(area.name);
|
|
|
- });
|
|
|
- if (needExpString) {
|
|
|
- workBookObj.sheet.setValue(chunkItem.row, expStringCol, summaryItem.expString);
|
|
|
- cache[chunkItem.row].expString = summaryItem.expString;
|
|
|
- items.forEach(item => {
|
|
|
- item.expString = summaryItem.expString;
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- workBookObj.sheet.resumeEvent();
|
|
|
- workBookObj.sheet.resumePaint();
|
|
|
- percent += step;
|
|
|
- $("#progress_modal_bar").css('width', `${percent}%`);
|
|
|
- await setTimeoutSync(100);
|
|
|
- }
|
|
|
-
|
|
|
- // 没匹配到的行,自动生成别名编码
|
|
|
- workBookObj.sheet.suspendEvent();
|
|
|
- workBookObj.sheet.suspendPaint();
|
|
|
- let curMaxClassCode = getMaxClassCode(priceInfoSummary);
|
|
|
- debugger;
|
|
|
- for (const row of noMatchRows) {
|
|
|
- const newClassCode = getNewMaxClassCode(curMaxClassCode);
|
|
|
- workBookObj.sheet.setValue(row, classCodeCol, newClassCode);
|
|
|
- cache[row].classCode = newClassCode;
|
|
|
- const items = getItemsFromTableItem(cache[row]);
|
|
|
- items.forEach(item => {
|
|
|
- item.classCode = newClassCode;
|
|
|
- });
|
|
|
- curMaxClassCode = newClassCode;
|
|
|
- }
|
|
|
- workBookObj.sheet.resumeEvent();
|
|
|
- workBookObj.sheet.resumePaint();
|
|
|
-
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- console.log(error);
|
|
|
- alert(error);
|
|
|
- }
|
|
|
- await setTimeoutSync(500);
|
|
|
- $.bootstrapLoading.progressEnd();
|
|
|
- } */
|
|
|
+
|
|
|
+ /* const aiMatch = async () => {
|
|
|
+ try {
|
|
|
+ // 获取信息价总表
|
|
|
+ const priceInfoSummary = await ajaxPost('/priceInfoSummary/getData', {}, 1000 * 60 * 5);
|
|
|
+ const summaryGroupMap = _.groupBy(priceInfoSummary, item => getFourCode(item.code));
|
|
|
+ const noCodeSummary = priceInfoSummary.filter(item => !item.code);
|
|
|
+ const totalRows = workBookObj.sheet.getRowCount();
|
|
|
+ const changedCells = [];
|
|
|
+ const noMatchRows = []; // 没有匹配、ai没有命中的行,后续需要自动生成别名编码(最大的别名编码+1)
|
|
|
+ for (let i = 0; i < totalRows; i++) {
|
|
|
+ const rowData = getRowData(workBookObj.sheet, i, setting.header);
|
|
|
+ // const code = rowData.code || '';
|
|
|
+ const code = getFourCode(rowData.code);
|
|
|
+ const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
+ if (toMatchSummary.length) {
|
|
|
+ changedCells.push({ row: i });
|
|
|
+ } else {
|
|
|
+ noMatchRows.push(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!changedCells.length) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const classCodeCol = setting.header.findIndex(h => h.dataCode === 'classCode');
|
|
|
+ const expStringCol = setting.header.findIndex(h => h.dataCode === 'expString');
|
|
|
+ // const chunks = _.chunk(changedCells, 20);
|
|
|
+ const chunks = _.chunk(changedCells, 1); // 只能一条一条匹配改成,否则经常ai服务经常挂
|
|
|
+ let percent = 0;
|
|
|
+ $.bootstrapLoading.progressStart('AI填值', false);
|
|
|
+ $("#progress_modal_body").text('正在进行AI填值,请稍后...');
|
|
|
+ await setTimeoutSync(500);
|
|
|
+ const matchResCache = {};
|
|
|
+
|
|
|
+ // 分块进行ai匹配
|
|
|
+ const step = 100 / (chunks.length || 1);
|
|
|
+ for (let i = 0; i < chunks.length; i++) {
|
|
|
+ const chunk = chunks[i];
|
|
|
+ const listA = [];
|
|
|
+ const listB = [];
|
|
|
+ const summaryData = [];
|
|
|
+ chunk.forEach(item => {
|
|
|
+ const rowData = getRowData(workBookObj.sheet, item.row, setting.header);
|
|
|
+ listA.push(`${rowData.name || ''} ${rowData.specs}`);
|
|
|
+ const code = getFourCode(rowData.code);
|
|
|
+ const toMatchSummary = code ? summaryGroupMap[code] || [] : noCodeSummary;
|
|
|
+ summaryData.push(toMatchSummary);
|
|
|
+ const summaryKeys = toMatchSummary.map(summary => `${summary.name || ''} ${summary.specs || ''}`);
|
|
|
+ listB.push([...new Set(summaryKeys)]);
|
|
|
+ });
|
|
|
+ const test = listB.map(item => item.length);
|
|
|
+ console.log(test);
|
|
|
+ const matchRes = matchResCache[listA[0]] ? matchResCache[listA[0]] : await ajaxPost('/priceInfoSummary/aiMatch', { listA, listB }, 1000 * 60 * 5);
|
|
|
+ matchResCache[listA[0]] = matchRes;
|
|
|
+ // 填匹配值到表格,不实时保存,因为需要人工核查
|
|
|
+ workBookObj.sheet.suspendEvent();
|
|
|
+ workBookObj.sheet.suspendPaint();
|
|
|
+ matchRes.forEach((item, index) => {
|
|
|
+ const firstMatch = item[0];
|
|
|
+ const chunkItem = chunk[index];
|
|
|
+ const summaryIndex = item[0].index;
|
|
|
+ const summaryItem = summaryData[index][summaryIndex];
|
|
|
+ const curUnit = cache[chunkItem.row]?.unit || '';
|
|
|
+ const summaryItemUnit = summaryItem?.unit || '';
|
|
|
+ // 相似度过低的、单位不一致的,不命中
|
|
|
+ if (firstMatch.similarity < 70 || curUnit !== summaryItemUnit) {
|
|
|
+ noMatchRows.push(chunkItem.row);
|
|
|
+ return;
|
|
|
+ };
|
|
|
+ if (chunkItem && summaryItem) {
|
|
|
+ workBookObj.sheet.setValue(chunkItem.row, classCodeCol, summaryItem.classCode);
|
|
|
+ cache[chunkItem.row].classCode = summaryItem.classCode;
|
|
|
+ const items = getItemsFromTableItem(cache[chunkItem.row]);
|
|
|
+ items.forEach(item => {
|
|
|
+ item.classCode = summaryItem.classCode;
|
|
|
+ });
|
|
|
+ // 如果实际行存在珠海地区的,才填计算式
|
|
|
+ const tableItems = getItemsFromTableItem(cache[chunkItem.row]);
|
|
|
+ const needExpString = tableItems.some(tItem => {
|
|
|
+ const area = AREA_BOOK.cache.find(areaItem => areaItem.ID === tItem.areaID)
|
|
|
+ return area && area.name && /珠海/.test(area.name);
|
|
|
+ });
|
|
|
+ if (needExpString) {
|
|
|
+ workBookObj.sheet.setValue(chunkItem.row, expStringCol, summaryItem.expString);
|
|
|
+ cache[chunkItem.row].expString = summaryItem.expString;
|
|
|
+ items.forEach(item => {
|
|
|
+ item.expString = summaryItem.expString;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ workBookObj.sheet.resumeEvent();
|
|
|
+ workBookObj.sheet.resumePaint();
|
|
|
+ percent += step;
|
|
|
+ $('#progress_modal_body').text(`正在进行AI填值,请稍后${i + 1}/${chunks.length}...`);
|
|
|
+ $("#progress_modal_bar").css('width', `${percent}%`);
|
|
|
+ await setTimeoutSync(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没匹配到的行,自动生成别名编码
|
|
|
+ workBookObj.sheet.suspendEvent();
|
|
|
+ workBookObj.sheet.suspendPaint();
|
|
|
+ let curMaxClassCode = getMaxClassCode(priceInfoSummary);
|
|
|
+ for (const row of noMatchRows) {
|
|
|
+ const newClassCode = getNewMaxClassCode(curMaxClassCode);
|
|
|
+ workBookObj.sheet.setValue(row, classCodeCol, newClassCode);
|
|
|
+ cache[row].classCode = newClassCode;
|
|
|
+ const items = getItemsFromTableItem(cache[row]);
|
|
|
+ items.forEach(item => {
|
|
|
+ item.classCode = newClassCode;
|
|
|
+ });
|
|
|
+ curMaxClassCode = newClassCode;
|
|
|
+ }
|
|
|
+ workBookObj.sheet.resumeEvent();
|
|
|
+ workBookObj.sheet.resumePaint();
|
|
|
+
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+ alert(error);
|
|
|
+ }
|
|
|
+ await setTimeoutSync(500);
|
|
|
+ $.bootstrapLoading.progressEnd();
|
|
|
+ } */
|
|
|
|
|
|
// 保存ai填值
|
|
|
const saveData = async () => {
|
|
@@ -643,6 +679,7 @@ $(document).ready(() => {
|
|
|
|
|
|
$('#empty-area').on('hidden.bs.modal', function () {
|
|
|
EMPTY_BOOK.clear();
|
|
|
+ $("#ai-match").text('AI填值');
|
|
|
});
|
|
|
|
|
|
// 保存至总表
|