diff options
| author | zyh <[email protected]> | 2024-11-12 14:29:11 +0800 |
|---|---|---|
| committer | zyh <[email protected]> | 2024-11-12 14:29:11 +0800 |
| commit | d110e2f49d64e637cf678c8095c214f7347c8d5d (patch) | |
| tree | b40797615ddf56514e07a3489bc8871efd43d3f3 /src | |
| parent | 9cbc37f70af9b843ea9141337913838ac712aa30 (diff) | |
ASW-121 fix: application 因文件内容提交失败后,修改内容丢失
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/monaco/index.vue | 7 | ||||
| -rw-r--r-- | src/i18n/en.js | 1 | ||||
| -rw-r--r-- | src/i18n/zh.js | 1 | ||||
| -rw-r--r-- | src/views/applications/edit.vue | 255 |
4 files changed, 199 insertions, 65 deletions
diff --git a/src/components/monaco/index.vue b/src/components/monaco/index.vue index 6333241..067c7b4 100644 --- a/src/components/monaco/index.vue +++ b/src/components/monaco/index.vue @@ -43,7 +43,7 @@ const props = defineProps({ }, }); -const emit = defineEmits(['update:modelValue', 'editorBlur']); +const emit = defineEmits(['update:modelValue', 'editorBlur', 'editorMarkers']); const systemStore = useSystemStore(); const monacoRef = ref(null); @@ -76,6 +76,9 @@ const initEditor = () => { editor.onDidBlurEditorText(() => { emit('editorBlur'); }); + monaco.editor.onDidChangeMarkers((e) => { + emit('editorMarkers', getModelMarkers()); + }); }; const getValue = () => { @@ -83,7 +86,7 @@ const getValue = () => { }; const getModelMarkers = () => { - return monaco.editor.getModelMarkers(); + return monaco.editor.getModelMarkers({ owner: 'json' }); }; // json schema校验 diff --git a/src/i18n/en.js b/src/i18n/en.js index 2f14bf9..e7d3376 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -191,6 +191,7 @@ export default { resolve_commit: 'You can resolve the merge conflict by editing the files directly. Commit these changes into {branch}.', commit_message: 'Commit message', commit_to_source: 'commit to source branch', + problems_file: '{number} problems in this file', }, pcap:{ pcap: 'PCAP', diff --git a/src/i18n/zh.js b/src/i18n/zh.js index f8934c3..2ea923b 100644 --- a/src/i18n/zh.js +++ b/src/i18n/zh.js @@ -191,6 +191,7 @@ export default { resolve_commit: '您可以通过直接编辑文件来解决合并冲突。将这些更改提交到{branch}。', commit_message: '提交消息', commit_to_source: '提交到源分支', + problems_file: '此文件中存在{number}个问题', }, pcap:{ pcap: 'PCAP', diff --git a/src/views/applications/edit.vue b/src/views/applications/edit.vue index f20219b..26b6e07 100644 --- a/src/views/applications/edit.vue +++ b/src/views/applications/edit.vue @@ -77,7 +77,14 @@ <svg> <use :xlink:href="getIcon(item.name)"></use> </svg> - <span class="file-item-name">{{ item.name }}</span> + <span + class="file-item-name" + :class="{ + error: errors[item.name], + }" + > + {{ item.name }} + </span> </div> <i @@ -106,7 +113,7 @@ <el-button type="primary" size="large" - :disabled="commiting" + :disabled="commitDisabled" @click="commit" > <span> @@ -124,39 +131,83 @@ <span>{{ t('application.change') }}</span> </template> <ul class="application-file-list"> - <li - class="application-file-item" + <el-popover v-for="item in modifiedList" :key="item.name" + :disabled="!errors[item.name]" + :offset="8" + trigger="hover" + placement="right-end" + popper-class="editor-problems-pop" > - <div class="file-item-info" :title="item.name"> - <svg> - <use :xlink:href="getIcon(item.name)"></use> - </svg> - <span class="file-item-name">{{ item.name }}</span> - </div> - <div class="flex-align-center" style="line-height: 100%"> - <i class="asw-icon icon-chehui" @click="recall(item)"></i> - <span - v-if="item.action == 'create'" - class="handleAction A" - > - A - </span> - <span - v-else-if="item.action == 'update'" - class="handleAction M" + <div v-if="errors[item.name]" class="editor-problems-list"> + <p class="editor-problems-title"> + {{ + t('application.problems_file', { + number: errors[item.name].length, + }) + }} + </p> + <p + v-for="error in errors[item.name]" + :key="error.message" + class="editor-problems-item" > - M - </span> - <span - v-else-if="item.action == 'delete'" - class="handleAction D" - > - D - </span> + <el-icon><CircleClose /></el-icon> + <span>{{ error.message }}</span> + </p> </div> - </li> + <template #reference> + <li class="application-file-item"> + <div class="file-item-info" :title="item.name"> + <svg> + <use :xlink:href="getIcon(item.name)"></use> + </svg> + <span + class="file-item-name" + :class="{ + error: errors[item.name], + }" + > + {{ item.name }} + </span> + <span + v-if="errors[item.name]" + class="file-item-error" + > + {{ errors[item.name].length }} + </span> + </div> + <div + class="flex-align-center" + style="line-height: 100%" + > + <i + class="asw-icon icon-chehui" + @click="recall(item)" + ></i> + <span + v-if="item.action == 'create'" + class="handleAction A" + > + A + </span> + <span + v-else-if="item.action == 'update'" + class="handleAction M" + > + M + </span> + <span + v-else-if="item.action == 'delete'" + class="handleAction D" + > + D + </span> + </div> + </li> + </template> + </el-popover> </ul> </el-collapse-item> </el-collapse> @@ -167,17 +218,54 @@ <div class="application-content"> <div class="application-content-header"> <el-scrollbar class="application-content-tablist"> - <div - class="application-content-tab" + <el-popover v-for="item in tabList" :key="item.name" - @click="tabChange(item)" - :class="{ active: fileActive.name == item.name }" - :title="item.name" + :disabled="!errors[item.name]" + :offset="8" + trigger="hover" + placement="bottom-start" + popper-class="editor-problems-pop" > - <span>{{ item.name }}</span> - <el-icon @click.stop="tabClose(item)"><Close /></el-icon> - </div> + <div v-if="errors[item.name]" class="editor-problems-list"> + <p class="editor-problems-title"> + {{ + t('application.problems_file', { + number: errors[item.name].length, + }) + }} + </p> + <p + v-for="error in errors[item.name]" + :key="error.message" + class="editor-problems-item" + > + <el-icon><CircleClose /></el-icon> + <span>{{ error.message }}</span> + </p> + </div> + <template #reference> + <div + class="application-content-tab" + :class="{ active: fileActive.name == item.name }" + @click="tabChange(item)" + :title="item.name" + > + <span + class="application-tab-name" + :class="{ + error: errors[item.name], + }" + > + {{ item.name }} + </span> + <span v-if="errors[item.name]" class="application-tab-error"> + {{ errors[item.name].length }} + </span> + <el-icon @click.stop="tabClose(item)"><Close /></el-icon> + </div> + </template> + </el-popover> </el-scrollbar> <div class="application-content-tool" @@ -251,6 +339,7 @@ v-model="fileActive.content" :language="getLanguage(fileActive.name)" @editorBlur="editorBlur" + @editorMarkers="editorMarkers($event, fileActive.name)" ></monaco> </div> <div v-if="showMind || showHistory" class="editor-code-right"> @@ -341,8 +430,6 @@ import { isImage, imageBase64Prefix, } from '@/utils/index'; -import Ajv from 'ajv'; -const ajv = new Ajv(); const router = useRouter(); const { t } = useI18n(); @@ -449,12 +536,16 @@ const deleteFile = (data) => { calcChange(); }; +const commitDisabled = computed(() => { + return Object.keys(errors.value).length || commiting.value; +}); + const lastCommitId = ref(''); const commitText = ref(''); const modifiedList = ref([]); const commiting = ref(false); const commit = async () => { - if (commiting.value) { + if (commitDisabled.value) { return; } commiting.value = true; @@ -519,27 +610,11 @@ const calcChange = () => { fileList.value.forEach((item) => { originFileList.value.forEach((subItem) => { if (item.path == subItem.path && item.content != subItem.content) { - let valid = true; - try { - if (item.name == 'meta.json') { - const data = JSON.parse(item.content); - const validate = ajv.compile(basicSchema); - valid = validate(data); - } else if (item.name == 'signature.json') { - const data = JSON.parse(item.content); - const validate = ajv.compile(signatureSchema); - valid = validate(data); - } - } catch (error) { - valid = false; - } - if (valid) { - const obj = { - ...item, - action: 'update', - }; - modifiedList.value.push(obj); - } + const obj = { + ...item, + action: 'update', + }; + modifiedList.value.push(obj); } }); }); @@ -549,6 +624,15 @@ const editorBlur = () => { calcChange(); }; +const errors = ref({}); +const editorMarkers = (markers, name) => { + if (markers.length) { + errors.value[name] = markers; + } else { + delete errors.value[name]; + } +}; + const tabList = ref([]); const tabChange = (data) => { if (fileActive.value.name == data.name) { @@ -941,6 +1025,14 @@ watch( white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + &.error { + color: var(--error); + } + } + .file-item-error { + font-size: 14px; + color: var(--error); + margin-left: 10px; } .icon-Delete, .icon-chehui { @@ -1032,13 +1124,21 @@ watch( font-weight: 700; } } - span { + .application-tab-name { font-size: 14px; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + &.error { + color: var(--error); + } + } + .application-tab-error { + font-size: 14px; + color: var(--error); + margin: 0 5px; } i { font-size: 16px; @@ -1151,3 +1251,32 @@ watch( } } </style> +<style lang="scss"> +.editor-problems-pop { + width: unset !important; + .editor-problems-list { + .editor-problems-title { + font-family: NotoSans-Bold; + font-size: 14px; + color: var(--text); + line-height: 22px; + font-weight: 700; + } + .editor-problems-item { + margin-top: 8px; + font-family: NotoSans-Bold; + font-size: 14px; + color: var(--text); + line-height: 16px; + font-weight: 400; + display: flex; + align-items: center; + .el-icon { + font-size: 16px; + color: var(--error); + margin-right: 4px; + } + } + } +} +</style> |
