mdk-skills 2.3.26 → 2.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/web-ui/dist/assets/{index-qfXYFvR5.css → index-B3_Vjkxx.css} +1 -1
- package/scripts/web-ui/dist/assets/{index-D57SEgQI.js → index-Dr0-RSil.js} +26 -26
- package/scripts/web-ui/dist/index.html +2 -2
- package/scripts/web-ui/server.js +146 -51
- package/scripts/web-ui/src/api/skills.js +18 -0
- package/scripts/web-ui/src/components/SkillCard.vue +2 -0
- package/scripts/web-ui/src/views/Dashboard.vue +20 -6
- package/scripts/web-ui/src/views/SceneSwitch.vue +267 -11
|
@@ -70,6 +70,25 @@
|
|
|
70
70
|
<n-tag v-if="profile.id === activeId" type="success" size="small">
|
|
71
71
|
当前场景
|
|
72
72
|
</n-tag>
|
|
73
|
+
<n-popover
|
|
74
|
+
v-if="failedProfiles[profile.id]"
|
|
75
|
+
trigger="hover"
|
|
76
|
+
placement="top"
|
|
77
|
+
>
|
|
78
|
+
<template #trigger>
|
|
79
|
+
<n-tag type="warning" size="small" style="cursor:pointer">
|
|
80
|
+
⚠ 未就绪
|
|
81
|
+
</n-tag>
|
|
82
|
+
</template>
|
|
83
|
+
<div style="font-size:13px">
|
|
84
|
+
<div v-if="failedProfiles[profile.id]?.failedDelete?.length">
|
|
85
|
+
删除失败:{{ failedProfiles[profile.id].failedDelete.length }} 个
|
|
86
|
+
</div>
|
|
87
|
+
<div v-if="failedProfiles[profile.id]?.failedInstall?.length">
|
|
88
|
+
安装失败:{{ failedProfiles[profile.id].failedInstall.length }} 个
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</n-popover>
|
|
73
92
|
<n-popover
|
|
74
93
|
v-if="profile.skills !== null"
|
|
75
94
|
trigger="hover"
|
|
@@ -121,12 +140,13 @@
|
|
|
121
140
|
<!-- 切换确认弹窗 -->
|
|
122
141
|
<ModalComp
|
|
123
142
|
:show="showApplyConfirm"
|
|
124
|
-
title="确认切换场景"
|
|
143
|
+
:title="applyState === 'executing' ? '正在切换场景' : applyState === 'failed' ? '切换未完成' : '确认切换场景'"
|
|
125
144
|
width="540px"
|
|
126
145
|
:mask-closable="false"
|
|
127
|
-
@update:show="(v) => { if (!v) showApplyConfirm = false }"
|
|
146
|
+
@update:show="(v) => { if (!v && applyState !== 'executing') showApplyConfirm = false }"
|
|
128
147
|
>
|
|
129
|
-
|
|
148
|
+
<!-- 状态:diff 预览 -->
|
|
149
|
+
<div v-if="applyState === 'preview' && targetProfile" class="diff-preview">
|
|
130
150
|
<p class="diff-hint">
|
|
131
151
|
从 <strong>{{ currentProfileName }}</strong> 切换到
|
|
132
152
|
<strong>{{ targetProfile.name }}</strong>
|
|
@@ -182,11 +202,71 @@
|
|
|
182
202
|
</div>
|
|
183
203
|
</div>
|
|
184
204
|
</div>
|
|
205
|
+
|
|
206
|
+
<!-- 状态:执行中 -->
|
|
207
|
+
<div v-if="applyState === 'executing'" class="executing-state">
|
|
208
|
+
<div class="executing-spinner">
|
|
209
|
+
<n-spin size="large" />
|
|
210
|
+
</div>
|
|
211
|
+
<p class="executing-text">正在切换到「{{ targetProfile?.name }}」...</p>
|
|
212
|
+
<p class="executing-hint">处理中,请稍候</p>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<!-- 状态:失败详情 -->
|
|
216
|
+
<div v-if="applyState === 'failed'" class="diff-preview">
|
|
217
|
+
<p class="diff-hint">
|
|
218
|
+
切换到 <strong>{{ targetProfile?.name }}</strong> 时部分技能未能处理
|
|
219
|
+
</p>
|
|
220
|
+
<div v-if="failedDelete.length > 0" class="failure-section">
|
|
221
|
+
<div class="failure-title removal-failure">
|
|
222
|
+
<n-icon size="16" color="#d03050"><TrashOutline /></n-icon>
|
|
223
|
+
<span>删除失败(被程序占用)</span>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="failure-list">
|
|
226
|
+
<div v-for="name in failedDelete" :key="'del-'+name" class="failure-item">
|
|
227
|
+
<span class="failure-name">{{ name }}</span>
|
|
228
|
+
<n-button size="tiny" secondary type="warning" @click="retrySingle('delete', name)">
|
|
229
|
+
重试
|
|
230
|
+
</n-button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
<div v-if="failedInstall.length > 0" class="failure-section">
|
|
235
|
+
<div class="failure-title install-failure">
|
|
236
|
+
<n-icon size="16" color="#d03050"><AddOutline /></n-icon>
|
|
237
|
+
<span>安装失败</span>
|
|
238
|
+
</div>
|
|
239
|
+
<div class="failure-list">
|
|
240
|
+
<div v-for="name in failedInstall" :key="'inst-'+name" class="failure-item">
|
|
241
|
+
<span class="failure-name">{{ name }}</span>
|
|
242
|
+
<n-button size="tiny" secondary type="warning" @click="retrySingle('install', name)">
|
|
243
|
+
重试
|
|
244
|
+
</n-button>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
<p class="failure-tip">请关闭相关程序后重试,或打开技能目录手动处理</p>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
185
251
|
<template #footer>
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
252
|
+
<template v-if="applyState === 'preview'">
|
|
253
|
+
<n-button @click="showApplyConfirm = false">取消</n-button>
|
|
254
|
+
<n-button type="primary" :loading="confirmLoading" @click="onConfirmApply">
|
|
255
|
+
确认切换
|
|
256
|
+
</n-button>
|
|
257
|
+
</template>
|
|
258
|
+
<template v-if="applyState === 'failed'">
|
|
259
|
+
<n-button size="small" @click="openSkillsDestFolder">📂 打开技能目录</n-button>
|
|
260
|
+
<n-button size="small" @click="onAbandon">放弃</n-button>
|
|
261
|
+
<n-button
|
|
262
|
+
size="small"
|
|
263
|
+
type="primary"
|
|
264
|
+
:loading="confirmLoading"
|
|
265
|
+
@click="retryAll"
|
|
266
|
+
>
|
|
267
|
+
重试所有失败项
|
|
268
|
+
</n-button>
|
|
269
|
+
</template>
|
|
190
270
|
</template>
|
|
191
271
|
</ModalComp>
|
|
192
272
|
|
|
@@ -382,6 +462,9 @@ import {
|
|
|
382
462
|
deleteProfile,
|
|
383
463
|
installSkills,
|
|
384
464
|
getSkillsReadme,
|
|
465
|
+
retryDelete,
|
|
466
|
+
retryInstall,
|
|
467
|
+
openSkillsDest,
|
|
385
468
|
} from "../api/skills";
|
|
386
469
|
import { recordUsage } from "../utils/usage";
|
|
387
470
|
|
|
@@ -428,6 +511,12 @@ const showApplyConfirm = ref(false);
|
|
|
428
511
|
const confirmLoading = ref(false);
|
|
429
512
|
const targetProfile = ref(null);
|
|
430
513
|
const diffResult = ref({ added: [], removed: [], kept: [] });
|
|
514
|
+
// 弹窗状态:preview → executing → failed
|
|
515
|
+
const applyState = ref("preview");
|
|
516
|
+
const failedDelete = ref([]);
|
|
517
|
+
const failedInstall = ref([]);
|
|
518
|
+
// 卡片角标:记录每个场景的失败历史
|
|
519
|
+
const failedProfiles = ref({});
|
|
431
520
|
const currentProfileName = computed(() => {
|
|
432
521
|
if (!activeId.value) return "无";
|
|
433
522
|
const p = profiles.value.find((p) => p.id === activeId.value);
|
|
@@ -603,7 +692,11 @@ async function onApplyCustom() {
|
|
|
603
692
|
try {
|
|
604
693
|
const res = await installSkills(customSelected.value);
|
|
605
694
|
if (res.ok) {
|
|
606
|
-
|
|
695
|
+
if (res.locked?.length) {
|
|
696
|
+
message.warning(`已安装 ${customSelected.value.length - res.locked.length} 个技能,${res.locked.length} 个被占用`);
|
|
697
|
+
} else {
|
|
698
|
+
message.success(`已安装 ${customSelected.value.length} 个技能`);
|
|
699
|
+
}
|
|
607
700
|
activeId.value = "custom";
|
|
608
701
|
showCustom.value = false;
|
|
609
702
|
customSelected.value.forEach(recordUsage);
|
|
@@ -677,32 +770,130 @@ function calcDiff(profile) {
|
|
|
677
770
|
function onApply(profile) {
|
|
678
771
|
targetProfile.value = profile;
|
|
679
772
|
diffResult.value = calcDiff(profile);
|
|
773
|
+
failedDelete.value = [];
|
|
774
|
+
failedInstall.value = [];
|
|
775
|
+
applyState.value = "preview";
|
|
680
776
|
showApplyConfirm.value = true;
|
|
681
777
|
}
|
|
682
778
|
|
|
683
779
|
async function onConfirmApply() {
|
|
684
780
|
if (!targetProfile.value) return;
|
|
781
|
+
applyState.value = "executing";
|
|
685
782
|
confirmLoading.value = true;
|
|
686
783
|
applying.value = targetProfile.value.id;
|
|
687
784
|
try {
|
|
688
785
|
const res = await applyProfile(targetProfile.value.id);
|
|
689
786
|
if (res.ok) {
|
|
690
787
|
activeId.value = targetProfile.value.id;
|
|
691
|
-
|
|
788
|
+
failedDelete.value = res.failedDelete || [];
|
|
789
|
+
failedInstall.value = res.failedInstall || [];
|
|
790
|
+
const hasFailed = failedDelete.value.length > 0 || failedInstall.value.length > 0;
|
|
791
|
+
|
|
792
|
+
if (hasFailed) {
|
|
793
|
+
applyState.value = "failed";
|
|
794
|
+
// 记录失败信息到卡片角标
|
|
795
|
+
failedProfiles.value = {
|
|
796
|
+
...failedProfiles.value,
|
|
797
|
+
[targetProfile.value.id]: {
|
|
798
|
+
failedDelete: [...failedDelete.value],
|
|
799
|
+
failedInstall: [...failedInstall.value],
|
|
800
|
+
},
|
|
801
|
+
};
|
|
802
|
+
} else {
|
|
803
|
+
// 全部成功,清理该场景的失败记录
|
|
804
|
+
const newFailed = { ...failedProfiles.value };
|
|
805
|
+
delete newFailed[targetProfile.value.id];
|
|
806
|
+
failedProfiles.value = newFailed;
|
|
807
|
+
message.success(`已切换到「${targetProfile.value.name}」`);
|
|
808
|
+
showApplyConfirm.value = false;
|
|
809
|
+
}
|
|
692
810
|
(targetProfile.value.skills || []).forEach(recordUsage);
|
|
693
|
-
showApplyConfirm.value = false;
|
|
694
811
|
emit("refresh");
|
|
695
|
-
// 刷新技能状态,确保下次差异计算基于最新数据
|
|
696
812
|
allSkills.value = await getSkills();
|
|
697
813
|
}
|
|
698
814
|
} catch {
|
|
699
815
|
message.error("切换失败");
|
|
816
|
+
applyState.value = "preview";
|
|
700
817
|
} finally {
|
|
701
818
|
confirmLoading.value = false;
|
|
702
819
|
applying.value = null;
|
|
703
820
|
}
|
|
704
821
|
}
|
|
705
822
|
|
|
823
|
+
// 单技能重试
|
|
824
|
+
async function retrySingle(type, name) {
|
|
825
|
+
const fn = type === "delete" ? retryDelete : retryInstall;
|
|
826
|
+
const label = type === "delete" ? "删除" : "安装";
|
|
827
|
+
try {
|
|
828
|
+
const res = await fn(name);
|
|
829
|
+
if (res.ok) {
|
|
830
|
+
message.success(`技能 "${name}" ${label}成功`);
|
|
831
|
+
if (type === "delete") {
|
|
832
|
+
failedDelete.value = failedDelete.value.filter((n) => n !== name);
|
|
833
|
+
} else {
|
|
834
|
+
failedInstall.value = failedInstall.value.filter((n) => n !== name);
|
|
835
|
+
}
|
|
836
|
+
// 更新角标
|
|
837
|
+
const pid = targetProfile.value?.id;
|
|
838
|
+
if (pid) {
|
|
839
|
+
const updated = { ...failedProfiles.value };
|
|
840
|
+
if (updated[pid]) {
|
|
841
|
+
updated[pid] = {
|
|
842
|
+
failedDelete: [...failedDelete.value],
|
|
843
|
+
failedInstall: [...failedInstall.value],
|
|
844
|
+
};
|
|
845
|
+
if (updated[pid].failedDelete.length === 0 && updated[pid].failedInstall.length === 0) {
|
|
846
|
+
delete updated[pid];
|
|
847
|
+
}
|
|
848
|
+
failedProfiles.value = updated;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
// 如果所有失败都解决了,自动关闭弹窗
|
|
852
|
+
if (failedDelete.value.length === 0 && failedInstall.value.length === 0) {
|
|
853
|
+
message.success(`已切换到「${targetProfile.value?.name}」`);
|
|
854
|
+
showApplyConfirm.value = false;
|
|
855
|
+
}
|
|
856
|
+
} else if (res.locked) {
|
|
857
|
+
message.warning(`技能 "${name}" 仍被占用,请关闭相关程序后重试`);
|
|
858
|
+
} else {
|
|
859
|
+
message.error(res.error || `${label}失败`);
|
|
860
|
+
}
|
|
861
|
+
} catch {
|
|
862
|
+
message.error(`${label}失败`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// 重试所有失败项
|
|
867
|
+
async function retryAll() {
|
|
868
|
+
// 先重试所有安装失败,再重试所有删除失败
|
|
869
|
+
for (const name of [...failedInstall.value]) {
|
|
870
|
+
await retrySingle("install", name);
|
|
871
|
+
if (showApplyConfirm.value === false) return; // 弹窗已关闭
|
|
872
|
+
}
|
|
873
|
+
for (const name of [...failedDelete.value]) {
|
|
874
|
+
await retrySingle("delete", name);
|
|
875
|
+
if (showApplyConfirm.value === false) return;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// 放弃切换
|
|
880
|
+
function onAbandon() {
|
|
881
|
+
showApplyConfirm.value = false;
|
|
882
|
+
const pid = targetProfile.value?.id;
|
|
883
|
+
if (pid && (failedDelete.value.length > 0 || failedInstall.value.length > 0)) {
|
|
884
|
+
message.warning(
|
|
885
|
+
`部分技能未就绪:` +
|
|
886
|
+
(failedDelete.value.length ? `删除失败 ${failedDelete.value.length} 个、` : "") +
|
|
887
|
+
(failedInstall.value.length ? `安装失败 ${failedInstall.value.length} 个` : "").replace(/、$/, "")
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// 打开项目技能目录
|
|
893
|
+
function openSkillsDestFolder() {
|
|
894
|
+
openSkillsDest().catch(() => message.error("打开目录失败"));
|
|
895
|
+
}
|
|
896
|
+
|
|
706
897
|
onMounted(() => {
|
|
707
898
|
loadData();
|
|
708
899
|
loadSkillsReadme();
|
|
@@ -877,6 +1068,62 @@ onMounted(() => {
|
|
|
877
1068
|
.search-input {
|
|
878
1069
|
margin-bottom: 8px;
|
|
879
1070
|
}
|
|
1071
|
+
|
|
1072
|
+
/* 执行中状态 */
|
|
1073
|
+
.executing-state {
|
|
1074
|
+
display: flex;
|
|
1075
|
+
flex-direction: column;
|
|
1076
|
+
align-items: center;
|
|
1077
|
+
padding: 32px 0;
|
|
1078
|
+
gap: 12px;
|
|
1079
|
+
}
|
|
1080
|
+
.executing-text {
|
|
1081
|
+
margin: 0;
|
|
1082
|
+
font-size: 15px;
|
|
1083
|
+
font-weight: 600;
|
|
1084
|
+
}
|
|
1085
|
+
.executing-hint {
|
|
1086
|
+
margin: 0;
|
|
1087
|
+
font-size: 13px;
|
|
1088
|
+
color: #999;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/* 失败区域 */
|
|
1092
|
+
.failure-section {
|
|
1093
|
+
display: flex;
|
|
1094
|
+
flex-direction: column;
|
|
1095
|
+
gap: 6px;
|
|
1096
|
+
margin-top: 4px;
|
|
1097
|
+
}
|
|
1098
|
+
.failure-title {
|
|
1099
|
+
display: flex;
|
|
1100
|
+
align-items: center;
|
|
1101
|
+
gap: 4px;
|
|
1102
|
+
font-size: 13px;
|
|
1103
|
+
font-weight: 600;
|
|
1104
|
+
}
|
|
1105
|
+
.failure-list {
|
|
1106
|
+
display: flex;
|
|
1107
|
+
flex-direction: column;
|
|
1108
|
+
gap: 4px;
|
|
1109
|
+
}
|
|
1110
|
+
.failure-item {
|
|
1111
|
+
display: flex;
|
|
1112
|
+
align-items: center;
|
|
1113
|
+
justify-content: space-between;
|
|
1114
|
+
padding: 4px 8px;
|
|
1115
|
+
background: #fff1f0;
|
|
1116
|
+
border-radius: 4px;
|
|
1117
|
+
font-size: 13px;
|
|
1118
|
+
}
|
|
1119
|
+
.failure-name {
|
|
1120
|
+
font-weight: 500;
|
|
1121
|
+
}
|
|
1122
|
+
.failure-tip {
|
|
1123
|
+
margin: 4px 0 0;
|
|
1124
|
+
font-size: 12px;
|
|
1125
|
+
color: #999;
|
|
1126
|
+
}
|
|
880
1127
|
</style>
|
|
881
1128
|
|
|
882
1129
|
<style>
|
|
@@ -910,4 +1157,13 @@ onMounted(() => {
|
|
|
910
1157
|
background: #8a6d00;
|
|
911
1158
|
color: #fff;
|
|
912
1159
|
}
|
|
1160
|
+
[data-theme="dark"] .failure-item {
|
|
1161
|
+
background: rgba(208, 48, 80, 0.15);
|
|
1162
|
+
}
|
|
1163
|
+
[data-theme="dark"] .executing-hint {
|
|
1164
|
+
color: #6c7086;
|
|
1165
|
+
}
|
|
1166
|
+
[data-theme="dark"] .failure-tip {
|
|
1167
|
+
color: #6c7086;
|
|
1168
|
+
}
|
|
913
1169
|
</style>
|