mirror of
http://112.124.100.131/ebiz-ai/ebiz-ai-knowledge-manage.git
synced 2025-12-10 19:36:50 +08:00
feat(knowledge): 添加检索设置功能
- 新增 SearchSetting 组件用于检索设置 - 在知识库详情页面添加检索设置按钮和弹窗 - 实现了向量检索、全文检索和混合检索的设置功能 - 添加了重排序模型、Top K 和 Score
This commit is contained in:
@@ -39,6 +39,7 @@ export function hitTest(data) {
|
|||||||
return request({
|
return request({
|
||||||
url: getUrl('/datasetsEx/hit/test'),
|
url: getUrl('/datasetsEx/hit/test'),
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
noLoading: true,
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import getUrl from '@/assets/js/utils/get-url'
|
|||||||
*/
|
*/
|
||||||
export function getRerankModels() {
|
export function getRerankModels() {
|
||||||
return request({
|
return request({
|
||||||
url: getUrl("/third/models/rerank"),
|
url: getUrl('/third/models/rerank'),
|
||||||
method: 'get',
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,40 +8,40 @@ export default Vue.extend({
|
|||||||
*/
|
*/
|
||||||
value: {
|
value: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @description 是否显示 tooltip
|
* @description 是否显示 tooltip
|
||||||
*/
|
*/
|
||||||
showTooltip: {
|
showTooltip: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
max: {
|
max: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1,
|
default: 1
|
||||||
},
|
},
|
||||||
step: {
|
step: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0.1,
|
default: 0.1
|
||||||
},
|
},
|
||||||
min: {
|
min: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0
|
||||||
},
|
},
|
||||||
marks: {
|
marks: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {}
|
||||||
},
|
},
|
||||||
hitTest: {
|
hitTest: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
prop: 'value',
|
prop: 'value',
|
||||||
event: 'input',
|
event: 'input'
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -58,8 +58,16 @@ export default Vue.extend({
|
|||||||
:max="max"
|
:max="max"
|
||||||
/>
|
/>
|
||||||
<!-- 靠左显示 -->
|
<!-- 靠左显示 -->
|
||||||
<div v-if="hitTest" class="col-span-2 row-span-1" style="justify-self: start">语义 {{ (max - value).toFixed(1) }}</div>
|
<div
|
||||||
|
v-if="hitTest"
|
||||||
|
class="col-span-2 row-span-1"
|
||||||
|
style="justify-self: start"
|
||||||
|
>
|
||||||
|
语义 {{ (max - value).toFixed(1) }}
|
||||||
|
</div>
|
||||||
<!-- 靠右显示 -->
|
<!-- 靠右显示 -->
|
||||||
<div v-if="hitTest" class="col-span-1 row-span-1" style="justify-self: end">关键词 {{ value.toFixed(1) }}</div>
|
<div v-if="hitTest" class="col-span-1 row-span-1" style="justify-self: end">
|
||||||
|
关键词 {{ value.toFixed(1) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export default {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
// 清空输入框
|
// 清空输入框
|
||||||
this.inputMessage = ''
|
// this.inputMessage = ''
|
||||||
},
|
},
|
||||||
formatScore(score) {
|
formatScore(score) {
|
||||||
return score.toFixed(3)
|
return score.toFixed(3)
|
||||||
@@ -137,7 +137,7 @@ export default {
|
|||||||
<!-- 中间内容区域 -->
|
<!-- 中间内容区域 -->
|
||||||
<div ref="contentArea" class="content-area">
|
<div ref="contentArea" class="content-area">
|
||||||
<div class="empty-state" v-if="messages.length === 0">
|
<div class="empty-state" v-if="messages.length === 0">
|
||||||
<el-image alt="当前会话尚无记录"></el-image>
|
<!-- <el-image alt="当前会话尚无记录"></el-image>-->
|
||||||
<p>这里会显示命中测试的记录</p>
|
<p>这里会显示命中测试的记录</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -174,7 +174,7 @@ export default {
|
|||||||
>
|
>
|
||||||
<!-- 内容详情 -->
|
<!-- 内容详情 -->
|
||||||
<section>
|
<section>
|
||||||
<p class="mb10 fs13 multi-ellipsis-2">
|
<p class="mb10 fs13 multi-ellipsis-2 mr20">
|
||||||
{{ contentItem.segment.content }}
|
{{ contentItem.segment.content }}
|
||||||
</p>
|
</p>
|
||||||
<el-tag
|
<el-tag
|
||||||
@@ -191,7 +191,10 @@ export default {
|
|||||||
<!-- score -->
|
<!-- score -->
|
||||||
<span class="score"
|
<span class="score"
|
||||||
><el-tag type="success"
|
><el-tag type="success"
|
||||||
>score: {{ contentItem.score.toFixed(3) }}</el-tag
|
>score:
|
||||||
|
{{
|
||||||
|
contentItem.score ? contentItem.score.toFixed(3) : '0.000'
|
||||||
|
}}</el-tag
|
||||||
></span
|
></span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -313,7 +316,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 具体的参数内容 -->
|
<!-- 具体的参数内容 -->
|
||||||
<section>
|
<section class="mr20">
|
||||||
<div class="grid grid-cols-2">
|
<div class="grid grid-cols-2">
|
||||||
<div class="col-span-1 flex align-items-c ">
|
<div class="col-span-1 flex align-items-c ">
|
||||||
<span class="mr5 flex" style="flex-flow: column nowrap">
|
<span class="mr5 flex" style="flex-flow: column nowrap">
|
||||||
@@ -346,7 +349,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<!-- 参数设置 -->
|
<!-- 参数设置 -->
|
||||||
<section
|
<section
|
||||||
class="mt10"
|
class="mt10 mr10"
|
||||||
v-if="paramsConfig.search_method === 'hybrid_search'"
|
v-if="paramsConfig.search_method === 'hybrid_search'"
|
||||||
>
|
>
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
@@ -423,7 +426,12 @@ export default {
|
|||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<el-button icon="el-icon-setting" size="small" slot="reference"
|
<el-button
|
||||||
|
icon="el-icon-setting"
|
||||||
|
size="small"
|
||||||
|
class="mr20"
|
||||||
|
slot="reference"
|
||||||
|
style="padding:11px 15px"
|
||||||
>参数设置
|
>参数设置
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
@@ -432,7 +440,9 @@ export default {
|
|||||||
v-model="inputMessage"
|
v-model="inputMessage"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
class="message-input"
|
class="message-input"
|
||||||
|
size="medium"
|
||||||
@keyup.enter.native="sendMessage"
|
@keyup.enter.native="sendMessage"
|
||||||
|
style="border-radius: 0px"
|
||||||
>
|
>
|
||||||
<template slot="append">
|
<template slot="append">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -650,6 +660,9 @@ export default {
|
|||||||
|
|
||||||
.message-input {
|
.message-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
/deep/ .el-input__inner {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
161
src/views/knowledge/detail/components/SearchSetting/Index.vue
Normal file
161
src/views/knowledge/detail/components/SearchSetting/Index.vue
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-drawer
|
||||||
|
title="搜索设置"
|
||||||
|
:visible.sync="visible"
|
||||||
|
size="50%"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<el-form ref="form" :model="form" label-width="80%" label-position="top">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="Embedding模型">
|
||||||
|
<el-select v-model="form.embeddingModel" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="item in modelList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<!-- <el-form-item label="检索设置">
|
||||||
|
<div class="flex justify-content-b">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in settingList"
|
||||||
|
:key="item.name"
|
||||||
|
class="setting_item flex"
|
||||||
|
:class="{ activeItem: index === activeIndex }"
|
||||||
|
@click="handleClick(index)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="@/assets/images/konwledge/knowledge-5.png"
|
||||||
|
alt=""
|
||||||
|
style="height: 40px"
|
||||||
|
/>
|
||||||
|
<div class="ml10">
|
||||||
|
<p class="fwb label fs15">
|
||||||
|
{{ item.name }}
|
||||||
|
<span v-if="index === 2" class="point fs12 ml10">推荐</span>
|
||||||
|
</p>
|
||||||
|
<p class="desc">{{ item.desc }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>-->
|
||||||
|
<el-collapse v-model="activeName" accordion>
|
||||||
|
<el-collapse-item
|
||||||
|
v-for="(item, index) in settingList"
|
||||||
|
:key="item.name"
|
||||||
|
:title="item.name"
|
||||||
|
:name="index"
|
||||||
|
>
|
||||||
|
<template slot="title">
|
||||||
|
<div class="flex align-items-c">
|
||||||
|
<img
|
||||||
|
src="@/assets/images/konwledge/knowledge-5.png"
|
||||||
|
alt=""
|
||||||
|
style="height: 20px"
|
||||||
|
/>
|
||||||
|
<div class="ml10">
|
||||||
|
<p class="fwb label fs15">
|
||||||
|
{{ item.name }}
|
||||||
|
<span v-if="index === 2" class="point fs12 ml10">推荐</span>
|
||||||
|
</p>
|
||||||
|
<p class="desc">{{ item.desc }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<search-form :activeIndex="activeName"></search-form>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</el-form>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import SearchForm from '@/views/knowledge/detail/components/SearchSetting/SearchForm.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SearchSetting',
|
||||||
|
components: { SearchForm },
|
||||||
|
props: {},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeName: 0,
|
||||||
|
visible: false,
|
||||||
|
form: {
|
||||||
|
embeddingModel: '',
|
||||||
|
modelOpen: false
|
||||||
|
},
|
||||||
|
modelList: [],
|
||||||
|
activeIndex: 0,
|
||||||
|
settingList: [
|
||||||
|
{
|
||||||
|
name: '向量检索',
|
||||||
|
desc: '通过生成查询嵌入并查询与其向量表示最相似的文本分段'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '全文检索',
|
||||||
|
desc:
|
||||||
|
'索引文档中的所有词汇,从而允许用户查询任意词汇,并返回包含这些词汇的文本片段'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '混合检索',
|
||||||
|
desc:
|
||||||
|
'同时执行全文检索和向量检索,并应用重排序步骤,从两类查询结果中选择匹配用户问题的最佳结果,用户可以选择设置权重或配置重新排序模型。'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
marks: {
|
||||||
|
// 20: '0',
|
||||||
|
// 80: '100'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('close')
|
||||||
|
},
|
||||||
|
handleClick(index) {
|
||||||
|
this.activeIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.point {
|
||||||
|
color: #296dff;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #296dff;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
color: #354052;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
color: #676f83;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .el-collapse-item__header {
|
||||||
|
height: 85px;
|
||||||
|
line-height: 23px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
/deep/ .is-active {
|
||||||
|
.el-collapse-item__header {
|
||||||
|
background-color: #f5f5fb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<div class="pl10 searchForm_cont">
|
||||||
|
<el-form :model="form" label-width="80%" label-position="top">
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-switch v-model="form.modelOpen"></el-switch>
|
||||||
|
<span>Rerank 模型</span>
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="dark"
|
||||||
|
content="重排序模型将根据候选文档列表与用户问题予以匹配程度进行重新排序,从而改进语义排序的结果"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<i class="el-icon-info ml5" style="color: #909399;"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-form-item>
|
||||||
|
<el-row v-if="activeIndex === 2">
|
||||||
|
<el-slider v-model="value" :marks="marks"> </el-slider
|
||||||
|
></el-row>
|
||||||
|
<el-row v-else>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="">
|
||||||
|
<el-select v-model="form.embeddingModel" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="item in modelList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="10" class="flex">
|
||||||
|
<el-form-item label="Top K">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.topK"
|
||||||
|
:min="1"
|
||||||
|
:max="10"
|
||||||
|
label="Top K"
|
||||||
|
controls-position="right"
|
||||||
|
size="mini"
|
||||||
|
></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
<div style="width: 300px;">
|
||||||
|
<el-slider v-model="value" :min="1" :max="10" :step="1"></el-slider>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="10">
|
||||||
|
<el-form-item label="Score阈值">
|
||||||
|
<el-slider
|
||||||
|
v-model="value"
|
||||||
|
show-input
|
||||||
|
show-input-controls
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:step="0.01"
|
||||||
|
:precision="2"
|
||||||
|
></el-slider>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SearchForm',
|
||||||
|
props: {
|
||||||
|
activeIndex: {
|
||||||
|
type: Number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
modelOpen: true,
|
||||||
|
embeddingModel: 'sentence-transformers/all-MiniLM-L6-v2',
|
||||||
|
topK: 3,
|
||||||
|
scoreThreshold: 0.5
|
||||||
|
},
|
||||||
|
modelList: [
|
||||||
|
{
|
||||||
|
value: 'sentence-transformers/all-MiniLM-L6-v2',
|
||||||
|
label: 'sentence-transformers/all-MiniLM-L6-v2'
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.searchForm_cont {
|
||||||
|
border-left: 1px solid #ebeef5;
|
||||||
|
border-right: 1px solid #ebeef5;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -49,14 +49,6 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- <el-button type="primary" size="medium" class="normal-button" @click="jumpEditKnowledge">修改知识库</el-button>-->
|
<!-- <el-button type="primary" size="medium" class="normal-button" @click="jumpEditKnowledge">修改知识库</el-button>-->
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
size="medium"
|
|
||||||
icon="el-icon-edit-outline"
|
|
||||||
class="primary-button"
|
|
||||||
@click="jumpEditKnowledge"
|
|
||||||
>修改知识库
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
@@ -65,6 +57,22 @@
|
|||||||
@click="jumpAddKnowledge"
|
@click="jumpAddKnowledge"
|
||||||
>上传知识
|
>上传知识
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="medium"
|
||||||
|
icon="el-icon-edit-outline"
|
||||||
|
class="primary-button"
|
||||||
|
@click="jumpEditKnowledge"
|
||||||
|
>修改知识库
|
||||||
|
</el-button>
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- size="medium"-->
|
||||||
|
<!-- icon="el-icon-search"-->
|
||||||
|
<!-- class="line-button"-->
|
||||||
|
<!-- @click="searchSetting"-->
|
||||||
|
<!-- >检索设置-->
|
||||||
|
<!-- </el-button>-->
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="el-icon-s-promotion"
|
icon="el-icon-s-promotion"
|
||||||
@@ -219,6 +227,8 @@
|
|||||||
@close="close"
|
@close="close"
|
||||||
></metadata-operator>
|
></metadata-operator>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
<!--检索设置弹窗-->
|
||||||
|
<search-setting ref="searchSetting"></search-setting>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -241,6 +251,7 @@ import knowledgePng_1 from '@/assets/images/konwledge/knowledge-1.png'
|
|||||||
import hitTest from '@/views/knowledge/detail/components/HitTest/Index.vue'
|
import hitTest from '@/views/knowledge/detail/components/HitTest/Index.vue'
|
||||||
import MetaData from '@/views/knowledge/detail/components/metaData/Index.vue'
|
import MetaData from '@/views/knowledge/detail/components/metaData/Index.vue'
|
||||||
import MetadataOperator from '@/views/knowledge/detail/components/metaData/MetadataOperator.vue'
|
import MetadataOperator from '@/views/knowledge/detail/components/metaData/MetadataOperator.vue'
|
||||||
|
import SearchSetting from '@/views/knowledge/detail/components/SearchSetting/Index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'index',
|
name: 'index',
|
||||||
@@ -295,6 +306,7 @@ export default {
|
|||||||
props: {},
|
props: {},
|
||||||
watch: {},
|
watch: {},
|
||||||
components: {
|
components: {
|
||||||
|
SearchSetting,
|
||||||
MetaData,
|
MetaData,
|
||||||
hitTest,
|
hitTest,
|
||||||
knowledgeForm,
|
knowledgeForm,
|
||||||
@@ -358,6 +370,12 @@ export default {
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 检索设置
|
||||||
|
*/
|
||||||
|
searchSetting() {
|
||||||
|
this.$refs.searchSetting.init()
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* @name 根据id 获取知识内容详情
|
* @name 根据id 获取知识内容详情
|
||||||
* @author Chen Yuda
|
* @author Chen Yuda
|
||||||
|
|||||||
Reference in New Issue
Block a user