sa2kit 1.2.0 → 1.2.1
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/dist/{UniversalFileService-CEZRJ87g.d.mts → UniversalFileService-C1rUWWU-.d.ts} +2 -258
- package/dist/{UniversalFileService-CEZRJ87g.d.ts → UniversalFileService-DrCK0-NL.d.mts} +2 -258
- package/dist/mmd/admin/index.d.mts +1594 -0
- package/dist/mmd/admin/index.d.ts +1594 -0
- package/dist/mmd/admin/index.js +1021 -0
- package/dist/mmd/admin/index.js.map +1 -0
- package/dist/mmd/admin/index.mjs +991 -0
- package/dist/mmd/admin/index.mjs.map +1 -0
- package/dist/mmd/index.d.mts +4 -193
- package/dist/mmd/index.d.ts +4 -193
- package/dist/types-C2ale3d9.d.mts +194 -0
- package/dist/types-C2ale3d9.d.ts +194 -0
- package/dist/types-Dg-U_chI.d.mts +258 -0
- package/dist/types-Dg-U_chI.d.ts +258 -0
- package/dist/universalFile/index.d.mts +3 -2
- package/dist/universalFile/index.d.ts +3 -2
- package/dist/universalFile/server/index.d.mts +3 -2
- package/dist/universalFile/server/index.d.ts +3 -2
- package/package.json +6 -1
|
@@ -0,0 +1,991 @@
|
|
|
1
|
+
import '../../chunk-BJTO5JO5.mjs';
|
|
2
|
+
import React2, { useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import { Upload, CheckCircle2, X, Loader2, Search, Save, Settings, ChevronUp, ChevronDown, Plus, GripVertical, Trash2, List, Database, BarChart3, FileText, Music, Film } from 'lucide-react';
|
|
4
|
+
import { relations } from 'drizzle-orm';
|
|
5
|
+
import { pgTable, timestamp, varchar, json, integer, uuid, boolean, text, index } from 'drizzle-orm/pg-core';
|
|
6
|
+
|
|
7
|
+
// src/mmd/admin/types.ts
|
|
8
|
+
var MMD_RESOURCE_TYPE_CONFIGS = {
|
|
9
|
+
model: {
|
|
10
|
+
moduleId: "mmd-models",
|
|
11
|
+
acceptedTypes: [".pmx", ".pmd"],
|
|
12
|
+
maxFileSize: 50,
|
|
13
|
+
description: "MMD\u6A21\u578B\u6587\u4EF6"
|
|
14
|
+
},
|
|
15
|
+
motion: {
|
|
16
|
+
moduleId: "mmd-motions",
|
|
17
|
+
acceptedTypes: [".vmd"],
|
|
18
|
+
maxFileSize: 20,
|
|
19
|
+
description: "MMD\u52A8\u4F5C\u6587\u4EF6"
|
|
20
|
+
},
|
|
21
|
+
camera: {
|
|
22
|
+
moduleId: "mmd-cameras",
|
|
23
|
+
acceptedTypes: [".vmd"],
|
|
24
|
+
maxFileSize: 10,
|
|
25
|
+
description: "MMD\u76F8\u673A\u52A8\u753B\u6587\u4EF6"
|
|
26
|
+
},
|
|
27
|
+
audio: {
|
|
28
|
+
moduleId: "mmd-audios",
|
|
29
|
+
acceptedTypes: [".mp3", ".wav", ".ogg", ".m4a"],
|
|
30
|
+
maxFileSize: 20,
|
|
31
|
+
description: "\u97F3\u9891\u6587\u4EF6"
|
|
32
|
+
},
|
|
33
|
+
stage: {
|
|
34
|
+
moduleId: "mmd-stages",
|
|
35
|
+
acceptedTypes: [".pmx", ".pmd", ".x"],
|
|
36
|
+
maxFileSize: 100,
|
|
37
|
+
description: "MMD\u821E\u53F0/\u573A\u666F\u6A21\u578B"
|
|
38
|
+
},
|
|
39
|
+
thumbnail: {
|
|
40
|
+
moduleId: "mmd-thumbnails",
|
|
41
|
+
acceptedTypes: [".jpg", ".jpeg", ".png", ".webp"],
|
|
42
|
+
maxFileSize: 5,
|
|
43
|
+
description: "\u7F29\u7565\u56FE"
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/mmd/admin/components/MmdResourceSelector.tsx
|
|
48
|
+
var MmdResourceSelector = ({
|
|
49
|
+
resourceType,
|
|
50
|
+
fileService,
|
|
51
|
+
userId,
|
|
52
|
+
value,
|
|
53
|
+
onChange,
|
|
54
|
+
required = false
|
|
55
|
+
}) => {
|
|
56
|
+
const [selectedFileId, setSelectedFileId] = useState(value);
|
|
57
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
58
|
+
const [files, setFiles] = useState([]);
|
|
59
|
+
const [loading, setLoading] = useState(false);
|
|
60
|
+
const [uploading, setUploading] = useState(false);
|
|
61
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
62
|
+
const [showUploader, setShowUploader] = useState(false);
|
|
63
|
+
const config = MMD_RESOURCE_TYPE_CONFIGS[resourceType];
|
|
64
|
+
if (!config) {
|
|
65
|
+
return /* @__PURE__ */ React2.createElement("div", { className: "p-4 bg-red-50 text-red-600 rounded-lg" }, "\u672A\u627E\u5230\u8D44\u6E90\u7C7B\u578B\u914D\u7F6E\uFF1A", resourceType);
|
|
66
|
+
}
|
|
67
|
+
const getFileIcon = () => {
|
|
68
|
+
switch (resourceType) {
|
|
69
|
+
case "model":
|
|
70
|
+
case "stage":
|
|
71
|
+
return /* @__PURE__ */ React2.createElement(Film, { className: "w-5 h-5" });
|
|
72
|
+
case "motion":
|
|
73
|
+
case "camera":
|
|
74
|
+
return /* @__PURE__ */ React2.createElement(FileText, { className: "w-5 h-5" });
|
|
75
|
+
case "audio":
|
|
76
|
+
return /* @__PURE__ */ React2.createElement(Music, { className: "w-5 h-5" });
|
|
77
|
+
default:
|
|
78
|
+
return /* @__PURE__ */ React2.createElement(FileText, { className: "w-5 h-5" });
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const loadFiles = useCallback(async () => {
|
|
82
|
+
if (!fileService || !config) return;
|
|
83
|
+
setLoading(true);
|
|
84
|
+
try {
|
|
85
|
+
const result = await fileService.queryFiles({
|
|
86
|
+
moduleId: config.moduleId,
|
|
87
|
+
pageSize: 50,
|
|
88
|
+
page: 1,
|
|
89
|
+
sortBy: "uploadTime",
|
|
90
|
+
sortOrder: "desc"
|
|
91
|
+
});
|
|
92
|
+
setFiles(result.items || []);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error("\u52A0\u8F7D\u6587\u4EF6\u5217\u8868\u5931\u8D25:", error);
|
|
95
|
+
} finally {
|
|
96
|
+
setLoading(false);
|
|
97
|
+
}
|
|
98
|
+
}, [fileService, config]);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
loadFiles();
|
|
101
|
+
}, [loadFiles]);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (selectedFileId && fileService) {
|
|
104
|
+
fileService.getFileMetadata(selectedFileId).then((file) => setSelectedFile(file)).catch((error) => console.error("\u52A0\u8F7D\u6587\u4EF6\u4FE1\u606F\u5931\u8D25:", error));
|
|
105
|
+
} else {
|
|
106
|
+
setSelectedFile(null);
|
|
107
|
+
}
|
|
108
|
+
}, [selectedFileId, fileService]);
|
|
109
|
+
const handleFileSelect = (file) => {
|
|
110
|
+
setSelectedFileId(file.id);
|
|
111
|
+
setSelectedFile(file);
|
|
112
|
+
fileService.getFileUrl(file.id).then((url) => onChange(file.id, url)).catch((error) => console.error("\u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25:", error));
|
|
113
|
+
};
|
|
114
|
+
const handleFileUpload = async (file) => {
|
|
115
|
+
if (!fileService) return;
|
|
116
|
+
setUploading(true);
|
|
117
|
+
try {
|
|
118
|
+
const fileMetadata = await fileService.uploadFile({
|
|
119
|
+
file,
|
|
120
|
+
moduleId: config.moduleId,
|
|
121
|
+
businessId: userId,
|
|
122
|
+
permission: "public"
|
|
123
|
+
});
|
|
124
|
+
await loadFiles();
|
|
125
|
+
handleFileSelect(fileMetadata);
|
|
126
|
+
setShowUploader(false);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error("\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25:", error);
|
|
129
|
+
alert(`\u4E0A\u4F20\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`);
|
|
130
|
+
} finally {
|
|
131
|
+
setUploading(false);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const filteredFiles = files.filter(
|
|
135
|
+
(file) => file.originalName.toLowerCase().includes(searchTerm.toLowerCase())
|
|
136
|
+
);
|
|
137
|
+
return /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300" }, getFileIcon(), config.description, required && /* @__PURE__ */ React2.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2.createElement(
|
|
138
|
+
"button",
|
|
139
|
+
{
|
|
140
|
+
onClick: () => setShowUploader(!showUploader),
|
|
141
|
+
className: "px-3 py-1 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
|
142
|
+
},
|
|
143
|
+
/* @__PURE__ */ React2.createElement(Upload, { className: "w-4 h-4" }),
|
|
144
|
+
"\u4E0A\u4F20\u65B0\u6587\u4EF6"
|
|
145
|
+
)), selectedFile && /* @__PURE__ */ React2.createElement("div", { className: "p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React2.createElement(CheckCircle2, { className: "w-5 h-5 text-green-600 dark:text-green-400" }), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("div", { className: "text-sm font-medium text-gray-900 dark:text-white" }, selectedFile.originalName), /* @__PURE__ */ React2.createElement("div", { className: "text-xs text-gray-500 dark:text-gray-400" }, (selectedFile.size / 1024 / 1024).toFixed(2), " MB"))), /* @__PURE__ */ React2.createElement(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
onClick: () => {
|
|
149
|
+
setSelectedFileId(void 0);
|
|
150
|
+
setSelectedFile(null);
|
|
151
|
+
onChange("", "");
|
|
152
|
+
},
|
|
153
|
+
className: "p-1 hover:bg-white/50 dark:hover:bg-black/20 rounded transition-colors"
|
|
154
|
+
},
|
|
155
|
+
/* @__PURE__ */ React2.createElement(X, { className: "w-4 h-4" })
|
|
156
|
+
)), showUploader && /* @__PURE__ */ React2.createElement("div", { className: "p-4 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg" }, /* @__PURE__ */ React2.createElement("div", { className: "text-sm text-gray-600 dark:text-gray-400 mb-3" }, "\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B: ", config.acceptedTypes.join(", "), /* @__PURE__ */ React2.createElement("br", null), "\u6700\u5927\u6587\u4EF6\u5927\u5C0F: ", config.maxFileSize, "MB"), /* @__PURE__ */ React2.createElement(
|
|
157
|
+
"input",
|
|
158
|
+
{
|
|
159
|
+
type: "file",
|
|
160
|
+
accept: config.acceptedTypes.join(","),
|
|
161
|
+
onChange: (e) => {
|
|
162
|
+
const file = e.target.files?.[0];
|
|
163
|
+
if (file) {
|
|
164
|
+
if (file.size > config.maxFileSize * 1024 * 1024) {
|
|
165
|
+
alert(`\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC7\u9650\u5236\uFF08\u6700\u5927 ${config.maxFileSize}MB\uFF09`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
handleFileUpload(file);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
disabled: uploading,
|
|
172
|
+
className: "w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 disabled:opacity-50"
|
|
173
|
+
}
|
|
174
|
+
), uploading && /* @__PURE__ */ React2.createElement("div", { className: "mt-3 flex items-center gap-2 text-sm text-blue-600" }, /* @__PURE__ */ React2.createElement(Loader2, { className: "w-4 h-4 animate-spin" }), "\u4E0A\u4F20\u4E2D...")), /* @__PURE__ */ React2.createElement("div", { className: "relative" }, /* @__PURE__ */ React2.createElement(Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" }), /* @__PURE__ */ React2.createElement(
|
|
175
|
+
"input",
|
|
176
|
+
{
|
|
177
|
+
type: "text",
|
|
178
|
+
placeholder: "\u641C\u7D22\u6587\u4EF6...",
|
|
179
|
+
value: searchTerm,
|
|
180
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
181
|
+
className: "w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
182
|
+
}
|
|
183
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "max-h-96 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-lg divide-y divide-gray-200 dark:divide-gray-700" }, loading ? /* @__PURE__ */ React2.createElement("div", { className: "p-8 text-center text-gray-500" }, /* @__PURE__ */ React2.createElement(Loader2, { className: "w-6 h-6 animate-spin mx-auto mb-2" }), "\u52A0\u8F7D\u4E2D...") : filteredFiles.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "p-8 text-center text-gray-500" }, searchTerm ? "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6" : "\u6682\u65E0\u6587\u4EF6\uFF0C\u8BF7\u4E0A\u4F20") : filteredFiles.map((file) => /* @__PURE__ */ React2.createElement(
|
|
184
|
+
"button",
|
|
185
|
+
{
|
|
186
|
+
key: file.id,
|
|
187
|
+
onClick: () => handleFileSelect(file),
|
|
188
|
+
className: `w-full p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors ${selectedFileId === file.id ? "bg-blue-50 dark:bg-blue-900/20" : ""}`
|
|
189
|
+
},
|
|
190
|
+
/* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React2.createElement("div", { className: "text-sm font-medium text-gray-900 dark:text-white truncate" }, file.originalName), /* @__PURE__ */ React2.createElement("div", { className: "text-xs text-gray-500 dark:text-gray-400 mt-0.5" }, (file.size / 1024 / 1024).toFixed(2), " MB \u2022", " ", new Date(file.uploadTime).toLocaleDateString())), selectedFileId === file.id && /* @__PURE__ */ React2.createElement(CheckCircle2, { className: "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 ml-2" }))
|
|
191
|
+
))), required && !selectedFileId && /* @__PURE__ */ React2.createElement("div", { className: "text-sm text-red-600 dark:text-red-400" }, "\u8BF7\u9009\u62E9\u4E00\u4E2A", config.description));
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/mmd/admin/components/MmdPlaylistEditor.tsx
|
|
195
|
+
var MmdPlaylistEditor = ({
|
|
196
|
+
playlistId,
|
|
197
|
+
fileService,
|
|
198
|
+
userId,
|
|
199
|
+
onSave,
|
|
200
|
+
onCancel
|
|
201
|
+
}) => {
|
|
202
|
+
const [playlistName, setPlaylistName] = useState("");
|
|
203
|
+
const [playlistDescription, setPlaylistDescription] = useState("");
|
|
204
|
+
const [loop, setLoop] = useState(false);
|
|
205
|
+
const [autoPlay, setAutoPlay] = useState(false);
|
|
206
|
+
const [preloadStrategy, setPreloadStrategy] = useState("none");
|
|
207
|
+
const [nodes, setNodes] = useState([]);
|
|
208
|
+
const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set([0]));
|
|
209
|
+
const [saving, setSaving] = useState(false);
|
|
210
|
+
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
211
|
+
const addNode = () => {
|
|
212
|
+
const newNode = {
|
|
213
|
+
name: `\u8282\u70B9 ${nodes.length + 1}`,
|
|
214
|
+
description: "",
|
|
215
|
+
loop: false,
|
|
216
|
+
sortOrder: nodes.length,
|
|
217
|
+
modelFileId: ""
|
|
218
|
+
};
|
|
219
|
+
setNodes([...nodes, newNode]);
|
|
220
|
+
setExpandedNodes(/* @__PURE__ */ new Set([...expandedNodes, nodes.length]));
|
|
221
|
+
};
|
|
222
|
+
const removeNode = (index2) => {
|
|
223
|
+
const newNodes = nodes.filter((_, i) => i !== index2);
|
|
224
|
+
newNodes.forEach((node, i) => {
|
|
225
|
+
node.sortOrder = i;
|
|
226
|
+
});
|
|
227
|
+
setNodes(newNodes);
|
|
228
|
+
const newExpanded = /* @__PURE__ */ new Set();
|
|
229
|
+
expandedNodes.forEach((i) => {
|
|
230
|
+
if (i < index2) newExpanded.add(i);
|
|
231
|
+
else if (i > index2) newExpanded.add(i - 1);
|
|
232
|
+
});
|
|
233
|
+
setExpandedNodes(newExpanded);
|
|
234
|
+
};
|
|
235
|
+
const moveNode = (index2, direction) => {
|
|
236
|
+
const newNodes = [...nodes];
|
|
237
|
+
const targetIndex = direction === "up" ? index2 - 1 : index2 + 1;
|
|
238
|
+
if (targetIndex < 0 || targetIndex >= newNodes.length) return;
|
|
239
|
+
if (!newNodes[index2] || !newNodes[targetIndex]) return;
|
|
240
|
+
[newNodes[index2], newNodes[targetIndex]] = [newNodes[targetIndex], newNodes[index2]];
|
|
241
|
+
newNodes.forEach((node, i) => {
|
|
242
|
+
node.sortOrder = i;
|
|
243
|
+
});
|
|
244
|
+
setNodes(newNodes);
|
|
245
|
+
const newExpanded = /* @__PURE__ */ new Set();
|
|
246
|
+
expandedNodes.forEach((i) => {
|
|
247
|
+
if (i === index2) newExpanded.add(targetIndex);
|
|
248
|
+
else if (i === targetIndex) newExpanded.add(index2);
|
|
249
|
+
else newExpanded.add(i);
|
|
250
|
+
});
|
|
251
|
+
setExpandedNodes(newExpanded);
|
|
252
|
+
};
|
|
253
|
+
const toggleNode = (index2) => {
|
|
254
|
+
const newExpanded = new Set(expandedNodes);
|
|
255
|
+
if (newExpanded.has(index2)) {
|
|
256
|
+
newExpanded.delete(index2);
|
|
257
|
+
} else {
|
|
258
|
+
newExpanded.add(index2);
|
|
259
|
+
}
|
|
260
|
+
setExpandedNodes(newExpanded);
|
|
261
|
+
};
|
|
262
|
+
const updateNode = (index2, updates) => {
|
|
263
|
+
const newNodes = [...nodes];
|
|
264
|
+
if (!newNodes[index2]) return;
|
|
265
|
+
newNodes[index2] = { ...newNodes[index2], ...updates };
|
|
266
|
+
setNodes(newNodes);
|
|
267
|
+
};
|
|
268
|
+
const validateForm = () => {
|
|
269
|
+
if (!playlistName.trim()) {
|
|
270
|
+
alert("\u8BF7\u8F93\u5165\u64AD\u653E\u5217\u8868\u540D\u79F0");
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
if (nodes.length === 0) {
|
|
274
|
+
alert("\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u64AD\u653E\u8282\u70B9");
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
278
|
+
const node = nodes[i];
|
|
279
|
+
if (!node || !node.name?.trim()) {
|
|
280
|
+
alert(`\u8282\u70B9 ${i + 1}: \u8BF7\u8F93\u5165\u8282\u70B9\u540D\u79F0`);
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
if (!node.modelFileId) {
|
|
284
|
+
alert(`\u8282\u70B9 ${i + 1}: \u8BF7\u9009\u62E9\u6A21\u578B\u6587\u4EF6`);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
};
|
|
290
|
+
const handleSave = async () => {
|
|
291
|
+
if (!validateForm()) return;
|
|
292
|
+
setSaving(true);
|
|
293
|
+
try {
|
|
294
|
+
console.log("\u4FDD\u5B58\u64AD\u653E\u5217\u8868:", {
|
|
295
|
+
name: playlistName,
|
|
296
|
+
description: playlistDescription,
|
|
297
|
+
loop,
|
|
298
|
+
autoPlay,
|
|
299
|
+
preloadStrategy,
|
|
300
|
+
nodes
|
|
301
|
+
});
|
|
302
|
+
alert("\u4FDD\u5B58\u6210\u529F\uFF01");
|
|
303
|
+
onSave?.(null);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error("\u4FDD\u5B58\u5931\u8D25:", error);
|
|
306
|
+
alert(`\u4FDD\u5B58\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`);
|
|
307
|
+
} finally {
|
|
308
|
+
setSaving(false);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
return /* @__PURE__ */ React2.createElement("div", { className: "max-w-6xl mx-auto p-6 bg-white dark:bg-gray-900 rounded-lg shadow-lg" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React2.createElement("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, playlistId ? "\u7F16\u8F91\u64AD\u653E\u5217\u8868" : "\u521B\u5EFA\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
|
|
312
|
+
"button",
|
|
313
|
+
{
|
|
314
|
+
onClick: handleSave,
|
|
315
|
+
disabled: saving,
|
|
316
|
+
className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
|
|
317
|
+
},
|
|
318
|
+
/* @__PURE__ */ React2.createElement(Save, { className: "w-4 h-4" }),
|
|
319
|
+
saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58"
|
|
320
|
+
), /* @__PURE__ */ React2.createElement(
|
|
321
|
+
"button",
|
|
322
|
+
{
|
|
323
|
+
onClick: onCancel,
|
|
324
|
+
className: "px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors flex items-center gap-2"
|
|
325
|
+
},
|
|
326
|
+
/* @__PURE__ */ React2.createElement(X, { className: "w-4 h-4" }),
|
|
327
|
+
"\u53D6\u6D88"
|
|
328
|
+
))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-4 mb-8 p-6 bg-gray-50 dark:bg-gray-800 rounded-lg" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-4" }, "\u57FA\u672C\u4FE1\u606F"), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u64AD\u653E\u5217\u8868\u540D\u79F0 ", /* @__PURE__ */ React2.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2.createElement(
|
|
329
|
+
"input",
|
|
330
|
+
{
|
|
331
|
+
type: "text",
|
|
332
|
+
value: playlistName,
|
|
333
|
+
onChange: (e) => setPlaylistName(e.target.value),
|
|
334
|
+
placeholder: "\u8F93\u5165\u64AD\u653E\u5217\u8868\u540D\u79F0...",
|
|
335
|
+
className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
336
|
+
}
|
|
337
|
+
)), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
|
|
338
|
+
"textarea",
|
|
339
|
+
{
|
|
340
|
+
value: playlistDescription,
|
|
341
|
+
onChange: (e) => setPlaylistDescription(e.target.value),
|
|
342
|
+
placeholder: "\u8F93\u5165\u63CF\u8FF0\u4FE1\u606F...",
|
|
343
|
+
rows: 3,
|
|
344
|
+
className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
345
|
+
}
|
|
346
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2.createElement(
|
|
347
|
+
"input",
|
|
348
|
+
{
|
|
349
|
+
type: "checkbox",
|
|
350
|
+
checked: loop,
|
|
351
|
+
onChange: (e) => setLoop(e.target.checked),
|
|
352
|
+
className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
353
|
+
}
|
|
354
|
+
), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u5217\u8868\u5FAA\u73AF")), /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2.createElement(
|
|
355
|
+
"input",
|
|
356
|
+
{
|
|
357
|
+
type: "checkbox",
|
|
358
|
+
checked: autoPlay,
|
|
359
|
+
onChange: (e) => setAutoPlay(e.target.checked),
|
|
360
|
+
className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
361
|
+
}
|
|
362
|
+
), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u81EA\u52A8\u64AD\u653E"))), /* @__PURE__ */ React2.createElement(
|
|
363
|
+
"button",
|
|
364
|
+
{
|
|
365
|
+
onClick: () => setShowAdvanced(!showAdvanced),
|
|
366
|
+
className: "flex items-center gap-2 text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"
|
|
367
|
+
},
|
|
368
|
+
/* @__PURE__ */ React2.createElement(Settings, { className: "w-4 h-4" }),
|
|
369
|
+
"\u9AD8\u7EA7\u9009\u9879",
|
|
370
|
+
showAdvanced ? /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-4 h-4" }) : /* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-4 h-4" })
|
|
371
|
+
), showAdvanced && /* @__PURE__ */ React2.createElement("div", { className: "space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u9884\u52A0\u8F7D\u7B56\u7565"), /* @__PURE__ */ React2.createElement(
|
|
372
|
+
"select",
|
|
373
|
+
{
|
|
374
|
+
value: preloadStrategy,
|
|
375
|
+
onChange: (e) => setPreloadStrategy(e.target.value),
|
|
376
|
+
className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
377
|
+
},
|
|
378
|
+
/* @__PURE__ */ React2.createElement("option", { value: "none" }, "\u4E0D\u9884\u52A0\u8F7D"),
|
|
379
|
+
/* @__PURE__ */ React2.createElement("option", { value: "next" }, "\u9884\u52A0\u8F7D\u4E0B\u4E00\u4E2A"),
|
|
380
|
+
/* @__PURE__ */ React2.createElement("option", { value: "all" }, "\u9884\u52A0\u8F7D\u5168\u90E8")
|
|
381
|
+
)))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, "\u64AD\u653E\u8282\u70B9 (", nodes.length, ")"), /* @__PURE__ */ React2.createElement(
|
|
382
|
+
"button",
|
|
383
|
+
{
|
|
384
|
+
onClick: addNode,
|
|
385
|
+
className: "px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2"
|
|
386
|
+
},
|
|
387
|
+
/* @__PURE__ */ React2.createElement(Plus, { className: "w-4 h-4" }),
|
|
388
|
+
"\u6DFB\u52A0\u8282\u70B9"
|
|
389
|
+
)), nodes.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "p-8 text-center text-gray-500 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-lg" }, '\u6682\u65E0\u8282\u70B9\uFF0C\u8BF7\u70B9\u51FB"\u6DFB\u52A0\u8282\u70B9"\u6309\u94AE\u5F00\u59CB') : nodes.map((node, index2) => /* @__PURE__ */ React2.createElement(
|
|
390
|
+
"div",
|
|
391
|
+
{
|
|
392
|
+
key: index2,
|
|
393
|
+
className: "border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden"
|
|
394
|
+
},
|
|
395
|
+
/* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-3 p-4 bg-gray-50 dark:bg-gray-800" }, /* @__PURE__ */ React2.createElement(
|
|
396
|
+
"button",
|
|
397
|
+
{
|
|
398
|
+
className: "cursor-move text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",
|
|
399
|
+
title: "\u62D6\u62FD\u6392\u5E8F"
|
|
400
|
+
},
|
|
401
|
+
/* @__PURE__ */ React2.createElement(GripVertical, { className: "w-5 h-5" })
|
|
402
|
+
), /* @__PURE__ */ React2.createElement(
|
|
403
|
+
"button",
|
|
404
|
+
{
|
|
405
|
+
onClick: () => toggleNode(index2),
|
|
406
|
+
className: "flex-1 flex items-center justify-between text-left"
|
|
407
|
+
},
|
|
408
|
+
/* @__PURE__ */ React2.createElement("span", { className: "font-medium text-gray-900 dark:text-white" }, index2 + 1, ". ", node.name || "\u672A\u547D\u540D\u8282\u70B9"),
|
|
409
|
+
expandedNodes.has(index2) ? /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-5 h-5 text-gray-400" }) : /* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-5 h-5 text-gray-400" })
|
|
410
|
+
), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
|
|
411
|
+
"button",
|
|
412
|
+
{
|
|
413
|
+
onClick: () => moveNode(index2, "up"),
|
|
414
|
+
disabled: index2 === 0,
|
|
415
|
+
className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30",
|
|
416
|
+
title: "\u4E0A\u79FB"
|
|
417
|
+
},
|
|
418
|
+
/* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-5 h-5" })
|
|
419
|
+
), /* @__PURE__ */ React2.createElement(
|
|
420
|
+
"button",
|
|
421
|
+
{
|
|
422
|
+
onClick: () => moveNode(index2, "down"),
|
|
423
|
+
disabled: index2 === nodes.length - 1,
|
|
424
|
+
className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30",
|
|
425
|
+
title: "\u4E0B\u79FB"
|
|
426
|
+
},
|
|
427
|
+
/* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-5 h-5" })
|
|
428
|
+
), /* @__PURE__ */ React2.createElement(
|
|
429
|
+
"button",
|
|
430
|
+
{
|
|
431
|
+
onClick: () => removeNode(index2),
|
|
432
|
+
className: "p-1 text-red-400 hover:text-red-600 dark:hover:text-red-300",
|
|
433
|
+
title: "\u5220\u9664"
|
|
434
|
+
},
|
|
435
|
+
/* @__PURE__ */ React2.createElement(Trash2, { className: "w-5 h-5" })
|
|
436
|
+
))),
|
|
437
|
+
expandedNodes.has(index2) && /* @__PURE__ */ React2.createElement("div", { className: "p-6 space-y-6 bg-white dark:bg-gray-900" }, /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u8282\u70B9\u540D\u79F0 ", /* @__PURE__ */ React2.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2.createElement(
|
|
438
|
+
"input",
|
|
439
|
+
{
|
|
440
|
+
type: "text",
|
|
441
|
+
value: node.name,
|
|
442
|
+
onChange: (e) => updateNode(index2, { name: e.target.value }),
|
|
443
|
+
className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
444
|
+
}
|
|
445
|
+
)), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u65F6\u957F\uFF08\u79D2\uFF09"), /* @__PURE__ */ React2.createElement(
|
|
446
|
+
"input",
|
|
447
|
+
{
|
|
448
|
+
type: "number",
|
|
449
|
+
value: node.duration || "",
|
|
450
|
+
onChange: (e) => updateNode(index2, { duration: parseInt(e.target.value) || void 0 }),
|
|
451
|
+
className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
452
|
+
}
|
|
453
|
+
))), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
|
|
454
|
+
"textarea",
|
|
455
|
+
{
|
|
456
|
+
value: node.description || "",
|
|
457
|
+
onChange: (e) => updateNode(index2, { description: e.target.value }),
|
|
458
|
+
rows: 2,
|
|
459
|
+
className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
460
|
+
}
|
|
461
|
+
)), /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2.createElement(
|
|
462
|
+
"input",
|
|
463
|
+
{
|
|
464
|
+
type: "checkbox",
|
|
465
|
+
checked: node.loop,
|
|
466
|
+
onChange: (e) => updateNode(index2, { loop: e.target.checked }),
|
|
467
|
+
className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
468
|
+
}
|
|
469
|
+
), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u5355\u66F2\u5FAA\u73AF")), /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 gap-6 pt-4 border-t border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement(
|
|
470
|
+
MmdResourceSelector,
|
|
471
|
+
{
|
|
472
|
+
resourceType: "model",
|
|
473
|
+
fileService,
|
|
474
|
+
userId,
|
|
475
|
+
value: node.modelFileId,
|
|
476
|
+
onChange: (fileId) => updateNode(index2, { modelFileId: fileId }),
|
|
477
|
+
required: true
|
|
478
|
+
}
|
|
479
|
+
), /* @__PURE__ */ React2.createElement(
|
|
480
|
+
MmdResourceSelector,
|
|
481
|
+
{
|
|
482
|
+
resourceType: "motion",
|
|
483
|
+
fileService,
|
|
484
|
+
userId,
|
|
485
|
+
value: node.motionFileId,
|
|
486
|
+
onChange: (fileId) => updateNode(index2, { motionFileId: fileId })
|
|
487
|
+
}
|
|
488
|
+
), /* @__PURE__ */ React2.createElement(
|
|
489
|
+
MmdResourceSelector,
|
|
490
|
+
{
|
|
491
|
+
resourceType: "audio",
|
|
492
|
+
fileService,
|
|
493
|
+
userId,
|
|
494
|
+
value: node.audioFileId,
|
|
495
|
+
onChange: (fileId) => updateNode(index2, { audioFileId: fileId })
|
|
496
|
+
}
|
|
497
|
+
)))
|
|
498
|
+
))));
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// src/mmd/admin/components/MmdAdminPanel.tsx
|
|
502
|
+
var MmdAdminPanel = ({
|
|
503
|
+
fileService,
|
|
504
|
+
userId,
|
|
505
|
+
apiBaseUrl = "/api/mmd",
|
|
506
|
+
showAdvancedOptions = true,
|
|
507
|
+
className = ""
|
|
508
|
+
}) => {
|
|
509
|
+
const [activeTab, setActiveTab] = useState("playlists");
|
|
510
|
+
const [showEditor, setShowEditor] = useState(false);
|
|
511
|
+
const [editingPlaylistId, setEditingPlaylistId] = useState();
|
|
512
|
+
const tabs = [
|
|
513
|
+
{ id: "playlists", label: "\u64AD\u653E\u5217\u8868", icon: List },
|
|
514
|
+
{ id: "presets", label: "\u9884\u8BBE\u9879", icon: Database },
|
|
515
|
+
{ id: "resources", label: "\u8D44\u6E90\u7BA1\u7406", icon: Settings },
|
|
516
|
+
...showAdvancedOptions ? [{ id: "stats", label: "\u7EDF\u8BA1", icon: BarChart3 }] : []
|
|
517
|
+
];
|
|
518
|
+
const handleCreatePlaylist = () => {
|
|
519
|
+
setEditingPlaylistId(void 0);
|
|
520
|
+
setShowEditor(true);
|
|
521
|
+
};
|
|
522
|
+
const handleCloseEditor = () => {
|
|
523
|
+
setShowEditor(false);
|
|
524
|
+
setEditingPlaylistId(void 0);
|
|
525
|
+
};
|
|
526
|
+
const handleSaveSuccess = (playlist) => {
|
|
527
|
+
console.log("\u4FDD\u5B58\u6210\u529F:", playlist);
|
|
528
|
+
handleCloseEditor();
|
|
529
|
+
};
|
|
530
|
+
if (showEditor) {
|
|
531
|
+
return /* @__PURE__ */ React2.createElement(
|
|
532
|
+
MmdPlaylistEditor,
|
|
533
|
+
{
|
|
534
|
+
playlistId: editingPlaylistId,
|
|
535
|
+
fileService,
|
|
536
|
+
userId,
|
|
537
|
+
onSave: handleSaveSuccess,
|
|
538
|
+
onCancel: handleCloseEditor
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
return /* @__PURE__ */ React2.createElement("div", { className: `min-h-screen bg-gray-50 dark:bg-gray-900 ${className}` }, /* @__PURE__ */ React2.createElement("div", { className: "bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between h-16" }, /* @__PURE__ */ React2.createElement("h1", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, "MMD \u540E\u53F0\u7BA1\u7406"), activeTab === "playlists" && /* @__PURE__ */ React2.createElement(
|
|
543
|
+
"button",
|
|
544
|
+
{
|
|
545
|
+
onClick: handleCreatePlaylist,
|
|
546
|
+
className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
|
|
547
|
+
},
|
|
548
|
+
/* @__PURE__ */ React2.createElement(Plus, { className: "w-4 h-4" }),
|
|
549
|
+
"\u521B\u5EFA\u64AD\u653E\u5217\u8868"
|
|
550
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "flex space-x-8" }, tabs.map((tab) => {
|
|
551
|
+
const Icon = tab.icon;
|
|
552
|
+
return /* @__PURE__ */ React2.createElement(
|
|
553
|
+
"button",
|
|
554
|
+
{
|
|
555
|
+
key: tab.id,
|
|
556
|
+
onClick: () => setActiveTab(tab.id),
|
|
557
|
+
className: `flex items-center gap-2 px-1 py-4 border-b-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"}`
|
|
558
|
+
},
|
|
559
|
+
/* @__PURE__ */ React2.createElement(Icon, { className: "w-4 h-4" }),
|
|
560
|
+
tab.label
|
|
561
|
+
);
|
|
562
|
+
})))), /* @__PURE__ */ React2.createElement("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8" }, activeTab === "playlists" && /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(List, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u64AD\u653E\u5217\u8868\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D..."), /* @__PURE__ */ React2.createElement("p", { className: "text-sm mt-2" }, '\u70B9\u51FB\u53F3\u4E0A\u89D2"\u521B\u5EFA\u64AD\u653E\u5217\u8868"\u6309\u94AE\u5F00\u59CB'))), activeTab === "presets" && /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(Database, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u9884\u8BBE\u9879\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D...")), activeTab === "resources" && /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(Settings, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u8D44\u6E90\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D...")), activeTab === "stats" && /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(BarChart3, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u7EDF\u8BA1\u529F\u80FD\u5F00\u53D1\u4E2D..."))));
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// src/mmd/admin/utils.ts
|
|
566
|
+
function extractFileIdsFromPlaylist(playlist, nodes) {
|
|
567
|
+
const fileIds = /* @__PURE__ */ new Set();
|
|
568
|
+
if (playlist.thumbnailFileId) {
|
|
569
|
+
fileIds.add(playlist.thumbnailFileId);
|
|
570
|
+
}
|
|
571
|
+
for (const node of nodes) {
|
|
572
|
+
if (node.thumbnailFileId) fileIds.add(node.thumbnailFileId);
|
|
573
|
+
if (node.modelFileId) fileIds.add(node.modelFileId);
|
|
574
|
+
if (node.motionFileId) fileIds.add(node.motionFileId);
|
|
575
|
+
if (node.cameraFileId) fileIds.add(node.cameraFileId);
|
|
576
|
+
if (node.audioFileId) fileIds.add(node.audioFileId);
|
|
577
|
+
if (node.stageModelFileId) fileIds.add(node.stageModelFileId);
|
|
578
|
+
if (node.additionalMotionFileIds) {
|
|
579
|
+
node.additionalMotionFileIds.forEach((id) => fileIds.add(id));
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return Array.from(fileIds);
|
|
583
|
+
}
|
|
584
|
+
function extractFileIdsFromResourceOptions(options) {
|
|
585
|
+
const fileIds = /* @__PURE__ */ new Set();
|
|
586
|
+
for (const option of options) {
|
|
587
|
+
fileIds.add(option.fileId);
|
|
588
|
+
if (option.thumbnailFileId) {
|
|
589
|
+
fileIds.add(option.thumbnailFileId);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return Array.from(fileIds);
|
|
593
|
+
}
|
|
594
|
+
function extractFileIdsFromPresetItem(item) {
|
|
595
|
+
const fileIds = /* @__PURE__ */ new Set();
|
|
596
|
+
if (item.thumbnailFileId) fileIds.add(item.thumbnailFileId);
|
|
597
|
+
if (item.modelFileId) fileIds.add(item.modelFileId);
|
|
598
|
+
if (item.motionFileId) fileIds.add(item.motionFileId);
|
|
599
|
+
if (item.cameraFileId) fileIds.add(item.cameraFileId);
|
|
600
|
+
if (item.audioFileId) fileIds.add(item.audioFileId);
|
|
601
|
+
if (item.stageModelFileId) fileIds.add(item.stageModelFileId);
|
|
602
|
+
if (item.additionalMotionFileIds) {
|
|
603
|
+
item.additionalMotionFileIds.forEach((id) => fileIds.add(id));
|
|
604
|
+
}
|
|
605
|
+
return Array.from(fileIds);
|
|
606
|
+
}
|
|
607
|
+
function convertPlaylistNodeToFrontend(node, fileUrls) {
|
|
608
|
+
return {
|
|
609
|
+
id: node.id,
|
|
610
|
+
playlistId: node.playlistId,
|
|
611
|
+
name: node.name,
|
|
612
|
+
description: node.description,
|
|
613
|
+
loop: node.loop,
|
|
614
|
+
duration: node.duration,
|
|
615
|
+
sortOrder: node.sortOrder,
|
|
616
|
+
config: node.config,
|
|
617
|
+
createdAt: node.createdAt,
|
|
618
|
+
updatedAt: node.updatedAt,
|
|
619
|
+
// URL 映射
|
|
620
|
+
thumbnailUrl: node.thumbnailFileId ? fileUrls[node.thumbnailFileId] : void 0,
|
|
621
|
+
modelUrl: fileUrls[node.modelFileId] || "",
|
|
622
|
+
motionUrl: node.motionFileId ? fileUrls[node.motionFileId] : void 0,
|
|
623
|
+
cameraUrl: node.cameraFileId ? fileUrls[node.cameraFileId] : void 0,
|
|
624
|
+
audioUrl: node.audioFileId ? fileUrls[node.audioFileId] : void 0,
|
|
625
|
+
stageModelUrl: node.stageModelFileId ? fileUrls[node.stageModelFileId] : void 0,
|
|
626
|
+
additionalMotionUrls: node.additionalMotionFileIds?.map((id) => fileUrls[id]).filter((url) => Boolean(url))
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function convertPlaylistToFrontend(playlist, nodes, fileUrls) {
|
|
630
|
+
return {
|
|
631
|
+
id: playlist.id,
|
|
632
|
+
name: playlist.name,
|
|
633
|
+
description: playlist.description,
|
|
634
|
+
loop: playlist.loop,
|
|
635
|
+
preloadStrategy: playlist.preloadStrategy,
|
|
636
|
+
autoPlay: playlist.autoPlay,
|
|
637
|
+
status: playlist.status,
|
|
638
|
+
sortOrder: playlist.sortOrder,
|
|
639
|
+
config: playlist.config,
|
|
640
|
+
createdBy: playlist.createdBy,
|
|
641
|
+
createdAt: playlist.createdAt,
|
|
642
|
+
updatedAt: playlist.updatedAt,
|
|
643
|
+
deletedAt: playlist.deletedAt,
|
|
644
|
+
// URL 映射
|
|
645
|
+
thumbnailUrl: playlist.thumbnailFileId ? fileUrls[playlist.thumbnailFileId] : void 0,
|
|
646
|
+
// 转换节点
|
|
647
|
+
nodes: nodes.sort((a, b) => a.sortOrder - b.sortOrder).map((node) => convertPlaylistNodeToFrontend(node, fileUrls))
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function convertResourceOptionToFrontend(option, fileUrls) {
|
|
651
|
+
return {
|
|
652
|
+
id: option.id,
|
|
653
|
+
name: option.name,
|
|
654
|
+
description: option.description,
|
|
655
|
+
resourceType: option.resourceType,
|
|
656
|
+
tags: option.tags,
|
|
657
|
+
sortOrder: option.sortOrder,
|
|
658
|
+
isActive: option.isActive,
|
|
659
|
+
createdBy: option.createdBy,
|
|
660
|
+
createdAt: option.createdAt,
|
|
661
|
+
updatedAt: option.updatedAt,
|
|
662
|
+
// URL 映射
|
|
663
|
+
fileUrl: fileUrls[option.fileId] || "",
|
|
664
|
+
thumbnailUrl: option.thumbnailFileId ? fileUrls[option.thumbnailFileId] : void 0
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function convertPresetItemToFrontend(item, fileUrls) {
|
|
668
|
+
return {
|
|
669
|
+
id: item.id,
|
|
670
|
+
name: item.name,
|
|
671
|
+
description: item.description,
|
|
672
|
+
sortOrder: item.sortOrder,
|
|
673
|
+
isActive: item.isActive,
|
|
674
|
+
tags: item.tags,
|
|
675
|
+
createdBy: item.createdBy,
|
|
676
|
+
createdAt: item.createdAt,
|
|
677
|
+
updatedAt: item.updatedAt,
|
|
678
|
+
// URL 映射
|
|
679
|
+
thumbnailUrl: item.thumbnailFileId ? fileUrls[item.thumbnailFileId] : void 0,
|
|
680
|
+
modelUrl: fileUrls[item.modelFileId] || "",
|
|
681
|
+
motionUrl: item.motionFileId ? fileUrls[item.motionFileId] : void 0,
|
|
682
|
+
cameraUrl: item.cameraFileId ? fileUrls[item.cameraFileId] : void 0,
|
|
683
|
+
audioUrl: item.audioFileId ? fileUrls[item.audioFileId] : void 0,
|
|
684
|
+
stageModelUrl: item.stageModelFileId ? fileUrls[item.stageModelFileId] : void 0,
|
|
685
|
+
additionalMotionUrls: item.additionalMotionFileIds?.map((id) => fileUrls[id]).filter((url) => Boolean(url))
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function convertNodeToMmdFormat(node) {
|
|
689
|
+
return {
|
|
690
|
+
id: node.id,
|
|
691
|
+
name: node.name,
|
|
692
|
+
loop: node.loop,
|
|
693
|
+
duration: node.duration,
|
|
694
|
+
thumbnail: node.thumbnailUrl,
|
|
695
|
+
resources: {
|
|
696
|
+
modelPath: node.modelUrl,
|
|
697
|
+
motionPath: node.motionUrl,
|
|
698
|
+
cameraPath: node.cameraUrl,
|
|
699
|
+
audioPath: node.audioUrl,
|
|
700
|
+
stageModelPath: node.stageModelUrl,
|
|
701
|
+
additionalMotions: node.additionalMotionUrls
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function convertPlaylistToMmdConfig(playlist) {
|
|
706
|
+
return {
|
|
707
|
+
id: playlist.id,
|
|
708
|
+
name: playlist.name,
|
|
709
|
+
nodes: playlist.nodes.map(convertNodeToMmdFormat),
|
|
710
|
+
loop: playlist.loop,
|
|
711
|
+
preload: playlist.preloadStrategy,
|
|
712
|
+
autoPlay: playlist.autoPlay
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function convertPresetItemToMmdResource(item) {
|
|
716
|
+
return {
|
|
717
|
+
id: item.id,
|
|
718
|
+
name: item.name,
|
|
719
|
+
thumbnail: item.thumbnailUrl,
|
|
720
|
+
description: item.description,
|
|
721
|
+
resources: {
|
|
722
|
+
modelPath: item.modelUrl,
|
|
723
|
+
motionPath: item.motionUrl,
|
|
724
|
+
cameraPath: item.cameraUrl,
|
|
725
|
+
audioPath: item.audioUrl,
|
|
726
|
+
stageModelPath: item.stageModelUrl,
|
|
727
|
+
additionalMotions: item.additionalMotionUrls
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function convertResourceOptionsToMmdFormat(options) {
|
|
732
|
+
const grouped = {
|
|
733
|
+
models: [],
|
|
734
|
+
motions: [],
|
|
735
|
+
cameras: [],
|
|
736
|
+
audios: [],
|
|
737
|
+
stages: []
|
|
738
|
+
};
|
|
739
|
+
for (const option of options) {
|
|
740
|
+
const resourceOption = {
|
|
741
|
+
id: option.id,
|
|
742
|
+
name: option.name,
|
|
743
|
+
path: option.fileUrl,
|
|
744
|
+
thumbnail: option.thumbnailUrl
|
|
745
|
+
};
|
|
746
|
+
switch (option.resourceType) {
|
|
747
|
+
case "model":
|
|
748
|
+
grouped.models.push(resourceOption);
|
|
749
|
+
break;
|
|
750
|
+
case "motion":
|
|
751
|
+
grouped.motions.push(resourceOption);
|
|
752
|
+
break;
|
|
753
|
+
case "camera":
|
|
754
|
+
grouped.cameras.push(resourceOption);
|
|
755
|
+
break;
|
|
756
|
+
case "audio":
|
|
757
|
+
grouped.audios.push(resourceOption);
|
|
758
|
+
break;
|
|
759
|
+
case "stage":
|
|
760
|
+
grouped.stages.push(resourceOption);
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return grouped;
|
|
765
|
+
}
|
|
766
|
+
function validateFileUrls(requiredFileIds, fileUrls) {
|
|
767
|
+
const missingIds = requiredFileIds.filter((id) => !fileUrls[id]);
|
|
768
|
+
return {
|
|
769
|
+
valid: missingIds.length === 0,
|
|
770
|
+
missingIds
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function generateMockFileUrls(fileIds) {
|
|
774
|
+
const urls = {};
|
|
775
|
+
for (const id of fileIds) {
|
|
776
|
+
urls[id] = `/mock/files/${id}`;
|
|
777
|
+
}
|
|
778
|
+
return urls;
|
|
779
|
+
}
|
|
780
|
+
function mergeFileUrlMaps(...maps) {
|
|
781
|
+
return Object.assign({}, ...maps);
|
|
782
|
+
}
|
|
783
|
+
function extractPathsFromMmdResources(resources) {
|
|
784
|
+
const paths = [resources.modelPath];
|
|
785
|
+
if (resources.motionPath) paths.push(resources.motionPath);
|
|
786
|
+
if (resources.cameraPath) paths.push(resources.cameraPath);
|
|
787
|
+
if (resources.audioPath) paths.push(resources.audioPath);
|
|
788
|
+
if (resources.stageModelPath) paths.push(resources.stageModelPath);
|
|
789
|
+
if (resources.additionalMotions) paths.push(...resources.additionalMotions);
|
|
790
|
+
return paths;
|
|
791
|
+
}
|
|
792
|
+
var mmdPlaylists = pgTable(
|
|
793
|
+
"mmd_playlists",
|
|
794
|
+
{
|
|
795
|
+
/** 主键ID */
|
|
796
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
797
|
+
/** 播放列表名称 */
|
|
798
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
799
|
+
/** 播放列表描述 */
|
|
800
|
+
description: text("description"),
|
|
801
|
+
/** 是否启用列表循环 */
|
|
802
|
+
loop: boolean("loop").notNull().default(false),
|
|
803
|
+
/** 预加载策略: none, next, all */
|
|
804
|
+
preloadStrategy: varchar("preload_strategy", { length: 20 }).notNull().default("none"),
|
|
805
|
+
/** 是否自动播放 */
|
|
806
|
+
autoPlay: boolean("auto_play").notNull().default(false),
|
|
807
|
+
/** 播放列表缩略图文件ID (关联 file_metadata.id) */
|
|
808
|
+
thumbnailFileId: uuid("thumbnail_file_id"),
|
|
809
|
+
/** 播放列表状态: draft, published, archived */
|
|
810
|
+
status: varchar("status", { length: 20 }).notNull().default("draft"),
|
|
811
|
+
/** 显示顺序 */
|
|
812
|
+
sortOrder: integer("sort_order").notNull().default(0),
|
|
813
|
+
/** 额外配置(JSON格式,存储舞台配置等) */
|
|
814
|
+
config: json("config"),
|
|
815
|
+
/** 创建者ID */
|
|
816
|
+
createdBy: varchar("created_by", { length: 255 }).notNull(),
|
|
817
|
+
/** 创建时间 */
|
|
818
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
819
|
+
/** 更新时间 */
|
|
820
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
|
821
|
+
/** 删除时间(软删除) */
|
|
822
|
+
deletedAt: timestamp("deleted_at")
|
|
823
|
+
},
|
|
824
|
+
(table) => ({
|
|
825
|
+
/** 按状态查询的索引 */
|
|
826
|
+
statusIndex: index("mmd_playlists_status_idx").on(table.status),
|
|
827
|
+
/** 按创建者查询的索引 */
|
|
828
|
+
createdByIndex: index("mmd_playlists_created_by_idx").on(table.createdBy),
|
|
829
|
+
/** 按删除状态查询的索引 */
|
|
830
|
+
deletedAtIndex: index("mmd_playlists_deleted_at_idx").on(table.deletedAt),
|
|
831
|
+
/** 按排序查询的索引 */
|
|
832
|
+
sortOrderIndex: index("mmd_playlists_sort_order_idx").on(table.sortOrder)
|
|
833
|
+
})
|
|
834
|
+
);
|
|
835
|
+
var mmdPlaylistNodes = pgTable(
|
|
836
|
+
"mmd_playlist_nodes",
|
|
837
|
+
{
|
|
838
|
+
/** 主键ID */
|
|
839
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
840
|
+
/** 所属播放列表ID */
|
|
841
|
+
playlistId: uuid("playlist_id").references(() => mmdPlaylists.id, { onDelete: "cascade" }).notNull(),
|
|
842
|
+
/** 节点名称 */
|
|
843
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
844
|
+
/** 节点描述 */
|
|
845
|
+
description: text("description"),
|
|
846
|
+
/** 是否启用节点循环 */
|
|
847
|
+
loop: boolean("loop").notNull().default(false),
|
|
848
|
+
/** 预计时长(秒) */
|
|
849
|
+
duration: integer("duration"),
|
|
850
|
+
/** 节点缩略图文件ID */
|
|
851
|
+
thumbnailFileId: uuid("thumbnail_file_id"),
|
|
852
|
+
/** 显示顺序 */
|
|
853
|
+
sortOrder: integer("sort_order").notNull().default(0),
|
|
854
|
+
/** 模型文件ID (关联 file_metadata.id) */
|
|
855
|
+
modelFileId: uuid("model_file_id").notNull(),
|
|
856
|
+
/** 动作文件ID */
|
|
857
|
+
motionFileId: uuid("motion_file_id"),
|
|
858
|
+
/** 相机动画文件ID */
|
|
859
|
+
cameraFileId: uuid("camera_file_id"),
|
|
860
|
+
/** 音频文件ID */
|
|
861
|
+
audioFileId: uuid("audio_file_id"),
|
|
862
|
+
/** 舞台模型文件ID */
|
|
863
|
+
stageModelFileId: uuid("stage_model_file_id"),
|
|
864
|
+
/** 附加动作文件ID列表(JSON数组) */
|
|
865
|
+
additionalMotionFileIds: json("additional_motion_file_ids").$type(),
|
|
866
|
+
/** 额外配置(JSON格式) */
|
|
867
|
+
config: json("config"),
|
|
868
|
+
/** 创建时间 */
|
|
869
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
870
|
+
/** 更新时间 */
|
|
871
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
872
|
+
},
|
|
873
|
+
(table) => ({
|
|
874
|
+
/** 按播放列表查询的索引 */
|
|
875
|
+
playlistIndex: index("mmd_playlist_nodes_playlist_idx").on(table.playlistId),
|
|
876
|
+
/** 按排序查询的索引 */
|
|
877
|
+
sortOrderIndex: index("mmd_playlist_nodes_sort_order_idx").on(table.sortOrder),
|
|
878
|
+
/** 按模型文件查询的索引 */
|
|
879
|
+
modelFileIndex: index("mmd_playlist_nodes_model_file_idx").on(table.modelFileId),
|
|
880
|
+
/** 组合索引:播放列表+排序 */
|
|
881
|
+
playlistSortIndex: index("mmd_playlist_nodes_playlist_sort_idx").on(
|
|
882
|
+
table.playlistId,
|
|
883
|
+
table.sortOrder
|
|
884
|
+
)
|
|
885
|
+
})
|
|
886
|
+
);
|
|
887
|
+
var mmdResourceOptions = pgTable(
|
|
888
|
+
"mmd_resource_options",
|
|
889
|
+
{
|
|
890
|
+
/** 主键ID */
|
|
891
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
892
|
+
/** 资源名称 */
|
|
893
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
894
|
+
/** 资源描述 */
|
|
895
|
+
description: text("description"),
|
|
896
|
+
/** 资源类型: model, motion, camera, audio, stage */
|
|
897
|
+
resourceType: varchar("resource_type", { length: 20 }).notNull(),
|
|
898
|
+
/** 文件ID (关联 file_metadata.id) */
|
|
899
|
+
fileId: uuid("file_id").notNull(),
|
|
900
|
+
/** 缩略图文件ID */
|
|
901
|
+
thumbnailFileId: uuid("thumbnail_file_id"),
|
|
902
|
+
/** 资源标签(JSON数组,用于分类和筛选) */
|
|
903
|
+
tags: json("tags").$type(),
|
|
904
|
+
/** 显示顺序 */
|
|
905
|
+
sortOrder: integer("sort_order").notNull().default(0),
|
|
906
|
+
/** 是否启用 */
|
|
907
|
+
isActive: boolean("is_active").notNull().default(true),
|
|
908
|
+
/** 创建者ID */
|
|
909
|
+
createdBy: varchar("created_by", { length: 255 }).notNull(),
|
|
910
|
+
/** 创建时间 */
|
|
911
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
912
|
+
/** 更新时间 */
|
|
913
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
914
|
+
},
|
|
915
|
+
(table) => ({
|
|
916
|
+
/** 按资源类型查询的索引 */
|
|
917
|
+
resourceTypeIndex: index("mmd_resource_options_resource_type_idx").on(table.resourceType),
|
|
918
|
+
/** 按文件ID查询的索引 */
|
|
919
|
+
fileIdIndex: index("mmd_resource_options_file_id_idx").on(table.fileId),
|
|
920
|
+
/** 按活跃状态查询的索引 */
|
|
921
|
+
isActiveIndex: index("mmd_resource_options_is_active_idx").on(table.isActive),
|
|
922
|
+
/** 按创建者查询的索引 */
|
|
923
|
+
createdByIndex: index("mmd_resource_options_created_by_idx").on(table.createdBy),
|
|
924
|
+
/** 组合索引:资源类型+活跃状态+排序 */
|
|
925
|
+
typeActiveSortIndex: index("mmd_resource_options_type_active_sort_idx").on(
|
|
926
|
+
table.resourceType,
|
|
927
|
+
table.isActive,
|
|
928
|
+
table.sortOrder
|
|
929
|
+
)
|
|
930
|
+
})
|
|
931
|
+
);
|
|
932
|
+
var mmdPresetItems = pgTable(
|
|
933
|
+
"mmd_preset_items",
|
|
934
|
+
{
|
|
935
|
+
/** 主键ID */
|
|
936
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
937
|
+
/** 预设名称 */
|
|
938
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
939
|
+
/** 预设描述 */
|
|
940
|
+
description: text("description"),
|
|
941
|
+
/** 缩略图文件ID */
|
|
942
|
+
thumbnailFileId: uuid("thumbnail_file_id"),
|
|
943
|
+
/** 模型文件ID */
|
|
944
|
+
modelFileId: uuid("model_file_id").notNull(),
|
|
945
|
+
/** 动作文件ID */
|
|
946
|
+
motionFileId: uuid("motion_file_id"),
|
|
947
|
+
/** 相机动画文件ID */
|
|
948
|
+
cameraFileId: uuid("camera_file_id"),
|
|
949
|
+
/** 音频文件ID */
|
|
950
|
+
audioFileId: uuid("audio_file_id"),
|
|
951
|
+
/** 舞台模型文件ID */
|
|
952
|
+
stageModelFileId: uuid("stage_model_file_id"),
|
|
953
|
+
/** 附加动作文件ID列表(JSON数组) */
|
|
954
|
+
additionalMotionFileIds: json("additional_motion_file_ids").$type(),
|
|
955
|
+
/** 显示顺序 */
|
|
956
|
+
sortOrder: integer("sort_order").notNull().default(0),
|
|
957
|
+
/** 是否启用 */
|
|
958
|
+
isActive: boolean("is_active").notNull().default(true),
|
|
959
|
+
/** 预设标签(JSON数组) */
|
|
960
|
+
tags: json("tags").$type(),
|
|
961
|
+
/** 创建者ID */
|
|
962
|
+
createdBy: varchar("created_by", { length: 255 }).notNull(),
|
|
963
|
+
/** 创建时间 */
|
|
964
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
965
|
+
/** 更新时间 */
|
|
966
|
+
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
967
|
+
},
|
|
968
|
+
(table) => ({
|
|
969
|
+
/** 按活跃状态查询的索引 */
|
|
970
|
+
isActiveIndex: index("mmd_preset_items_is_active_idx").on(table.isActive),
|
|
971
|
+
/** 按排序查询的索引 */
|
|
972
|
+
sortOrderIndex: index("mmd_preset_items_sort_order_idx").on(table.sortOrder),
|
|
973
|
+
/** 按创建者查询的索引 */
|
|
974
|
+
createdByIndex: index("mmd_preset_items_created_by_idx").on(table.createdBy),
|
|
975
|
+
/** 按模型文件查询的索引 */
|
|
976
|
+
modelFileIndex: index("mmd_preset_items_model_file_idx").on(table.modelFileId)
|
|
977
|
+
})
|
|
978
|
+
);
|
|
979
|
+
var mmdPlaylistsRelations = relations(mmdPlaylists, ({ many }) => ({
|
|
980
|
+
nodes: many(mmdPlaylistNodes)
|
|
981
|
+
}));
|
|
982
|
+
var mmdPlaylistNodesRelations = relations(mmdPlaylistNodes, ({ one }) => ({
|
|
983
|
+
playlist: one(mmdPlaylists, {
|
|
984
|
+
fields: [mmdPlaylistNodes.playlistId],
|
|
985
|
+
references: [mmdPlaylists.id]
|
|
986
|
+
})
|
|
987
|
+
}));
|
|
988
|
+
|
|
989
|
+
export { MMD_RESOURCE_TYPE_CONFIGS, MmdAdminPanel, MmdPlaylistEditor, MmdResourceSelector, convertNodeToMmdFormat, convertPlaylistNodeToFrontend, convertPlaylistToFrontend, convertPlaylistToMmdConfig, convertPresetItemToFrontend, convertPresetItemToMmdResource, convertResourceOptionToFrontend, convertResourceOptionsToMmdFormat, extractFileIdsFromPlaylist, extractFileIdsFromPresetItem, extractFileIdsFromResourceOptions, extractPathsFromMmdResources, generateMockFileUrls, mergeFileUrlMaps, mmdPlaylistNodes, mmdPlaylistNodesRelations, mmdPlaylists, mmdPlaylistsRelations, mmdPresetItems, mmdResourceOptions, validateFileUrls };
|
|
990
|
+
//# sourceMappingURL=index.mjs.map
|
|
991
|
+
//# sourceMappingURL=index.mjs.map
|