mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2025-05-23 19:35:24 +00:00
多文件支持
This commit is contained in:
parent
60091e88a2
commit
8839c37256
BIN
public/assets/Other/Contact.png
Normal file
BIN
public/assets/Other/Contact.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
74
src/App.vue
74
src/App.vue
@ -5,7 +5,7 @@ import ProjectExplorer from './components/ProjectExplorer.vue';
|
||||
import { computed, ref, onMounted, onUnmounted } from "vue";
|
||||
import { useFilesStore } from "@/stores/files";
|
||||
import Vue3DraggableResizable from 'vue3-draggable-resizable';
|
||||
import {TabPaneName, TabsPaneContext} from "element-plus";
|
||||
import { TabPaneName, TabsPaneContext } from "element-plus";
|
||||
|
||||
const filesStore = useFilesStore();
|
||||
|
||||
@ -21,12 +21,49 @@ const onResizing = (x, y, width, height) => {
|
||||
height.value = height;
|
||||
};
|
||||
|
||||
const onHandleInport = (file) => {
|
||||
yysRef.value.importGroups(file);
|
||||
const handleExport = () => {
|
||||
const dataStr = JSON.stringify(filesStore.fileList, null, 2);
|
||||
const blob = new Blob([dataStr], { type: 'application/json;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'files.json';
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const onHandleInport = (file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result as string);
|
||||
if (data[0].visible === true) {
|
||||
// 新版本格式:直接替换 fileList
|
||||
filesStore.$patch({ fileList: data });
|
||||
} else {
|
||||
// 旧版本格式:仅包含 groups 数组
|
||||
const newFile = {
|
||||
label: `File ${filesStore.fileList.length + 1}`,
|
||||
name: String(filesStore.fileList.length + 1),
|
||||
visible: true,
|
||||
groups: data
|
||||
};
|
||||
filesStore.addFile(newFile);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to import file', error);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
// const onHandleInport = (file) => {
|
||||
//
|
||||
// handleImport(file);
|
||||
// };
|
||||
|
||||
const onHandleExport = () => {
|
||||
yysRef.value.exportGroups();
|
||||
handleExport();
|
||||
};
|
||||
|
||||
const element = ref({
|
||||
@ -39,20 +76,18 @@ const element = ref({
|
||||
|
||||
const handleFileSelected = (fileId) => {
|
||||
filesStore.setActiveFile(fileId);
|
||||
filesStore.setVisible(fileId, true);
|
||||
};
|
||||
|
||||
const handleTabsEdit = (
|
||||
targetName: TabPaneName | undefined,
|
||||
targetName: String | undefined,
|
||||
action: 'remove' | 'add'
|
||||
)=> {
|
||||
const tabIndex = filesStore.fileList.findIndex(file => file.name === parseInt(name.toString()));
|
||||
if (tabIndex !== -1) {
|
||||
filesStore.fileList.splice(tabIndex, 1);
|
||||
if (filesStore.fileList.length > 0) {
|
||||
filesStore.setActiveFile(filesStore.fileList[0].name);
|
||||
} else {
|
||||
filesStore.setActiveFile(-1); // 或者其他适当的值表示没有活动文件
|
||||
}
|
||||
) => {
|
||||
if (action === 'remove') {
|
||||
filesStore.closeTab(targetName);
|
||||
} else if (action === 'add') {
|
||||
const newFileName = `File ${filesStore.fileList.length + 1}`;
|
||||
filesStore.addFile({ label: newFileName, name: newFileName });
|
||||
}
|
||||
};
|
||||
|
||||
@ -67,6 +102,11 @@ onUnmounted(() => {
|
||||
windowHeight.value = window.innerHeight;
|
||||
});
|
||||
});
|
||||
|
||||
const activeFileGroups = computed(() => {
|
||||
const activeFile = filesStore.fileList.find(file => file.name === filesStore.activeFile);
|
||||
return activeFile ? activeFile.groups : [];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -77,7 +117,7 @@ onUnmounted(() => {
|
||||
<div class="main-content">
|
||||
<!-- 侧边栏 -->
|
||||
<aside class="sidebar">
|
||||
<ProjectExplorer :files="filesStore.fileList" @file-selected="handleFileSelected" />
|
||||
<ProjectExplorer :allFiles="filesStore.fileList" @file-selected="handleFileSelected" />
|
||||
</aside>
|
||||
|
||||
<!-- 工作区 -->
|
||||
@ -91,12 +131,12 @@ onUnmounted(() => {
|
||||
@edit="handleTabsEdit"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="(file, index) in filesStore.fileList"
|
||||
v-for="(file, index) in filesStore.visibleFiles"
|
||||
:key="index"
|
||||
:label="file.label"
|
||||
:name="file.name.toString()"
|
||||
>
|
||||
<Yys ref="yysRef" />
|
||||
<Yys :groups="activeFileGroups" ref="yysRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</main>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="project-explorer">
|
||||
<el-tree
|
||||
:data="files"
|
||||
:data="allFiles"
|
||||
:props="defaultProps"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
@ -9,10 +9,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
files: {
|
||||
allFiles: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
@ -130,6 +130,7 @@ import YuhunSelect from "@/components/YuhunSelect.vue";
|
||||
import {useI18n} from 'vue-i18n'
|
||||
// import YuhunSelect from "./YuhunSelect.vue";
|
||||
|
||||
|
||||
// 获取当前的 i18n 实例
|
||||
const {t} = useI18n()
|
||||
|
||||
@ -339,7 +340,7 @@ const cancel = () => {
|
||||
|
||||
const confirm = () => {
|
||||
shikigami.value.edit = true
|
||||
emit('updateProperty', JSON.parse(JSON.stringify(shikigami.value)))
|
||||
emit('updateProperty', shikigami.value);
|
||||
resetData()
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,37 @@
|
||||
t('setWatermark')
|
||||
}}
|
||||
</el-button>
|
||||
<!-- 新增的按钮 -->
|
||||
<el-button type="info" @click="showUpdateLog">更新日志</el-button>
|
||||
<el-button type="warning" @click="showFeedbackForm">问题反馈</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 更新日志对话框 -->
|
||||
<el-dialog v-model="state.showUpdateLogDialog" title="更新日志" width="60%">
|
||||
<ul>
|
||||
<li v-for="(log, index) in updateLogs" :key="index">
|
||||
<strong>版本 {{ log.version }} - {{ log.date }}</strong>
|
||||
<ul>
|
||||
<li v-for="(change, idx) in log.changes" :key="idx">{{ change }}</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 更新日志对话框 -->
|
||||
<el-dialog v-model="state.showFeedbackFormDialog" title="更新日志" width="60%">
|
||||
<span style="font-size: 24px;">备注阴阳师</span>
|
||||
<br/>
|
||||
<img src="/assets/Other/Contact.png"
|
||||
style="cursor: pointer; vertical-align: bottom; width: 200px; height: auto;"/>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 预览弹窗 -->
|
||||
<el-dialog id="preview-container" v-model="state.previewVisible" width="80%" height="80%" :before-close="handleClose">
|
||||
<el-dialog id="preview-container" v-model="state.previewVisible" width="80%" height="80%"
|
||||
:before-close="handleClose">
|
||||
<div style="max-height: 500px; overflow-y: auto;">
|
||||
<img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; display: block;"/>
|
||||
</div>
|
||||
<!-- <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>
|
||||
@ -62,15 +85,46 @@ import {useI18n} from 'vue-i18n';
|
||||
|
||||
// 获取当前的 i18n 实例
|
||||
const {t} = useI18n();
|
||||
const emit = defineEmits(['handleExport', 'handleImport'])
|
||||
const emit = defineEmits(['handleExport', 'handleImport']);
|
||||
|
||||
// 定义响应式数据
|
||||
const state = reactive({
|
||||
previewImage: null, // 用于存储预览图像的数据URL
|
||||
previewVisible: false, // 控制预览弹窗的显示状态
|
||||
showWatermarkDialog: false, // 控制水印设置弹窗的显示状态
|
||||
showWatermarkDialog: false, // 控制水印设置弹窗的显示状态,
|
||||
showUpdateLogDialog: false, // 控制更新日志对话框的显示状态
|
||||
showFeedbackFormDialog: false, // 控制反馈表单对话框的显示状态
|
||||
});
|
||||
|
||||
// 版本记录数据
|
||||
const updateLogs = [
|
||||
{
|
||||
version: '2.0.0',
|
||||
date: '2025-03-16',
|
||||
changes: [
|
||||
'修复了相同式神不能正确设置属性的问题',
|
||||
'支持了多文件编辑',
|
||||
'PS:当前导出截图宽度无法'
|
||||
]
|
||||
},
|
||||
{
|
||||
version: '1.0.0',
|
||||
date: '2025-03-09',
|
||||
changes: [
|
||||
'首次发布'
|
||||
]
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
const showUpdateLog = () => {
|
||||
state.showUpdateLogDialog = !state.showUpdateLogDialog;
|
||||
};
|
||||
|
||||
const showFeedbackForm = () => {
|
||||
state.showFeedbackFormDialog = !state.showFeedbackFormDialog;
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
emit('handleExport');
|
||||
};
|
||||
@ -86,7 +140,6 @@ const handleImport = () => {
|
||||
input.click();
|
||||
};
|
||||
|
||||
|
||||
const watermark = reactive({
|
||||
text: '示例水印',
|
||||
fontSize: 30,
|
||||
@ -150,7 +203,6 @@ function calculateVisualHeight(selector) {
|
||||
return rows.reduce((sum, row) => sum + row.maxHeight, 0);
|
||||
}
|
||||
|
||||
|
||||
const ignoreElements = (element) => {
|
||||
return element.classList.contains('ql-toolbar') || element.classList.contains('el-tabs__header');
|
||||
};
|
||||
@ -248,6 +300,7 @@ const prepareCapture = async () => {
|
||||
document.head.removeChild(style);
|
||||
}
|
||||
};
|
||||
|
||||
const downloadImage = () => {
|
||||
if (state.previewImage) {
|
||||
const link = document.createElement('a');
|
||||
@ -272,7 +325,6 @@ const handleClose = (done) => {
|
||||
right: 0;
|
||||
height: 48px;
|
||||
background: #f8f8f8;
|
||||
//border-bottom: 1px solid #eee; display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
z-index: 100;
|
||||
|
@ -14,7 +14,7 @@
|
||||
/>
|
||||
|
||||
|
||||
<draggable :list="state.groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;"
|
||||
<draggable :list="groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;"
|
||||
handle=".drag-handle">
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, toRefs} from 'vue';
|
||||
import draggable from 'vuedraggable';
|
||||
import ShikigamiSelect from './ShikigamiSelect.vue';
|
||||
@ -139,35 +139,15 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import shikigamiData from '../data/Shikigami.json';
|
||||
import _ from 'lodash';
|
||||
|
||||
const props = defineProps<{
|
||||
groups: any[];
|
||||
}>();
|
||||
|
||||
const dialogTableVisible = ref(false)
|
||||
// 定义响应式数据
|
||||
const state = reactive({
|
||||
showSelectShikigami: false,
|
||||
showProperty: false,
|
||||
groups: [
|
||||
{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},
|
||||
{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},
|
||||
],
|
||||
groupIndex: 0,
|
||||
positionIndex: 0,
|
||||
currentShikigami: {},
|
||||
@ -187,9 +167,9 @@ const copy = (str) => {
|
||||
|
||||
const paste = (groupIndex, type) => {
|
||||
if ('shortDescription' == type)
|
||||
state.groups[groupIndex].shortDescription = clipboard.value
|
||||
props.groups[groupIndex].shortDescription = clipboard.value
|
||||
else if ('details' == type)
|
||||
state.groups[groupIndex].details = clipboard.value
|
||||
props.groups[groupIndex].details = clipboard.value
|
||||
}
|
||||
|
||||
// 定义工具栏选项
|
||||
@ -223,23 +203,23 @@ const editShikigami = (groupIndex, positionIndex) => {
|
||||
state.showSelectShikigami = true;
|
||||
state.groupIndex = groupIndex;
|
||||
state.positionIndex = positionIndex;
|
||||
state.currentShikigami = state.groups[groupIndex].groupInfo[positionIndex];
|
||||
state.currentShikigami = props.groups[groupIndex].groupInfo[positionIndex];
|
||||
};
|
||||
|
||||
const updateShikigami = (shikigami) => {
|
||||
console.log("parent====> ", shikigami);
|
||||
state.showSelectShikigami = false;
|
||||
|
||||
const oldProperties = state.groups[state.groupIndex].groupInfo[state.positionIndex].properties;
|
||||
state.groups[state.groupIndex].groupInfo[state.positionIndex] = _.cloneDeep(shikigami);
|
||||
state.groups[state.groupIndex].groupInfo[state.positionIndex].properties = oldProperties;
|
||||
const oldProperties = props.groups[state.groupIndex].groupInfo[state.positionIndex].properties;
|
||||
props.groups[state.groupIndex].groupInfo[state.positionIndex] = _.cloneDeep(shikigami);
|
||||
props.groups[state.groupIndex].groupInfo[state.positionIndex].properties = oldProperties;
|
||||
};
|
||||
|
||||
const editProperty = (groupIndex, positionIndex) => {
|
||||
state.showProperty = true;
|
||||
state.groupIndex = groupIndex;
|
||||
state.positionIndex = positionIndex;
|
||||
state.currentShikigami = state.groups[groupIndex].groupInfo[positionIndex];
|
||||
state.currentShikigami = props.groups[groupIndex].groupInfo[positionIndex];
|
||||
};
|
||||
|
||||
const closeProperty = () => {
|
||||
@ -250,19 +230,19 @@ const closeProperty = () => {
|
||||
const updateProperty = (property) => {
|
||||
state.showProperty = false;
|
||||
state.currentShikigami = {};
|
||||
state.groups[state.groupIndex].groupInfo[state.positionIndex].properties = _.cloneDeep(property);
|
||||
props.groups[state.groupIndex].groupInfo[state.positionIndex].properties = _.cloneDeep(property);
|
||||
};
|
||||
|
||||
const removeGroupElement = (groupIndex, positionIndex) => {
|
||||
state.groups[groupIndex].groupInfo.splice(positionIndex, 1);
|
||||
props.groups[groupIndex].groupInfo.splice(positionIndex, 1);
|
||||
};
|
||||
|
||||
const removeGroup = (groupIndex) => {
|
||||
state.groups.splice(groupIndex, 1);
|
||||
props.groups.splice(groupIndex, 1);
|
||||
};
|
||||
|
||||
const addGroup = () => {
|
||||
state.groups.push({
|
||||
props.groups.push({
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
@ -270,12 +250,12 @@ const addGroup = () => {
|
||||
};
|
||||
|
||||
const addGroupElement = (groupIndex) => {
|
||||
state.groups[groupIndex].groupInfo.push({});
|
||||
props.groups[groupIndex].groupInfo.push({});
|
||||
};
|
||||
|
||||
|
||||
const exportGroups = () => {
|
||||
const dataStr = JSON.stringify(state.groups, null, 2);
|
||||
const dataStr = JSON.stringify(props.groups, null, 2);
|
||||
const blob = new Blob([dataStr], {type: 'application/json'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
@ -326,7 +306,7 @@ const importGroups = (file) => {
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const importedData = JSON.parse(e.target.result);
|
||||
state.groups = importedData;
|
||||
props.groups = importedData;
|
||||
ElMessage.success('导入成功');
|
||||
} catch (error) {
|
||||
ElMessage.error('文件格式错误');
|
||||
|
@ -1,16 +1,69 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import {defineStore} from 'pinia';
|
||||
|
||||
export const useFilesStore = defineStore('files', {
|
||||
state: () => ({
|
||||
fileList: [{ label: 'File 1', name: 1 },{ label: 'File 2', name: 2 }],
|
||||
activeFile: 1,
|
||||
fileList: [
|
||||
{
|
||||
label: 'File 1',
|
||||
name: "1",
|
||||
visible: true,
|
||||
groups: [
|
||||
{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},
|
||||
{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
}
|
||||
]
|
||||
}, {
|
||||
label: 'File 2',
|
||||
name: "2",
|
||||
visible: true,
|
||||
groups:[
|
||||
{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
},
|
||||
{
|
||||
shortDescription: '',
|
||||
groupInfo: [{}, {}, {}, {}, {}],
|
||||
details: ''
|
||||
}
|
||||
]
|
||||
}],
|
||||
activeFile: "1",
|
||||
}),
|
||||
getters: {
|
||||
visibleFiles: (state) => state.fileList.filter(file => file.visible),
|
||||
},
|
||||
actions: {
|
||||
addFile(file: { label: string; name: number }) {
|
||||
this.fileList.push(file);
|
||||
this.fileList.push({...file, visible: true});
|
||||
this.activeFile = file.name;
|
||||
},
|
||||
setActiveFile(fileId: number) {
|
||||
this.activeFile = fileId;
|
||||
},
|
||||
setVisible(fileId: number, visibility: boolean) {
|
||||
const file = this.fileList.find(file => file.name === fileId);
|
||||
if (file) {
|
||||
file.visible = visibility;
|
||||
}
|
||||
},
|
||||
closeTab(fileName: String) {
|
||||
const file = this.fileList.find(file => file.name === fileName);
|
||||
if (file) {
|
||||
file.visible = false;
|
||||
if (this.activeFile === fileName) {
|
||||
const nextVisibleFile = this.visibleFiles[0];
|
||||
this.activeFile = nextVisibleFile ? nextVisibleFile.name : -1;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user