diff --git a/src/components/ShikigamiSelect.vue b/src/components/ShikigamiSelect.vue index 0a1a581..dd78c9f 100644 --- a/src/components/ShikigamiSelect.vue +++ b/src/components/ShikigamiSelect.vue @@ -6,11 +6,19 @@ :before-close="cancel" > <span>当前选择式神:{{ current.name }}</span> + <div style="display: flex; align-items: center;"> + <el-input + placeholder="请输入内容" + v-model="searchText" + style="width: 200px; margin-right: 10px;" + /> + </div> <el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleClick" + editable > <el-tab-pane v-for="(rarity, index) in rarityLevels" @@ -20,7 +28,7 @@ > <div style="max-height: 600px; overflow-y: auto;"> <el-space wrap size="large"> - <div v-for="i in filterShikigamiByRarity(rarity.name)" :key="i.name"> + <div v-for="i in filterShikigamiByRarityAndSearch(rarity.name,searchText)" :key="i.name"> <el-button style="width: 100px; height: 100px;" @click.stop="confirm(i)" @@ -59,6 +67,7 @@ const props = defineProps({ const emit = defineEmits(['closeSelectShikigami', 'updateShikigami']) +const searchText = ref('') // 新增搜索文本 const activeName = ref('ALL') let current = ref({name:''}) const show = ref(false) @@ -96,13 +105,25 @@ const cancel = () => { const confirm = (shikigami: Shikigami) => { emit('updateShikigami', shikigami) + searchText.value='' + activeName.value='ALL' // cancel() } -const filterShikigamiByRarity = (rarity: string) => { - if (rarity.toLowerCase() === 'all') return shikigamiData - return shikigamiData.filter(item => - item.rarity.toLowerCase() === rarity.toLowerCase() - ) + +// 修改后的过滤函数 +const filterShikigamiByRarityAndSearch = (rarity: string, search: string) => { + let filteredList = shikigamiData; + if (rarity.toLowerCase() !== 'all') { + filteredList = filteredList.filter(item => + item.rarity.toLowerCase() === rarity.toLowerCase() + ); + } + if (search.trim() !== '') { + return filteredList.filter(item => + item.name.toLowerCase().includes(search.toLowerCase()) + ); + } + return filteredList; } </script> \ No newline at end of file diff --git a/src/components/Toolbar.vue b/src/components/Toolbar.vue index 93e191b..0d62f38 100644 --- a/src/components/Toolbar.vue +++ b/src/components/Toolbar.vue @@ -4,13 +4,10 @@ <el-button icon="Upload" type="primary" @click="handleImport">{{ t('import') }}</el-button> <el-button icon="Download" type="primary" @click="handleExport">{{ t('export') }}</el-button> <el-button icon="Share" type="primary" @click="prepareCapture">{{ t('prepareCapture') }}</el-button> - <el-button icon="Setting" type="primary" @click="state.showWatermarkDialog = true">{{ - t('setWatermark') - }} - </el-button> - <!-- 新增的按钮 --> - <el-button type="info" @click="showUpdateLog">更新日志</el-button> - <el-button type="warning" @click="showFeedbackForm">问题反馈</el-button> + <el-button icon="Setting" type="primary" @click="state.showWatermarkDialog = true">{{ t('setWatermark') }}</el-button> + <el-button type="info" @click="loadExample">{{ t('loadExample') }}</el-button> + <el-button type="info" @click="showUpdateLog">{{ t('updateLog') }}</el-button> + <el-button type="warning" @click="showFeedbackForm">{{ t('feedback') }}</el-button> </div> <!-- 更新日志对话框 --> @@ -25,7 +22,7 @@ </ul> </el-dialog> - <!-- 更新日志对话框 --> + <!-- 问题反馈对话框 --> <el-dialog v-model="state.showFeedbackFormDialog" title="更新日志" width="60%"> <span style="font-size: 24px;">备注阴阳师</span> <br/> @@ -79,13 +76,17 @@ </template> <script setup lang="ts"> -import {ref, reactive} from 'vue'; +import {ref, reactive, onMounted} from 'vue'; import html2canvas from "html2canvas"; import {useI18n} from 'vue-i18n'; import updateLogs from "../data/updateLog.json" +import filesStoreExample from "../data/filesStoreExample.json" import {useFilesStore} from "@/ts/files"; +import {ElMessageBox} from "element-plus"; +import {useGlobalMessage} from "@/ts/useGlobalMessage"; const filesStore = useFilesStore(); +const { showMessage } = useGlobalMessage(); // 获取当前的 i18n 实例 const {t} = useI18n(); @@ -99,18 +100,47 @@ const state = reactive({ showFeedbackFormDialog: false, // 控制反馈表单对话框的显示状态 }); +const loadExample = () => { + ElMessageBox.confirm( + '加载样例会覆盖当前数据,是否覆盖?', + '提示', + { + confirmButtonText: '覆盖', + cancelButtonText: '取消', + type: 'warning', + } + ).then(() => { + filesStore.$patch({fileList: filesStoreExample}); + showMessage('success', '数据已恢复'); + }).catch(() => { + showMessage('info', '选择了不恢复旧数据'); + }); +} +const CURRENT_APP_VERSION = updateLogs[0].version; const showUpdateLog = () => { state.showUpdateLogDialog = !state.showUpdateLogDialog; }; +onMounted(() => { + const lastVersion = localStorage.getItem('appVersion'); + + if (lastVersion !== CURRENT_APP_VERSION) { + // 如果版本号不同,则显示更新日志对话框 + state.showUpdateLogDialog = true; + // 更新本地存储中的版本号为当前版本 + localStorage.setItem('appVersion', CURRENT_APP_VERSION); + } +}); + + const showFeedbackForm = () => { state.showFeedbackFormDialog = !state.showFeedbackFormDialog; }; const handleExport = () => { const dataStr = JSON.stringify(filesStore.fileList, null, 2); - const blob = new Blob([dataStr], { type: 'application/json;charset=utf-8' }); + const blob = new Blob([dataStr], {type: 'application/json;charset=utf-8'}); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; @@ -132,8 +162,8 @@ const handleImport = () => { const data = JSON.parse(e.target.result as string); if (data[0].visible === true) { // 新版本格式:直接替换 fileList - filesStore.$patch({ fileList: data }); - } else { + filesStore.$patch({fileList: data}); + } else { // 旧版本格式:仅包含 groups 数组 const newFile = { label: `File ${filesStore.fileList.length + 1}`, @@ -154,18 +184,27 @@ const handleImport = () => { }; const watermark = reactive({ - text: '示例水印', - fontSize: 30, - color: 'rgba(184, 184, 184, 0.3)', - angle: -20, - rows: 1, // 新增行数 - cols: 1 // 新增列数 + text: localStorage.getItem('watermark.text') || '示例水印', + fontSize: Number(localStorage.getItem('watermark.fontSize')) || 30, + color: localStorage.getItem('watermark.color') || 'rgba(184, 184, 184, 0.3)', + angle: Number(localStorage.getItem('watermark.angle')) || -20, + rows: Number(localStorage.getItem('watermark.rows')) || 1, + cols: Number(localStorage.getItem('watermark.cols')) || 1, }); const applyWatermarkSettings = () => { + // 保存水印设置到 localStorage + localStorage.setItem('watermark.text', watermark.text); + localStorage.setItem('watermark.fontSize', String(watermark.fontSize)); + localStorage.setItem('watermark.color', watermark.color); + localStorage.setItem('watermark.angle', String(watermark.angle)); + localStorage.setItem('watermark.rows', String(watermark.rows)); + localStorage.setItem('watermark.cols', String(watermark.cols)); + state.showWatermarkDialog = false; }; + // 计算视觉总高度 function calculateVisualHeight(selector) { // 1. 获取所有目标元素 diff --git a/src/components/Yys.vue b/src/components/Yys.vue index 8cbdb4b..40c07cd 100644 --- a/src/components/Yys.vue +++ b/src/components/Yys.vue @@ -16,8 +16,6 @@ <draggable :list="groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;" handle=".drag-handle"> - - <template class="group" #item="{ element: group, index: groupIndex }"> <el-row :span="24"> <div class="group-item"> @@ -33,23 +31,23 @@ </div> <div class="opt-right"> <el-button class="drag-handle" type="primary" icon="Rank" circle></el-button> - <el-button type="primary" icon="Plus" circle @click="addGroup"></el-button> + <el-button type="primary" @click="addGroup">{{t('AddGroup')}}</el-button> + <el-button type="primary" @click="addGroupElement(groupIndex)">{{t('AddShikigami')}}</el-button> <el-button type="danger" icon="Delete" circle @click="removeGroup(groupIndex)"></el-button> </div> </div> - <QuillEditor v-model:content="group.shortDescription" contentType="html" theme="snow" - :toolbar="toolbarOptions"/> + <QuillEditor ref="shortDescriptionEditor" v-model:content="group.shortDescription" contentType="html" theme="snow" :toolbar="toolbarOptions"/> </div> <div class="group-body"> <draggable :list="group.groupInfo" item-key="name" class="body-content"> <template #item="{element : position, index:positionIndex}"> - <div> - <el-col> + <div> + <el-col> <el-card class="group-card" shadow="never"> <div class="opt-btn" data-html2canvas-ignore="true"> <!-- Add delete button here --> <el-button type="danger" icon="Delete" circle @click="removeGroupElement(groupIndex, positionIndex)"/> - <el-button type="primary" icon="Plus" circle @click="addGroupElement(groupIndex)"/> + <!-- <el-button type="primary" icon="Plus" circle @click="addGroupElement(groupIndex)"/> --> </div> <div class="avatar-container"> <!-- 头像图片 --> @@ -89,34 +87,38 @@ </div> </el-card> </el-col> - </div> + </div> </template> </draggable> </div> <div class="group-footer"> - <div class="opt-left" data-html2canvas-ignore="true"> + <div class="group-opt" data-html2canvas-ignore="true"> + <div class="opt-left"> <el-button type="primary" icon="CopyDocument" @click="copy(group.details)">{{ t('Copy') }}</el-button> <el-button type="primary" icon="Document" @click="paste(groupIndex,'details')">{{ t('Paste') }} </el-button> </div> - <QuillEditor v-model:content="group.details" contentType="html" theme="snow" :toolbar="toolbarOptions"/> + </div> + <QuillEditor ref="detailsEditor" v-model:content="group.details" contentType="html" theme="snow" :toolbar="toolbarOptions" /> </div> </div> + <div class="divider-horizontal"></div> </el-row> </template> </draggable> </template> <script setup lang="ts"> -import {ref, reactive, toRefs} from 'vue'; +import {ref, reactive, toRefs, nextTick} from 'vue'; import draggable from 'vuedraggable'; import ShikigamiSelect from './ShikigamiSelect.vue'; import ShikigamiProperty from './ShikigamiProperty.vue'; import html2canvas from 'html2canvas'; import {useI18n} from 'vue-i18n' -import {QuillEditor} from '@vueup/vue-quill' +import { Quill, QuillEditor } from '@vueup/vue-quill' +import '@vueup/vue-quill/dist/vue-quill.bubble.css' import '@vueup/vue-quill/dist/vue-quill.snow.css' // 引入样式文件 import * as ElementPlusIconsVue from '@element-plus/icons-vue' import shikigamiData from '../data/Shikigami.json'; @@ -151,30 +153,48 @@ const copy = (str) => { } const paste = (groupIndex, type) => { + console.log("paste", groupIndex, type, clipboard.value) if ('shortDescription' == type) props.groups[groupIndex].shortDescription = clipboard.value else if ('details' == type) props.groups[groupIndex].details = clipboard.value } -// 定义工具栏选项 -const toolbarOptions = [ +// 自定义字体注册 +const registerFonts = () => { + const Font = Quill.import('attributors/style/font') + Font.whitelist = ['SimSun', 'SimHei', 'KaiTi', 'FangSong', 'Microsoft YaHei', 'PingFang SC'] + Quill.register(Font, true) +} - [{'color': []}, {'background': []}], +// 自定义字号注册 +const registerSizes = () => { + const Size = Quill.import('attributors/style/size') + Size.whitelist = ['12px', '14px', '16px', '18px', '21px', '29px', '32px', '34px'] + Quill.register(Size, true) +} + +// 执行注册 +registerFonts() +registerSizes() + +// 工具栏配置 +const toolbarOptions = ref([ + [{ font: ['SimSun', 'SimHei', 'KaiTi', 'FangSong', 'Microsoft YaHei', 'PingFang SC'] }], + [{ header: 1 }, { header: 2 }], + [{ size: ['12px', '14px', '16px', '18px', '21px', '29px', '32px', '34px'] }], ['bold', 'italic', 'underline', 'strike'], + [{ color: [] }, { background: [] }], // ['blockquote', 'code-block'], - // ['link', 'image', 'video', 'formula'], - [{'header': 1}, {'header': 2}], - [{'list': 'ordered'}, {'list': 'bullet'}, {'list': 'check'}], - // [{ 'script': 'sub'}, { 'script': 'super' }], - [{'indent': '-1'}, {'indent': '+1'}], - // [{ 'size': ['small', false, 'large', 'huge'] }], - // [{ 'header': [1, 2, 3, 4, 5, 6, false] }], - // [{ 'font': [] }], - [{'align': []}], - [{'direction': 'rtl'}], + [ { list: 'bullet' }, { list: 'ordered' }, {'list': 'check'}], + + [{ indent: '-1' }, { indent: '+1' }], + [{ align: [] }], + [{ direction: 'rtl' }], + // [{ header: [1, 2, 3, 4, 5, 6, false] }], + // ['link', 'image', 'video'], // ['clean'] -]; +] as const) // 定义方法 const closeSelectShikigami = () => { @@ -192,7 +212,7 @@ const editShikigami = (groupIndex, positionIndex) => { }; const updateShikigami = (shikigami) => { - console.log("parent====> ", shikigami); + console.log("parent====> ", shikigami, state); state.showSelectShikigami = false; const oldProperties = props.groups[state.groupIndex].groupInfo[state.positionIndex].properties; @@ -263,10 +283,21 @@ const addGroup = () => { groupInfo: [{}, {}, {}, {}, {}], details: '' }); + + const container = document.getElementById('main-container'); + + + nextTick(() => { + container.scrollTo({ + top: container.scrollHeight, + behavior: 'smooth' // 可选平滑滚动 + }); + }) }; const addGroupElement = (groupIndex) => { props.groups[groupIndex].groupInfo.push({}); + editShikigami(groupIndex, props.groups[groupIndex].groupInfo.length - 1); }; @@ -331,8 +362,30 @@ const importGroups = (file) => { reader.readAsText(file); }; +// 定义 QuillEditor 的 ref +const shortDescriptionEditor = ref<InstanceType<typeof QuillEditor>>() +const detailsEditor = ref<InstanceType<typeof QuillEditor>>() + +// 保存方法 +const saveQuillDesc = async (): Promise<string> => { + if (!shortDescriptionEditor.value) { + throw new Error('Quill editor instance not found') + } + return shortDescriptionEditor.value.getHTML() +} + +// 保存方法 +const saveQuillDetail = async (): Promise<string> => { + if (!detailsEditor.value) { + throw new Error('Quill detailsEditor instance not found') + } + return detailsEditor.value.getHTML() +} + // 暴露方法给父组件 defineExpose({ + saveQuillDesc, + saveQuillDetail, exportGroups, importGroups }); @@ -388,11 +441,24 @@ defineExpose({ justify-content: space-between; } +.group-item { + width: 100%; +} + .group-body { padding: 20px; width: 80%; } +/* 水平分割线 */ +.divider-horizontal { + border: 0; + height: 1px; + background: #e0e0e0; + margin: 24px 0; + width: 100%; +} + .body-content { display: flex; flex-direction: row; @@ -430,6 +496,7 @@ defineExpose({ .opt-btn { position: absolute; top: 0px; + right: 0px; z-index: 10; opacity: 0; } @@ -450,3 +517,132 @@ defineExpose({ } </style> + +<style> +.ql-container { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; +} + +.ql-toolbar { + border-top-left-radius: 5px; + border-top-right-radius: 5px; + + .ql-tooltip[data-mode="link"]::before { + content: "链接地址:"; + } + .ql-tooltip[data-mode="video"]::before { + content: "视频地址:"; + } + .ql-tooltip.ql-editing { + a.ql-action::after { + content: "保存"; + border-right: 0px; + padding-right: 0px; + } + } + + .ql-picker.ql-font { + .ql-picker-label[data-value=SimSun]::before, + .ql-picker-item[data-value=SimSun]::before { + content: "宋体"; + font-family: SimSun; + font-size: 15px; + } + .ql-picker-label[data-value=SimHei]::before, + .ql-picker-item[data-value=SimHei]::before { + content: "黑体"; + font-family: SimHei; + font-size: 15px; + } + .ql-picker-label[data-value=KaiTi]::before, + .ql-picker-item[data-value=KaiTi]::before { + content: "楷体"; + font-family: KaiTi; + font-size: 15px; + } + .ql-picker-label[data-value=FangSong]::before, + .ql-picker-item[data-value=FangSong]::before { + content: "仿宋"; + font-family: FangSong; + font-size: 15px; + } + .ql-picker-label[data-value="Microsoft YaHei"]::before, + .ql-picker-item[data-value="Microsoft YaHei"]::before { + content: "微软雅黑"; + font-family: 'Microsoft YaHei'; + font-size: 15px; + } + .ql-picker-label[data-value="PingFang SC"]::before, + .ql-picker-item[data-value="PingFang SC"]::before { + content: "苹方"; + font-family: 'PingFang SC'; + font-size: 15px; + } + } + + .ql-picker.ql-size { + .ql-picker-label::before, + .ql-picker-item::before { + font-size: 14px !important; + content: "五号" !important; + } + .ql-picker-label[data-value="12px"]::before { + content: "小五" !important; + } + .ql-picker-item[data-value="12px"]::before { + font-size: 12px; + content: "小五" !important; + } + .ql-picker-label[data-value="16px"]::before { + content: "小四" !important; + } + .ql-picker-item[data-value="16px"]::before { + font-size: 16px; + content: "小四" !important; + } + .ql-picker-label[data-value="18px"]::before { + content: "四号" !important; + } + .ql-picker-item[data-value="18px"]::before { + font-size: 18px; + content: "四号" !important; + } + .ql-picker-label[data-value="21px"]::before { + content: "三号" !important; + } + .ql-picker-item[data-value="21px"]::before { + font-size: 21px; + content: "三号" !important; + } + .ql-picker-label[data-value="24px"]::before { + content: "小二" !important; + } + .ql-picker-item[data-value="24px"]::before { + font-size: 24px; + content: "小二" !important; + } + .ql-picker-label[data-value="29px"]::before { + content: "二号" !important; + } + .ql-picker-item[data-value="29px"]::before { + font-size: 29px; + content: "二号" !important; + } + .ql-picker-label[data-value="32px"]::before { + content: "小一" !important; + } + .ql-picker-item[data-value="32px"]::before { + font-size: 32px; + content: "小一" !important; + } + .ql-picker-label[data-value="34px"]::before { + content: "一号" !important; + } + .ql-picker-item[data-value="34px"]::before { + font-size: 34px; + content: "一号" !important; + } + } +} +</style> \ No newline at end of file diff --git a/src/data/filesStoreExample.json b/src/data/filesStoreExample.json new file mode 100644 index 0000000..b5e9f1f --- /dev/null +++ b/src/data/filesStoreExample.json @@ -0,0 +1,183 @@ +[ + { + "label": "Welcome", + "name": "1", + "type":"PVE", + "visible": true, + "groups": [ + { + "shortDescription": "<h1>鬼灵歌姬</h1><h2><em>这是一个演示项目,用于测试显示6个式神的对齐效果</em></h2>", + "groupInfo": [ + { + "avatar": "/assets/Shikigami/sp/372.png", + "name": "因幡辉夜姬", + "rarity": "SP" + }, + { + "avatar": "/assets/Shikigami/ssr/356.png", + "name": "千姬", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/sp/554.png", + "name": "纺愿缘结神", + "rarity": "SP" + }, + { + "avatar": "/assets/Shikigami/ssr/556.png", + "name": "天照", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/ssr/557.png", + "name": "伊邪那美", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/sp/367.png", + "name": "绘世花鸟卷", + "rarity": "SP" + } + ], + "details": "<h2><strong>开局因幡普攻主怪后锁二</strong></h2>" + }, + { + "shortDescription": "<h1>魂土15秒</h1><p><em>相同式神编辑不同属性</em></p>", + "groupInfo": [ + { + "avatar": "/assets/Shikigami/ssr/364.png", + "name": "阿修罗", + "rarity": "SSR", + "properties": { + "edit": true, + "yuhun": { + "yuhunSetEffect": [ + { + "name": "狂骨", + "shortName": "狂", + "type": "attack", + "avatar": "/assets/Yuhun/狂骨.png" + }, + { + "name": "荒骷髅", + "shortName": "荒", + "type": "PVE", + "avatar": "/assets/Yuhun/荒骷髅.png" + } + ], + "target": "1", + "property2": [ + "Attack" + ], + "property4": [ + "Attack" + ], + "property6": [ + "Crit", + "CritDamage" + ] + }, + "levelRequired": "40", + "speed": "", + "skillRequiredMode": "all", + "skillRequired": [ + "5", + "5", + "5" + ] + } + }, + { + "avatar": "/assets/Shikigami/ssr/364.png", + "name": "阿修罗", + "rarity": "SSR", + "properties": { + "edit": true, + "yuhun": { + "yuhunSetEffect": [ + { + "name": "狂骨", + "shortName": "狂", + "type": "attack", + "avatar": "/assets/Yuhun/狂骨.png" + }, + { + "name": "鬼灵歌伎", + "shortName": "歌伎", + "type": "PVE", + "avatar": "/assets/Yuhun/鬼灵歌伎.png" + } + ], + "target": "0", + "property2": [ + "Attack" + ], + "property4": [ + "Attack" + ], + "property6": [ + "Crit", + "CritDamage" + ] + }, + "levelRequired": "40", + "speed": "", + "skillRequiredMode": "all", + "skillRequired": [ + "5", + "5", + "5" + ] + } + }, + { + "avatar": "/assets/Shikigami/ssr/370.png", + "name": "饭笥", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/ssr/369.png", + "name": "食灵", + "rarity": "SSR" + }, + { + "avatar": "/assets/Shikigami/r/205.png", + "name": "座敷童子", + "rarity": "R" + } + ], + "details": "" + } + ] + }, + { + "label": "Test", + "name": "2", + "visible": true, + "type":"PVE", + "groups": [ + { + "shortDescription": "<h1>御魂·悲鸣</h1><p>这是一个演示项目,用于测试不同标签页的切换效果</p>", + "groupInfo": [ + {}, + {}, + {}, + {}, + {} + ], + "details": "" + }, + { + "shortDescription": "", + "groupInfo": [ + {}, + {}, + {}, + {}, + {} + ], + "details": "" + } + ] + } +] \ No newline at end of file diff --git a/src/data/updateLog.json b/src/data/updateLog.json index d74a308..c2e9451 100644 --- a/src/data/updateLog.json +++ b/src/data/updateLog.json @@ -1,4 +1,19 @@ [ + { + "version": "2.2.0", + "date": "2025-03-21", + "changes": [ + "优化文本编辑显示", + "增加缓存机制,支持在未保存的情况下恢复数据,水印设置信息" + ] + }, + { + "version": "2.1.0", + "date": "2025-03-19", + "changes": [ + "增加式神搜索功能" + ] + }, { "version": "2.0.3", "date": "2025-03-20", diff --git a/src/locales/zh.json b/src/locales/zh.json index 35217d5..0967052 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -2,8 +2,13 @@ "import": "导入", "export": "导出", "setWatermark": "设置水印", + "loadExample": "加载样例", + "updateLog":"更新日志", + "feedback":"问题反馈", "Copy": "复制", "Paste": "粘贴", + "AddGroup": "新增小队", + "AddShikigami": "新增式神", "editProperties": "配置属性", "prepareCapture": "生成截图", "yuhunSelect": "请选择御魂", diff --git a/src/main.js b/src/main.js index 6cac8e5..2ce0f0d 100644 --- a/src/main.js +++ b/src/main.js @@ -16,6 +16,7 @@ import zh from './locales/zh.json' import ja from './locales/ja.json' import { createPinia } from 'pinia' // 导入 Pinia +import {useFilesStore} from "@/ts/files"; const app = createApp(App) @@ -59,4 +60,8 @@ app.use(pinia) // 使用 Pinia .use(i18n) .use(ElementPlus) .use(Vue3DraggableResizable) - .mount('#app') \ No newline at end of file + .mount('#app') + +const filesStore = useFilesStore(); +filesStore.setupAutoSave(); +filesStore.initializeWithPrompt(); \ No newline at end of file diff --git a/src/ts/files.ts b/src/ts/files.ts index 9896c95..c5ec2f7 100644 --- a/src/ts/files.ts +++ b/src/ts/files.ts @@ -3,197 +3,75 @@ import {ElMessageBox} from "element-plus"; import {useGlobalMessage} from "./useGlobalMessage"; const { showMessage } = useGlobalMessage(); -export const useFilesStore = defineStore('files', { - state: () => ({ - fileList: [ - { - "label": "Welcome", - "name": "1", - "type":"PVE", - "visible": true, - "groups": [ - { - "shortDescription": "<h1>鬼灵歌姬</h1><h2><em>这是一个演示项目,用于测试显示6个式神的对齐效果</em></h2>", - "groupInfo": [ - { - "avatar": "/assets/Shikigami/sp/372.png", - "name": "因幡辉夜姬", - "rarity": "SP" - }, - { - "avatar": "/assets/Shikigami/ssr/356.png", - "name": "千姬", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/sp/554.png", - "name": "纺愿缘结神", - "rarity": "SP" - }, - { - "avatar": "/assets/Shikigami/ssr/556.png", - "name": "天照", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/ssr/557.png", - "name": "伊邪那美", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/sp/367.png", - "name": "绘世花鸟卷", - "rarity": "SP" - } - ], - "details": "<h2><strong>开局因幡普攻主怪后锁二</strong></h2>" - }, - { - "shortDescription": "<h1>魂土15秒</h1><p><em>相同式神编辑不同属性</em></p>", - "groupInfo": [ - { - "avatar": "/assets/Shikigami/ssr/364.png", - "name": "阿修罗", - "rarity": "SSR", - "properties": { - "edit": true, - "yuhun": { - "yuhunSetEffect": [ - { - "name": "狂骨", - "shortName": "狂", - "type": "attack", - "avatar": "/assets/Yuhun/狂骨.png" - }, - { - "name": "荒骷髅", - "shortName": "荒", - "type": "PVE", - "avatar": "/assets/Yuhun/荒骷髅.png" - } - ], - "target": "1", - "property2": [ - "Attack" - ], - "property4": [ - "Attack" - ], - "property6": [ - "Crit", - "CritDamage" - ] - }, - "levelRequired": "40", - "speed": "", - "skillRequiredMode": "all", - "skillRequired": [ - "5", - "5", - "5" - ] - } - }, - { - "avatar": "/assets/Shikigami/ssr/364.png", - "name": "阿修罗", - "rarity": "SSR", - "properties": { - "edit": true, - "yuhun": { - "yuhunSetEffect": [ - { - "name": "狂骨", - "shortName": "狂", - "type": "attack", - "avatar": "/assets/Yuhun/狂骨.png" - }, - { - "name": "鬼灵歌伎", - "shortName": "歌伎", - "type": "PVE", - "avatar": "/assets/Yuhun/鬼灵歌伎.png" - } - ], - "target": "0", - "property2": [ - "Attack" - ], - "property4": [ - "Attack" - ], - "property6": [ - "Crit", - "CritDamage" - ] - }, - "levelRequired": "40", - "speed": "", - "skillRequiredMode": "all", - "skillRequired": [ - "5", - "5", - "5" - ] - } - }, - { - "avatar": "/assets/Shikigami/ssr/370.png", - "name": "饭笥", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/ssr/369.png", - "name": "食灵", - "rarity": "SSR" - }, - { - "avatar": "/assets/Shikigami/r/205.png", - "name": "座敷童子", - "rarity": "R" - } - ], - "details": "" - } - ] - }, - { - "label": "Test", - "name": "2", - "visible": true, - "type":"PVE", - "groups": [ - { - "shortDescription": "<h1>御魂·悲鸣</h1><p>这是一个演示项目,用于测试不同标签页的切换效果</p>", - "groupInfo": [ - {}, - {}, - {}, - {}, - {} - ], - "details": "" - }, - { - "shortDescription": "", - "groupInfo": [ - {}, - {}, - {}, - {}, - {} - ], - "details": "" - } - ] - } - ], + +function getDefaultState() { + return { + fileList: [{ + "label": "File 1", + "name": "1", + "visible": true, + "type":"PVE", + "groups": [ + { + "shortDescription": "", + "groupInfo": [ + {}, {}, {}, {}, {} + ], + "details": "" + } + ] + }], activeFile: "1", - }), + }; +} + +function saveStateToLocalStorage(state) { + localStorage.setItem('filesStore', JSON.stringify(state)); +} + +function clearFilesStoreLocalStorage() { + localStorage.removeItem('filesStore') +} + +function loadStateFromLocalStorage() { + return JSON.parse(localStorage.getItem('filesStore')); +} + +export const useFilesStore = defineStore('files', { + state: () => getDefaultState(), getters: { visibleFiles: (state) => state.fileList.filter(file => file.visible), }, actions: { + initializeWithPrompt() { + const savedState = loadStateFromLocalStorage(); + const defaultState = getDefaultState(); + + const isSame = JSON.stringify(savedState) === JSON.stringify(defaultState); + if (savedState && !isSame) { + ElMessageBox.confirm( + '检测到有未保存的旧数据,是否恢复?', + '提示', + { + confirmButtonText: '恢复', + cancelButtonText: '不恢复', + type: 'warning', + } + ).then(() => { + this.fileList = savedState.fileList || []; + this.activeFile = savedState.activeFile || "1"; + showMessage('success', '数据已恢复'); + }).catch(() => { + clearFilesStoreLocalStorage(); + showMessage('info', '选择了不恢复旧数据'); + }); + } + }, + setupAutoSave() { + setInterval(() => { + saveStateToLocalStorage(this.$state); + }, 30000); // 设置间隔时间为30秒 + }, addFile(file) { this.fileList.push({...file, visible: true}); this.activeFile = file.name;