From 7f34930969354eac5475a7632a4651302f664c74 Mon Sep 17 00:00:00 2001
From: rookie4show <rookie4show@gmail.com>
Date: Sun, 23 Feb 2025 23:41:15 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=88=AA=E5=9B=BE=E9=A2=84?=
 =?UTF-8?q?=E8=A7=88=E5=8A=9F=E8=83=BD,=E5=B9=B6=E4=BD=BF=E7=94=A8Composit?=
 =?UTF-8?q?ion=20API=E9=87=8D=E5=86=99=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package-lock.json      |  61 ++++++++--
 package.json           |   3 +-
 src/App.vue            |   2 +-
 src/components/Yys.vue | 246 +++++++++++++++++++++++------------------
 src/main.js            |   1 +
 5 files changed, 195 insertions(+), 118 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 7ff81a0..c857629 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,8 @@
       "version": "0.0.0",
       "dependencies": {
         "@element-plus/icons-vue": "^2.3.1",
-        "element-plus": "^2.4.3",
+        "element-plus": "^2.9.1",
+        "html2canvas": "^1.4.1",
         "vue": "^3.3.10",
         "vuedraggable": "^4.1.0"
       },
@@ -1045,6 +1046,14 @@
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
     "node_modules/big-integer": {
       "version": "1.6.52",
       "resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.52.tgz",
@@ -1166,6 +1175,14 @@
         "node": ">= 8"
       }
     },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/cssesc": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@@ -1184,9 +1201,9 @@
       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
     },
     "node_modules/dayjs": {
-      "version": "1.11.10",
-      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz",
-      "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+      "version": "1.11.13",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
     },
     "node_modules/debug": {
       "version": "4.3.4",
@@ -1261,9 +1278,9 @@
       }
     },
     "node_modules/element-plus": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.4.3.tgz",
-      "integrity": "sha512-b3q26j+lM4SBqiyzw8HybybGnP2pk4MWgrnzzzYW5qKQUgV6EG1Zg7nMCfgCVccI8tNvZoTiUHb2mFaiB9qT8w==",
+      "version": "2.9.1",
+      "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.9.1.tgz",
+      "integrity": "sha512-9Agqf/jt4Ugk7EZ6C5LME71sgkvauPCsnvJN12Xid2XVobjufxMGpRE4L7pS4luJMOmFAH3J0NgYEGZT5r+NDg==",
       "dependencies": {
         "@ctrl/tinycolor": "^3.4.1",
         "@element-plus/icons-vue": "^2.3.1",
@@ -1273,7 +1290,7 @@
         "@types/lodash-es": "^4.17.6",
         "@vueuse/core": "^9.1.0",
         "async-validator": "^4.2.5",
-        "dayjs": "^1.11.3",
+        "dayjs": "^1.11.13",
         "escape-html": "^1.0.3",
         "lodash": "^4.17.21",
         "lodash-es": "^4.17.21",
@@ -1753,6 +1770,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/human-signals": {
       "version": "4.3.1",
       "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-4.3.1.tgz",
@@ -2614,6 +2643,14 @@
         "node": "^14.18.0 || >=16.0.0"
       }
     },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
@@ -2692,6 +2729,14 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "dev": true
     },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "node_modules/vite": {
       "version": "5.0.9",
       "resolved": "https://registry.npmmirror.com/vite/-/vite-5.0.9.tgz",
diff --git a/package.json b/package.json
index 8188588..200f44a 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,8 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.3.1",
-    "element-plus": "^2.4.3",
+    "element-plus": "^2.9.1",
+    "html2canvas": "^1.4.1",
     "vue": "^3.3.10",
     "vuedraggable": "^4.1.0"
   },
diff --git a/src/App.vue b/src/App.vue
index 25ea34b..0fce184 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -3,7 +3,7 @@ import Yys from './components/Yys.vue'
 </script>
 
 <template>
-  <main>
+  <main id="main-container">
     <Yys />
   </main>
 </template>
diff --git a/src/components/Yys.vue b/src/components/Yys.vue
index 9bdb8a6..772a936 100644
--- a/src/components/Yys.vue
+++ b/src/components/Yys.vue
@@ -1,18 +1,18 @@
 <template>
   <ShikigamiSelect
-      :showSelectShikigami="showSelectShikigami"
-      :currentShikigami="currentShikigami"
+      :showSelectShikigami="state.showSelectShikigami"
+      :currentShikigami="state.currentShikigami"
       @closeSelectShikigami="closeSelectShikigami"
       @updateShikigami="updateShikigami"
   />
 
   <ShikigamiProperty
