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)