0. 作品介紹

無人機造型動態設計模擬


50 1 0
非教學
封面圖 封面

1. AI 提示詞

把這段指令貼給 AI,讓它幫你生成程式碼。

我想要建立一個 【產品造型設計系統】
幫我用Google 試算表建立 【無人機造型資料庫】
我希望有些資料我可以直接在試算表更改首頁可以直接呈現
需求說明 【現有產品照片、造型風格、需要有拉桿調整造型】
要把它變成程式碼我要發佈
請完整敘述操作步驟 越詳細越好
作業環境
Google試算表
Google Apps Script
我看不懂程式碼,請給我後端gs完整程式碼、前端index完整程式碼

2. 資料表規則

請照下面規則建立分頁與欄位。

資料表規則圖
第1分頁: 分頁1 A 欄: ID B 欄: 名稱 C 欄: 造型風格 D 欄: 圖片網址 E 欄: 長度參數 F 欄: 寬度參數 G 欄: 流線度 H 欄: 圖片顯示

📄 解析結果(自動表格)

分頁:分頁1
A 欄 B 欄 C 欄 D 欄 E 欄 F 欄 G 欄 H 欄
ID 名稱 造型風格 圖片網址 長度參數 寬度參數 流線度 圖片顯示

3. 開啟 Apps Script

從試算表開 Apps Script,才能直接用 SpreadsheetApp 讀寫資料。

  1. 試算表上方選單:擴充功能 → Apps Script
  2. 刪掉預設的程式碼(或全部選取覆蓋)
  3. 保留/建立檔案:
    • Code.gs
    • index.html

4. 貼上後端(Code.gs)

把後端程式貼到 Apps Script 的 Code.gs。

Code.gs(後端)
/**
 * 系統核心:提供網頁服務
 */
function doGet() {
  return HtmlService.createTemplateFromFile('index')
    .evaluate()
    .setTitle('無人機造型設計系統')
    .addMetaTag('viewport', 'width=device-width, initial-scale=1')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

/**
 * 從試算表讀取資料並自動處理圖片連結
 */
function getDroneData() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('Data');
  if (!sheet) return [];

  const data = sheet.getDataRange().getValues();
  const headers = data.shift(); // 取得標題列
  
  return data.map((row, rowIndex) => {
    let obj = {};
    headers.forEach((header, i) => {
      let value = row[i];
      // 如果是圖片網址且來自 Google Drive,自動轉換為直連格式
      if (header === '圖片網址' && value.toString().includes('drive.google.com')) {
        value = convertDriveLink(value.toString());
      }
      obj[header] = value;
    });
    obj['index'] = rowIndex; // 給前端標記編號用
    return obj;
  });
}

/**
 * 內部工具:將 Google Drive 網址轉為圖片標籤可讀取的格式
 */
function convertDriveLink(url) {
  var id = "";
  if (url.indexOf("id=") != -1) {
    id = url.split("id=")[1].split("&")[0];
  } else {
    var parts = url.split("/");
    id = parts[parts.indexOf("d") + 1];
  }
  return "https://lh3.googleusercontent.com/d/" + id;
}
貼到 Apps Script 的 Code.gs 檔案

5. 貼上前端(index.html)

把前端程式貼到 Apps Script 的 index.html。