-      :showProperty="showProperty"
-      :currentShikigami="currentShikigami"
+      :showProperty="state.showProperty"
+      :currentShikigami="state.currentShikigami"
       @closeProperty="closeProperty"
       @updateProperty="updateProperty"
   />
-  <draggable :list="groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;"
+  <draggable :list="state.groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;"
              handle=".drag-handle">
 
     <template #item="{ element: group, index: groupIndex }">
@@ -71,115 +71,145 @@
     <!--    </el-row>-->
   </draggable>
   <div style="margin: 20px">
-    <span>配置结果</span>
+
+    <!-- 现有的代码 -->
+    <div style="margin: 20px">
+      <span>配置结果</span>
+      <!-- 触发截图的按钮 -->
+      <button @click="prepareCapture">生成截图</button>
+    </div>
+
+    <!-- 预览弹窗 -->
+    <el-dialog id="preview-container" v-model="state.previewVisible" width="50%" :before-close="handleClose">
+      <img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; height: auto;" />
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="state.previewVisible = false">取 消</el-button>
+        <el-button type="primary" @click="downloadImage">下 载</el-button>
+      </span>
+    </el-dialog>
+
   </div>
 </template>
 
-<script>
-import shikigamiData from "../data/Shikigami.json";
-
-import {ref, reactive, toRefs} from "vue";
+<script setup>
+import { ref, reactive, toRefs } from 'vue';
 import draggable from 'vuedraggable';
+import ShikigamiSelect from './ShikigamiSelect.vue';
+import ShikigamiProperty from './ShikigamiProperty.vue';
+import html2canvas from 'html2canvas';
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import shikigamiData from '../data/Shikigami.json';
+const dialogTableVisible = ref(false)
+// 定义响应式数据
+const state = reactive({
+  showSelectShikigami: false,
+  showProperty: false,
+  groups: [
+    [{}, {}, {}, {}, {}],
+    [{}, {}, {}, {}, {}]
+  ],
+  groupIndex: 0,
+  positionIndex: 0,
+  currentShikigami: {},
+  previewImage: null, // 用于存储预览图像的数据URL
+  previewVisible: false, // 控制预览弹窗的显示状态
+});
+
+// 定义方法
+const closeSelectShikigami = () => {
+  console.log("close select ====");
+  state.showSelectShikigami = false;
+  state.currentShikigami = {};
+};
+
+const editShikigami = (groupIndex, positionIndex) => {
+  console.log("==== 选择式神 ===", groupIndex, positionIndex);
+  state.showSelectShikigami = true;
+  state.groupIndex = groupIndex;
+  state.positionIndex = positionIndex;
+};
+
+const updateShikigami = (shikigami) => {
+  console.log("parent====> ", shikigami);
+  state.showSelectShikigami = false;
+
+  const oldProperties = state.groups[state.groupIndex][state.positionIndex].properties;
+  state.groups[state.groupIndex][state.positionIndex] = shikigami;
+  state.groups[state.groupIndex][state.positionIndex].properties = oldProperties;
+};
+
+const editProperties = (groupIndex, positionIndex) => {
+  state.showProperty = true;
+  state.groupIndex = groupIndex;
+  state.positionIndex = positionIndex;
+  state.currentShikigami = state.groups[groupIndex][positionIndex];
+  console.log("currentShikigami", JSON.stringify(state.currentShikigami));
+};
+
+const closeProperty = () => {
+  state.showProperty = false;
+  state.currentShikigami = state.groups[state.groupIndex][state.positionIndex];
+};
+
+const updateProperty = (property) => {
+  state.showProperty = false;
+  state.currentShikigami = {};
+  state.groups[state.groupIndex][state.positionIndex].properties = property;
+};
+
+const removeGroupElement = (groupIndex, positionIndex) => {
+  state.groups[groupIndex].splice(positionIndex, 1);
+};
+
+const removeGroup = (groupIndex) => {
+  state.groups.splice(groupIndex, 1);
+};
+
+const addGroup = () => {
+  state.groups.push([{}, {}, {}, {}, {}]);
+};
+
+const addGroupElement = (groupIndex) => {
+  state.groups[groupIndex].push({});
+};
+
+const prepareCapture = async () => {
+  state.previewVisible = true; // 显示预览弹窗
+
+  // 捕获页面元素并生成图片
+  try {
+    const element = document.querySelector('#main-container'); // 替换为要捕获的元素选择器
+    if (!element) {
+      console.error('Element with ID "main-container" not found.');
+      state.previewVisible = false;
+      return;
+    }
+
+    const canvas = await html2canvas(element);
+    state.previewImage = canvas.toDataURL();
+    if (!state.previewImage) {
+      console.error('Failed to generate image data URL.');
+      state.previewVisible = false;
+    }
+  } catch (error) {
+    console.error('Failed to capture screenshot', error);
+    state.previewVisible = false;
+  }
+};
+
+const downloadImage = () => {
+  if (state.previewImage) {
+    const link = document.createElement('a');
+    link.href = state.previewImage;
+    link.download = 'screenshot.png'; // 设置下载的文件名
+    link.click();
+    state.previewVisible = false; // 关闭预览弹窗
+  }
+};
 
