Skip to content

Commit 498d3e4

Browse files
feat(front): 谱面管理左侧列表搜索、1440 断点修复、右侧编辑区空态 (#80)
* feat(front): 谱面管理左侧列表加搜索框 - 按名称 / 作曲 / 谱师 / ID 过滤,命中口径复用 utils/musicSearch 的 matchMusicKeyword, 和批量操作页保持同一口径 - 过滤只作用于渲染列表(displayedMusic),selectedMusic 仍由未过滤的 musicList 派生, 所以选中曲被过滤掉时右侧编辑面板不会变空 - 无匹配显示空态;有内容时显示清空按钮,Esc 也能清空;输入法组字中按 Esc 不清空 - 修既有跳转失效(只选中不滚动):批量页调用方只等 100ms,而 tab 切换是 0.3s 的 out-in 过渡,那时 VList 还没挂载。改成等 ref 就绪(setTimeout 轮询,2s 兜底) - 修既有跳转失效(左侧停在目录管理时):跳转先把 leftPanel 切回列表再定位 - 搜索框占位符去掉「别名」:matchMusicKeyword 本来就不搜别名,且这个 key 被批量操作页 与工具页共用,三处文案随实现一起对齐 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * fix(front): 修 1440 宽窗口下谱面管理左侧面板不展开 - 抽屉布局原来写 max-[1440px],而汉堡按钮是 min-[1440px]:hidden,两边在正好 1440 宽时 同时命中:左侧被 translate 推出屏幕、打开它的按钮又被隐藏,谱面管理页完全没法用。 1440 宽的屏幕把窗口最大化就会踩到 - 抽屉改成 max-[1439px],与 min-[1440px] 以 1440 为界且不重叠;1440 及以上固定展开 - 1439 及以下行为不变(原来的 max-width:1440 同样覆盖 1439),只有正好 1440 从坏掉变成桌面布局 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * feat(front): A000 目录下编辑按钮保留但禁用 - 原来选中 A000(游戏本体目录)时,右上角直接换成一句提示文案,按钮消失,位置跟着跳 - 改成按钮始终在,用 pointer-events-none + op-50 禁用;提示改为灰色小字放在按钮下面 - 复制与导出不在此列,它不改 A000 的内容,保持可用 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * feat(front): 未选中谱面时右侧给出空态提示 - 没选中任何曲目时右侧编辑区原本整块空白,看不出是「没选」还是「没加载出来」 - 补一个居中的「请选择谱面以编辑」,0.5 不透明度、稍大字号(三语同步) AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * fix(front): A000 只读改成真正拦住,不再只靠样式 浏览器审查指出 pointer-events-none 只挡鼠标:A000 下用 Tab 聚焦后回车、或按 Ctrl+S, 仍能触发导入/保存,会往游戏本体目录写数据。原来的「禁用」只有视觉效果。 - 新增 store.refs 的 isReadOnlyDir(selectedADir === 'A000'),所有写入口先看它 - 保存:按钮 disabled,save() 里再判一次(Ctrl+S 走的就是这个函数) - 删除 / 导入 / 创建:按钮 disabled + 入口函数早退 - 替换谱面(ChartPanel 两个入口)与封面替换(JacketBox,含拖拽路径):入口早退 - 创建曲目对话框在确认时重新判目录,堵住「打开后别处切到 A000 再确认」的竞态 验证:A000 下四个按钮均为 disabled,模拟 Ctrl+S 与点击均未产生任何写请求(用一次 性 mock 观察,未打真实后端);pnpm typecheck / build 通过。 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * fix(front): 搜索支持补零 ID,跳转时拉开抽屉并作废过期跳转 - 列表里的 ID 是补零的 6 位显示(000123),但搜索只匹配原始数值,搜 000123 无结果; matchMusicKeyword 现在同时匹配补零形式(批量操作页与工具页搜索一并修好) - 窄屏(<1440)下列表是抽屉:批量跳转只切了 leftPanel,抽屉仍关着,用户看不到目标; 抽屉状态移到 views/Charts/refs.ts,跳转时一并拉开 - 等待列表挂载期间用户手动点了别的曲目时,之前那次跳转不再作数(避免选中的是 A、 列表滚到 B) 验证:mock 数据下 000123→标准谱、010123→DX 谱、123→两者;typecheck / build 通过。 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * fix(front): 导入与替换谱面固定目标目录,不再写入时重读选中目录 审查指出:入口的只读判断和真正的写入之间隔着若干异步步骤(选文件、确认弹窗、逐条处理), 而写入时又读了一次 selectedADir。期间目录被切到 A000,写操作就会落到只读目录。 - 导入:startProcess 入口快照 importDir,之后 AddMusic / ImportChart / SetAudio / SetAudioPreview / SetMusicJacket / SetMovie / 回滚用的 DeleteMusic 全部用它 - 替换谱面:prepareReplaceChart 时快照 replaceDir,确认后 ReplaceChart 用它 这样「中途切目录」只会影响下一次导入,不会把正在进行的那次带偏。 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * fix(front): 封面写入用曲目自身目录,A000 不显示可点手型,抽屉随卸载复位 - JacketBox 的 SetMusicJacket 原来传的是「当前选中目录」;选文件是异步的,期间切目录会 写错地方。改成传曲目自己的 assetDir(本来也该如此) - A000 下封面不再显示 cursor-pointer:现在点了没反应,手型会让人以为操作失败 - musicDrawerOpen 是模块级状态,切到别的侧栏页再回来会保留;Charts 卸载时复位 AI-assisted-by: DeepSeek-V4-Flash (DeepSeek Harness) * fix(front): 搜索清空按钮悬停时不再错位 - 绝对定位居中改用 top-0 bottom-0 + my-auto,不再依赖 -translate-y-1/2 - 原因:@munet/ui 全局的 button:hover { transform: scale(1.02) } 特异性更高,会把 UnoCSS 的 translate 整条顶掉,导致悬停时按钮向下掉半个高度 * fix(front): 搜索结果框右侧留出清空按钮的位置 - innerClass 里的 pr-10 改用 pr-10! - 原因:MuNET-UI TextInput 的 .wrapper input 选择器特异性 0,1,1 高于普通工具类,原来的 padding-right 没生效,长关键词尾部会被清空按钮盖住 * fix(front): 自动保存用曲目自身目录,且不写入只读的 A000 - saveMusicIfNeeded 由窗口失焦和切歌自动触发,原来用 selectedADir,切目录后会写错目录 - 增加 A000 拦截,避免待保存的内容被写进只读目录 * fix(front): 谱面难度字段的写入也拦住 A000 - ChartPanel 的 sync 直接调 EditChart* 接口,之前只靠输入组件的 disabled 注入 - 入口处补 isReadOnlyDir 判断,与保存 / 删除 / 导入的处理保持一致 * fix(front): 点清空搜索后仍能继续输入 - 在清空按钮上阻止 mousedown 的默认行为,焦点不会离开输入框 - 否则点完清空焦点落在按钮上,想接着打字还得再点一次输入框 * fix(front): 自动保存只认被编辑过的那条同 ID 曲目 - 同 ID 曲目可能同时存在于 A000 和用户目录,按 id 直接 find 会命中别目录那条,modified 为 false 导致自动保存被误判成不需要保存、直接跳过 - 查询加上 modified 条件定位真正被编辑的条目,资产目录仍取曲目自身的 assetDir --------- Co-authored-by: Clansty <i@gao4.pw>
1 parent 0198835 commit 498d3e4

16 files changed

Lines changed: 216 additions & 65 deletions

File tree

‎MaiChartManager/Front/src/components/DragDropDispatcher/ReplaceChartModal.tsx‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Button, Modal, Radio, showTransactionalDialog, addToast } from '@munet/
44
import { computed, defineComponent, ref, shallowRef } from 'vue';
55
import JacketBox from '../JacketBox';
66
import { DIFFICULTY } from '@/consts';
7+
import { isReadOnlyDir } from "@/store/refs";
78
import api from '@/client/api';
89
import CheckingModal from "@/views/Charts/ImportCreateChartButton/ImportChartButton/CheckingModal";
910
import LevelTagsDisplay from "@/components/LevelTagsDisplay";
@@ -23,6 +24,10 @@ export let prepareReplaceChart = async (fileHandle?: FileSystemFileHandle, side?
2324
export default defineComponent({
2425
setup() {
2526

27+
// 替换一旦开始就把目标目录固定下来,确认时不能再读 selectedADir:
28+
// 期间切到只读的 A000 会把写操作带偏
29+
let replaceDir = selectedADir.value;
30+
2631
const checking = ref(false);
2732
const file = shallowRef<File | null>(null);
2833
const show = ref<"" | "ma2" | "maidata" | "failed">("");
@@ -40,6 +45,9 @@ export default defineComponent({
4045

4146
// 注:本功能的逻辑是,如果选择的是ma2文件,则只替换指定难度的谱面;如果选择的是maidata,则替换整首歌的所有难度。
4247
prepareReplaceChart = async (fHandle?: FileSystemFileHandle, side?: ChartSide) => {
48+
// A000 只读:替换谱面是写操作,入口处直接挡掉
49+
if (isReadOnlyDir.value) return;
50+
replaceDir = selectedADir.value;
4351
targetSide.value = side;
4452
let selectedFile: File;
4553
if (!fHandle) {
@@ -86,7 +94,7 @@ export default defineComponent({
8694
const level = replacingMaidata ? -1 : selectedLevel.value;
8795
const side = replacingMaidata ? undefined : targetSide.value;
8896
show.value = "";
89-
const result = (await api.ReplaceChart(selectMusicId.value, level, selectedADir.value, {
97+
const result = (await api.ReplaceChart(selectMusicId.value, level, replaceDir, {
9098
file: uploadFile,
9199
shift: tempOption.value.shift,
92100
side,

‎MaiChartManager/Front/src/components/JacketBox.tsx‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { computed, defineComponent, PropType, ref } from "vue";
22
import noJacket from "@/assets/noJacket.webp";
33
import api, { getUrl } from "@/client/api";
44
import { showTransactionalDialog } from "@munet/ui";
5-
import { globalCapture, selectedADir, selectedMusic } from "@/store/refs";
5+
import { globalCapture, isReadOnlyDir, selectedADir, selectedMusic } from "@/store/refs";
66
import { MusicXmlWithABJacket } from "@/client/apiGen";
77
import { useI18n } from 'vue-i18n';
88
import { pickFile } from "@/utils/pickFile";
@@ -24,6 +24,8 @@ export default defineComponent({
2424
if (props.upload)
2525
upload = async (fileHandle?: FileSystemFileHandle) => {
2626
if (!props.upload) return;
27+
// A000 只读:点封面或拖拽换图都会走到这里
28+
if (isReadOnlyDir.value) return;
2729
try {
2830
let file: File;
2931
if (!fileHandle) {
@@ -35,7 +37,8 @@ export default defineComponent({
3537
file = await fileHandle.getFile();
3638
}
3739

38-
const res = await api.SetMusicJacket(props.info.id!, selectedADir.value, { file });
40+
// 用曲目自己的 assetDir:选文件是异步的,期间目录可能被切走/切到 A000
41+
const res = await api.SetMusicJacket(props.info.id!, props.info.assetDir ?? selectedADir.value, { file });
3942
if (res.error) {
4043
const error = res.error as any;
4144
await showTransactionalDialog(t('jacket.setFailed'), error.message || error, undefined, true);
@@ -56,6 +59,7 @@ export default defineComponent({
5659
}
5760
}
5861

59-
return () => <img src={jacketUrl.value} class={`object-fill rounded-lg ${props.upload && 'cursor-pointer'}`} onClick={props.upload ? () => upload() : undefined} />
62+
const canUpload = computed(() => props.upload && !isReadOnlyDir.value);
63+
return () => <img src={jacketUrl.value} class={`object-fill rounded-lg ${canUpload.value && 'cursor-pointer'}`} onClick={canUpload.value ? () => upload() : undefined} />
6064
}
6165
})

‎MaiChartManager/Front/src/locales/en.yaml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ assetDir:
6060
music:
6161
list:
6262
title: Chart List
63-
searchPlaceholder: Search title / composer / charter / alias / ID
63+
searchPlaceholder: Search title / composer / charter / ID
6464
tableTitle: Title
6565
tableCover: Cover
6666
tableVersion: Version
@@ -71,6 +71,8 @@ music:
7171
filterStandard: Standard
7272
filterDX: DX
7373
filterUtage: Utage
74+
noMatch: No matching songs
75+
clearSearch: Clear search
7476
unknown: Unknown
7577
filterBasic: Basic
7678
filterMaster: Master
@@ -81,6 +83,7 @@ music:
8183
sortByName: Name
8284
sortByVersion: Release
8385
edit:
86+
selectHint: Select a chart to edit
8487
name: Song Title
8588
artist: Artist
8689
bpm: BPM

‎MaiChartManager/Front/src/locales/zh-TW.yaml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ assetDir:
5858
music:
5959
list:
6060
title: 譜面列表
61-
searchPlaceholder: 搜尋名稱 / 作曲 / 譜師 / 別名 / ID
61+
searchPlaceholder: 搜尋名稱 / 作曲 / 譜師 / ID
6262
tableTitle: 標題
6363
tableCover: 封面
6464
tableVersion: 版本
@@ -69,6 +69,8 @@ music:
6969
filterStandard: 標準
7070
filterDX: DX
7171
filterUtage: 宴會場
72+
noMatch: 沒有符合的曲目
73+
clearSearch: 清除搜尋
7274
unknown: 未知
7375
filterBasic: 綠
7476
filterMaster: 紫
@@ -79,6 +81,7 @@ music:
7981
sortByName: 名稱
8082
sortByVersion: 更新時間
8183
edit:
84+
selectHint: 請選擇譜面以編輯
8285
name: 歌曲名稱
8386
artist: 作者
8487
bpm: BPM

‎MaiChartManager/Front/src/locales/zh.yaml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ assetDir:
5858
music:
5959
list:
6060
title: 谱面列表
61-
searchPlaceholder: 搜索名称 / 作曲 / 谱师 / 别名 / ID
61+
searchPlaceholder: 搜索名称 / 作曲 / 谱师 / ID
6262
tableTitle: 标题
6363
tableCover: 封面
6464
tableVersion: 版本
@@ -69,6 +69,8 @@ music:
6969
filterStandard: 标准
7070
filterDX: DX
7171
filterUtage: 宴会场
72+
noMatch: 没有匹配的曲目
73+
clearSearch: 清空搜索
7274
unknown: 未知
7375
filterBasic: 绿
7476
filterMaster: 紫
@@ -79,6 +81,7 @@ music:
7981
sortByName: 名称
8082
sortByVersion: 更新时间
8183
edit:
84+
selectHint: 请选择谱面以编辑
8285
name: 名称
8386
artist: 作者
8487
bpm: BPM

‎MaiChartManager/Front/src/store/refs.ts‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export const musicList = computed(() =>
7777
export const selectedMusic = computed(() => musicList.value.find(m => m.id === selectMusicId.value));
7878
export const selectedLevel = ref(0);
7979

80+
// A000 是游戏本体目录,只读。所有写入入口(保存/删除/导入/创建/替换谱面)都要先看它,
81+
// 只靠禁用样式挡不住键盘和 Ctrl+S 这类快捷操作。
82+
export const isReadOnlyDir = computed(() => selectedADir.value === 'A000');
83+
8084
// 如ReplaceChart等后端接口可能会涉及对MusicXml中的信息进行修改后保存。此时前端updateMusicList时会发现相关数据出现变更,触发了MusicEdit/ChartPanel中的watch,造成发送多余的edit请求、同时modified也被错误设置为true。
8185
// 因此,对于涉及内部对xml进行修改后会自动保存的后端接口,可以在updateMusicList期间打开本选项,以阻止MusicEdit/ChartPanel中的sync动作。
8286
export const disableSync = ref(false);
@@ -85,9 +89,13 @@ export { aquaMaiConfig } from "@/views/ModManager/refs";
8589

8690
export const saveMusicIfNeeded = async (id: number) => {
8791
if (!id) return;
88-
const music = musicListAll.value.find(m => m.id === id);
89-
if (!music?.modified) return;
90-
await api.SaveMusic(id, selectedADir.value);
92+
// 同 ID 曲目可能同时存在于多个目录(A000 与用户目录),按 id 单独取会命中别目录那条、
93+
// 因它的 modified 是 false 而误判成「不用保存」。modified 只会标在正在编辑的曲目上,拿它定位即可。
94+
const music = musicListAll.value.find(m => m.id === id && m.modified);
95+
// 写入用曲目自己的 assetDir:这里是被失焦 / 切歌自动触发的,那一刻 selectedADir 可能已经变了,
96+
// 切到只读的 A000 时更不能把待保存的内容写进去
97+
if (!music?.assetDir || music.assetDir === 'A000') return;
98+
await api.SaveMusic(id, music.assetDir);
9199
await updateMusicList();
92100
}
93101

‎MaiChartManager/Front/src/utils/musicSearch.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ export const matchMusicKeyword = (music: MusicXmlWithABJacket, keyword: string)
2323
const value = keyword.trim().toLowerCase();
2424
if (!value) return true;
2525

26+
// 列表里的 ID 按 6 位补零显示(000123),所以补零形式也要能搜到
27+
const rawId = (music.id ?? 0).toString();
28+
2629
return (music.name ?? '').toLowerCase().includes(value)
2730
|| (music.artist ?? '').toLowerCase().includes(value)
28-
|| (music.id ?? 0).toString().includes(value)
31+
|| rawId.includes(value)
32+
|| rawId.padStart(6, '0').includes(value)
2933
|| (music.charts ?? []).some(chart => chart.designer?.toLowerCase().includes(value));
3034
};

‎MaiChartManager/Front/src/views/Charts/ImportCreateChartButton/CreateMusicModal.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { computed, defineComponent, ref, watch } from "vue";
22
import { Button, Modal, NumberInput, Radio } from "@munet/ui";
3-
import { assetDirs, musicList, selectedADir, selectMusicId, updateMusicList } from "@/store/refs";
3+
import { assetDirs, isReadOnlyDir, musicList, selectedADir, selectMusicId, updateMusicList } from "@/store/refs";
44
import dxIcon from "@/assets/dxIcon.png";
55
import stdIcon from "@/assets/stdIcon.png";
66
import api from "@/client/api";
@@ -35,6 +35,8 @@ export default defineComponent({
3535
})
3636

3737
const save = async () => {
38+
// 对话框打开期间目录可能被切到只读的 A000,保存前重新判一次
39+
if (isReadOnlyDir.value) return;
3840
show.value = false;
3941
await api.AddMusic(id.value, selectedADir.value);
4042
await updateMusicList();

‎MaiChartManager/Front/src/views/Charts/ImportCreateChartButton/ImportChartButton/index.tsx‎

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import SelectFileTypeTip from "./SelectFileTypeTip";
44
import { LicenseStatus, MessageLevel } from "@/client/apiGen";
55
import CheckingModal from "./CheckingModal";
66
import api, { getUrl } from "@/client/api";
7-
import { globalCapture, selectedADir, selectMusicId, updateMusicList, version as appVersion } from "@/store/refs";
7+
import { globalCapture, isReadOnlyDir, selectedADir, selectMusicId, updateMusicList, version as appVersion } from "@/store/refs";
88
import { appSettings } from "@/store/settings";
99
import ErrorDisplayIdInput from "./ErrorDisplayIdInput";
1010
import ImportStepDisplay from "./ImportStepDisplay";
@@ -97,14 +97,18 @@ export default defineComponent({
9797
return !reject;
9898
}
9999

100+
// 导入一旦开始就把目标目录固定下来:中途切目录(尤其是切到只读的 A000)
101+
// 也不能把写操作带偏,所有写入都用这个快照
102+
let importDir = selectedADir.value;
103+
100104
const uploadMovie = (id: number, movie: File, offset: number) => new Promise<void>((resolve, reject) => {
101105
currentMovieProgress.value = 0;
102106
const body = new FormData();
103107
body.append('padding', offset.toString());
104108
body.append('file', movie);
105109
body.append('file', movie);
106110
const controller = new AbortController();
107-
fetchEventSource(getUrl(`SetMovieApi/${selectedADir.value}/${id}`), {
111+
fetchEventSource(getUrl(`SetMovieApi/${importDir}/${id}`), {
108112
signal: controller.signal,
109113
method: 'PUT',
110114
body,
@@ -144,7 +148,7 @@ export default defineComponent({
144148
try {
145149
music.importStep = IMPORT_STEP.create;
146150

147-
const createRet = (await api.AddMusic(music.id, selectedADir.value)).data;
151+
const createRet = (await api.AddMusic(music.id, importDir)).data;
148152
if (createRet) throw new Error(createRet);
149153

150154
music.importStep = IMPORT_STEP.chart;
@@ -163,13 +167,13 @@ export default defineComponent({
163167
utageRightLevel: music.utageMapping.rightLevel,
164168
} : {}),
165169
debug: import.meta.env.DEV,
166-
assetDir: selectedADir.value,
170+
assetDir: importDir,
167171
})).data;
168172

169173
errors.value.push(...res.errors!.map(it => ({ ...it, name: music.name })));
170174
if (res.fatal) {
171175
try {
172-
await api.DeleteMusic(music.id, selectedADir.value);
176+
await api.DeleteMusic(music.id, importDir);
173177
} catch {
174178
}
175179
return;
@@ -181,14 +185,14 @@ export default defineComponent({
181185
// 音频上应该应用的延迟audioPadding = 谱面上应用的延迟chartPadding - &first
182186
let audioPadding = chartPadding - music.first;
183187

184-
await api.SetAudio(music.id, selectedADir.value, { file: music.track, padding: audioPadding, ignoreGapless: !!tempOptions.value.ignoreGapless });
188+
await api.SetAudio(music.id, importDir, { file: music.track, padding: audioPadding, ignoreGapless: !!tempOptions.value.ignoreGapless });
185189
if (music.previewTime) {
186190
// 因为&demo_seek和&demo_len都是相对于原始音源的,所以这里的时间必须要扣掉上面SetAudio时应用的padding。
187191
// 根据SetAudioApi的定义,padding为正表示歌曲前面被加了空白,因此原本的预览段在新的裁剪后的音频中的位置也要相应后移;padding为负反之。
188192
// 因此,直接给startTime和endTime都加上audioPadding即可。
189193
music.previewTime.startTime! += audioPadding;
190194
music.previewTime.endTime! += audioPadding
191-
await api.SetAudioPreview(music.id, selectedADir.value, music.previewTime);
195+
await api.SetAudioPreview(music.id, importDir, music.previewTime);
192196
}
193197

194198
if (music.movie && !tempOptions.value.disableBga) {
@@ -202,7 +206,7 @@ export default defineComponent({
202206
}
203207

204208
music.importStep = IMPORT_STEP.jacket;
205-
if (music.bg) await api.SetMusicJacket(music.id, selectedADir.value, { file: music.bg });
209+
if (music.bg) await api.SetMusicJacket(music.id, importDir, { file: music.bg });
206210

207211
music.importStep = IMPORT_STEP.finish;
208212
} catch (e) {
@@ -218,14 +222,17 @@ export default defineComponent({
218222
// 如果是在创建乐曲这步就挂了,说明乐曲XML没有创建成功,则不需要删除乐曲。
219223
// 否则,在ID冲突的情况下,会把原本的乐曲给删除掉,见 https://github.com/MuNET-OSS/MaiChartManager/issues/34
220224
try {
221-
await api.DeleteMusic(music.id, selectedADir.value);
225+
await api.DeleteMusic(music.id, importDir);
222226
} catch {
223227
}
224228
}
225229
}
226230
}
227231

228232
startProcess = async (dir?: ImportDirectory | ImportDirectory[]) => {
233+
// A000 只读:拖拽进来的导入也要挡住,不只是点按钮
234+
if (isReadOnlyDir.value) return;
235+
importDir = selectedADir.value;
229236
let id = getNextUnusedMusicId();
230237
const usedIds = [] as number[];
231238
errors.value = [];
@@ -305,7 +312,7 @@ export default defineComponent({
305312
}
306313
}
307314

308-
return () => <Button onClick={() => startProcess()} variant="secondary">
315+
return () => <Button onClick={() => startProcess()} disabled={isReadOnlyDir.value} variant="secondary">
309316
{t('chart.import.title')}
310317
<SelectFileTypeTip show={step.value === STEP.selectFile} closeModal={closeModal} />
311318
<CheckingModal title={t('chart.import.checkingTitle')} show={step.value === STEP.checking} closeModal={closeModal} />

‎MaiChartManager/Front/src/views/Charts/ImportCreateChartButton/index.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@ import { Button, DropMenu } from "@munet/ui";
22
import { computed, defineComponent, ref } from "vue";
33
import ImportChartButton from "./ImportChartButton";
44
import CreateMusicModal from "@/views/Charts/ImportCreateChartButton/CreateMusicModal";
5+
import { isReadOnlyDir } from "@/store/refs";
56
import { useI18n } from 'vue-i18n';
67

78
export default defineComponent({
89
setup() {
910
const showCreate = ref(false);
1011
const { t } = useI18n();
1112
const options = computed(() => [
12-
{ label: t('chart.import.create'), action: () => { showCreate.value = true; } },
13+
{ label: t('chart.import.create'), action: () => { showCreate.value = true; }, disabled: isReadOnlyDir.value },
1314
]);
1415

1516
return () => <div class="flex relative">
1617
<ImportChartButton />
1718
<DropMenu options={options.value} buttonText="" align="right">
1819
{{
19-
trigger: (onClick: any) => <Button variant="secondary" class="px-.5 b-l b-l-solid b-l-[rgba(255,255,255,0.5)]" onClick={onClick}>
20+
trigger: (onClick: any) => <Button variant="secondary" disabled={isReadOnlyDir.value} class="px-.5 b-l b-l-solid b-l-[rgba(255,255,255,0.5)]" onClick={onClick}>
2021
<span class="i-mdi-arrow-down-drop text-6 translate-y-.25" />
2122
</Button>
2223
}}

0 commit comments

Comments
 (0)