feat(module): 添加新页面
- 增添meili-config页面用于配置melisearch - 将原先的module.vue改为meili-index.vue,页面逻辑不变
This commit is contained in:
@ -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,
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
137
src/meilisearch_module/meili-config.vue
Normal file
137
src/meilisearch_module/meili-config.vue
Normal 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>
|
||||
68
src/meilisearch_module/meili-index.vue
Normal file
68
src/meilisearch_module/meili-index.vue
Normal 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>
|
||||
@ -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>
|
||||
Reference in New Issue
Block a user