From 0756bf12d10cf1b7f78c571de0a9ad69cbaeb7ca Mon Sep 17 00:00:00 2001
From: curtis <curtis@i-mps.com>
Date: 星期一, 30 三月 2026 14:24:17 +0800
Subject: [PATCH] fix: 更新內部引用方法參照

---
 uiOutput/index.html |  408 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 408 insertions(+), 0 deletions(-)

diff --git a/uiOutput/index.html b/uiOutput/index.html
new file mode 100644
index 0000000..0c61824
--- /dev/null
+++ b/uiOutput/index.html
@@ -0,0 +1,408 @@
+<!DOCTYPE html>
+<html lang="zh-TW">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>DFM to Vue Preview</title>
+
+    <!-- 引入 Vue 3 -->
+    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
+
+    <!-- 引入 Tailwind CSS 用於即時預覽 -->
+    <script src="https://cdn.tailwindcss.com"></script>
+
+    <!-- 使用 vue3-sfc-loader 來動態編譯 .vue 檔案 -->
+    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader@0.8.4/dist/vue3-sfc-loader.js"></script>
+
+    <!-- 加入 TypeScript 的 Babel transpiler 用於即時編譯 .ts -->
+    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
+
+    <style>
+        body, html {
+            margin: 0;
+            padding: 0;
+            height: 100%;
+            background-color: #f3f4f6; /* Tailwind gray-100 */
+            font-family: sans-serif;
+        }
+
+        #app {
+            display: flex;
+            flex-direction: column;
+            height: 100vh;
+        }
+
+        /* 上方清單區塊 (1.4.1.1) */
+        .list-section {
+            background-color: #ffffff;
+            border-bottom: 1px solid #d1d5db; /* gray-300 */
+            padding: 16px;
+            box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+            z-index: 10;
+        }
+
+        .component-btn {
+            background-color: #3b82f6; /* blue-500 */
+            color: white;
+            padding: 8px 16px;
+            border-radius: 4px;
+            border: none;
+            cursor: pointer;
+            font-size: 14px;
+            margin-right: 8px;
+            transition: background-color 0.2s;
+        }
+
+        .component-btn:hover {
+            background-color: #2563eb; /* blue-600 */
+        }
+
+        .component-btn.active {
+            background-color: #1e40af; /* blue-800 */
+            box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.2);
+        }
+
+        /* 下方預覽區塊 (1.4.1.2) */
+        .preview-section {
+            flex: 1;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            padding: 24px;
+            overflow: auto;
+            background-color: #e5e7eb; /* Tailwind gray-200 */
+        }
+
+        /* 模擬 Windows 視窗外框 */
+        .preview-container {
+            background-color: white;
+            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+            border: 1px solid #9ca3af;
+            border-radius: 4px;
+            overflow: hidden;
+            display: flex;
+            flex-direction: column;
+            /* 讓內部的子元件決定自己的寬高,但不能超過螢幕 */
+            max-width: 100%;
+            max-height: 100%;
+        }
+
+        .window-title {
+            background-color: #0058b0;
+            color: white;
+            padding: 6px 10px;
+            font-size: 12px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            user-select: none;
+        }
+
+        .window-controls span {
+            display: inline-block;
+            width: 16px;
+            height: 16px;
+            background-color: #ccc;
+            margin-left: 4px;
+            border-radius: 2px;
+            text-align: center;
+            line-height: 14px;
+            color: black;
+            cursor: pointer;
+            font-size: 10px;
+            font-weight: bold;
+        }
+
+        .window-controls span:hover {
+            background-color: #e81123;
+            color: white;
+        }
+
+        /* 載入中動畫 */
+        .loader {
+            border: 4px solid #f3f3f3;
+            border-top: 4px solid #3b82f6;
+            border-radius: 50%;
+            width: 30px;
+            height: 30px;
+            animation: spin 1s linear infinite;
+        }
+
+        @keyframes spin {
+            0% {
+                transform: rotate(0deg);
+            }
+            100% {
+                transform: rotate(360deg);
+            }
+        }
+    </style>
+</head>
+<body>
+
+<div id="app">
+    <!-- 1.4.1.1: List button 區塊 -->
+    <div class="list-section">
+        <h2 class="text-lg font-bold text-gray-700 mb-3">Converted Vue Components</h2>
+        <div class="flex flex-wrap gap-2">
+            <button
+                    v-for="comp in availableComponents"
+                    :key="comp.id"
+                    @click="loadComponent(comp)"
+                    :class="['component-btn', { active: currentComponent && currentComponent.id === comp.id }]"
+            >
+                {{ comp.name }}
+            </button>
+        </div>
+    </div>
+
+    <!-- 1.4.1.2: Preview viewer 區塊 -->
+    <div class="preview-section relative">
+        <div v-if="isLoading"
+             class="absolute inset-0 flex flex-col items-center justify-center bg-gray-200 bg-opacity-75 z-20">
+            <div class="loader mb-4"></div>
+            <div class="text-gray-600 font-medium">Loading component...</div>
+        </div>
+
+        <div v-else-if="!currentComponent" class="text-gray-500 text-lg">
+            Please select a component from the top menu to preview.
+        </div>
+
+        <div v-else class="preview-container">
+            <!-- 模擬 Windows 視窗標題列 -->
+            <div class="window-title">
+                <span>{{ currentComponent.windowTitle }}</span>
+                <div class="window-controls">
+                    <span>_</span>
+                    <span>□</span>
+                    <span @click="closePreview">x</span>
+                </div>
+            </div>
+
+            <!-- 動態載入的 Vue 元件將會渲染在這裡 -->
+            <component :is="dynamicComponent"></component>
+        </div>
+    </div>
+</div>
+
+<script type="module">
+  const {loadModule} = window['vue3-sfc-loader'];
+
+  // 用來暫存修改過的檔案內容
+  const virtualFileSystem = {};
+
+  // 為了避免重複宣告 `ref`, `onMounted` 導致 SyntaxError
+  // 我們在全域環境中先宣告好 Vue 的依賴,只宣告一次
+  if (!window.__VUE_SETUP__) {
+    window.__VUE_SETUP__ = true;
+    const script = document.createElement('script');
+    script.textContent = `
+                // 全域提供 Vue API,避免在每個動態腳本中重複 let/const
+                window.ref = Vue.ref;
+                window.reactive = Vue.reactive;
+                window.computed = Vue.computed;
+                window.onMounted = Vue.onMounted;
+                window.onUnmounted = Vue.onUnmounted;
+                window.watch = Vue.watch;
+                window.defineComponent = Vue.defineComponent;
+            `;
+    document.head.appendChild(script);
+  }
+
+  // SFC Loader 設定,加入對 TS 的編譯支援
+  const options = {
+    moduleCache: {
+      vue: Vue
+    },
+    async getFile(url) {
+      // 處理虛擬 URL
+      if (url.startsWith('virtual-url-')) {
+        const content = virtualFileSystem[url];
+        if (content) {
+          return {
+            getContentData: asBinary => asBinary ? new TextEncoder().encode(content) : content,
+            type: '.vue'
+          };
+        }
+      }
+
+      // 處理相依的 .ts / .js 邏輯檔案
+      if (url.endsWith('.ts') || url.endsWith('.js')) {
+        const res = await fetch(url);
+        if (!res.ok) throw Object.assign(new Error(res.statusText + ' ' + url), {res});
+        const code = await res.text();
+
+        // 用 Babel 拔除 TypeScript 型別,並轉成 ES5
+        const transpiled = Babel.transform(code, {
+          presets: ['typescript'],
+          filename: url // 加入 filename 參數解決 Babel 錯誤
+        }).code;
+
+        // 將 export 轉化為掛載到 window
+        let modifiedCode = transpiled.replace(/export\s+(?:function|const)\s+(use\w+Logic)/g, 'window.$1 = function');
+
+        return {
+          getContentData: asBinary => asBinary ? new TextEncoder().encode(modifiedCode) : modifiedCode,
+          type: '.js'
+        };
+      }
+
+      const res = await fetch(url);
+      if (!res.ok) throw Object.assign(new Error(res.statusText + ' ' + url), {res});
+
+      return {
+        getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
+      }
+    },
+    addStyle(textContent) {
+      const style = Object.assign(document.createElement('style'), {textContent});
+      const ref = document.head.getElementsByTagName('style')[0] || null;
+      document.head.insertBefore(style, ref);
+    },
+    // 設定處理 `<script lang="ts">` 的勾子
+    handleModule(type, getContentData, path, options) {
+      switch (type) {
+        case '.ts':
+          const code = getContentData(false);
+          const jsCode = Babel.transform(code, {
+            presets: ['typescript'],
+            filename: path || 'file.ts' // 加入 filename 參數解決 Babel 錯誤
+          }).code;
+          return {getContentData: _ => jsCode, type: '.js'};
+      }
+    }
+  };
+
+  const app = Vue.createApp({
+    data() {
+      return {
+        availableComponents: [
+          {
+            id: 'DocList',
+            name: 'DocList.vue',
+            vuePath: './DocList/DocList.vue',
+            jsPath: './DocList/DocList.ts',
+            windowTitle: '歷史類畫面'
+          },
+          {
+            id: 'DocPrt',
+            name: 'DocPrt.vue',
+            vuePath: './DocPrt/DocPrt.vue',
+            jsPath: './DocPrt/DocPrt.ts',
+            windowTitle: '列印畫面'
+          },
+          {
+            id: 'ErrList',
+            name: 'ErrList.vue',
+            vuePath: './ErrList/ErrList.vue',
+            jsPath: './ErrList/ErrList.ts',
+            windowTitle: '檢核失敗原因畫面'
+          },
+          {
+            id: 'OldCaseInfo',
+            name: 'OldCaseInfo.vue',
+            vuePath: './OldCaseInfo/OldCaseInfo.vue',
+            jsPath: './OldCaseInfo/OldCaseInfo.ts',
+            windowTitle: '舊案引用畫面'
+          },
+          {
+            id: 'PatchFom',
+            name: 'PatchFom.vue',
+            vuePath: './PatchFom/PatchFom.vue',
+            jsPath: './PatchFom/PatchFom.ts',
+            windowTitle: '空白頁設定'
+          },
+          {
+            id: 'CB_IMGPSScanImp',
+            name: 'CB_IMGPSScanImp.vue',
+            vuePath: './CB_IMGPSScanImp/CB_IMGPSScanImp.vue',
+            jsPath: './CB_IMGPSScanImp/CB_IMGPSScanImp.ts',
+            windowTitle: 'CB_IMGPSScanX'
+          }
+        ],
+        currentComponent: null,
+        dynamicComponent: null,
+        isLoading: false
+      }
+    },
+    methods: {
+      async loadComponent(comp) {
+        if (this.currentComponent && this.currentComponent.id === comp.id) return;
+
+        this.isLoading = true;
+        this.currentComponent = comp;
+        this.dynamicComponent = null;
+
+        try {
+          // 1. 載入並執行 TypeScript 邏輯檔案
+          const res = await fetch(comp.jsPath);
+          if (!res.ok) throw new Error(`Failed to load ${comp.jsPath}`);
+          const tsCode = await res.text();
+
+          // 透過 Babel 將 TypeScript 轉譯為 JavaScript
+          const jsCode = Babel.transform(tsCode, {
+            presets: ['typescript'],
+            filename: comp.jsPath
+          }).code;
+
+          // 移除 import vue,將 export 改為 window 掛載
+          // 注意:這裡不宣告 const { ref, ... } = Vue,因為我們在全域已經掛載了 window.ref
+          // 只需要把 ts 轉譯後的 var _vue = require("vue"); 之類的給清除
+          let modifiedCode = jsCode
+            .replace(/import\s+\{.*?\}\s+from\s+['"]vue['"];/g, '')
+            .replace(/(var|let|const)\s+\w+\s*=\s*require\(['"]vue['"]\);/g, '')
+            // 將 babel 轉譯後可能產生的 _vue.ref 換回全域的 ref
+            .replace(/_vue\./g, '')
+            .replace(/export\s+(?:function|const)\s+(use\w+Logic)/g, 'window.$1 = function');
+
+          const scriptId = `script-${comp.id}`;
+          let oldScript = document.getElementById(scriptId);
+          if (oldScript) oldScript.remove();
+
+          const script = document.createElement('script');
+          script.id = scriptId;
+
+          // 使用 IIFE (立即執行函式) 包裝,避免區域變數互相污染
+          script.textContent = `
+                            (function() {
+                                ${modifiedCode}
+                            })();
+                        `;
+          document.head.appendChild(script);
+
+          // 2. 載入 Vue SFC
+          this.dynamicComponent = Vue.markRaw(Vue.defineAsyncComponent(() => {
+            return fetch(comp.vuePath)
+              .then(res => res.text())
+              .then(content => {
+                // 替換 .vue 裡面的 import
+                const modifiedContent = content.replace(
+                  /import\s+\{\s*(use\w+Logic)\s*\}\s+from\s+['"].*?\.ts['"];/g,
+                  'const { $1 } = window;'
+                );
+
+                const virtualUrl = `virtual-url-${comp.id}-${Date.now()}.vue`;
+                virtualFileSystem[virtualUrl] = modifiedContent;
+
+                return loadModule(virtualUrl, options);
+              });
+          }));
+        } catch (err) {
+          console.error(`Error loading component ${comp.name}:`, err);
+          alert(`載入元件失敗: ${err.message}`);
+          this.currentComponent = null;
+        } finally {
+          this.isLoading = false;
+        }
+      },
+      closePreview() {
+        this.currentComponent = null;
+        this.dynamicComponent = null;
+      }
+    }
+  });
+
+  app.mount('#app');
+</script>
+</body>
+</html>

--
Gitblit v1.8.0