index.html(前端)
<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <base target="_top">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <style>
        .drone-img-container {
            perspective: 1000px;
            background: radial-gradient(circle, #ffffff 0%, #e5e7eb 100%);
            min-height: 200px;
        }
        .drone-preview {
            transition: transform 0.1s ease-out, opacity 0.2s ease;
            max-width: 100%;
            max-height: 180px;
            object-fit: contain;
        }
        .selected-card {
            border: 3px solid #3b82f6 !important;
            box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
        }
        input[type=range] {
            accent-color: #3b82f6;
        }
    </style>
</head>
<body class="bg-gray-100 min-h-screen">

    <header class="bg-gray-900 text-white p-5 shadow-xl">
        <div class="container mx-auto flex justify-between items-center">
            <div>
                <h1 class="text-2xl font-black tracking-widest uppercase">Drone Design System</h1>
                <p class="text-xs text-gray-400">無人機造型動態設計資料庫</p>
            </div>
            <div class="text-right text-xs">
                <span class="bg-green-500 px-2 py-1 rounded text-white">系統已連線</span>
            </div>
        </div>
    </header>

    <main class="container mx-auto py-8 px-4">
        <div class="flex flex-col lg:flex-row gap-8">
            
            <div class="lg:w-1/3 space-y-6">
                <div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-200 sticky top-10">
                    <h2 class="text-lg font-bold mb-6 flex items-center">
                        <span class="w-2 h-6 bg-blue-600 mr-2 rounded-full"></span>
                        造型參數模擬調整
                    </h2>
                    
                    <div class="space-y-8">
                        <div>
                            <div class="flex justify-between mb-2">
                                <label class="text-sm font-semibold text-gray-600">機身寬度比例 (Width)</label>
                                <span id="valW" class="text-blue-600 font-mono text-sm">100%</span>
                            </div>
                            <input type="range" id="sliderW" min="50" max="150" value="100" class="w-full">
                        </div>

                        <div>
                            <div class="flex justify-between mb-2">
                                <label class="text-sm font-semibold text-gray-600">機身長度比例 (Length)</label>
                                <span id="valH" class="text-blue-600 font-mono text-sm">100%</span>
                            </div>
                            <input type="range" id="sliderH" min="50" max="150" value="100" class="w-full">
                        </div>

                        <div>
                            <div class="flex justify-between mb-2">
                                <label class="text-sm font-semibold text-gray-600">機身流線度 (Opacity/Flow)</label>
                                <span id="valO" class="text-blue-600 font-mono text-sm">100%</span>
                            </div>
                            <input type="range" id="sliderO" min="20" max="100" value="100" class="w-full">
                        </div>
                    </div>

                    <div id="statusTip" class="mt-8 p-4 bg-gray-50 border border-dashed border-gray-300 rounded-lg text-sm text-gray-500 text-center">
                        請先點擊右側卡片「選中」<br>即可開始調整造型
                    </div>
                </div>
            </div>

            <div class="lg:w-2/3">
                <div id="droneList" class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div class="col-span-full py-20 text-center">
                        <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
                        <p class="text-gray-500 italic">正在從 Google Sheets 同步資料庫...</p>
                    </div>
                </div>
            </div>
        </div>
    </main>

    <script>
        let selectedIndex = null;

        // 初始化:向後端請求資料
        window.onload = () => {
            google.script.run.withSuccessHandler(renderDrones).getDroneData();
        };

        function renderDrones(data) {
            const list = document.getElementById('droneList');
            list.innerHTML = '';

            if (data.length === 0) {
                list.innerHTML = '<p class="col-span-full text-center p-10 bg-white rounded-xl">資料庫中目前沒有資料。</p>';
                return;
            }

            data.forEach((item) => {
                const card = document.createElement('div');
                card.id = `card-${item.index}`;
                card.className = "bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden cursor-pointer hover:shadow-lg transition-all";
                card.onclick = () => selectDrone(item.index);
                
                card.innerHTML = `
                    <div class="drone-img-container flex items-center justify-center p-6">
                        <img src="${item['圖片網址']}" id="drone-img-${item.index}" class="drone-preview" 
                             onerror="this.src='https://via.placeholder.com/300x200?text=圖片讀取失敗'">
                    </div>
                    <div class="p-5">
                        <div class="flex justify-between items-start mb-2">
                            <h3 class="text-lg font-bold text-gray-800">${item['產品名稱']}</h3>
                            <span class="text-xs bg-blue-100 text-blue-600 px-2 py-1 rounded font-bold">${item['造型風格']}</span>
                        </div>
                        <div class="grid grid-cols-3 gap-2 text-center text-[10px] text-gray-400 uppercase mt-4">
                            <div class="bg-gray-50 p-2 rounded">長度: ${item['長度參數']}</div>
                            <div class="bg-gray-50 p-2 rounded">寬度: ${item['寬度參數']}</div>
                            <div class="bg-gray-50 p-2 rounded">流線: ${item['流線度']}</div>
                        </div>
                    </div>
                `;
                list.appendChild(card);
            });
        }

        function selectDrone(idx) {
            // 清除之前的選中狀態
            if (selectedIndex !== null) {
                document.getElementById(`card-${selectedIndex}`).classList.remove('selected-card');
            }
            // 設定新的選中狀態
            selectedIndex = idx;
            document.getElementById(`card-${idx}`).classList.add('selected-card');
            document.getElementById('statusTip').innerHTML = `<b class="text-blue-600">已選中:正在調整無人機影像</b>`;
        }

        // 監聽拉桿並連動
        const sW = document.getElementById('sliderW');
        const sH = document.getElementById('sliderH');
        const sO = document.getElementById('sliderO');

        const updateTransform = () => {
            if (selectedIndex === null) return;
            
            const img = document.getElementById(`drone-img-${selectedIndex}`);
            const w = sW.value;
            const h = sH.value;
            const o = sO.value;

            // 更新顯示數值
            document.getElementById('valW').innerText = w + '%';
            document.getElementById('valH').innerText = h + '%';
            document.getElementById('valO').innerText = o + '%';

            // 即時變形圖片 (模擬造型調整)
            img.style.transform = `scaleX(${w/100}) scaleY(${h/100})`;
            img.style.opacity = o / 100;
        };

        sW.oninput = updateTransform;
        sH.oninput = updateTransform;
        sO.oninput = updateTransform;
    </script>
</body>
</html>
貼到 Apps Script 的 index.html 檔案

6. 部署

部署成 Web App 後,就會拿到可分享網址。

  1. Apps Script 右上:部署 → 新部署
  2. 類型選:Web app
  3. 執行身分:你
  4. 存取權限:任何人
  5. 完成後複製網址

💬 評論區

老師們的交流與提問

載入評論中...