-import ShikigamiSelect from "./ShikigamiSelect.vue";
-import ShikigamiProperty from "./ShikigamiProperty.vue";
-
-const showSelectShikigami = ref(false)
-const activeName = ref('first')
-
-
-export default {
-  data() {
-    return {
-      showSelectShikigami: false,
-      showProperty: false,
-      groups: [
-        [{}, {}, {}, {}, {}],
-        [{}, {}, {}, {}, {}]
-      ],
-      // items: [
-      //   {
-      //     "name": "妖刀姬",
-      //     "rarity": "SSR",
-      //     "avatar":"/assets/Shikigami/ssr/Yoto Hime.png"
-      //   },
-      //   {
-      //     "name": "妖刀姬",
-      //     "rarity": "SSR",
-      //     "avatar":"/assets/Shikigami/ssr/Yoto Hime.png"
-      //   },
-      //   {
-      //     "name": "妖刀姬",
-      //     "rarity": "SSR",
-      //     "avatar":"/assets/Shikigami/ssr/Yoto Hime.png"
-      //   }
-      // ],
-      groupIndex: 0,
-      positionIndex: 0,
-      currentShikigami: {},
-      drag: false,
-    };
-  },
-  components: {
-    draggable,
-    ShikigamiSelect,
-    ShikigamiProperty,
-
-  },
-
-  methods: {
-    closeSelectShikigami() {
-      console.log("close select ====");
-      this.showSelectShikigami = false;
-      this.currentShikigami = {};
-    },
-    editShikigami(groupIndex, positionIndex) {
-      console.log("==== 选择式神 ===", groupIndex, positionIndex);
-      this.showSelectShikigami = true;
-      this.groupIndex = groupIndex;
-      this.positionIndex = positionIndex;
-    },
-    updateShikigami(shikigami) {
-      console.log("parent====> ", shikigami);
-      this.showSelectShikigami = false;
-
-      const oldProperties = this.groups[this.groupIndex][this.positionIndex].properties;
-      this.groups[this.groupIndex][this.positionIndex] = shikigami;
-      this.groups[this.groupIndex][this.positionIndex].properties = oldProperties;
-      // this.items[this.index].name = shikigami.name;
-      // this.currentShikigami = {};
-    },
-    editProperties(groupIndex, positionIndex) {
-
-      this.showProperty = true;
-      this.groupIndex = groupIndex;
-      this.positionIndex = positionIndex;
-      this.currentShikigami = this.groups[this.groupIndex][this.positionIndex];
-      console.log("currentShikigami", JSON.stringify(this.currentShikigami));
-    },
-    closeProperty() {
-      this.showProperty = false;
-      this.currentShikigami = this.groups[this.groupIndex][this.positionIndex];
-    },
-    updateProperty(property) {
-      this.showProperty = false;
-      this.currentShikigami = {};
-      this.groups[this.groupIndex][this.positionIndex].properties = property;
-    },
-    removeGroupElement(groupIndex, positionIndex) {
-      this.groups[groupIndex].splice(positionIndex, 1);
-    },
-    removeGroup(groupIndex) {
-      this.groups.splice(groupIndex, 1);
-    },
-    addGroup() {
-      this.groups.push([{}, {}, {}, {}, {}]);
-    },
-    addGroupElement(groupIndex) {
-      this.groups[groupIndex].push({});
-    },
-  },
+const handleClose = (done) => {
+  state.previewImage = null; // 清除预览图像
+  done(); // 关闭弹窗
 };
 </script>
 
diff --git a/src/main.js b/src/main.js
index 9ddddb0..3ba4afd 100644
--- a/src/main.js
+++ b/src/main.js
@@ -3,6 +3,7 @@ import App from './App.vue'
 
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
+
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
 const app = createApp(App)