<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      基于vue3項目開發+MonacoEditor實現外部引入依賴,界面化所見即所得

      最近一個項目中,基于vue3開發,想開發一個在線管理組件庫的功能,具體業務實現:

      1. 在私庫Nexus上傳組件包;

      2. 然后用UNPKG實現路徑訪問在線解壓文件;

      3. 解壓文件上傳到gitee組件庫中查看;

      4. 然后通過頁面配置填寫需要引入的依賴地址(直接通過UNPKG讀取包內文件內容),頁面中填寫dist文件夾中的文件路徑,支持在當前組件中引入多個外部依賴(css、js);

      5. 可以讀取reademe.md文件并在頁面中展示;

      6. 輸入并引入依賴后,在編輯器中輸入樣例代碼,切換preview實現預覽;

      主要實現邏輯:用MonacoEditor作為編輯器組件,然后用importmap的方式在html頁面的head中引入頁面中配置的依賴地址,在編輯器中編輯代碼然后通過響應式傳入并渲染到html中進行功能展示。

      代碼片段截圖

      tab切換編輯器和視圖展示:

      監聽切換tab,切換到視圖界面獲取編輯器中代碼:

      根據UNPKG地址獲取md內容并用marked展示:


      根據頁面上填寫的js和css文件地址合并數組傳入preview組件:

      preview組件中監聽監聽props.state做數據更新,props.resources監聽更新html頭部的importmap引入依賴:

      創建沙盒,拼接html中head內的importmap,沙盒加載以及銷毀部分,通過postMessage傳遞渲染內容:

      html頁面中監聽postMessage傳遞的內容,然后用handleEval方法添加加載腳本:

      MonacoEditor編輯器配置

      功能頁面展示如下:

      代碼編輯和readme.md引入展示:

      示例代碼preview展示:

      全部功能代碼如下:

      表單

      點擊查看代碼
      <template>
        <div class="crud-page" v-loading="loading">
          <el-form
            ref="ruleFormRef"
            :inline="true"
            :model="crudForm"
            class="crudForm"
            :rules="rules"
            label-width="150px"
          >
            <el-row class="componentMsg">
              <p>組件信息</p>
              <el-col :span="12">
                <el-form-item label="組件名稱:" prop="name">
                  <el-input
                    v-model="crudForm.name"
                    :disabled="disabledStatus"
                    placeholder="請輸入組件名稱"
                    clearable
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="組件分類:" prop="type">
                  <el-tree-select
                    v-model="crudForm.type"
                    :disabled="disabledStatus"
                    :props="treeprops"
                    check-strictly
                    filterable
                    :data="typeOptions"
                    :render-after-expand="false"
                  />
                  <!-- <el-select v-model="crudForm.type" placeholder="請選擇組件分類">
                    <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
                  </el-select> -->
                  <!-- <el-cascader v-model="editypeList" :options="typeOptions" :show-all-levels="false" @change="editType"/> -->
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="組件標簽:" prop="tagList">
                  <!-- <el-select v-model="crudForm.tag" multiple placeholder="請選擇組件標簽">
                    <el-option label="標簽一" value="tag1" />
                    <el-option label="標簽二" value="tag2" />
                  </el-select> -->
                  <el-select
                    v-model="tagList"
                    multiple
                    filterable
                    allow-create
                    default-first-option
                    :reserve-keyword="false"
                    placeholder="請選擇組件標簽"
                    @change="createTagFun"
                    :disabled="disabledStatus"
                  >
                    <el-option
                      v-for="item in tagOptions"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="版本號:">
                  <el-input
                    v-model="crudForm.version"
                    placeholder="請輸入版本號"
                    clearable
                    :disabled="disabledStatus"
                  />
                </el-form-item>
              </el-col>
              <!-- <el-col :span="12">
                <el-form-item label="所屬任務:" prop="task">
                  <el-select v-model="crudForm.task" placeholder="請選擇所屬任務">
                    <el-option label="所屬任務" value="suoshurenwu" />
                  </el-select>
                </el-form-item>
              </el-col> -->
              <el-col :span="12">
                <el-form-item label="組件描述:" prop="description">
                  <el-input
                    type="textarea"
                    v-model="crudForm.description"
                    :rows="5"
                    :max-rows="5"
                    placeholder="請輸入組件描述"
                    :disabled="disabledStatus"
                  ></el-input>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="發布日志:" prop="publishLog">
                  <el-input
                    type="textarea"
                    v-model="crudForm.publishLog"
                    :rows="5"
                    :max-rows="5"
                    placeholder="請輸入發布日志"
                    :disabled="disabledStatus"
                  ></el-input>
                </el-form-item>
              </el-col>
      
              <el-col :span="12" v-show="!disabledStatus">
                <el-form-item label="倉庫地址:">
                  <el-input
                    v-model="crudForm.warehouseAddress"
                    placeholder="gitee倉庫地址,例如:/packages/vue3/xxx"
                    clearable
                    :disabled="importDisabled"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="disabledStatus">
                <el-form-item label="倉庫地址:" prop="publishLog">
                  <el-link
                    :href="
                      'https://gitee.com/ksbpump/ki-components/tree/develop' + crudForm.warehouseAddress
                    "
                    target="_blank"
                  >
                    去倉庫查看
                    <el-icon><Right /></el-icon>
                  </el-link>
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus">
                <el-button
                  :icon="Search"
                  style="float: left; margin-left: 10px"
                  @click="viewMd"
                  type="primary"
                >
                  查看README.md
                </el-button>
              </el-col>
      
              <el-col :span="12" v-show="!disabledStatus">
                <el-form-item
                  v-for="(ssJsConfig, index) in crudForm.ssJsConfig"
                  :key="index"
                  :label="index === 0 ? '組件js依賴:' : ' '"
                >
                  <el-input
                    v-model="ssJsConfig.dataKey"
                    placeholder="組件別名"
                    clearable
                    :disabled="importDisabled"
                    style="width: 20%"
                  />
                  <el-input
                    v-model="ssJsConfig.dataValue"
                    placeholder="組件依賴路徑,例如:/dist/index.ems.js"
                    clearable
                    :disabled="importDisabled"
                    style="width: 75%"
                  />
                  <el-icon
                    size="16px"
                    color="#f56c6c"
                    style="margin-left: 5px; cursor: pointer"
                    @click="deleteSsJs(ssJsConfig)"
                    v-show="index != 0"
                  >
                    <RemoveFilled />
                  </el-icon>
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus" style="vertical-align: middle">
                <el-button
                  :icon="Plus"
                  style="float: left; margin-left: 10px"
                  @click="addSsJs"
                  type="success"
                  :disabled="importDisabled"
                >
                  添加組件js依賴
                </el-button>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus">
                <el-form-item
                  v-for="(ssCssConfig, index) in crudForm.ssCssConfig"
                  :key="index"
                  :label="index === 0 ? '組件css依賴:' : ' '"
                >
                  <el-input
                    v-model="ssCssConfig.dataValue"
                    placeholder="組件依賴路徑,例如:/dist/index.min.css"
                    clearable
                    :disabled="importDisabled"
                    style="width: 95%"
                  />
                  <el-icon
                    size="16px"
                    color="#f56c6c"
                    style="margin-left: 5px; cursor: pointer"
                    @click="deleteSsCss(ssCssConfig)"
                    v-show="index != 0"
                  >
                    <RemoveFilled />
                  </el-icon>
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus">
                <el-button
                  :icon="Plus"
                  style="float: left; margin-left: 10px"
                  @click="addSsCss"
                  type="success"
                  :disabled="importDisabled"
                >
                  添加組件css依賴
                </el-button>
              </el-col>
      
              <!-- 全局部分 -->
              <el-col :span="12" v-show="!disabledStatus">
                <el-form-item
                  v-for="(ssJsConfig, index) in crudForm.ssAllJsConfig"
                  :key="index"
                  :label="index === 0 ? '全局組件js依賴:' : ' '"
                >
                  <el-input
                    v-model="ssJsConfig.dataKey"
                    placeholder="組件別名"
                    clearable
                    :disabled="importDisabled"
                    style="width: 20%"
                  />
                  <el-input
                    v-model="ssJsConfig.name"
                    placeholder="組件名稱"
                    clearable
                    :disabled="importDisabled"
                    style="width: 20%"
                  />
                  <el-input
                    v-model="ssJsConfig.versionNum"
                    placeholder="組件版本號"
                    clearable
                    :disabled="importDisabled"
                    style="width: 20%"
                  />
                  <el-input
                    v-model="ssJsConfig.dataValue"
                    placeholder="組件依賴路徑,例如:/dist/index.ems.js"
                    clearable
                    :disabled="importDisabled"
                    style="width: 35%"
                  />
                  <el-icon
                    size="16px"
                    color="#f56c6c"
                    style="margin-left: 5px; cursor: pointer"
                    @click="deleteSsAllJs(ssJsConfig)"
                    v-show="index != 0"
                  >
                    <RemoveFilled />
                  </el-icon>
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus" style="vertical-align: middle">
                <el-button
                  :icon="Plus"
                  style="float: left; margin-left: 10px"
                  @click="addSsAllJs"
                  type="success"
                  :disabled="importDisabled"
                >
                  添加全局組件js依賴
                </el-button>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus">
                <el-form-item
                  v-for="(ssCssConfig, index) in crudForm.ssAllCssConfig"
                  :key="index"
                  :label="index === 0 ? '全局組件css依賴:' : ' '"
                >
                  <el-input
                    v-model="ssCssConfig.name"
                    placeholder="組件名稱"
                    clearable
                    :disabled="importDisabled"
                    style="width: 20%"
                  />
                  <el-input
                    v-model="ssCssConfig.versionNum"
                    placeholder="組件版本號"
                    clearable
                    :disabled="importDisabled"
                    style="width: 20%"
                  />
                  <el-input
                    v-model="ssCssConfig.dataValue"
                    placeholder="組件依賴路徑,例如:/dist/index.min.css"
                    clearable
                    :disabled="importDisabled"
                    style="width: 55%"
                  />
                  <el-icon
                    size="16px"
                    color="#f56c6c"
                    style="margin-left: 5px; cursor: pointer"
                    @click="deleteSsAllCss(ssCssConfig)"
                    v-show="index != 0"
                  >
                    <RemoveFilled />
                  </el-icon>
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus">
                <el-button
                  :icon="Plus"
                  style="float: left; margin-left: 10px"
                  @click="addSsAllCss"
                  type="success"
                  :disabled="importDisabled"
                >
                  添加全局組件css依賴
                </el-button>
              </el-col>
      
              <el-col class="install-picture" :span="12">
                <el-form-item label="效果圖:" prop="images">
                  <SingleImageUpload
                    v-model="crudForm.images"
                    :style="{ width: '64px', height: '64px' }"
                    :disabled="disabledStatus"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12" v-show="!disabledStatus">
                <el-button
                  :icon="BottomLeft"
                  style="float: left; margin-left: 10px"
                  @click="importModel"
                  type="info"
                  :disabled="importDisabled"
                >
                  引入全部依賴
                </el-button>
              </el-col>
            </el-row>
            <el-row class="componentExample">
              <p>使用示例</p>
              <el-col class="install-example" :span="24">
                <span>使用示例:</span>
                <div class="exampleDiv" v-if="showExample">
                  <el-tooltip class="box-item" effect="dark" content="復制" placement="top">
                    <el-icon
                      style="position: absolute; right: 25px; top: 23px; cursor: pointer; z-index: 100"
                    >
                      <DocumentCopy />
                    </el-icon>
                  </el-tooltip>
                  <el-tabs type="border-card" v-model="tab">
                    <el-tab-pane name="code">
                      <template #label>
                        <span class="custom-tabs-label">
                          <el-icon><postcard /></el-icon>
                          <span>Code</span>
                        </span>
                      </template>
                      <MonacoEditor
                        ref="editorRef"
                        v-model="crudForm.installExample"
                        :visible="props.componentModel"
                        :style="{ height: '415px' }"
                        :options="editorOptions"
                        @update:modelValue="getCode"
                      />
                    </el-tab-pane>
                    <el-tab-pane name="preview">
                      <template #label>
                        <span class="custom-tabs-label">
                          <el-icon><Monitor /></el-icon>
                          <span>Preview</span>
                        </span>
                      </template>
                      <Preview
                        v-if="showExample"
                        :state="state"
                        :tab="tab"
                        :resources="resources"
                      ></Preview>
                    </el-tab-pane>
                  </el-tabs>
                </div>
                <div v-else class="exampleText">
                  請填寫組件在倉庫中的地址和組件依賴信息后,點擊引入依賴按鈕后進行示例填寫
                </div>
              </el-col>
            </el-row>
            <el-row class="componentShuo" v-show="componentShuoShow">
              <p>使用說明</p>
              <div id="preview"></div>
            </el-row>
          </el-form>
          <el-row class="history-table">
            <p>版本歷史</p>
            <el-table :data="tableData" style="width: 100%">
              <el-table-column prop="version" label="版本號" />
              <el-table-column prop="publishLog" label="更新日志" />
              <el-table-column prop="updateTime" label="更新時間" />
              <el-table-column prop="address" label="操作" width="80">
                <template #default="scope">
                  <el-button link type="primary" size="small" @click="viewLogs(scope.row)">
                    查看
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-row>
          <el-drawer
            v-model="innerDrawer"
            title="版本歷史"
            :append-to-body="true"
            :before-close="handleClose"
            size="800"
            class="innerDrawer"
          >
            <el-form
              ref="ruleFormRef"
              :inline="true"
              :model="histroyForm"
              class="histroyForm"
              label-width="100px"
            >
              <el-row>
                <el-col :span="12">
                  <el-form-item label="組件名稱:" prop="name">
                    <el-input
                      v-model="histroyForm.name"
                      :disabled="true"
                      placeholder="請輸入組件名稱"
                      clearable
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item label="組件分類:" prop="type">
                    <el-tree-select
                      v-model="histroyForm.type"
                      :disabled="true"
                      :props="treeprops"
                      check-strictly
                      filterable
                      :data="typeOptions"
                      :render-after-expand="false"
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item label="組件標簽:" prop="historyTagList">
                    <el-select
                      v-model="historyTagList"
                      multiple
                      filterable
                      allow-create
                      default-first-option
                      :reserve-keyword="false"
                      placeholder="請選擇組件標簽"
                      :disabled="true"
                    >
                      <el-option
                        v-for="item in tagOptions"
                        :key="item.value"
                        :label="item.label"
                        :value="item.value"
                      />
                    </el-select>
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item label="版本號:" prop="version">
                    <el-input
                      v-model="histroyForm.version"
                      placeholder="請輸入版本號"
                      clearable
                      :disabled="true"
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="12">
                  <el-form-item label="更新時間:" prop="updateTime">
                    <el-date-picker
                      style="width: 100%"
                      v-model="histroyForm.updateTime"
                      type="datetime"
                      placeholder="請選擇時間"
                      value-format="YYYY-MM-DD HH:mm:ss"
                      :disabled="true"
                    />
                  </el-form-item>
                </el-col>
                <el-col :span="24">
                  <el-form-item label="組件描述:" prop="description">
                    <el-input
                      type="textarea"
                      v-model="histroyForm.description"
                      :rows="5"
                      :max-rows="5"
                      placeholder="請輸入組件描述"
                      :disabled="true"
                    ></el-input>
                  </el-form-item>
                </el-col>
                <el-col :span="24">
                  <el-form-item label="更新日志:" prop="publishLog">
                    <el-input
                      type="textarea"
                      v-model="histroyForm.publishLog"
                      :rows="5"
                      :max-rows="5"
                      placeholder="請輸入更新日志"
                      :disabled="true"
                    ></el-input>
                  </el-form-item>
                </el-col>
              </el-row>
            </el-form>
          </el-drawer>
        </div>
      </template>
      <script setup lang="ts">
      import { ref, reactive, onMounted, nextTick, watch } from "vue";
      import type { FormInstance, FormRules } from "element-plus";
      import Preview from "@/components/VueSfcEditor/preview/index.vue";
      import {
        Postcard,
        Monitor,
        Search,
        Download,
        Right,
        CirclePlusFilled,
        RemoveFilled,
        BottomLeft,
      } from "@element-plus/icons-vue";
      import ComponentManageAPI from "@/api/modules/componentManage.api";
      import { parse, compileScript, compileTemplate, compileStyle } from "@vue/compiler-sfc";
      import { ElMessage } from "element-plus";
      
      import { marked } from "marked";
      import "highlight.js/styles/github.css";
      import hljs from "highlight.js";
      
      const loading = ref(false);
      const props = defineProps({
        componentForm: {
          type: Object,
          default: () => ({}),
        },
        componentModel: {
          type: Boolean,
          default: false,
        },
      });
      
      const tab = ref("code");
      
      const editStatus = ref("");
      
      watch(
        () => props.componentModel,
        (val) => {
          if (val) {
            tab.value = "code";
            getCode("");
          }
        }
      );
      
      const disabledStatus = ref(false);
      
      watch(
        () => tab.value,
        (val) => {
          if (val === "preview") {
            getCode(editorRef.value.getEditValue());
          }
        }
      );
      
      marked.use({
        async: false,
        highlight: (code, lang) => {
          const validLang = hljs.getLanguage(lang) ? lang : "plaintext";
          return hljs.highlight(code, { language: validLang }).value;
        },
      });
      
      marked.setOptions({
        gfm: true, // 支持 GitHub 風格語法
        breaks: false, // 換行符處理
        pedantic: false, // 避免嚴格模式導致解析異常
      });
      
      interface RuleForm {
        id: string;
        name: string;
        type: string;
        tagStr: string;
        updateTime: string;
        warehouseAddress: string;
        // task: string;
        installType: string;
        install: string;
        description: string;
        installExample: string;
        version: string;
        images: string;
        publishLog: string;
        status: string;
        readme: string;
        ssJsConfig: {
          dataKey: string;
          dataValue: string;
        }[];
        ssCssConfig: {
          dataValue: string;
        }[];
        ssAllJsConfig: {
          dataKey: string;
          name: string;
          versionNum: string;
          dataValue: string;
        }[];
        ssAllCssConfig: {
          name: string;
          versionNum: string;
          dataValue: string;
        }[];
      }
      const ruleFormRef = ref<FormInstance>();
      const crudForm = reactive<RuleForm>({
        id: "",
        // name: "ksb-vis-timeline",
        name: "",
        type: "",
        tagStr: "",
        updateTime: "",
        // warehouseAddress: "/packages/vue3/ui-data",
        warehouseAddress: "",
        // task: "",
        installType: "NPM",
        install: "",
        description: "",
        installExample: "",
        // version: "0.0.0-no-version",
        version: "",
        images: "",
        publishLog: "",
        status: "0",
        readme: "",
        ssJsConfig: [
          // {
          //   dataKey: "vis",
          //   dataValue: "/dist/vis-timeline-graph2d.esm.js",
          // },
          {
            dataKey: "",
            dataValue: "",
          },
        ],
        ssCssConfig: [
          // {
          //   dataValue: "/dist/vis-timeline-graph2d.min.css",
          // },
          {
            dataValue: "",
          },
        ],
        ssAllJsConfig: [
          // {
          //   dataKey: "vue",
          //   name: "vue",
          //   versionNum: "3.4.19",
          //   dataValue: "/dist/vue.esm-browser.js",
          // },
          {
            dataKey: "",
            name: "",
            versionNum: "",
            dataValue: "",
          },
        ],
        ssAllCssConfig: [
          // {
          //   name: "",
          //   versionNum: "",
          //   dataValue: "",
          // },
          {
            name: "",
            versionNum: "",
            dataValue: "",
          },
        ],
      });
      const rules = reactive<FormRules<RuleForm>>({
        name: [{ required: true, message: "組件名稱不能為空", trigger: "blur" }],
        type: [{ required: true, message: "請選擇組件分類", trigger: "change" }],
        description: [{ required: true, message: "請輸入組件描述", trigger: "blur" }],
      });
      
      // const installTypes = ["NPM", "pnpm", "bun", "yarn"];
      const installTypes = ["NPM"];
      
      import { Delete, Download, Plus, ZoomIn } from "@element-plus/icons-vue";
      
      import type { UploadFile } from "element-plus";
      import edit from "@/views/demo/curd/config/edit";
      import { c } from "vite/dist/node/moduleRunnerTransport.d-DJ_mE5sf";
      import { editor } from "monaco-editor";
      import func from "vue-temp/vue-editor-bridge";
      
      const disabled = ref(false);
      
      const editorRef = ref<any>(null);
      
      // 添加 Monaco Editor 配置
      const editorOptions = {
        value: "",
        language: "html",
        theme: "vs-dark",
        automaticLayout: true,
        minimap: {
          enabled: false,
        },
        scrollBeyondLastLine: false,
        fontSize: 14,
        tabSize: 2,
        wordWrap: "on",
        formatOnPaste: true,
        formatOnType: true,
        suggestOnTriggerCharacters: true,
        acceptSuggestionOnEnter: "on",
        quickSuggestions: true,
        parameterHints: {
          enabled: true,
        },
        // 添加自定義配置以處理內存文件
        model: {
          uri: "inmemory://model/1",
          language: "html",
        },
      };
      
      const getCode = (code?: string) => {
        crudForm.installExample = code ?? "";
        state.code = code ?? "";
      };
      
      const state = reactive({
        // sfc 源代碼
        code: "",
        // code: props?.componentData?.content || DefaultCode.trim(),
        updateCode(code) {
          state.code = code;
        },
      
        // 編譯過程
        compile(code) {
          // 直接返回代碼內容,不進行 Vue 編譯
          return code;
        },
      });
      provide("store", state);
      // console.log(state);
      
      const treeprops = {
        label: "className",
        children: "childrenList",
        value: "id",
      };
      
      interface Tree {
        id: string;
        className: string;
        parentId: string;
        childrenList?: Tree[];
        classDesc: string;
        classCode: string;
      }
      
      const typeOptions = ref([]);
      function getTypeListFun() {
        ComponentManageAPI.getTypeList({}).then((res) => {
          const dataTree: Tree[] = JSON.parse(JSON.stringify(res));
          typeOptions.value = dataTree;
        });
      }
      
      const tagOptions = ref([]);
      function getTagListFun() {
        ComponentManageAPI.getTagList({}).then((res) => {
          tagOptions.value = [];
          res.forEach((item) => {
            tagOptions.value.push({
              value: item.tagName,
              label: item.tagName,
            });
          });
        });
      }
      
      const tagList = ref([]);
      
      function createTagFun() {
        // 提取B的value集合,使用Set提高檢索效率
        const BValueSet = new Set(tagOptions.value.map((item) => item.value));
        // 篩選A中存在但B中缺失的值
        const missingInB = tagList.value.filter((item) => !BValueSet.has(item));
        if (missingInB.length > 0) {
          ComponentManageAPI.createTag(missingInB.toString()).then((res) => {
            getTagListFun();
          });
        }
        crudForm.tagStr = tagList.value.toString();
      }
      
      function selectInstallTypeFun(type: string) {
        crudForm.installType = type;
      }
      
      let tableData = ref([]);
      const historyTagList = ref([]);
      
      const histroyForm = reactive({
        name: "",
        type: "",
        tagStr: "",
        description: "",
        installType: "",
        install: "",
        version: "",
        updateTime: "",
        publishLog: "",
      });
      const innerDrawer = ref(false);
      function viewLogs(row) {
        innerDrawer.value = true;
      
        histroyForm.name = row.name;
        histroyForm.type = row.type;
        histroyForm.tagStr = row.tagStr;
        histroyForm.description = row.description;
        histroyForm.installType = row.installType;
        histroyForm.install = row.install;
        histroyForm.version = row.version;
        histroyForm.updateTime = row.updateTime;
        histroyForm.publishLog = row.publishLog;
      
        historyTagList.value = row.tagStr.split(",");
      }
      function handleClose() {
        innerDrawer.value = false;
      }
      
      const getEditorCode = () => {
        crudForm.installExample = editorRef.value.getEditValue();
      };
      
      const componentShuoShow = ref(false);
      
      const UNPKGAddress = "http://10.22.0.120:4000";
      // 查看md
      function viewMd() {
        if (crudForm.name == "" || crudForm.version == "") {
          ElMessage({
            type: "error",
            message: "請填寫組件名稱和版本號后操作",
          });
          return;
        }
      
        fetchReadme(crudForm.name, crudForm.version).then((content) => {
          if (content) {
            nextTick(() => {
              const previewElement = document.getElementById("preview");
              if (previewElement) {
                previewElement.innerHTML = marked.parse(content);
                // 添加代碼塊樣式
                previewElement.querySelectorAll("pre code").forEach((block) => {
                  hljs.highlightElement(block);
                });
                componentShuoShow.value = true;
              } else {
                console.error("Preview element not found");
              }
            });
          }
        });
      }
      
      async function fetchReadme(packageName: string, version = "latest") {
        const url = UNPKGAddress + `/${packageName}@${version}/README.md`;
        try {
          const response = await fetch(url, { mode: "cors" });
          if (!response.ok) {
            ElMessage({
              type: "error",
              message: "HTTP錯誤" + ` ${response.status}`,
            });
          }
          const readmeContent = await response.text();
          console.log(readmeContent); // 輸出或處理內容
          return readmeContent;
        } catch (error) {
          ElMessage({
            type: "error",
            message: "文件獲取失敗",
          });
          return null;
        }
      }
      
      const importDisabled = ref(false);
      interface ResourceItem {
        type: string;
        name?: string;
        url: string;
      }
      const showExample = ref(false);
      const resources = ref<ResourceItem[]>([]);
      function importModel() {
        if (crudForm.name == "" || crudForm.version == "") {
          ElMessage({
            type: "error",
            message: "請填寫組件名稱和版本號后操作",
          });
          return;
        }
        if (
          !validateConfigs(
            crudForm.ssJsConfig,
            crudForm.ssCssConfig,
            crudForm.ssAllJsConfig,
            crudForm.ssAllCssConfig
          )
        ) {
          ElMessage({
            type: "error",
            message: "請正確填寫依賴后操作",
          });
          return;
        }
        
      resources.value = [];
        let list: any[] = [];
        crudForm.ssJsConfig.map((item) => {
          if (item.dataKey && item.dataValue) {
            list.push({
              type: "js",
              name: item.dataKey,
              url: UNPKGAddress + `/${crudForm.name}@${crudForm.version}${item.dataValue}`,
            });
          }
        });
        crudForm.ssAllJsConfig.map((item) => {
          if (item.dataKey && item.name && item.versionNum && item.dataValue) {
            list.push({
              type: "js",
              name: item.dataKey,
              url: UNPKGAddress + `/${item.name}@${item.versionNum}${item.dataValue}`,
            });
          }
        });
        crudForm.ssCssConfig.map((item) => {
          if (item.dataValue) {
            list.push({
              type: "css",
              url: UNPKGAddress + `/${crudForm.name}@${crudForm.version}${item.dataValue}`,
            });
          }
        });
        crudForm.ssAllCssConfig.map((item) => {
          if (item.name && item.versionNum && item.dataValue) {
            list.push({
              type: "css",
              url: UNPKGAddress + `/${item.name}@${item.versionNum}${item.dataValue}`,
            });
          }
        });
        
        resources.value = list;
        showExample.value = true;
      }
      
      async function fetchJsCss(packageName: string, version = "latest", dataValue: string) {
        const url = UNPKGAddress + `/${packageName}@${version}${dataValue}`;
        try {
          const response = await fetch(url, { mode: "cors" });
          if (!response.ok) {
            ElMessage({
              type: "error",
              message: "HTTP錯誤" + ` ${response.status}`,
            });
          }
          const readmeContent = await response.text();
          console.log(readmeContent); // 輸出或處理內容
          return readmeContent;
        } catch (error) {
          ElMessage({
            type: "error",
            message: "文件獲取失敗",
          });
          return null;
        }
      }
      
      function validateConfigs(
        ssJsConfig: any,
        ssCssConfig: any,
        ssAllJsConfig: any,
        ssAllCssConfig: any
      ): boolean {
        // ====================== 防御性校驗 ======================
        // 檢查輸入是否為數組
        if (
          !Array.isArray(ssJsConfig) ||
          !Array.isArray(ssCssConfig) ||
          !Array.isArray(ssAllJsConfig) ||
          !Array.isArray(ssAllCssConfig)
        ) {
          throw new Error("配置必須為數組類型");
        }
      
        // ====================== 核心校驗邏輯 ======================
        // 校驗規則1:檢查 ssJsConfig 中的每條數據是否符合互斥規則
        const isJsConfigValid = ssJsConfig.every((item) => {
          // 確保處理字符串類型 (防御非字符串輸入)
          const dataKey = String(item?.dataKey ?? "").trim();
          const dataValue = String(item?.dataValue ?? "").trim();
      
          // 互斥規則:兩個字段要么同時為空,要么同時有值
          return (dataKey === "" && dataValue === "") || (dataKey !== "" && dataValue !== "");
        });
      
        const isAllJsConfigValid = ssAllJsConfig.every((item) => {
          // 確保處理字符串類型 (防御非字符串輸入)
          const dataKey = String(item?.dataKey ?? "").trim();
          const name = String(item?.name ?? "").trim();
          const versionNum = String(item?.versionNum ?? "").trim();
          const dataValue = String(item?.dataValue ?? "").trim();
      
          // 互斥規則:兩個字段要么同時為空,要么同時有值
          return (
            (dataKey === "" && name === "" && versionNum === "" && dataValue === "") ||
            (dataKey !== "" && name !== "" && versionNum !== "" && dataValue !== "")
          );
        });
      
        // 規則1不通過:直接返回錯誤
        if (!isJsConfigValid || !isAllJsConfigValid) {
          return false;
        }
      
        // ====================== 最終結果 ======================
        return true; // 或根據業務需求返回其他標識
      }
      
      // 組件內增刪
      function deleteSsJs(ssJsConfig: any) {
        let index = crudForm.ssJsConfig.indexOf(ssJsConfig);
        if (index !== -1) {
          crudForm.ssJsConfig.splice(index, 1);
        }
      }
      
      function addSsJs() {
        crudForm.ssJsConfig.push({
          dataKey: "",
          dataValue: "",
        });
      }
      
      function deleteSsCss(ssCssConfig: any) {
        let index = crudForm.ssCssConfig.indexOf(ssCssConfig);
        if (index !== -1) {
          crudForm.ssCssConfig.splice(index, 1);
        }
      }
      
      function addSsCss() {
        crudForm.ssCssConfig.push({
          dataValue: "",
        });
      }
      
      // 全局組件增刪
      function deleteSsAllJs(ssJsConfig: any) {
        let index = crudForm.ssAllJsConfig.indexOf(ssJsConfig);
        if (index !== -1) {
          crudForm.ssAllJsConfig.splice(index, 1);
        }
      }
      
      function addSsAllJs() {
        crudForm.ssAllJsConfig.push({
          dataKey: "",
          name: "",
          versionNum: "",
          dataValue: "",
        });
      }
      
      function deleteSsAllCss(ssCssConfig: any) {
        let index = crudForm.ssAllCssConfig.indexOf(ssCssConfig);
        if (index !== -1) {
          crudForm.ssAllCssConfig.splice(index, 1);
        }
      }
      
      function addSsAllCss() {
        crudForm.ssAllCssConfig.push({
          name: "",
          versionNum: "",
          dataValue: "",
        });
      }
      
      onMounted(() => {
        getTypeListFun();
        getTagListFun();
      });
      
      function getVersionListFun() {
        ComponentManageAPI.getVersionList({ componentId: crudForm.id }, 999, 1).then((res) => {
          tableData.value = res.records;
        });
      }
      // 暴露給父組件的方法和屬性
      defineExpose({
        ruleFormRef,
        crudForm,
        state,
        getEditorCode,
        setFormData: (data: any) => {
          if (data) {
            crudForm.id = data.id || "";
            crudForm.name = data.name || "";
            crudForm.type = data.type || "";
            crudForm.tagStr = data.tagStr || "";
            crudForm.installType = data.installType || "";
            crudForm.install = data.install || "";
            crudForm.description = data.description || "";
            crudForm.installExample = data.installExample || "";
            crudForm.version = data.version || "";
            crudForm.images = data.images || "";
            crudForm.publishLog = data.publishLog || "";
            crudForm.status = "0";
            crudForm.updateTime = data.updateTime || "";
      
            crudForm.warehouseAddress = data.warehouseAddress || "";
            crudForm.ssJsConfig = data.ssJsConfig || [{ dataKey: "", dataValue: "" }];
            crudForm.ssCssConfig = data.ssCssConfig || [{ dataValue: "" }];
            crudForm.ssAllJsConfig = data.ssAllJsConfig || [
              { dataKey: "", name: "", versionNum: "", dataValue: "" },
            ];
            crudForm.ssAllCssConfig = data.ssAllCssConfig || [
              { name: "", versionNum: "", dataValue: "" },
            ];
      
            tagList.value = [];
            historyTagList.value = [];
      
            // if (data.type) {
            //   editypeList.value = [data.type];
            // }
      
            if (data.tagStr) {
              tagList.value = data.tagStr.split(",");
            }
      
            if (data.status == "view") {
              editStatus.value = "view";
              disabledStatus.value = true;
              viewMd();
              getVersionListFun();
              importModel();
            } else if (data.status == "add") {
              editStatus.value = "add";
              disabledStatus.value = false;
              componentShuoShow.value = false;
              resources.value = [];
              importDisabled.value = false;
              showExample.value = false;
            } else {
              editStatus.value = "edit";
              getVersionListFun();
              importModel();
              disabledStatus.value = false;
              componentShuoShow.value = false;
              importDisabled.value = true;
              showExample.value = true;
            }
          }
        },
      });
      </script>
      <style scoped lang="scss">
      .crud-page {
        position: relative;
        height: 100%;
      }
      ::v-deep .crudForm {
        .el-form-item {
          width: 100%;
          margin-right: 0;
          margin-bottom: 18px;
        }
        .el-cascader {
          width: 100%;
        }
      }
      
      .install-instructions,
      .install-example {
        display: flex;
        justify-content: center;
        margin-bottom: 18px;
        position: relative;
        span {
          width: 100px;
          text-align: right;
          font-size: 14px;
          display: inline-block;
          padding-right: 12px;
          color: #606266;
        }
      }
      ::v-deep .install-picture {
        display: flex;
        justify-content: center;
        margin-bottom: 0;
        .el-input__inner {
          cursor: pointer;
        }
      }
      .installDiv {
        border: 1px solid #dcdfe6;
        border-radius: 5px;
        display: inline-block;
        width: calc(100% - 100px);
        height: 110px;
        padding: 10px;
        .el-form-item {
          margin-bottom: 0;
        }
      }
      ::v-deep .exampleDiv {
        border: 1px solid #dcdfe6;
        border-radius: 5px;
        display: inline-block;
        width: calc(100% - 100px);
        height: 500px;
        padding: 10px;
        .el-tabs__content {
          height: 435px;
          padding: 10px;
          overflow: auto;
        }
      }
      .exampleText {
        display: inline-block;
        width: calc(100% - 100px);
        color: #606266;
      }
      ::v-deep textarea {
        resize: none; /* 禁止調整大小 */
      }
      
      .exampleDiv > .el-tabs__content {
        padding: 32px;
        color: #6b778c;
        font-size: 32px;
        font-weight: 600;
      }
      .exampleDiv .custom-tabs-label .el-icon {
        vertical-align: middle;
      }
      .exampleDiv .custom-tabs-label span {
        vertical-align: middle;
        margin-left: 4px;
        width: auto;
      }
      
      ::v-deep .viewShowClass {
        p {
          padding-left: 20px;
          margin: 0 0 20px;
          font-size: 16px;
          font-weight: bold;
          color: #202020;
        }
        .el-input {
          width: 100%;
        }
      }
      ::v-deep .history-table {
        // width: 100%;
        padding: 0 20px;
        margin: 20px 0;
        p {
          // padding-left:20px;
          margin: 0 0 20px;
          font-size: 16px;
          font-weight: bold;
          color: #202020;
        }
        .el-table__header th {
          background-color: #f6f6f6;
        }
      }
      .innerDrawer {
        .el-form-item {
          margin: 0 0 18px 0;
          width: 100%;
          .el-input {
            width: 100%;
          }
          .el-select {
            width: 100%;
          }
        }
        .install-instructions {
          .el-input {
            width: 100%;
          }
        }
      }
      ::v-deep .componentMsg {
        p {
          padding-left: 20px;
          margin: 0 0 20px;
          font-size: 16px;
          font-weight: bold;
          color: #202020;
          width: 100%;
        }
        .el-input {
          width: 100%;
        }
      }
      ::v-deep .componentExample {
        p {
          padding-left: 20px;
          margin: 0 0 20px;
          font-size: 16px;
          font-weight: bold;
          color: #202020;
          width: 100%;
        }
        .el-input {
          width: 100%;
        }
      }
      ::v-deep .componentShuo {
        p {
          padding-left: 20px;
          margin: 0 0 20px;
          font-size: 16px;
          font-weight: bold;
          color: #202020;
          width: 100%;
        }
        .el-input {
          width: 100%;
        }
      }
      .el-tab-pane {
        height: 100%;
        overflow-y: auto;
      }
      #preview {
        padding: 20px;
        border: 1px solid #dcdfe6;
      }
      </style>
      

      preview/index.vue

      點擊查看代碼
      <template>
        <div class="preview" ref="preview"></div>
      </template>
      
      <script setup>
      import { ref, onMounted, watch, inject, onUnmounted } from "vue";
      import PreviewTemplate from "./preview-template.html?raw";
      
      const preview = ref();
      
      let proxy = ref(null);
      
      // 注入store
      const store = inject("store");
      
      const template = ref(null)
      
      const props = defineProps({
        state: {
          type: Object,
          default: () => ({}),
        },
        // 修改為更通用的資源引入配置
        resources: {
          type: Array,
          default: () => [
            // { type: 'js', name: 'vue', url: 'https://unpkg.com/vue@3.4.21/dist/vue.esm-browser.js' },
            // { type: 'js', name: 'element-plus', url: 'https://unpkg.com/element-plus@2.4.1/dist/index.full.mjs' },
            // { type: 'css', url: 'https://unpkg.com/element-plus@2.4.1/dist/index.css' }
          ]
        }
      });
      
      watch(
        () => props.state,
        (newVal) => {
          // console.log('444444444444444444444')
          // console.log(template)
          setTimeout(() => {
            proxy.value = createProxy(template.value);
          }, 100)
        },
        { deep: true, immediate: true}
      );
      
      // 監聽resources變化,重新創建沙盒
      watch(
        () => props.resources,
        async () => {
          await createSandbox();
          // 重新創建代理
          proxy.value = createProxy(template.value);
        },
        { deep: true }
      );
      
      // 創建沙盒
      function createSandbox () {
        // 清理舊的 iframe
        if (template.value) {
          template.value.remove();
          template.value = null;
        }
        
        template.value = document.createElement("iframe");
        template.value.setAttribute("frameborder", "0");
        template.value.style = "width: 100%; height:100%";
        
        // 創建基礎HTML內容
        const baseHtml = PreviewTemplate;
        
        // 動態生成importmap
        const jsResources = props.resources.filter(resource => resource.type === 'js');
        const importMapScript = `<script type="importmap">
          {
            "imports": {
              ${jsResources.map(resource => `"${resource.name}": "${resource.url}"`).join(',\n        ')}
            }
          }
        <\/script>`;
        
        // 動態生成CSS鏈接
        const cssResources = props.resources.filter(resource => resource.type === 'css');
        const cssLinks = cssResources.map(resource => 
          `<link href="${resource.url}" rel="stylesheet" type="text/css" />`
        ).join('\n  ');
        
        // 組合最終的HTML內容
        template.value.srcdoc = baseHtml.replace('</head>', `${importMapScript}\n  ${cssLinks}\n</head>`);
        
        preview.value.appendChild(template.value);
      
        // 等待iframe加載完成
        return new Promise((resolve) => {
          template.value.onload = () => {
            resolve();
          };
        });
      }
      
      // 創建代理,用于監聽code 變化,告訴沙盒重新渲染
      function createProxy (iframe) {
        let _iframe = iframe;
      
        const stopWatch = watch(() => store?.code, (newCode) => {
          if (newCode) {
            compile(newCode);
          }
        }, { immediate: true });
      
        function compile (code) {
          if (!code?.trim()) {
            code = "<script setup> // <\/script>"
          }
      
          const compiledCode = store?.compile(code);
      
          if (_iframe?.contentWindow) {
            _iframe.contentWindow.postMessage(
              { type: "eval", code: compiledCode },
              "*"
            );
          }
        }
      
        // 銷毀沙盒
        function destory () {
          _iframe?.remove();
          _iframe = null;
          stopWatch?.();
        }
      
        return {
          compile,
          destory,
        };
      }
      
      onMounted(async () => {
        await createSandbox();
        proxy.value = createProxy(template.value);
      });
      
      onUnmounted(() => proxy.value?.destory());
      </script>
      
      <style scoped>
      .preview {
        width: 100%;
        height: 100%;
        overflow: hidden;
      }
      </style>
      

      html預覽html

      點擊查看代碼
      <!doctype html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <style>
            body {
              margin: 0;
              padding: 20px;
            }
            #preview-content {
              width: 100%;
              height: 100%;
            }
          </style>
        </head>
        <body>
          <div id="preview-content"></div>
      
          <script>
            // 監聽 message,preview/index.vue 通過 postmessage 傳遞需要執行的代碼
            window.addEventListener("message", ({ data }) => {
              const { type, code } = data;
              if (type === "eval") {
                handleEval(code);
              }
            });
      
            // 處理需要執行的代碼
            function handleEval(code) {
              const previewContent = document.getElementById('preview-content');
              if (!previewContent) return;
      
              try {
                // 清空之前的內容
                previewContent.innerHTML = '';
      
                // 分離 HTML 和 JavaScript 代碼
                const htmlMatch = code.match(/<div[^>]*>[\s\S]*?<\/div>/);
                const scriptMatches = code.matchAll(/<script[^>]*>([\s\S]*?)<\/script>/g);
                const scriptSrcMatches = code.matchAll(/<script[^>]*src="([^"]*)"[^>]*>/g);
      
                // 創建一個臨時的容器來處理腳本
                const tempContainer = document.createElement('div');
                tempContainer.style.display = 'none';
                document.body.appendChild(tempContainer);
      
                // 先添加所有外部腳本
                const externalScripts = [];
                for (const match of scriptSrcMatches) {
                  const script = document.createElement('script');
                  script.src = match[1];
                  externalScripts.push(script);
                  tempContainer.appendChild(script);
                }
      
                // 等待外部腳本加載完成
                Promise.all(externalScripts.map(script => {
                  return new Promise((resolve, reject) => {
                    script.onload = resolve;
                    script.onerror = reject;
                  });
                })).then(() => {
                  // 渲染 HTML 內容
                  if (htmlMatch) {
                    previewContent.innerHTML = htmlMatch[0];
                  } else {
                    // 如果沒有找到 HTML 內容,直接設置整個代碼
                    previewContent.innerHTML = code;
                  }
      
                  // 添加并執行所有內聯腳本
                  for (const match of scriptMatches) {
                    const scriptContent = match[1];
                    const isModule = match[0].includes('type="module"');
                    const hasImport = scriptContent.includes('import ');
      
                    if (isModule || hasImport) {
                      // 對于 ES 模塊,創建新的腳本標簽
                      const script = document.createElement('script');
                      script.type = 'module';
                      // 如果使用全局 Vue,需要修改 import 語句
                      const modifiedContent = scriptContent.replace(
                        /import\s*{\s*([^}]+)\s*}\s*from\s*['"]vue['"]/g,
                        'const { $1 } = Vue'
                      );
                      script.textContent = modifiedContent;
                      previewContent.appendChild(script);
                    } else {
                      // 對于全局腳本,使用 Function 構造器執行
                      try {
                        new Function(scriptContent)();
                      } catch (error) {
                        console.error('Error executing script:', error);
                        previewContent.innerHTML = `<pre>Error executing script: ${error.message}</pre>`;
                      }
                    }
                  }
      
                  // 清理臨時容器
                  tempContainer.remove();
                }).catch(error => {
                  console.error('Error loading scripts:', error);
                  previewContent.innerHTML = `<pre>Error loading scripts: ${error.message}</pre>`;
                  tempContainer.remove();
                });
              } catch (error) {
                console.error('Error displaying content:', error);
                previewContent.innerHTML = `<pre>Error: ${error.message}</pre>`;
              }
            }
          </script>
        </body>
      </html>
      
      

      MonacoEditor配置

      點擊查看代碼
      <template>
        <div v-show="visible" class="youlai-editor-wrapper" ref="monacoEdit"></div>
      </template>
      
      <script setup>
      import { toRaw, onUnmounted, onMounted, watch } from "vue";
      // 導入monaco編輯器
      import * as monaco from "monaco-editor/esm/vs/editor/editor.main.js";
      import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution";
      
      // 編輯器容器div
      const monacoEdit = ref(null);
      // 編輯器實列
      const editor = ref(null);
      
      const emits = defineEmits(["update:modelValue"]);
      const props = defineProps({
        modelValue: {
          type: String,
          default: "",
        },
        visible: {
          type: Boolean,
          default: false,
        },
      });
      
      // 注冊 Vue 語言和語法高亮
      const registerVueLanguage = () => {
        monaco.languages.register({ id: "vue" });
        monaco.languages.setMonarchTokensProvider("vue", {
          tokenizer: {
            root: [
              [/<template>/, "keyword"],
              [/<script>/, "keyword"],
              [/<style>/, "keyword"],
              [/<\/template>/, "keyword"],
              [/<\/script>/, "keyword"],
              [/<\/style>/, "keyword"],
              [/@\w+/, "decorator"],
              [/{{[^}]+}}/, "variable"],
              [/v-\w+/, "directive"],
              [/:[a-zA-Z0-9_-]+/, "binding"],
              [/@[a-zA-Z0-9_-]+/, "event"],
            ],
          },
        });
      };
      
      // 配置 TypeScript 編譯選項
      const configureTypeScript = () => {
        monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
          target: monaco.languages.typescript.ScriptTarget.ES2020,
          module: monaco.languages.typescript.ModuleKind.ESNext,
          moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
          strict: true,
          esModuleInterop: true,
          skipLibCheck: true,
          allowSyntheticDefaultImports: true,
          sourceMap: true,
          jsx: monaco.languages.typescript.JsxEmit.React,
          baseUrl: ".",
          paths: {
            "@/*": ["src/*"],
          },
        });
        // monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
        //   allowNonTsExtensions: true,
        // })
      };
      
      // 添加 Vue 3 類型定義
      const addVueTypeDefinitions = () => {
        monaco.languages.typescript.typescriptDefaults.addExtraLib(
          `
          // Vue 3 核心類型定義
          declare module 'vue' {
            import { ComponentOptions } from 'vue';
            
            export declare const createApp: (rootComponent: ComponentOptions<any>) => any;
            
            export interface DefineComponent<
              PropsOrPropOptions = {},
              RawBindings = {},
              D = {},
              C extends ComputedOptions = ComputedOptions,
              M extends MethodOptions = MethodOptions,
              Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
              Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
              E extends EmitsOptions = EmitsOptions,
              EE extends string = string,
              PropsDefaults = PropsDefaultsType<PropsOrPropOptions>
            > {
              // Vue 組件類型定義
            }
            
            // 其他 Vue 3 類型...
          }
          
          // Vue Router 類型定義
          declare module 'vue-router' {
            import { Router, RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
            
            export declare const createRouter: (options: any) => Router;
            export declare const createWebHistory: () => any;
            
            // 其他 Vue Router 類型...
          }
        `,
          "node_modules/@types/vue/index.d.ts"
        );
      };
      
      const initEdit = () => {
        setTimeout(() => {
          if (monacoEdit.value) {
            // 注冊 Vue 語言支持
            registerVueLanguage();
            // 配置 TypeScript
            configureTypeScript();
            // 添加 Vue 類型定義
            addVueTypeDefinitions();
      
            // 創建模型
            const model = monaco.editor.createModel(
              props.modelValue,
              "html",
              monaco.Uri.parse("inmemory://model/1")
            );
      
            // 創建編輯器實列
            editor.value = monaco.editor.create(monacoEdit.value, {
              model,
              theme: "vs-light", // 官方自帶三種主題vs, hc-black, or vs-dark
              autoIndex: true,
              language: "html", // 語言類型
              tabCompletion: "on",
              cursorSmoothCaretAnimation: true,
              minimap: {
                enabled: true,
              },
              formatOnPaste: false,
              mouseWheelZoom: true,
              folding: true, //代碼折疊
              selectOnLineNumbers: true, // 顯示行號
              wordWrap: "on", // 代碼超出換行
              overviewRulerBorder: false, // 不要滾動條的邊框
              foldingHighlight: true, // 折疊等高線
              foldingStrategy: "indentation", // 折疊方式  auto | indentation
              showFoldingControls: "always", // 是否一直顯示折疊 always | mouseover
              disableLayerHinting: true, // 等寬優化
              emptySelectionClipboard: false, // 空選擇剪切板
              selectionClipboard: false, // 選擇剪切板
              automaticLayout: true, // 自動布局
              codeLens: false, // 代碼鏡頭
              scrollBeyondLastLine: false, // 滾動完最后一行后再滾動一屏幕
              colorDecorators: true, // 顏色裝飾器
              accessibilitySupport: "off", // 輔助功能支持  "auto" | "off" | "on"
              lineNumbers: "on", // 行號 取值: "on" | "off" | "relative" | "interval" | function
              lineNumbersMinChars: 5, // 行號最小字符   number
              enableSplitViewResizing: false,
              readOnly: false, //是否只讀  取值 true | false
              scrollbar: {
                useShadows: false,
                verticalHasArrows: false,
                horizontalHasArrows: false,
                vertical: "visible",
                horizontal: "visible",
                verticalScrollbarSize: 10,
                horizontalScrollbarSize: 10,
                arrowSize: 30,
                mouseWheelScrollSensitivity: 1,
                alwaysConsumeMouseWheel: false,
              },
            });
      
            // 編輯器內容變更時回調
            editor.value.onDidChangeModelContent(() => {
              let code = toRaw(editor.value).getValue();
              emits("update:modelValue", code);
            });
      
            // 添加被動事件監聽器
            const editorElement = monacoEdit.value;
            if (editorElement) {
              editorElement.addEventListener("wheel", () => {}, { passive: true });
              editorElement.addEventListener("touchstart", () => {}, { passive: true });
              editorElement.addEventListener("touchmove", () => {}, { passive: true });
            }
          }
        }, 100);
      };
      
      const getEditValue = () => {
        return toRaw(editor.value).getValue();
      };
      
      defineExpose({ getEditValue });
      
      // 添加對 visible prop 的監聽
      watch(
        () => props.visible,
        (newVal) => {
          if (newVal) {
            // 當對話框顯示時,確保編輯器已初始化
            // if (!editor.value) {
            //   initEdit();
            // }
            if (editor.value) {
              const rawEditor = toRaw(editor.value);
              const model = rawEditor.getModel();
      
              // 分步釋放資源
              rawEditor.dispose();
              if (model && !model.isDisposed()) {
                model.dispose();
              }
      
              initEdit();
            }else{
              initEdit();
            }
          }
        },
        { immediate: true }
      );
      
      
      onUnmounted(() => {
        if (editor.value) {
          const rawEditor = toRaw(editor.value);
          const model = rawEditor.getModel();
      
          // 分步釋放資源
          rawEditor.dispose();
          if (model && !model.isDisposed()) {
            model.dispose();
          }
        }
      });
      </script>
      
      <style scoped>
      .youlai-editor-wrapper {
        width: 100%;
      }
      </style>
      
      
      posted on 2025-05-28 11:11  溫華從此不練劍  閱讀(698)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 日本免费一区二区三区| 国产成人精品无人区一区| 澄迈县| 91麻豆精品国产91久| 精品一区二区中文字幕| 国产一区视频一区欧美| 国产成人精品中文字幕| 国产精品无遮挡猛进猛出| 本道久久综合无码中文字幕 | 欧美亚洲另类制服卡通动漫| 国产成人高清亚洲综合| 欧美孕妇乳喷奶水在线观看| 国产普通话对白刺激| 国产成人无码av大片大片在线观看| 久久精品国产99国产精品严洲 | 国产女人在线视频| 国产av亚洲精品ai换脸电影| 亚洲中文久久久久久精品国产| 中文字幕av一区二区三区| 国产成人亚洲综合图区| 免费观看全黄做爰大片| 久久碰国产一区二区三区| 美女黄网站人色视频免费国产| 亚洲一区二区三区水蜜桃| 他掀开裙子把舌头伸进去添视频| 免费人成年激情视频在线观看| 国产精品中文字幕久久| 末发育娇小性色xxxxx视频| 久久青青草原精品国产app| 国产成人欧美日本在线观看| 内射中出无码护士在线| 欧美老少配性行为| 十四以下岁毛片带血a级| 在国产线视频A在线视频| 国产原创自拍三级在线观看| 一区二区三区人妻无码| 成人免费A级毛片无码片2022| 芳草地社区在线视频| 亚洲 日本 欧洲 欧美 视频| 国产蜜臀视频一区二区三区| 亚洲精品一区国产|