feat(module): 添加新页面

- 增添meili-config页面用于配置melisearch
- 将原先的module.vue改为meili-index.vue,页面逻辑不变
This commit is contained in:
2025-10-21 08:53:36 +00:00
parent 0efd05a968
commit 19aa6886bd
4 changed files with 212 additions and 11 deletions

View File

@ -1,5 +1,6 @@
import { defineModule } from '@directus/extensions-sdk';
import ModuleComponent from './module.vue';
import MeiliIndex from './meili-index.vue';
import MeiliConfig from './meili-config.vue';
export default defineModule({
id: 'meilisearch',
@ -8,7 +9,11 @@ export default defineModule({
routes: [
{
path: '',
component: ModuleComponent,
component: MeiliIndex,
},
{
path: 'config',
component: MeiliConfig,
}
],
});

View File

@ -0,0 +1,137 @@
<template>
<private-view title="MeiliSearch 配置">
<template #navigation>
<v-list nav :model-value="selected" dense>
<v-list-item to="/meilisearch" exact>
索引配置
</v-list-item>
<v-list-item to="/meilisearch/config">
MeiliSearch 配置
</v-list-item>
</v-list>
</template>
<section>
<v-form collection="meili_search_config" v-model="formData" ref="form">
<v-text-field v-model="formData.host" label="MeiliSearch Host" required />
<v-text-field v-model="formData.apiKey" label="API Key" required />
</v-form>
<div class="button-group">
<v-button v-if="!pending" color="secondary" @click="testConnection">
测试连接
</v-button>
<v-button v-if="pending" disabled>
正在测试... <v-progress-circular indeterminate size="20" />
</v-button>
<v-button color="primary" @click="saveConfig">
保存配置
</v-button>
</div>
<Bounce>
<div class="connection-result" v-if="statusMessage">
<v-notice :type="statusSuccess ? 'success' : 'error'">
{{ statusMessage }}
</v-notice>
</div>
</Bounce>
</section>
</private-view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useApi } from '@directus/extensions-sdk';
const selected = ref(null);
const api = useApi();
const statusMessage = ref<string | null>(null);
const statusSuccess = ref(false);
const pending = ref(false);
let configExists = false;
const formData = ref<{ host: string; apiKey: string }>({
host: '',
apiKey: '',
});
const fetchConfig = async () => {
const resp = await api.get('/items/meili_search_config?limit=1');
if (resp.data.data) {
formData.value.host = resp.data.data.host;
formData.value.apiKey = resp.data.data.apiKey;
configExists = true;
}
};
const testConnection = async () => {
statusMessage.value = '';
try {
pending.value = true;
const resp = await api.post('/meilisearch/connection-test', { host: formData.value.host, apiKey: formData.value.apiKey });
if (resp.data.success) {
statusSuccess.value = true;
statusMessage.value = '连接成功!';
} else {
statusSuccess.value = false;
statusMessage.value = `连接失败:${resp.data.message}`;
}
} catch (err: any) {
statusSuccess.value = false;
statusMessage.value = `请求出错:${err.message}`;
} finally {
pending.value = false;
}
};
const saveConfig = async () => {
// 与之前示例相同的保存逻辑…
if (!formData.value.host) {
statusSuccess.value = false;
statusMessage.value = '请填写 Host';
return;
}
try {
let response;
if (configExists) {
response = await api.patch('/items/meili_search_config', formData.value);
} else {
response = await api.post('/items/meili_search_config', formData.value);
configExists = true;
}
if (response.status !== 200) {
throw new Error('保存配置时发生错误');
}
statusSuccess.value = true;
statusMessage.value = '配置已保存!';
} catch (err: any) {
statusSuccess.value = false;
statusMessage.value = `保存失败:${err.message}`;
}
};
onMounted(() => {
fetchConfig();
});
</script>
<style scoped>
section {
padding: 0 var(--content-padding);
}
.button-group {
display: flex;
align-items: center;
gap: 16px;
margin-top: 16px;
}
.connection-result {
margin-top: 16px;
}
</style>

View File

@ -0,0 +1,68 @@
<template>
<private-view title="Meili Sync">
<template #title-outer:prepend>
<v-icon name="feature_search" />
</template>
<template #navigation>
<v-list nav :model-value="selected" dense>
<v-list-item to="/meilisearch" exact>
索引配置
</v-list-item>
<v-list-item to="/meilisearch/config">
MeiliSearch 配置
</v-list-item>
</v-list>
</template>
<section>
<v-notice type="info">请选择希望被 MeiliSearch 索引的集合</v-notice>
<v-table :headers="headers" :items="collections" :items-per-page="10" class="mt-4">
<template #item.enabled="{ item }">
<v-checkbox v-model="item.enabled" :label="item.enabled ? '已启用' : '未启用'" />
</template>"
</v-table>
</section>
</private-view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useApi } from '@directus/extensions-sdk';
const api = useApi();
const collections = ref([]);
const headers = [
{ text: '集合名称', value: 'collection' },
{ text: '说明', value: 'note' },
{ text: '启用索引', value: 'enabled' },
];
// const fetchConfigs = async () => {
// const resp = await api.get('/items/meili_index_configs', { params: { limit: -1 } });
// configs.value = resp.data.data;
// }
const fetchCollections = async () => {
const resp = await api.get('/meilisearch/collections');
collections.value = resp.data.map((col: any) => ({
...col,
enabled: false,
}));
};
const selected = ref(null);
onMounted(() => {
fetchCollections();
// fetchConfigs();
})
</script>
<style scoped>
section {
padding: 0 var(--content-padding);
}
</style>

View File

@ -1,9 +0,0 @@
<template>
<private-view title="My Custom Module">Content goes here...</private-view>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({});
</script>