summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzyh <[email protected]>2024-11-12 14:29:11 +0800
committerzyh <[email protected]>2024-11-12 14:29:11 +0800
commitd110e2f49d64e637cf678c8095c214f7347c8d5d (patch)
treeb40797615ddf56514e07a3489bc8871efd43d3f3
parent9cbc37f70af9b843ea9141337913838ac712aa30 (diff)
ASW-121 fix: application 因文件内容提交失败后,修改内容丢失
-rw-r--r--src/components/monaco/index.vue7
-rw-r--r--src/i18n/en.js1
-rw-r--r--src/i18n/zh.js1
-rw-r--r--src/views/applications/edit.vue255
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>