koishi-plugin-media-luna 0.0.7 → 0.0.9
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/client/api.ts +37 -0
- package/client/components/SetupWizard.vue +284 -0
- package/client/components/setup/SetupAuth.vue +323 -0
- package/client/components/setup/SetupStorage.vue +159 -0
- package/client/pages/index.vue +51 -22
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/lib/core/api/index.d.ts +1 -0
- package/lib/core/api/index.d.ts.map +1 -1
- package/lib/core/api/index.js +5 -1
- package/lib/core/api/index.js.map +1 -1
- package/lib/core/api/setup-api.d.ts +6 -0
- package/lib/core/api/setup-api.d.ts.map +1 -0
- package/lib/core/api/setup-api.js +205 -0
- package/lib/core/api/setup-api.js.map +1 -0
- package/lib/plugins/connector-chat-api/index.js +2 -2
- package/lib/plugins/connector-dalle/index.js +2 -2
- package/lib/plugins/connector-dalle/index.js.map +1 -1
- package/lib/plugins/connector-flux/index.js +2 -2
- package/lib/plugins/connector-sd-webui/index.js +2 -2
- package/lib/plugins/webui-auth/index.d.ts.map +1 -1
- package/lib/plugins/webui-auth/index.js +9 -2
- package/lib/plugins/webui-auth/index.js.map +1 -1
- package/lib/plugins/webui-auth/service.d.ts.map +1 -1
- package/lib/plugins/webui-auth/service.js +27 -18
- package/lib/plugins/webui-auth/service.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="setup-storage">
|
|
3
|
+
<h3>存储配置</h3>
|
|
4
|
+
<p class="step-desc">选择生成图片的存储方式。推荐使用本地存储或 S3 兼容存储。</p>
|
|
5
|
+
|
|
6
|
+
<div v-if="loading" class="loading-state">
|
|
7
|
+
<k-icon name="sync" class="spin" />
|
|
8
|
+
<span>加载配置中...</span>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<template v-else>
|
|
12
|
+
<!-- 使用 ConfigRenderer 渲染配置字段 -->
|
|
13
|
+
<ConfigRenderer
|
|
14
|
+
:fields="fields"
|
|
15
|
+
v-model="localConfig"
|
|
16
|
+
/>
|
|
17
|
+
|
|
18
|
+
<!-- 警告提示(当选择不使用时) -->
|
|
19
|
+
<div v-if="localConfig.backend === 'none'" class="warning-box">
|
|
20
|
+
<k-icon name="warning" />
|
|
21
|
+
<div>
|
|
22
|
+
<strong>注意</strong>
|
|
23
|
+
<p>选择"不使用"将保留生成服务返回的原始 URL,这些 URL 可能会过期或无法访问。建议配置存储后端以确保图片长期可用。</p>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<!-- 操作按钮 -->
|
|
29
|
+
<div class="step-actions">
|
|
30
|
+
<k-button type="primary" :loading="saving" :disabled="loading" @click="handleNext">
|
|
31
|
+
下一步
|
|
32
|
+
</k-button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { ref, watch, onMounted } from 'vue'
|
|
39
|
+
import { setupApi } from '../../api'
|
|
40
|
+
import type { ConfigField } from '../../types'
|
|
41
|
+
import ConfigRenderer from '../ConfigRenderer.vue'
|
|
42
|
+
|
|
43
|
+
const props = defineProps<{
|
|
44
|
+
modelValue: Record<string, any>
|
|
45
|
+
saving: boolean
|
|
46
|
+
}>()
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits<{
|
|
49
|
+
(e: 'update:modelValue', value: Record<string, any>): void
|
|
50
|
+
(e: 'next'): void
|
|
51
|
+
}>()
|
|
52
|
+
|
|
53
|
+
const loading = ref(true)
|
|
54
|
+
const fields = ref<ConfigField[]>([])
|
|
55
|
+
const localConfig = ref<Record<string, any>>({})
|
|
56
|
+
|
|
57
|
+
// 同步 localConfig 到父组件
|
|
58
|
+
watch(localConfig, (newVal) => {
|
|
59
|
+
emit('update:modelValue', { ...newVal })
|
|
60
|
+
}, { deep: true })
|
|
61
|
+
|
|
62
|
+
// 加载配置字段和当前值
|
|
63
|
+
const loadConfig = async () => {
|
|
64
|
+
try {
|
|
65
|
+
loading.value = true
|
|
66
|
+
// 并行获取字段定义和当前配置
|
|
67
|
+
const [fieldsResult, configResult] = await Promise.all([
|
|
68
|
+
setupApi.getStorageFields(),
|
|
69
|
+
setupApi.getStorageConfig()
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
fields.value = fieldsResult
|
|
73
|
+
|
|
74
|
+
// 合并当前配置(填充默认值)
|
|
75
|
+
const newConfig: Record<string, any> = { ...props.modelValue }
|
|
76
|
+
for (const field of fieldsResult) {
|
|
77
|
+
if (configResult[field.key] !== undefined) {
|
|
78
|
+
newConfig[field.key] = configResult[field.key]
|
|
79
|
+
} else if (newConfig[field.key] === undefined && field.default !== undefined) {
|
|
80
|
+
newConfig[field.key] = field.default
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
localConfig.value = newConfig
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error('Failed to load storage config:', e)
|
|
86
|
+
} finally {
|
|
87
|
+
loading.value = false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const handleNext = () => {
|
|
92
|
+
emit('next')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
onMounted(loadConfig)
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
.setup-storage h3 {
|
|
100
|
+
font-size: 1.25rem;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
color: var(--k-color-text);
|
|
103
|
+
margin: 0 0 0.5rem 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.step-desc {
|
|
107
|
+
color: var(--k-color-text-description);
|
|
108
|
+
margin: 0 0 1.5rem 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.loading-state {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
gap: 0.5rem;
|
|
116
|
+
padding: 3rem;
|
|
117
|
+
color: var(--k-color-text-description);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.spin {
|
|
121
|
+
animation: spin 1s linear infinite;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@keyframes spin {
|
|
125
|
+
to { transform: rotate(360deg); }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* 警告框 */
|
|
129
|
+
.warning-box {
|
|
130
|
+
display: flex;
|
|
131
|
+
gap: 1rem;
|
|
132
|
+
padding: 1rem;
|
|
133
|
+
margin-top: 1rem;
|
|
134
|
+
background: color-mix(in srgb, var(--k-color-warning) 10%, transparent);
|
|
135
|
+
border: 1px solid var(--k-color-warning);
|
|
136
|
+
border-radius: 8px;
|
|
137
|
+
color: var(--k-color-warning);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.warning-box strong {
|
|
141
|
+
display: block;
|
|
142
|
+
margin-bottom: 0.25rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.warning-box p {
|
|
146
|
+
margin: 0;
|
|
147
|
+
font-size: 0.9rem;
|
|
148
|
+
color: var(--k-color-text);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* 操作按钮 */
|
|
152
|
+
.step-actions {
|
|
153
|
+
margin-top: 2rem;
|
|
154
|
+
padding-top: 1.5rem;
|
|
155
|
+
border-top: 1px solid var(--k-color-border);
|
|
156
|
+
display: flex;
|
|
157
|
+
justify-content: flex-end;
|
|
158
|
+
}
|
|
159
|
+
</style>
|
package/client/pages/index.vue
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<k-layout class="app-layout media-luna-app">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
<!-- 设置向导 -->
|
|
4
|
+
<SetupWizard v-if="showSetupWizard" @complete="handleSetupComplete" />
|
|
5
|
+
|
|
6
|
+
<!-- 主界面 -->
|
|
7
|
+
<template v-else>
|
|
8
|
+
<div class="top-nav">
|
|
9
|
+
<div class="nav-container">
|
|
10
|
+
<div class="logo-area"><span class="logo-text">MEDIA LUNA</span></div>
|
|
11
|
+
<div class="nav-tabs" role="tablist">
|
|
12
|
+
<div
|
|
13
|
+
v-for="item in menuItems"
|
|
14
|
+
:key="item.id"
|
|
15
|
+
class="nav-tab"
|
|
16
|
+
:class="{ active: currentView === item.id }"
|
|
17
|
+
@click="currentView = item.id"
|
|
18
|
+
role="tab"
|
|
19
|
+
:aria-selected="currentView === item.id"
|
|
20
|
+
>
|
|
21
|
+
<component :is="item.icon" class="tab-icon" />
|
|
22
|
+
<span>{{ item.label }}</span>
|
|
23
|
+
</div>
|
|
18
24
|
</div>
|
|
19
25
|
</div>
|
|
20
26
|
</div>
|
|
21
|
-
</div>
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
<div class="main-content">
|
|
29
|
+
<keep-alive>
|
|
30
|
+
<component :is="activeComponent" />
|
|
31
|
+
</keep-alive>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
28
34
|
</k-layout>
|
|
29
35
|
</template>
|
|
30
36
|
|
|
@@ -35,8 +41,28 @@ import PresetsView from '../components/PresetsView.vue'
|
|
|
35
41
|
import TasksView from '../components/TasksView.vue'
|
|
36
42
|
import GenerateView from '../components/GenerateView.vue'
|
|
37
43
|
import SettingsView from '../components/SettingsView.vue'
|
|
44
|
+
import SetupWizard from '../components/SetupWizard.vue'
|
|
45
|
+
import { setupApi } from '../api'
|
|
38
46
|
|
|
39
47
|
const currentView = ref('generate')
|
|
48
|
+
const showSetupWizard = ref(false)
|
|
49
|
+
|
|
50
|
+
// 检查是否需要显示设置向导
|
|
51
|
+
const checkSetupStatus = async () => {
|
|
52
|
+
try {
|
|
53
|
+
const status = await setupApi.status()
|
|
54
|
+
showSetupWizard.value = status.needsSetup
|
|
55
|
+
} catch (e) {
|
|
56
|
+
// 出错时不显示向导,正常进入应用
|
|
57
|
+
console.error('Failed to check setup status:', e)
|
|
58
|
+
showSetupWizard.value = false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 设置完成回调
|
|
63
|
+
const handleSetupComplete = () => {
|
|
64
|
+
showSetupWizard.value = false
|
|
65
|
+
}
|
|
40
66
|
|
|
41
67
|
const activeComponent = computed(() => {
|
|
42
68
|
switch (currentView.value) {
|
|
@@ -69,7 +95,10 @@ function restoreHeader() {
|
|
|
69
95
|
const el = document.querySelector('.layout-header') as HTMLElement
|
|
70
96
|
if (el) el.style.display = prevHeaderDisplay || ''
|
|
71
97
|
}
|
|
72
|
-
onMounted(
|
|
98
|
+
onMounted(() => {
|
|
99
|
+
hideHeader()
|
|
100
|
+
checkSetupStatus()
|
|
101
|
+
})
|
|
73
102
|
onBeforeUnmount(restoreHeader)
|
|
74
103
|
</script>
|
|
75
104
|
|