Để triển khai Cách 1: Nút bấm 1 chạm tự động hóa (Google Apps Script), tôi đã chuẩn bị sẵn một đoạn mã tối ưu hóa đặc thù cho cấu trúc bảng tính hiện tại của bạn.
Kịch bản này khi chạy sẽ tự động thực hiện 3 công việc trong vài giây:
C7:C41 trên sheet Dashboard (bỏ qua các dòng trống).Tổng hợp cho số lượng thành viên thực tế và điền vào cột labeled_by (cột BM).function myFunction() {...})./**
* TỰ ĐỘNG CHIA POI, ĐỔI TÊN TAB VÀ ĐỒNG BỘ DỮ LIỆU SANG 35 TAB THÀNH VIÊN
*/
function tuDongChiaPOIVaDongBo() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const dashboardSheet = ss.getSheetByName("Dashboard");
const tongHopSheet = ss.getSheetByName("Tổng hợp");
if (!dashboardSheet || !tongHopSheet) {
SpreadsheetApp.getUi().alert("Lỗi: Không tìm thấy sheet 'Dashboard' hoặc 'Tổng hợp'.");
return;
}
// 1. Lấy danh sách thành viên từ Dashboard (C7:C41)
const memberValues = dashboardSheet.getRange("C7:C41").getValues();
const members = [];
for (let i = 0; i < memberValues.length; i++) {
const name = String(memberValues[i][0]).trim();
if (name !== "" && !name.toLowerCase().startsWith("thành viên")) {
members.push(name);
}
}
const numMembers = members.length;
if (numMembers === 0) {
SpreadsheetApp.getUi().alert("Không tìm thấy thành viên hợp lệ nào trong cột C7:C41 ở Dashboard!");
return;
}
// 2. Xác định số lượng POI trong sheet Tổng hợp (Dữ liệu từ dòng 4)
const lastRow = tongHopSheet.getLastRow();
if (lastRow < 4) {
SpreadsheetApp.getUi().alert("Sheet 'Tổng hợp' chưa có dữ liệu POI từ dòng 4 trở đi!");
return;
}
const totalPOIs = lastRow - 3;
const baseCount = Math.floor(totalPOIs / numMembers);
const remainder = totalPOIs % numMembers;
// 3. Phân bổ tên thành viên vào cột labeled_by (cột 65 / BM)
const labeledByCol = 65; // Cột BM
const assignedNames = [];
const memberRanges = {};
let currentStart = 4;
for (let m = 0; m < numMembers; m++) {
const count = baseCount + (m < remainder ? 1 : 0);
const memberName = members[m];
memberRanges[memberName] = { start: currentStart, count: count };
for (let c = 0; c < count; c++) {
assignedNames.push([memberName]);
}
currentStart += count;
}
// Ghi tên vào cột BM của sheet Tổng hợp
tongHopSheet.getRange(4, labeledByCol, totalPOIs, 1).setValues(assignedNames);
// 4. Lấy danh sách các tab thành viên hiện có (ngoại trừ các tab hệ thống)
const excludeSheets = ["Tổng hợp", "Dashboard", "Data Catalog"];
const allSheets = ss.getSheets();
const memberSheets = allSheets.filter(s => !excludeSheets.includes(s.getName()));
// Lấy dữ liệu toàn bộ bảng Tổng hợp (từ dòng 4 đến dòng cuối, từ cột 1 đến 76 - cột BX)
const totalCols = 76;
const fullData = tongHopSheet.getRange(4, 1, totalPOIs, totalCols).getValues();
// 5. Đổi tên sheet và đồng bộ dữ liệu vào từng tab thành viên
for (let i = 0; i < memberSheets.length; i++) {
const sheet = memberSheets[i];
if (i < numMembers) {
const targetName = members[i];
// Đổi tên tab nếu khác tên
if (sheet.getName() !== targetName) {
try {
sheet.setName(targetName);
} catch (e) {
// Tránh lỗi trùng tên tạm thời nếu có
sheet.setName(targetName + " (" + (i + 1) + ")");
}
}
// Xóa dữ liệu cũ từ dòng 4 trở đi (giữ nguyên 3 dòng tiêu đề)
const currentLastRow = sheet.getLastRow();
if (currentLastRow >= 4) {
sheet.getRange(4, 1, currentLastRow - 3, totalCols).clearContent();
}
// Lấy các dòng POI thuộc về thành viên này
const info = memberRanges[targetName];
if (info && info.count > 0) {
const offset = info.start - 4;
const memberRows = fullData.slice(offset, offset + info.count);
sheet.getRange(4, 1, info.count, totalCols).setValues(memberRows);
}
} else {
// Đổi tên các sheet còn lại về mặc định và xóa dữ liệu
const fallbackName = "Thành viên " + (i + 1);
if (sheet.getName() !== fallbackName) {
sheet.setName(fallbackName);
}
const currentLastRow = sheet.getLastRow();
if (currentLastRow >= 4) {
sheet.getRange(4, 1, currentLastRow - 3, totalCols).clearContent();
}
}
}
SpreadsheetApp.getUi().alert(
"✅ ĐÃ ĐỒNG BỘ THÀNH CÔNG!\n\n" +
"- Tổng số POI: " + totalPOIs + "\n" +
"- Số thành viên: " + numMembers + "\n" +
"- Số POI/người: " + baseCount + " - " + (baseCount + (remainder > 0 ? 1 : 0)) + " POI\n" +
"- Đã chia tên trên sheet Tổng hợp và sao chép sang từng tab thành viên."
);
}
Ctrl + S).Dashboard.G2:G4).tuDongChiaPOIVaDongBoC7:C41 trên sheet Dashboard.labeled_by, dữ liệu 35 tab và số liệu Dashboard sẽ tự động được chia đều và cập nhật ngay lập tức!
comments (0)