修改日志管理代码,首页修改指标说明提示修改,
设置-功能按钮,设置-样式修改,知识库跳转,设置-全部字段必填,设置-将智能助手配置放菜单管理下
正在显示
13 个修改的文件
包含
432 行增加
和
192 行删除
| @@ -15,5 +15,9 @@ export const getLoginfo = (params) => defHttp.get({ url: Api.loginfo, params }, | @@ -15,5 +15,9 @@ export const getLoginfo = (params) => defHttp.get({ url: Api.loginfo, params }, | ||
| 15 | */ | 15 | */ |
| 16 | export const getVisitInfo = (params) => defHttp.get({ url: Api.visitInfo, params }, { isTransformResponse: false }); | 16 | export const getVisitInfo = (params) => defHttp.get({ url: Api.visitInfo, params }, { isTransformResponse: false }); |
| 17 | 17 | ||
| 18 | -export const getStatistics = (params?: { rangeType?: string | null; startTime?: string; endTime?: string }) => | ||
| 19 | - defHttp.get({ url: '/airaglog/airagLog/getStatistics', params }); | 18 | +export const getStatistics = () => defHttp.get({ url: '/airaglog/airagLog/getStatistics' }); |
| 19 | + | ||
| 20 | +export const getButtonStats = (params?: { rangeType?: string | null; startTime?: string; endTime?: string }) => | ||
| 21 | + defHttp.get({ url: '/airaglog/airagLog/getButtonStats', params }); | ||
| 22 | + | ||
| 23 | +export const getQuestionStatistics = () => defHttp.get({ url: '/airaglog/airagLog/getQuestionStatistics' }); |
| @@ -9,20 +9,18 @@ | @@ -9,20 +9,18 @@ | ||
| 9 | :class="[index + 1 < 4 && '!md:mr-4']" | 9 | :class="[index + 1 < 4 && '!md:mr-4']" |
| 10 | > | 10 | > |
| 11 | <template #action> | 11 | <template #action> |
| 12 | - <a-tooltip title="指标说明"> | 12 | + <a-tooltip> |
| 13 | <Icon :icon="item.icon" :size="20" /> | 13 | <Icon :icon="item.icon" :size="20" /> |
| 14 | </a-tooltip> | 14 | </a-tooltip> |
| 15 | </template> | 15 | </template> |
| 16 | <div v-if="type === 'chart'"> | 16 | <div v-if="type === 'chart'"> |
| 17 | - <!-- <Trend term="周同比" :percentage="12" v-if="index === 0" />--> | ||
| 18 | - <!-- <Trend term="日同比" :percentage="11" v-if="index === 0" :type="false" />--> | ||
| 19 | <div v-if="index === 0" class="p-2 px-4 flex justify-between"> | 17 | <div v-if="index === 0" class="p-2 px-4 flex justify-between"> |
| 20 | <span>平均问答次数</span> | 18 | <span>平均问答次数</span> |
| 21 | <span>{{ formatNumber(statistics.averageCount) }}</span> | 19 | <span>{{ formatNumber(statistics.averageCount) }}</span> |
| 22 | </div> | 20 | </div> |
| 23 | - <SingleLine v-if="index === 1" :option="option" :chartData="chartData" :seriesColor="seriesColor" height="50px" /> | 21 | + <SingleLine v-if="index === 1" :option="option" :chartData="dailyCountsData" :seriesColor="seriesColor" height="50px" /> |
| 24 | 22 | ||
| 25 | - <Bar v-if="index === 2" :option="option" :chartData="chartData" :seriesColor="seriesColor" height="50px" /> | 23 | + <Bar v-if="index === 2" :option="option" :chartData="dailyRejectedCountsData" :seriesColor="seriesColor" height="50px" /> |
| 26 | 24 | ||
| 27 | <Trend | 25 | <Trend |
| 28 | v-if="index === 3" | 26 | v-if="index === 3" |
| @@ -32,11 +30,11 @@ | @@ -32,11 +30,11 @@ | ||
| 32 | /> | 30 | /> |
| 33 | </div> | 31 | </div> |
| 34 | <div v-else> | 32 | <div v-else> |
| 35 | - <SingleLine :seriesColor="seriesColor" v-if="index === 0" :option="option" :chartData="chartData" height="50px" /> | 33 | + <SingleLine :seriesColor="seriesColor" v-if="index === 0" :option="option" height="50px" /> |
| 36 | 34 | ||
| 37 | - <SingleLine :seriesColor="seriesColor" v-if="index === 1" :option="option" :chartData="chartData" height="50px" /> | 35 | + <SingleLine :seriesColor="seriesColor" v-if="index === 1" :option="option" :chartData="dailyCountsData" height="50px" /> |
| 38 | 36 | ||
| 39 | - <Bar :seriesColor="seriesColor" v-if="index === 2" :option="option" :chartData="chartData" height="50px" /> | 37 | + <Bar :seriesColor="seriesColor" v-if="index === 2" :option="option" :chartData="dailyRejectedCountsData" height="50px" /> |
| 40 | 38 | ||
| 41 | <Progress v-if="index === 3" :percent="78" :show-info="false" /> | 39 | <Progress v-if="index === 3" :percent="78" :show-info="false" /> |
| 42 | </div> | 40 | </div> |
| @@ -87,34 +85,45 @@ | @@ -87,34 +85,45 @@ | ||
| 87 | default: 'chart', | 85 | default: 'chart', |
| 88 | }, | 86 | }, |
| 89 | }); | 87 | }); |
| 90 | - const option = ref({ xAxis: { show: false, boundaryGap: false }, yAxis: { show: false, boundaryGap: false, max: 220 } }); | 88 | + const dailyCountsData = computed(() => { |
| 89 | + if (!props.statistics.dailyCounts) return []; | ||
| 90 | + return props.statistics.dailyCounts.map((item) => ({ | ||
| 91 | + name: formatDayLabel(item.date), | ||
| 92 | + value: item.count, | ||
| 93 | + })); | ||
| 94 | + }); | ||
| 91 | 95 | ||
| 92 | - const chartData = ref([ | ||
| 93 | - { | ||
| 94 | - name: '1月', | ||
| 95 | - value: 50, | ||
| 96 | - }, | ||
| 97 | - { | ||
| 98 | - name: '2月', | ||
| 99 | - value: 100, | ||
| 100 | - }, | ||
| 101 | - { | ||
| 102 | - name: '3月', | ||
| 103 | - value: 150, | ||
| 104 | - }, | ||
| 105 | - { | ||
| 106 | - name: '4月', | ||
| 107 | - value: 40, | 96 | + const dailyRejectedCountsData = computed(() => { |
| 97 | + if (!props.statistics.dailyRejectedCounts) return []; | ||
| 98 | + return props.statistics.dailyRejectedCounts.map((item) => ({ | ||
| 99 | + name: formatDayLabel(item.date), | ||
| 100 | + value: item.count, | ||
| 101 | + })); | ||
| 102 | + }); | ||
| 103 | + | ||
| 104 | + const formatDayLabel = (dateStr: string) => { | ||
| 105 | + const date = new Date(dateStr); | ||
| 106 | + console.log('格式化日期:', dateStr); | ||
| 107 | + return `${date.getMonth() + 1}/${date.getDate()}`; // 格式: 月/日 | ||
| 108 | + }; | ||
| 109 | + const option = ref({ | ||
| 110 | + xAxis: { | ||
| 111 | + show: true, // 显示X轴 | ||
| 112 | + type: 'category', | ||
| 113 | + boundaryGap: false, | ||
| 108 | }, | 114 | }, |
| 109 | - { | ||
| 110 | - name: '5月', | ||
| 111 | - value: 110, | 115 | + yAxis: { |
| 116 | + show: true, // 显示Y轴 | ||
| 117 | + boundaryGap: false, | ||
| 118 | + min: 0, // 设置最小值 | ||
| 112 | }, | 119 | }, |
| 113 | - { | ||
| 114 | - name: '6月', | ||
| 115 | - value: 120, | 120 | + grid: { |
| 121 | + top: 5, | ||
| 122 | + bottom: 5, | ||
| 123 | + left: 5, | ||
| 124 | + right: 5, | ||
| 116 | }, | 125 | }, |
| 117 | - ]); | 126 | + }); |
| 118 | const seriesColor = computed(() => { | 127 | const seriesColor = computed(() => { |
| 119 | return getThemeColor.value; | 128 | return getThemeColor.value; |
| 120 | }); | 129 | }); |
| @@ -14,18 +14,18 @@ | @@ -14,18 +14,18 @@ | ||
| 14 | <a-range-picker :style="{ width: '256px' }" @change="handleDateChange" :value="dateRange" /> | 14 | <a-range-picker :style="{ width: '256px' }" @change="handleDateChange" :value="dateRange" /> |
| 15 | </div> | 15 | </div> |
| 16 | </template> | 16 | </template> |
| 17 | - <a-tab-pane loading="true" tab="每月问答次数" key="1"> | 17 | + <a-tab-pane loading="true" tab="按钮统计数据" key="1"> |
| 18 | <a-row> | 18 | <a-row> |
| 19 | <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24"> | 19 | <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24"> |
| 20 | <Bar | 20 | <Bar |
| 21 | - :chartData="barData" | 21 | + :chartData="barChartData" |
| 22 | :option="{ title: { text: '', textStyle: { fontWeight: 'lighter' } } }" | 22 | :option="{ title: { text: '', textStyle: { fontWeight: 'lighter' } } }" |
| 23 | height="40vh" | 23 | height="40vh" |
| 24 | :seriesColor="seriesColor" | 24 | :seriesColor="seriesColor" |
| 25 | /> | 25 | /> |
| 26 | </a-col> | 26 | </a-col> |
| 27 | <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24"> | 27 | <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24"> |
| 28 | - <RankList title="按钮统计榜" :list="buttonStats" /> | 28 | + <RankList title="按钮统计榜" :list="buttonStatsData" /> |
| 29 | </a-col> | 29 | </a-col> |
| 30 | </a-row> | 30 | </a-row> |
| 31 | </a-tab-pane> | 31 | </a-tab-pane> |
| @@ -49,35 +49,61 @@ | @@ -49,35 +49,61 @@ | ||
| 49 | </a-card> | 49 | </a-card> |
| 50 | </template> | 50 | </template> |
| 51 | <script lang="ts" setup> | 51 | <script lang="ts" setup> |
| 52 | - import { computed, ref } from 'vue'; | 52 | + import { computed, onMounted, ref } from 'vue'; |
| 53 | import Bar from '/@/components/chart/Bar.vue'; | 53 | import Bar from '/@/components/chart/Bar.vue'; |
| 54 | import RankList from '/@/components/chart/RankList.vue'; | 54 | import RankList from '/@/components/chart/RankList.vue'; |
| 55 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | 55 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
| 56 | + import { getButtonStats } from '@/views/dashboard/Analysis/api'; | ||
| 56 | 57 | ||
| 58 | + // 删除props中的barData和buttonStats | ||
| 57 | defineProps({ | 59 | defineProps({ |
| 58 | loading: { | 60 | loading: { |
| 59 | type: Boolean, | 61 | type: Boolean, |
| 60 | }, | 62 | }, |
| 61 | - barData: { | ||
| 62 | - type: Array as () => Array<{ name: string; value: number }>, // 直接定义类型 | ||
| 63 | - default: () => [], | ||
| 64 | - }, | ||
| 65 | - buttonStats: { | ||
| 66 | - // 添加按钮统计属性 | ||
| 67 | - type: Array as () => Array<{ name: string; total: number }>, | ||
| 68 | - default: () => [], | ||
| 69 | - }, | ||
| 70 | }); | 63 | }); |
| 71 | 64 | ||
| 72 | const activeRange = ref('all'); | 65 | const activeRange = ref('all'); |
| 73 | const dateRange = ref<any>(null); | 66 | const dateRange = ref<any>(null); |
| 67 | + const buttonStatsData = ref<Array<{ name: string; total: number }>>([]); // 存储按钮统计数据 | ||
| 68 | + const loadingStats = ref(false); // 按钮数据加载状态 | ||
| 74 | 69 | ||
| 75 | const emit = defineEmits(['range-change']); | 70 | const emit = defineEmits(['range-change']); |
| 76 | 71 | ||
| 72 | + // 获取按钮统计数据 | ||
| 73 | + const fetchButtonStats = async (params?: { rangeType?: string | null; startTime?: string; endTime?: string }) => { | ||
| 74 | + try { | ||
| 75 | + loadingStats.value = true; | ||
| 76 | + const res = await getButtonStats(params); | ||
| 77 | + buttonStatsData.value = res || []; | ||
| 78 | + } catch (error) { | ||
| 79 | + console.error('获取按钮统计数据失败', error); | ||
| 80 | + buttonStatsData.value = []; | ||
| 81 | + } finally { | ||
| 82 | + loadingStats.value = false; | ||
| 83 | + } | ||
| 84 | + }; | ||
| 85 | + | ||
| 86 | + // 将按钮统计数据转换为柱状图格式 | ||
| 87 | + const barChartData = computed(() => { | ||
| 88 | + return buttonStatsData.value.map((item) => ({ | ||
| 89 | + name: item.name, | ||
| 90 | + value: item.total, | ||
| 91 | + })); | ||
| 92 | + }); | ||
| 93 | + | ||
| 94 | + // 初始化加载数据 | ||
| 95 | + onMounted(() => { | ||
| 96 | + fetchButtonStats(); | ||
| 97 | + }); | ||
| 98 | + | ||
| 77 | const changeRange = (rangeType: string) => { | 99 | const changeRange = (rangeType: string) => { |
| 78 | activeRange.value = rangeType; | 100 | activeRange.value = rangeType; |
| 79 | dateRange.value = null; // 清除日期选择 | 101 | dateRange.value = null; // 清除日期选择 |
| 80 | 102 | ||
| 103 | + // 根据范围类型构建参数 | ||
| 104 | + const params = rangeType === 'all' ? null : { rangeType }; | ||
| 105 | + fetchButtonStats(params); // 获取对应范围的按钮数据 | ||
| 106 | + | ||
| 81 | if (rangeType === 'all') { | 107 | if (rangeType === 'all') { |
| 82 | emit('range-change', { rangeType: null }); | 108 | emit('range-change', { rangeType: null }); |
| 83 | } else { | 109 | } else { |
| @@ -89,16 +115,23 @@ | @@ -89,16 +115,23 @@ | ||
| 89 | if (dateStrings[0] && dateStrings[1]) { | 115 | if (dateStrings[0] && dateStrings[1]) { |
| 90 | activeRange.value = 'custom'; | 116 | activeRange.value = 'custom'; |
| 91 | dateRange.value = dates; | 117 | dateRange.value = dates; |
| 92 | - emit('range-change', { | 118 | + |
| 119 | + // 构建自定义范围参数 | ||
| 120 | + const params = { | ||
| 93 | rangeType: 'custom', | 121 | rangeType: 'custom', |
| 94 | startTime: dateStrings[0] + ' 00:00:00', | 122 | startTime: dateStrings[0] + ' 00:00:00', |
| 95 | endTime: dateStrings[1] + ' 23:59:59', | 123 | endTime: dateStrings[1] + ' 23:59:59', |
| 96 | - }); | 124 | + }; |
| 125 | + | ||
| 126 | + fetchButtonStats(params); // 获取自定义范围的按钮数据 | ||
| 127 | + emit('range-change', params); | ||
| 97 | } else { | 128 | } else { |
| 98 | activeRange.value = 'all'; | 129 | activeRange.value = 'all'; |
| 130 | + fetchButtonStats(); // 重置为全部数据 | ||
| 99 | emit('range-change', { rangeType: null }); | 131 | emit('range-change', { rangeType: null }); |
| 100 | } | 132 | } |
| 101 | }; | 133 | }; |
| 134 | + | ||
| 102 | const { getThemeColor } = useRootSetting(); | 135 | const { getThemeColor } = useRootSetting(); |
| 103 | const seriesColor = computed(() => { | 136 | const seriesColor = computed(() => { |
| 104 | return getThemeColor.value; | 137 | return getThemeColor.value; |
| 1 | <template> | 1 | <template> |
| 2 | <div class="p-4"> | 2 | <div class="p-4"> |
| 3 | <ChartGroupCard class="enter-y" :loading="loading" type="chart" :statistics="statistics" /> | 3 | <ChartGroupCard class="enter-y" :loading="loading" type="chart" :statistics="statistics" /> |
| 4 | - <SaleTabCard class="!my-4 enter-y" :loading="loading" :barData="barData" :buttonStats="buttonStats" @range-change="handleRangeChange" /> | 4 | + <SaleTabCard class="!my-4 enter-y" :loading="loading" @range-change="handleRangeChange" /> |
| 5 | <a-row> | 5 | <a-row> |
| 6 | <a-col :span="24"> | 6 | <a-col :span="24"> |
| 7 | - <a-card :loading="loading" :bordered="false" title="最近一周访问量统计"> | 7 | + <a-card :loading="questionLoading" :bordered="false" title="问题输入量统计"> |
| 8 | <div class="infoArea"> | 8 | <div class="infoArea"> |
| 9 | - <HeadInfo title="今日IP" :iconColor="ipColor" :content="loginfo.todayIp" icon="environment" /> | ||
| 10 | - <HeadInfo title="今日访问" :iconColor="visitColor" :content="loginfo.todayVisitCount" icon="team" /> | ||
| 11 | - <HeadInfo title="总访问量" :iconColor="seriesColor" :content="loginfo.totalVisitCount" icon="rise" /> | 9 | + <HeadInfo title="今日" :iconColor="ipColor" :content="formatNumber(questionStats.todayCount)" icon="environment" /> |
| 10 | + <HeadInfo title="本周" :iconColor="ipColor" :content="formatNumber(questionStats.weekCount)" icon="environment" /> | ||
| 11 | + <HeadInfo title="本月" :iconColor="ipColor" :content="formatNumber(questionStats.monthCount)" icon="environment" /> | ||
| 12 | + <HeadInfo title="本年" :iconColor="ipColor" :content="formatNumber(questionStats.yearCount)" icon="environment" /> | ||
| 13 | + <HeadInfo title="总计" :iconColor="seriesColor" :content="formatNumber(questionStats.totalCount)" icon="rise" /> | ||
| 12 | </div> | 14 | </div> |
| 13 | <!-- <LineMulti :chartData="lineMultiData" height="33vh" type="line" :option="{ legend: { top: 'bottom' } }" />--> | 15 | <!-- <LineMulti :chartData="lineMultiData" height="33vh" type="line" :option="{ legend: { top: 'bottom' } }" />--> |
| 14 | </a-card> | 16 | </a-card> |
| @@ -17,14 +19,15 @@ | @@ -17,14 +19,15 @@ | ||
| 17 | </div> | 19 | </div> |
| 18 | </template> | 20 | </template> |
| 19 | <script lang="ts" setup> | 21 | <script lang="ts" setup> |
| 20 | - import { computed, onMounted, ref, watch } from 'vue'; | 22 | + import { onMounted, ref, watch } from 'vue'; |
| 21 | import ChartGroupCard from '../components/ChartGroupCard.vue'; | 23 | import ChartGroupCard from '../components/ChartGroupCard.vue'; |
| 22 | import SaleTabCard from '../components/SaleTabCard.vue'; | 24 | import SaleTabCard from '../components/SaleTabCard.vue'; |
| 23 | import HeadInfo from '/@/components/chart/HeadInfo.vue'; | 25 | import HeadInfo from '/@/components/chart/HeadInfo.vue'; |
| 24 | - import { getLoginfo, getStatistics, getVisitInfo } from '../api'; | 26 | + import { getLoginfo, getQuestionStatistics, getStatistics, getVisitInfo } from '../api'; |
| 25 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | 27 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
| 26 | 28 | ||
| 27 | const loading = ref(true); | 29 | const loading = ref(true); |
| 30 | + const questionLoading = ref(true); | ||
| 28 | const { getThemeColor } = useRootSetting(); | 31 | const { getThemeColor } = useRootSetting(); |
| 29 | 32 | ||
| 30 | // 默认范围类型(全部) | 33 | // 默认范围类型(全部) |
| @@ -49,10 +52,40 @@ | @@ -49,10 +52,40 @@ | ||
| 49 | rejectedCount: 0, | 52 | rejectedCount: 0, |
| 50 | totalCount: 0, | 53 | totalCount: 0, |
| 51 | averageCount: 0, | 54 | averageCount: 0, |
| 52 | - buttonStats: [] as { name: string; total: number }[], | ||
| 53 | monthlyData: [] as { month: string; count: number }[], | 55 | monthlyData: [] as { month: string; count: number }[], |
| 56 | + dailyCounts: [] as { date: string; count: number }[], | ||
| 57 | + dailyRejectedCounts: [] as { date: string; count: number }[], | ||
| 54 | }); | 58 | }); |
| 55 | 59 | ||
| 60 | + const questionStats = ref({ | ||
| 61 | + todayCount: 0, | ||
| 62 | + weekCount: 0, | ||
| 63 | + monthCount: 0, | ||
| 64 | + yearCount: 0, | ||
| 65 | + totalCount: 0, | ||
| 66 | + }); | ||
| 67 | + | ||
| 68 | + // 获取问题统计数据的方法 | ||
| 69 | + async function fetchQuestionStatistics() { | ||
| 70 | + try { | ||
| 71 | + questionLoading.value = true; | ||
| 72 | + const res = await getQuestionStatistics(); | ||
| 73 | + console.log('问题统计数据:', res); | ||
| 74 | + | ||
| 75 | + // 更新数据(根据实际接口返回结构调整) | ||
| 76 | + questionStats.value = { | ||
| 77 | + todayCount: res.todayCount ?? 0, | ||
| 78 | + weekCount: res.weekCount ?? 0, | ||
| 79 | + monthCount: res.monthCount ?? 0, | ||
| 80 | + yearCount: res.yearCount ?? 0, | ||
| 81 | + totalCount: res.totalCount ?? 0, | ||
| 82 | + }; | ||
| 83 | + } catch (error) { | ||
| 84 | + console.error('获取问题统计失败', error); | ||
| 85 | + } finally { | ||
| 86 | + questionLoading.value = false; | ||
| 87 | + } | ||
| 88 | + } | ||
| 56 | // 实时数据获取函数 | 89 | // 实时数据获取函数 |
| 57 | async function fetchStatistics() { | 90 | async function fetchStatistics() { |
| 58 | try { | 91 | try { |
| @@ -72,8 +105,9 @@ | @@ -72,8 +105,9 @@ | ||
| 72 | rejectedCount: res.rejectedCount || res.data?.rejectedCount || 0, | 105 | rejectedCount: res.rejectedCount || res.data?.rejectedCount || 0, |
| 73 | totalCount: res.totalCount || res.data?.totalCount || 0, | 106 | totalCount: res.totalCount || res.data?.totalCount || 0, |
| 74 | averageCount: res.averageCount || 0, | 107 | averageCount: res.averageCount || 0, |
| 75 | - buttonStats: res.buttonStats || [], | ||
| 76 | monthlyData: res.monthlyData || res.data?.monthlyData || generateMonthlyData(), | 108 | monthlyData: res.monthlyData || res.data?.monthlyData || generateMonthlyData(), |
| 109 | + dailyCounts: res.dailyCounts || [], | ||
| 110 | + dailyRejectedCounts: res.dailyRejectedCounts || [], | ||
| 77 | }; | 111 | }; |
| 78 | 112 | ||
| 79 | console.log('更新后的 statistics:', statistics.value); | 113 | console.log('更新后的 statistics:', statistics.value); |
| @@ -90,27 +124,13 @@ | @@ -90,27 +124,13 @@ | ||
| 90 | })); | 124 | })); |
| 91 | } | 125 | } |
| 92 | 126 | ||
| 93 | - // 转换按钮统计数据格式 | ||
| 94 | - const buttonStats = computed(() => { | ||
| 95 | - return statistics.value.buttonStats.map((item) => ({ | ||
| 96 | - name: item.name, | ||
| 97 | - total: item.total, | ||
| 98 | - })); | ||
| 99 | - }); | ||
| 100 | - // 在 setup 中添加转换函数 | ||
| 101 | - const barData = computed(() => { | ||
| 102 | - if (!statistics.value.monthlyData) return []; | ||
| 103 | - | ||
| 104 | - return statistics.value.monthlyData.map((item) => { | ||
| 105 | - return { | ||
| 106 | - name: `${item.month}`, | ||
| 107 | - value: item.count, | 127 | + const formatNumber = (num: number) => { |
| 128 | + return num.toLocaleString(); | ||
| 108 | }; | 129 | }; |
| 109 | - }); | ||
| 110 | - }); | ||
| 111 | 130 | ||
| 112 | onMounted(() => { | 131 | onMounted(() => { |
| 113 | fetchStatistics(); | 132 | fetchStatistics(); |
| 133 | + fetchQuestionStatistics(); | ||
| 114 | }); | 134 | }); |
| 115 | 135 | ||
| 116 | setTimeout(() => { | 136 | setTimeout(() => { |
| @@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
| 39 | </a-card> | 39 | </a-card> |
| 40 | </a-col> | 40 | </a-col> |
| 41 | <a-col v-if="knowledgeList && knowledgeList.length > 0" :xxl="4" :xl="6" :lg="6" :md="6" :sm="12" :xs="24" v-for="item in knowledgeList"> | 41 | <a-col v-if="knowledgeList && knowledgeList.length > 0" :xxl="4" :xl="6" :lg="6" :md="6" :sm="12" :xs="24" v-for="item in knowledgeList"> |
| 42 | - <a-card class="knowledge-card pointer" @click="handleDocClick(item.id)"> | 42 | + <a-card class="knowledge-card pointer" @click="handleDocClick(item)"> |
| 43 | <div class="knowledge-header"> | 43 | <div class="knowledge-header"> |
| 44 | <div class="flex"> | 44 | <div class="flex"> |
| 45 | <img class="header-img" src="./icon/knowledge.png" /> | 45 | <img class="header-img" src="./icon/knowledge.png" /> |
| @@ -113,6 +113,7 @@ | @@ -113,6 +113,7 @@ | ||
| 113 | import AiragKnowledgeDocListModal from './components/AiragKnowledgeDocListModal.vue'; | 113 | import AiragKnowledgeDocListModal from './components/AiragKnowledgeDocListModal.vue'; |
| 114 | import Icon from '@/components/Icon'; | 114 | import Icon from '@/components/Icon'; |
| 115 | import { useMessage } from '@/hooks/web/useMessage'; | 115 | import { useMessage } from '@/hooks/web/useMessage'; |
| 116 | + import { useRouter } from 'vue-router'; | ||
| 116 | 117 | ||
| 117 | export default { | 118 | export default { |
| 118 | name: 'KnowledgeBaseList', | 119 | name: 'KnowledgeBaseList', |
| @@ -128,7 +129,7 @@ | @@ -128,7 +129,7 @@ | ||
| 128 | setup() { | 129 | setup() { |
| 129 | //模型列表 | 130 | //模型列表 |
| 130 | const knowledgeList = ref([]); | 131 | const knowledgeList = ref([]); |
| 131 | - | 132 | + const router = useRouter(); |
| 132 | //注册modal | 133 | //注册modal |
| 133 | const [registerModal, { openModal }] = useModal(); | 134 | const [registerModal, { openModal }] = useModal(); |
| 134 | const [docListRegister, { openModal: openDocModal }] = useModal(); | 135 | const [docListRegister, { openModal: openDocModal }] = useModal(); |
| @@ -246,8 +247,15 @@ | @@ -246,8 +247,15 @@ | ||
| 246 | * | 247 | * |
| 247 | * @param id | 248 | * @param id |
| 248 | */ | 249 | */ |
| 249 | - function handleDocClick(id) { | ||
| 250 | - openDocModal(true, { id }); | 250 | + function handleDocClick(item) { |
| 251 | + console.log('测试' + item); | ||
| 252 | + router.push({ | ||
| 253 | + path: '/super/airag/test/EmbeddingsList', | ||
| 254 | + query: { | ||
| 255 | + knowledgeId: item.id, | ||
| 256 | + knowledgeName: item.name, | ||
| 257 | + }, | ||
| 258 | + }); | ||
| 251 | } | 259 | } |
| 252 | 260 | ||
| 253 | /** | 261 | /** |
| 1 | <template> | 1 | <template> |
| 2 | <div class="p-2"> | 2 | <div class="p-2"> |
| 3 | <BasicModal destroyOnClose @register="registerModal" :canFullscreen="false" width="600px" :title="title" @ok="handleOk" @cancel="handleCancel"> | 3 | <BasicModal destroyOnClose @register="registerModal" :canFullscreen="false" width="600px" :title="title" @ok="handleOk" @cancel="handleCancel"> |
| 4 | - <BasicForm @register="registerForm"></BasicForm> | 4 | + <BasicForm @register="registerForm" /> |
| 5 | </BasicModal> | 5 | </BasicModal> |
| 6 | </div> | 6 | </div> |
| 7 | </template> | 7 | </template> |
| @@ -4,14 +4,13 @@ | @@ -4,14 +4,13 @@ | ||
| 4 | <div class="p-2"> | 4 | <div class="p-2"> |
| 5 | <div class="header"> | 5 | <div class="header"> |
| 6 | <a-tag color="#a9c8ff"> | 6 | <a-tag color="#a9c8ff"> |
| 7 | - <span>{{hitTextDescData.source}}</span> | 7 | + <span>{{ hitTextDescData.source }}</span> |
| 8 | </a-tag> | 8 | </a-tag> |
| 9 | </div> | 9 | </div> |
| 10 | <div class="content"> | 10 | <div class="content"> |
| 11 | <MarkdownViewer :value="hitTextDescData.content" /> | 11 | <MarkdownViewer :value="hitTextDescData.content" /> |
| 12 | </div> | 12 | </div> |
| 13 | </div> | 13 | </div> |
| 14 | - | ||
| 15 | </BasicModal> | 14 | </BasicModal> |
| 16 | </template> | 15 | </template> |
| 17 | 16 | ||
| @@ -32,18 +31,18 @@ | @@ -32,18 +31,18 @@ | ||
| 32 | }, | 31 | }, |
| 33 | emits: ['success', 'register'], | 32 | emits: ['success', 'register'], |
| 34 | setup(props, { emit }) { | 33 | setup(props, { emit }) { |
| 35 | - let hitTextDescData = ref<any>({}) | 34 | + let hitTextDescData = ref<any>({}); |
| 36 | 35 | ||
| 37 | //注册modal | 36 | //注册modal |
| 38 | const [registerModal, { closeModal, setModalProps }] = useModalInner(async (data) => { | 37 | const [registerModal, { closeModal, setModalProps }] = useModalInner(async (data) => { |
| 39 | hitTextDescData.value.source = 'score' + ' ' + data.score.toFixed(2); | 38 | hitTextDescData.value.source = 'score' + ' ' + data.score.toFixed(2); |
| 40 | hitTextDescData.value.content = data.content; | 39 | hitTextDescData.value.content = data.content; |
| 41 | - setModalProps({ header: '300px' }) | 40 | + setModalProps({ header: '300px' }); |
| 42 | }); | 41 | }); |
| 43 | 42 | ||
| 44 | return { | 43 | return { |
| 45 | registerModal, | 44 | registerModal, |
| 46 | - hitTextDescData | 45 | + hitTextDescData, |
| 47 | }; | 46 | }; |
| 48 | }, | 47 | }, |
| 49 | }; | 48 | }; |
| @@ -16,16 +16,16 @@ | @@ -16,16 +16,16 @@ | ||
| 16 | </a-layout-sider> | 16 | </a-layout-sider> |
| 17 | <a-layout-content :style="contentStyle"> | 17 | <a-layout-content :style="contentStyle"> |
| 18 | <div v-if="selectedKey === 'document'"> | 18 | <div v-if="selectedKey === 'document'"> |
| 19 | - <a-input v-model:value="searchText" placeholder="请输入文档名称,回车搜索" class="search-title" @pressEnter="reload"/> | 19 | + <a-input v-model:value="searchText" placeholder="请输入文档名称,回车搜索" class="search-title" @press-enter="reload" /> |
| 20 | <a-row :span="24" class="knowledge-row"> | 20 | <a-row :span="24" class="knowledge-row"> |
| 21 | <a-col :xxl="4" :xl="6" :lg="6" :md="6" :sm="12" :xs="24"> | 21 | <a-col :xxl="4" :xl="6" :lg="6" :md="6" :sm="12" :xs="24"> |
| 22 | <a-card class="add-knowledge-card" :bodyStyle="cardBodyStyle"> | 22 | <a-card class="add-knowledge-card" :bodyStyle="cardBodyStyle"> |
| 23 | - <span style="line-height: 18px;font-weight: 500;color:#676f83;font-size: 12px">创建文档</span> | 23 | + <span style="line-height: 18px; font-weight: 500; color: #676f83; font-size: 12px">创建文档</span> |
| 24 | <div class="add-knowledge-doc" @click="handleCreateText"> | 24 | <div class="add-knowledge-doc" @click="handleCreateText"> |
| 25 | - <Icon icon="ant-design:form-outlined" size="13"></Icon><span>手动录入</span> | 25 | + <Icon icon="ant-design:form-outlined" size="13" /><span>手动录入</span> |
| 26 | </div> | 26 | </div> |
| 27 | <div class="add-knowledge-doc" @click="handleCreateUpload"> | 27 | <div class="add-knowledge-doc" @click="handleCreateUpload"> |
| 28 | - <Icon icon="ant-design:cloud-upload-outlined" size="13"></Icon><span>文件上传</span> | 28 | + <Icon icon="ant-design:cloud-upload-outlined" size="13" /><span>文件上传</span> |
| 29 | </div> | 29 | </div> |
| 30 | <div class="add-knowledge-doc" @click="handleCreateUploadLibrary"> | 30 | <div class="add-knowledge-doc" @click="handleCreateUploadLibrary"> |
| 31 | <a-upload | 31 | <a-upload |
| @@ -38,7 +38,7 @@ | @@ -38,7 +38,7 @@ | ||
| 38 | :action="uploadUrl" | 38 | :action="uploadUrl" |
| 39 | @change="handleUploadChange" | 39 | @change="handleUploadChange" |
| 40 | > | 40 | > |
| 41 | - <Icon style="margin-left: 0" icon="ant-design:project-outlined" size="13"></Icon> | 41 | + <Icon style="margin-left: 0" icon="ant-design:project-outlined" size="13" /> |
| 42 | <span>文档库上传</span> | 42 | <span>文档库上传</span> |
| 43 | </a-upload> | 43 | </a-upload> |
| 44 | </div> | 44 | </div> |
| @@ -48,14 +48,49 @@ | @@ -48,14 +48,49 @@ | ||
| 48 | <a-card class="knowledge-card pointer" @click="handleEdit(item)"> | 48 | <a-card class="knowledge-card pointer" @click="handleEdit(item)"> |
| 49 | <div class="knowledge-header"> | 49 | <div class="knowledge-header"> |
| 50 | <div class="header-text flex"> | 50 | <div class="header-text flex"> |
| 51 | - <Icon v-if="item.type==='text'" icon="ant-design:file-text-outlined" size="32" color="#00a7d0"></Icon> | ||
| 52 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === 'pdf'" icon="ant-design:file-pdf-outlined" size="32" color="rgb(211, 47, 47)"></Icon> | ||
| 53 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === 'docx'" icon="ant-design:file-word-outlined" size="32" color="rgb(68, 138, 255)"></Icon> | ||
| 54 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === 'pptx'" icon="ant-design:file-ppt-outlined" size="32" color="rgb(245, 124, 0)"></Icon> | ||
| 55 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === 'xlsx'" icon="ant-design:file-excel-outlined" size="32" color="rgb(98, 187, 55)"></Icon> | ||
| 56 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === 'txt'" icon="ant-design:file-text-outlined" size="32" color="#00a7d0"></Icon> | ||
| 57 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === 'md'" icon="ant-design:file-markdown-outlined" size="32" color="#292929"></Icon> | ||
| 58 | - <Icon v-if="item.type==='file' && getFileSuffix(item.metadata) === ''" icon="ant-design:file-unknown-outlined" size="32" color="#f5f5dc"></Icon> | 51 | + <Icon v-if="item.type === 'text'" icon="ant-design:file-text-outlined" size="32" color="#00a7d0" /> |
| 52 | + <Icon | ||
| 53 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === 'pdf'" | ||
| 54 | + icon="ant-design:file-pdf-outlined" | ||
| 55 | + size="32" | ||
| 56 | + color="rgb(211, 47, 47)" | ||
| 57 | + /> | ||
| 58 | + <Icon | ||
| 59 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === 'docx'" | ||
| 60 | + icon="ant-design:file-word-outlined" | ||
| 61 | + size="32" | ||
| 62 | + color="rgb(68, 138, 255)" | ||
| 63 | + /> | ||
| 64 | + <Icon | ||
| 65 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === 'pptx'" | ||
| 66 | + icon="ant-design:file-ppt-outlined" | ||
| 67 | + size="32" | ||
| 68 | + color="rgb(245, 124, 0)" | ||
| 69 | + /> | ||
| 70 | + <Icon | ||
| 71 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === 'xlsx'" | ||
| 72 | + icon="ant-design:file-excel-outlined" | ||
| 73 | + size="32" | ||
| 74 | + color="rgb(98, 187, 55)" | ||
| 75 | + /> | ||
| 76 | + <Icon | ||
| 77 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === 'txt'" | ||
| 78 | + icon="ant-design:file-text-outlined" | ||
| 79 | + size="32" | ||
| 80 | + color="#00a7d0" | ||
| 81 | + /> | ||
| 82 | + <Icon | ||
| 83 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === 'md'" | ||
| 84 | + icon="ant-design:file-markdown-outlined" | ||
| 85 | + size="32" | ||
| 86 | + color="#292929" | ||
| 87 | + /> | ||
| 88 | + <Icon | ||
| 89 | + v-if="item.type === 'file' && getFileSuffix(item.metadata) === ''" | ||
| 90 | + icon="ant-design:file-unknown-outlined" | ||
| 91 | + size="32" | ||
| 92 | + color="#f5f5dc" | ||
| 93 | + /> | ||
| 59 | <span class="ellipsis header-title">{{ item.title }}</span> | 94 | <span class="ellipsis header-title">{{ item.title }}</span> |
| 60 | </div> | 95 | </div> |
| 61 | </div> | 96 | </div> |
| @@ -65,35 +100,35 @@ | @@ -65,35 +100,35 @@ | ||
| 65 | <div class="flex" style="justify-content: space-between"> | 100 | <div class="flex" style="justify-content: space-between"> |
| 66 | <div class="card-text"> | 101 | <div class="card-text"> |
| 67 | 状态: | 102 | 状态: |
| 68 | - <div v-if="item.status==='complete'" class="card-text-status"> | ||
| 69 | - <Icon icon="ant-design:check-circle-outlined" size="16" color="#56D1A7"></Icon> | 103 | + <div v-if="item.status === 'complete'" class="card-text-status"> |
| 104 | + <Icon icon="ant-design:check-circle-outlined" size="16" color="#56D1A7" /> | ||
| 70 | <span class="ml-2">已完成</span> | 105 | <span class="ml-2">已完成</span> |
| 71 | </div> | 106 | </div> |
| 72 | - <div v-else-if="item.status==='building'" class="card-text-status"> | ||
| 73 | - <a-spin v-if="item.loading" :spinning="item.loading" :indicator="indicator"></a-spin> | 107 | + <div v-else-if="item.status === 'building'" class="card-text-status"> |
| 108 | + <a-spin v-if="item.loading" :spinning="item.loading" :indicator="indicator" /> | ||
| 74 | <span class="ml-2">构建中</span> | 109 | <span class="ml-2">构建中</span> |
| 75 | </div> | 110 | </div> |
| 76 | - <div v-else-if="item.status==='draft'" class="card-text-status"> | ||
| 77 | - <img src="../icon/draft.png" style="width: 16px;height: 16px" /> | 111 | + <div v-else-if="item.status === 'draft'" class="card-text-status"> |
| 112 | + <img src="../icon/draft.png" style="width: 16px; height: 16px" /> | ||
| 78 | <span class="ml-2">草稿</span> | 113 | <span class="ml-2">草稿</span> |
| 79 | </div> | 114 | </div> |
| 80 | </div> | 115 | </div> |
| 81 | <a-dropdown placement="bottomRight" :trigger="['click']"> | 116 | <a-dropdown placement="bottomRight" :trigger="['click']"> |
| 82 | <div class="ant-dropdown-link pointer operation" @click.prevent.stop> | 117 | <div class="ant-dropdown-link pointer operation" @click.prevent.stop> |
| 83 | - <Icon icon="ant-design:ellipsis-outlined" size="16"></Icon> | 118 | + <Icon icon="ant-design:ellipsis-outlined" size="16" /> |
| 84 | </div> | 119 | </div> |
| 85 | <template #overlay> | 120 | <template #overlay> |
| 86 | <a-menu> | 121 | <a-menu> |
| 87 | <a-menu-item key="vectorization" @click="handleVectorization(item.id)"> | 122 | <a-menu-item key="vectorization" @click="handleVectorization(item.id)"> |
| 88 | - <Icon icon="ant-design:retweet-outlined" size="16"></Icon> | 123 | + <Icon icon="ant-design:retweet-outlined" size="16" /> |
| 89 | 向量化 | 124 | 向量化 |
| 90 | </a-menu-item> | 125 | </a-menu-item> |
| 91 | <a-menu-item key="edit" @click="handleEdit(item)"> | 126 | <a-menu-item key="edit" @click="handleEdit(item)"> |
| 92 | - <Icon icon="ant-design:edit-outlined" size="16"></Icon> | 127 | + <Icon icon="ant-design:edit-outlined" size="16" /> |
| 93 | 编辑 | 128 | 编辑 |
| 94 | </a-menu-item> | 129 | </a-menu-item> |
| 95 | <a-menu-item key="delete" @click="handleDelete(item.id)"> | 130 | <a-menu-item key="delete" @click="handleDelete(item.id)"> |
| 96 | - <Icon icon="ant-design:delete-outlined" size="16"></Icon> | 131 | + <Icon icon="ant-design:delete-outlined" size="16" /> |
| 97 | 删除 | 132 | 删除 |
| 98 | </a-menu-item> | 133 | </a-menu-item> |
| 99 | </a-menu> | 134 | </a-menu> |
| @@ -129,13 +164,13 @@ | @@ -129,13 +164,13 @@ | ||
| 129 | <span>{{ hitShowSearchText }}</span> | 164 | <span>{{ hitShowSearchText }}</span> |
| 130 | </div> | 165 | </div> |
| 131 | <div class="content-card"> | 166 | <div class="content-card"> |
| 132 | - <a-row :span="24" class="knowledge-row" v-if="hitTextList.length>0"> | 167 | + <a-row :span="24" class="knowledge-row" v-if="hitTextList.length > 0"> |
| 133 | <a-col :xxl="6" :xl="6" :lg="6" :md="6" :sm="12" :xs="24" v-for="item in hitTextList"> | 168 | <a-col :xxl="6" :xl="6" :lg="6" :md="6" :sm="12" :xs="24" v-for="item in hitTextList"> |
| 134 | <a-card class="hit-card pointer" style="border-color: #ffffff" @click="hitTextDescClick(item)"> | 169 | <a-card class="hit-card pointer" style="border-color: #ffffff" @click="hitTextDescClick(item)"> |
| 135 | <div class="card-title"> | 170 | <div class="card-title"> |
| 136 | - <div style="display: flex;"> | ||
| 137 | - <Icon icon="ant-design:appstore-outlined" size="14"></Icon> | ||
| 138 | - <span style="margin-left: 4px">Chunk-{{item.chunk}}</span> | 171 | + <div style="display: flex"> |
| 172 | + <Icon icon="ant-design:appstore-outlined" size="14" /> | ||
| 173 | + <span style="margin-left: 4px">Chunk-{{ item.chunk }}</span> | ||
| 139 | <span style="margin-left: 10px">{{ item.content.length }} 字符</span> | 174 | <span style="margin-left: 10px">{{ item.content.length }} 字符</span> |
| 140 | </div> | 175 | </div> |
| 141 | <a-tag class="card-title-tag" color="#a9c8ff"> | 176 | <a-tag class="card-title-tag" color="#a9c8ff"> |
| @@ -146,7 +181,7 @@ | @@ -146,7 +181,7 @@ | ||
| 146 | {{ item.content }} | 181 | {{ item.content }} |
| 147 | </div> | 182 | </div> |
| 148 | <div class="card-footer"> | 183 | <div class="card-footer"> |
| 149 | - {{item.docName}} | 184 | + {{ item.docName }} |
| 150 | </div> | 185 | </div> |
| 151 | </a-card> | 186 | </a-card> |
| 152 | </a-col> | 187 | </a-col> |
| @@ -154,9 +189,7 @@ | @@ -154,9 +189,7 @@ | ||
| 154 | <div v-else-if="notHit"> | 189 | <div v-else-if="notHit"> |
| 155 | <a-empty :image-style="{ margin: '0 auto', height: '160px', verticalAlign: 'middle', borderStyle: 'none' }"> | 190 | <a-empty :image-style="{ margin: '0 auto', height: '160px', verticalAlign: 'middle', borderStyle: 'none' }"> |
| 156 | <template #description> | 191 | <template #description> |
| 157 | - <div style="margin-top: 26px; font-size: 20px; color: #000; text-align: center !important"> | ||
| 158 | - 没有命中的分段 | ||
| 159 | - </div> | 192 | + <div style="margin-top: 26px; font-size: 20px; color: #000; text-align: center !important"> 没有命中的分段 </div> |
| 160 | </template> | 193 | </template> |
| 161 | </a-empty> | 194 | </a-empty> |
| 162 | </div> | 195 | </div> |
| @@ -167,18 +200,18 @@ | @@ -167,18 +200,18 @@ | ||
| 167 | <ul> | 200 | <ul> |
| 168 | <li> | 201 | <li> |
| 169 | <span>条数:</span> | 202 | <span>条数:</span> |
| 170 | - <a-input-number :min="1" v-model:value="topNumber"></a-input-number> | 203 | + <a-input-number :min="1" v-model:value="topNumber" /> |
| 171 | </li> | 204 | </li> |
| 172 | <li> | 205 | <li> |
| 173 | <span>Score阈值:</span> | 206 | <span>Score阈值:</span> |
| 174 | - <a-input-number :min="0" :step="0.01" :max="1" v-model:value="similarity"></a-input-number> | 207 | + <a-input-number :min="0" :step="0.01" :max="1" v-model:value="similarity" /> |
| 175 | </li> | 208 | </li> |
| 176 | </ul> | 209 | </ul> |
| 177 | </div> | 210 | </div> |
| 178 | <div class="hit-test-footer"> | 211 | <div class="hit-test-footer"> |
| 179 | - <a-input v-model:value="hitText" size="large" placeholder="请输入" style="width: 100%" @pressEnter="hitTestClick"> | 212 | + <a-input v-model:value="hitText" size="large" placeholder="请输入" style="width: 100%" @press-enter="hitTestClick"> |
| 180 | <template #suffix> | 213 | <template #suffix> |
| 181 | - <Icon icon="ant-design:send-outlined" style="transform: rotate(-33deg); cursor: pointer" size="22" @click="hitTestClick"></Icon> | 214 | + <Icon icon="ant-design:send-outlined" style="transform: rotate(-33deg); cursor: pointer" size="22" @click="hitTestClick" /> |
| 182 | </template> | 215 | </template> |
| 183 | </a-input> | 216 | </a-input> |
| 184 | </div> | 217 | </div> |
| @@ -189,9 +222,9 @@ | @@ -189,9 +222,9 @@ | ||
| 189 | </BasicModal> | 222 | </BasicModal> |
| 190 | 223 | ||
| 191 | <!-- 手工录入文本 --> | 224 | <!-- 手工录入文本 --> |
| 192 | - <AiragKnowledgeDocTextModal @register="docTextRegister" @success="handleSuccess"></AiragKnowledgeDocTextModal> | 225 | + <AiragKnowledgeDocTextModal @register="docTextRegister" @success="handleSuccess" /> |
| 193 | <!-- 文本明细 --> | 226 | <!-- 文本明细 --> |
| 194 | - <AiTextDescModal @register="docTextDescRegister"></AiTextDescModal> | 227 | + <AiTextDescModal @register="docTextDescRegister" /> |
| 195 | </div> | 228 | </div> |
| 196 | </template> | 229 | </template> |
| 197 | 230 | ||
| @@ -206,11 +239,11 @@ | @@ -206,11 +239,11 @@ | ||
| 206 | import AiTextDescModal from './AiTextDescModal.vue'; | 239 | import AiTextDescModal from './AiTextDescModal.vue'; |
| 207 | import { useMessage } from '@/hooks/web/useMessage'; | 240 | import { useMessage } from '@/hooks/web/useMessage'; |
| 208 | import { LoadingOutlined } from '@ant-design/icons-vue'; | 241 | import { LoadingOutlined } from '@ant-design/icons-vue'; |
| 209 | - import {Avatar, message, Modal, Pagination} from 'ant-design-vue'; | 242 | + import { Avatar, message, Modal, Pagination } from 'ant-design-vue'; |
| 210 | import { useUserStore } from '@/store/modules/user'; | 243 | import { useUserStore } from '@/store/modules/user'; |
| 211 | import { getFileAccessHttpUrl, getHeaders } from '@/utils/common/compUtils'; | 244 | import { getFileAccessHttpUrl, getHeaders } from '@/utils/common/compUtils'; |
| 212 | import defaultImg from '/@/assets/images/header.jpg'; | 245 | import defaultImg from '/@/assets/images/header.jpg'; |
| 213 | - import Icon from "@/components/Icon"; | 246 | + import Icon from '@/components/Icon'; |
| 214 | import { useGlobSetting } from '/@/hooks/setting'; | 247 | import { useGlobSetting } from '/@/hooks/setting'; |
| 215 | 248 | ||
| 216 | export default { | 249 | export default { |
| @@ -273,7 +306,7 @@ | @@ -273,7 +306,7 @@ | ||
| 273 | const headers = getHeaders(); | 306 | const headers = getHeaders(); |
| 274 | const globSetting = useGlobSetting(); | 307 | const globSetting = useGlobSetting(); |
| 275 | //上传路径 | 308 | //上传路径 |
| 276 | - const uploadUrl = ref<string>(globSetting.domainUrl+"/airag/knowledge/doc/import/zip"); | 309 | + const uploadUrl = ref<string>(globSetting.domainUrl + '/airag/knowledge/doc/import/zip'); |
| 277 | 310 | ||
| 278 | //菜单项 | 311 | //菜单项 |
| 279 | const menuItems = ref<any>([ | 312 | const menuItems = ref<any>([ |
| @@ -325,7 +358,7 @@ | @@ -325,7 +358,7 @@ | ||
| 325 | const indicator = h(LoadingOutlined, { | 358 | const indicator = h(LoadingOutlined, { |
| 326 | style: { | 359 | style: { |
| 327 | fontSize: '16px', | 360 | fontSize: '16px', |
| 328 | - marginRight: '2px' | 361 | + marginRight: '2px', |
| 329 | }, | 362 | }, |
| 330 | spin: true, | 363 | spin: true, |
| 331 | }); | 364 | }); |
| @@ -336,14 +369,14 @@ | @@ -336,14 +369,14 @@ | ||
| 336 | * 手工录入文本 | 369 | * 手工录入文本 |
| 337 | */ | 370 | */ |
| 338 | function handleCreateText() { | 371 | function handleCreateText() { |
| 339 | - docTextOpenModal(true, { knowledgeId: knowledgeId.value, type: "text" }); | 372 | + docTextOpenModal(true, { knowledgeId: knowledgeId.value, type: 'text' }); |
| 340 | } | 373 | } |
| 341 | 374 | ||
| 342 | /** | 375 | /** |
| 343 | * 文件上传 | 376 | * 文件上传 |
| 344 | */ | 377 | */ |
| 345 | function handleCreateUpload() { | 378 | function handleCreateUpload() { |
| 346 | - docTextOpenModal(true, { knowledgeId: knowledgeId.value, type: "file" }); | 379 | + docTextOpenModal(true, { knowledgeId: knowledgeId.value, type: 'file' }); |
| 347 | } | 380 | } |
| 348 | 381 | ||
| 349 | /** | 382 | /** |
| @@ -377,8 +410,8 @@ | @@ -377,8 +410,8 @@ | ||
| 377 | cancelText: '取消', | 410 | cancelText: '取消', |
| 378 | onOk: () => { | 411 | onOk: () => { |
| 379 | knowledgeDeleteBatchDoc({ ids: id }, reload); | 412 | knowledgeDeleteBatchDoc({ ids: id }, reload); |
| 380 | - } | ||
| 381 | - }) | 413 | + }, |
| 414 | + }); | ||
| 382 | } | 415 | } |
| 383 | 416 | ||
| 384 | /** | 417 | /** |
| @@ -394,7 +427,7 @@ | @@ -394,7 +427,7 @@ | ||
| 394 | * 文档新增和编辑成功回调 | 427 | * 文档新增和编辑成功回调 |
| 395 | */ | 428 | */ |
| 396 | function handleSuccess() { | 429 | function handleSuccess() { |
| 397 | - if(!timer.value){ | 430 | + if (!timer.value) { |
| 398 | reload(); | 431 | reload(); |
| 399 | } | 432 | } |
| 400 | clearInterval(timer.value); | 433 | clearInterval(timer.value); |
| @@ -408,7 +441,7 @@ | @@ -408,7 +441,7 @@ | ||
| 408 | function triggeringTimer() { | 441 | function triggeringTimer() { |
| 409 | timer.value = setInterval(() => { | 442 | timer.value = setInterval(() => { |
| 410 | reload(); | 443 | reload(); |
| 411 | - },5000) | 444 | + }, 5000); |
| 412 | } | 445 | } |
| 413 | 446 | ||
| 414 | /** | 447 | /** |
| @@ -420,7 +453,7 @@ | @@ -420,7 +453,7 @@ | ||
| 420 | setTimeout(() => { | 453 | setTimeout(() => { |
| 421 | pageNo.value = 1; | 454 | pageNo.value = 1; |
| 422 | pageSize.value = 10; | 455 | pageSize.value = 10; |
| 423 | - searchText.value = ""; | 456 | + searchText.value = ''; |
| 424 | 457 | ||
| 425 | reload(); | 458 | reload(); |
| 426 | }); | 459 | }); |
| @@ -446,7 +479,8 @@ | @@ -446,7 +479,8 @@ | ||
| 446 | knowId: knowledgeId.value, | 479 | knowId: knowledgeId.value, |
| 447 | topNumber: topNumber.value, | 480 | topNumber: topNumber.value, |
| 448 | similarity: similarity.value, | 481 | similarity: similarity.value, |
| 449 | - }).then((res) => { | 482 | + }) |
| 483 | + .then((res) => { | ||
| 450 | if (res.success) { | 484 | if (res.success) { |
| 451 | if (res.result) { | 485 | if (res.result) { |
| 452 | hitTextList.value = res.result; | 486 | hitTextList.value = res.result; |
| @@ -459,7 +493,8 @@ | @@ -459,7 +493,8 @@ | ||
| 459 | hitText.value = ''; | 493 | hitText.value = ''; |
| 460 | notHit.value = hitTextList.value.length == 0; | 494 | notHit.value = hitTextList.value.length == 0; |
| 461 | spinning.value = false; | 495 | spinning.value = false; |
| 462 | - }).catch(()=>{ | 496 | + }) |
| 497 | + .catch(() => { | ||
| 463 | spinning.value = false; | 498 | spinning.value = false; |
| 464 | }); | 499 | }); |
| 465 | } | 500 | } |
| @@ -491,22 +526,22 @@ | @@ -491,22 +526,22 @@ | ||
| 491 | knowledgeId: knowledgeId.value, | 526 | knowledgeId: knowledgeId.value, |
| 492 | title: '*' + searchText.value + '*', | 527 | title: '*' + searchText.value + '*', |
| 493 | column: 'createTime', | 528 | column: 'createTime', |
| 494 | - order: 'desc' | 529 | + order: 'desc', |
| 495 | }; | 530 | }; |
| 496 | await knowledgeDocList(params).then((res) => { | 531 | await knowledgeDocList(params).then((res) => { |
| 497 | if (res.success) { | 532 | if (res.success) { |
| 498 | //update-begin---author:wangshuai---date:2025-03-21---for:【QQYUN-11636】向量化功能改成异步--- | 533 | //update-begin---author:wangshuai---date:2025-03-21---for:【QQYUN-11636】向量化功能改成异步--- |
| 499 | - if(res.result.records){ | 534 | + if (res.result.records) { |
| 500 | let clearTimer = true; | 535 | let clearTimer = true; |
| 501 | for (const item of res.result.records) { | 536 | for (const item of res.result.records) { |
| 502 | - if(item.status && item.status === 'building' ){ | 537 | + if (item.status && item.status === 'building') { |
| 503 | clearTimer = false; | 538 | clearTimer = false; |
| 504 | item.loading = true; | 539 | item.loading = true; |
| 505 | - }else{ | 540 | + } else { |
| 506 | item.loading = false; | 541 | item.loading = false; |
| 507 | } | 542 | } |
| 508 | } | 543 | } |
| 509 | - if(clearTimer){ | 544 | + if (clearTimer) { |
| 510 | clearInterval(timer.value); | 545 | clearInterval(timer.value); |
| 511 | } | 546 | } |
| 512 | } | 547 | } |
| @@ -535,7 +570,7 @@ | @@ -535,7 +570,7 @@ | ||
| 535 | * 获取文件后缀 | 570 | * 获取文件后缀 |
| 536 | */ | 571 | */ |
| 537 | function getFileSuffix(metadata) { | 572 | function getFileSuffix(metadata) { |
| 538 | - if(metadata){ | 573 | + if (metadata) { |
| 539 | let filePath = JSON.parse(metadata).filePath; | 574 | let filePath = JSON.parse(metadata).filePath; |
| 540 | const index = filePath.lastIndexOf('.'); | 575 | const index = filePath.lastIndexOf('.'); |
| 541 | return index > 0 ? filePath.substring(index + 1).toLowerCase() : ''; | 576 | return index > 0 ? filePath.substring(index + 1).toLowerCase() : ''; |
| @@ -562,10 +597,10 @@ | @@ -562,10 +597,10 @@ | ||
| 562 | function handleUploadChange(info) { | 597 | function handleUploadChange(info) { |
| 563 | let { file } = info; | 598 | let { file } = info; |
| 564 | if (file.status === 'error') { | 599 | if (file.status === 'error') { |
| 565 | - createMessage.error(file.response.message ||`${file.name} 上传失败.`); | 600 | + createMessage.error(file.response.message || `${file.name} 上传失败.`); |
| 566 | } | 601 | } |
| 567 | if (file.status === 'done') { | 602 | if (file.status === 'done') { |
| 568 | - if(!file.response.success){ | 603 | + if (!file.response.success) { |
| 569 | createMessage.warning(file.response.message); | 604 | createMessage.warning(file.response.message); |
| 570 | return; | 605 | return; |
| 571 | } | 606 | } |
| @@ -574,10 +609,10 @@ | @@ -574,10 +609,10 @@ | ||
| 574 | } | 609 | } |
| 575 | } | 610 | } |
| 576 | 611 | ||
| 577 | - onBeforeMount(()=>{ | 612 | + onBeforeMount(() => { |
| 578 | clearInterval(timer.value); | 613 | clearInterval(timer.value); |
| 579 | timer.value = null; | 614 | timer.value = null; |
| 580 | - }) | 615 | + }); |
| 581 | 616 | ||
| 582 | return { | 617 | return { |
| 583 | registerModal, | 618 | registerModal, |
| @@ -615,7 +650,7 @@ | @@ -615,7 +650,7 @@ | ||
| 615 | handlePageChange, | 650 | handlePageChange, |
| 616 | searchText, | 651 | searchText, |
| 617 | reload, | 652 | reload, |
| 618 | - cardBodyStyle:{ textAlign: 'left', width: '100%' }, | 653 | + cardBodyStyle: { textAlign: 'left', width: '100%' }, |
| 619 | getFileSuffix, | 654 | getFileSuffix, |
| 620 | notHit, | 655 | notHit, |
| 621 | indicator, | 656 | indicator, |
| @@ -703,7 +738,7 @@ | @@ -703,7 +738,7 @@ | ||
| 703 | border-radius: 10px; | 738 | border-radius: 10px; |
| 704 | background: #fcfcfd; | 739 | background: #fcfcfd; |
| 705 | border: 1px solid #f0f0f0; | 740 | border: 1px solid #f0f0f0; |
| 706 | - box-shadow: 0 2px 4px rgba(0,0,0,0.1); | 741 | + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| 707 | transition: all 0.3s ease; | 742 | transition: all 0.3s ease; |
| 708 | .card-title { | 743 | .card-title { |
| 709 | justify-content: space-between; | 744 | justify-content: space-between; |
| @@ -715,7 +750,7 @@ | @@ -715,7 +750,7 @@ | ||
| 715 | } | 750 | } |
| 716 | } | 751 | } |
| 717 | .hit-card:hover { | 752 | .hit-card:hover { |
| 718 | - box-shadow: 0 6px 12px rgba(0,0,0,0.15) !important; | 753 | + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15) !important; |
| 719 | } | 754 | } |
| 720 | .pointer { | 755 | .pointer { |
| 721 | cursor: pointer; | 756 | cursor: pointer; |
| @@ -732,7 +767,7 @@ | @@ -732,7 +767,7 @@ | ||
| 732 | margin-top: 16px; | 767 | margin-top: 16px; |
| 733 | text-align: left; | 768 | text-align: left; |
| 734 | font-size: 12px; | 769 | font-size: 12px; |
| 735 | - color: #676F83; | 770 | + color: #676f83; |
| 736 | } | 771 | } |
| 737 | 772 | ||
| 738 | .card-title-tag { | 773 | .card-title-tag { |
| @@ -753,7 +788,7 @@ | @@ -753,7 +788,7 @@ | ||
| 753 | width: calc(100% - 20px); | 788 | width: calc(100% - 20px); |
| 754 | background: #fcfcfd; | 789 | background: #fcfcfd; |
| 755 | border: 1px solid #f0f0f0; | 790 | border: 1px solid #f0f0f0; |
| 756 | - box-shadow: 0 2px 4px rgba(0,0,0,0.1); | 791 | + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| 757 | transition: all 0.3s ease; | 792 | transition: all 0.3s ease; |
| 758 | .add-knowledge-card-icon { | 793 | .add-knowledge-card-icon { |
| 759 | padding: 8px; | 794 | padding: 8px; |
| @@ -768,7 +803,7 @@ | @@ -768,7 +803,7 @@ | ||
| 768 | height: 166px; | 803 | height: 166px; |
| 769 | background: #fcfcfd; | 804 | background: #fcfcfd; |
| 770 | border: 1px solid #f0f0f0; | 805 | border: 1px solid #f0f0f0; |
| 771 | - box-shadow: 0 2px 4px rgba(0,0,0,0.1); | 806 | + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| 772 | transition: all 0.3s ease; | 807 | transition: all 0.3s ease; |
| 773 | .knowledge-header { | 808 | .knowledge-header { |
| 774 | position: relative; | 809 | position: relative; |
| @@ -780,7 +815,7 @@ | @@ -780,7 +815,7 @@ | ||
| 780 | height: 40px; | 815 | height: 40px; |
| 781 | margin-right: 12px; | 816 | margin-right: 12px; |
| 782 | } | 817 | } |
| 783 | - .header-title{ | 818 | + .header-title { |
| 784 | font-weight: bold; | 819 | font-weight: bold; |
| 785 | color: #354052; | 820 | color: #354052; |
| 786 | margin-left: 4px; | 821 | margin-left: 4px; |
| @@ -796,12 +831,14 @@ | @@ -796,12 +831,14 @@ | ||
| 796 | } | 831 | } |
| 797 | } | 832 | } |
| 798 | 833 | ||
| 799 | - .add-knowledge-card,.knowledge-card{ | 834 | + .add-knowledge-card, |
| 835 | + .knowledge-card { | ||
| 800 | transition: box-shadow 0.3s ease; | 836 | transition: box-shadow 0.3s ease; |
| 801 | } | 837 | } |
| 802 | 838 | ||
| 803 | - .add-knowledge-card:hover,.knowledge-card:hover{ | ||
| 804 | - box-shadow: 0 6px 12px rgba(0,0,0,0.15); | 839 | + .add-knowledge-card:hover, |
| 840 | + .knowledge-card:hover { | ||
| 841 | + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); | ||
| 805 | } | 842 | } |
| 806 | 843 | ||
| 807 | .ellipsis { | 844 | .ellipsis { |
| @@ -815,21 +852,21 @@ | @@ -815,21 +852,21 @@ | ||
| 815 | padding: 16px; | 852 | padding: 16px; |
| 816 | } | 853 | } |
| 817 | 854 | ||
| 818 | - .card-text{ | 855 | + .card-text { |
| 819 | font-size: 12px; | 856 | font-size: 12px; |
| 820 | display: flex; | 857 | display: flex; |
| 821 | margin-top: 10px; | 858 | margin-top: 10px; |
| 822 | align-items: center; | 859 | align-items: center; |
| 823 | } | 860 | } |
| 824 | 861 | ||
| 825 | - .search-title{ | 862 | + .search-title { |
| 826 | width: 200px; | 863 | width: 200px; |
| 827 | margin-top: 10px; | 864 | margin-top: 10px; |
| 828 | display: block; | 865 | display: block; |
| 829 | margin-left: 20px; | 866 | margin-left: 20px; |
| 830 | } | 867 | } |
| 831 | 868 | ||
| 832 | - .operation{ | 869 | + .operation { |
| 833 | border: none; | 870 | border: none; |
| 834 | margin-top: 10px; | 871 | margin-top: 10px; |
| 835 | align-items: end; | 872 | align-items: end; |
| @@ -839,38 +876,38 @@ | @@ -839,38 +876,38 @@ | ||
| 839 | position: absolute; | 876 | position: absolute; |
| 840 | } | 877 | } |
| 841 | 878 | ||
| 842 | - .knowledge-card:hover{ | ||
| 843 | - .operation{ | 879 | + .knowledge-card:hover { |
| 880 | + .operation { | ||
| 844 | display: block !important; | 881 | display: block !important; |
| 845 | } | 882 | } |
| 846 | } | 883 | } |
| 847 | 884 | ||
| 848 | - .add-knowledge-doc{ | 885 | + .add-knowledge-doc { |
| 849 | margin-top: 6px; | 886 | margin-top: 6px; |
| 850 | - color:#6F6F83; | 887 | + color: #6f6f83; |
| 851 | font-size: 13px; | 888 | font-size: 13px; |
| 852 | width: 100%; | 889 | width: 100%; |
| 853 | cursor: pointer; | 890 | cursor: pointer; |
| 854 | display: flex; | 891 | display: flex; |
| 855 | - span{ | 892 | + span { |
| 856 | margin-left: 4px; | 893 | margin-left: 4px; |
| 857 | line-height: 28px; | 894 | line-height: 28px; |
| 858 | } | 895 | } |
| 859 | } | 896 | } |
| 860 | - .add-knowledge-doc:hover{ | 897 | + .add-knowledge-doc:hover { |
| 861 | background: #c8ceda33; | 898 | background: #c8ceda33; |
| 862 | } | 899 | } |
| 863 | - .operation{ | 900 | + .operation { |
| 864 | background-color: unset; | 901 | background-color: unset; |
| 865 | border: none; | 902 | border: none; |
| 866 | margin-right: 2px; | 903 | margin-right: 2px; |
| 867 | } | 904 | } |
| 868 | - .operation:hover{ | 905 | + .operation:hover { |
| 869 | color: #000000; | 906 | color: #000000; |
| 870 | - background-color: rgba(0,0,0,0.05); | 907 | + background-color: rgba(0, 0, 0, 0.05); |
| 871 | border: none; | 908 | border: none; |
| 872 | } | 909 | } |
| 873 | - .ant-dropdown-link{ | 910 | + .ant-dropdown-link { |
| 874 | font-size: 14px; | 911 | font-size: 14px; |
| 875 | height: 24px; | 912 | height: 24px; |
| 876 | padding: 0 7px; | 913 | padding: 0 7px; |
| @@ -878,18 +915,18 @@ | @@ -878,18 +915,18 @@ | ||
| 878 | align-content: center; | 915 | align-content: center; |
| 879 | text-align: center; | 916 | text-align: center; |
| 880 | } | 917 | } |
| 881 | - .card-footer{ | 918 | + .card-footer { |
| 882 | margin-top: 4px; | 919 | margin-top: 4px; |
| 883 | font-weight: 400; | 920 | font-weight: 400; |
| 884 | color: #1f2329; | 921 | color: #1f2329; |
| 885 | text-align: left; | 922 | text-align: left; |
| 886 | font-size: 12px; | 923 | font-size: 12px; |
| 887 | } | 924 | } |
| 888 | - .card-text-status{ | 925 | + .card-text-status { |
| 889 | display: flex; | 926 | display: flex; |
| 890 | align-items: center; | 927 | align-items: center; |
| 891 | } | 928 | } |
| 892 | - .ml-2{ | 929 | + .ml-2 { |
| 893 | margin-left: 2px; | 930 | margin-left: 2px; |
| 894 | } | 931 | } |
| 895 | </style> | 932 | </style> |
| @@ -36,7 +36,7 @@ export const columns: BasicColumn[] = [ | @@ -36,7 +36,7 @@ export const columns: BasicColumn[] = [ | ||
| 36 | dataIndex: 'buttonValues', | 36 | dataIndex: 'buttonValues', |
| 37 | }, | 37 | }, |
| 38 | { | 38 | { |
| 39 | - title: '按钮code', | 39 | + title: '按钮标识', |
| 40 | align: 'center', | 40 | align: 'center', |
| 41 | dataIndex: 'code', | 41 | dataIndex: 'code', |
| 42 | }, | 42 | }, |
| @@ -98,7 +98,7 @@ export const formSchema: FormSchema[] = [ | @@ -98,7 +98,7 @@ export const formSchema: FormSchema[] = [ | ||
| 98 | // componentProps: {}, | 98 | // componentProps: {}, |
| 99 | // }, | 99 | // }, |
| 100 | { | 100 | { |
| 101 | - label: '按钮code', | 101 | + label: '按钮标识', |
| 102 | field: 'code', | 102 | field: 'code', |
| 103 | component: 'Input', | 103 | component: 'Input', |
| 104 | required: true, | 104 | required: true, |
| @@ -59,12 +59,7 @@ export const columns: BasicColumn[] = [ | @@ -59,12 +59,7 @@ export const columns: BasicColumn[] = [ | ||
| 59 | }, | 59 | }, |
| 60 | }, | 60 | }, |
| 61 | { | 61 | { |
| 62 | - title: '按钮名称', | ||
| 63 | - align: 'center', | ||
| 64 | - dataIndex: 'buttonName', | ||
| 65 | - }, | ||
| 66 | - { | ||
| 67 | - title: 'code', | 62 | + title: '按钮标识', |
| 68 | align: 'center', | 63 | align: 'center', |
| 69 | dataIndex: 'code', | 64 | dataIndex: 'code', |
| 70 | }, | 65 | }, |
| 1 | <template> | 1 | <template> |
| 2 | <a-card title="智能助手配置" class="config-card"> | 2 | <a-card title="智能助手配置" class="config-card"> |
| 3 | - <a-form :model="formState" layout="vertical"> | 3 | + <a-form :model="formState" layout="vertical" ref="formRef" :rules="rules"> |
| 4 | <!-- 向量模型 --> | 4 | <!-- 向量模型 --> |
| 5 | - <a-form-item label="向量模型" class="form-item"> | 5 | + <a-form-item label="向量模型" class="form-item required" name="embeddingId"> |
| 6 | <a-select | 6 | <a-select |
| 7 | v-model:value="formState.embeddingId" | 7 | v-model:value="formState.embeddingId" |
| 8 | placeholder="请选择向量模型" | 8 | placeholder="请选择向量模型" |
| @@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
| 13 | </a-form-item> | 13 | </a-form-item> |
| 14 | 14 | ||
| 15 | <!-- 大语言模型 --> | 15 | <!-- 大语言模型 --> |
| 16 | - <a-form-item label="大语言模型" class="form-item"> | 16 | + <a-form-item label="大语言模型" class="form-item required" name="llmId"> |
| 17 | <a-select | 17 | <a-select |
| 18 | v-model:value="formState.llmId" | 18 | v-model:value="formState.llmId" |
| 19 | placeholder="请选择大语言模型" | 19 | placeholder="请选择大语言模型" |
| @@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
| 24 | </a-form-item> | 24 | </a-form-item> |
| 25 | 25 | ||
| 26 | <!-- 知识库 --> | 26 | <!-- 知识库 --> |
| 27 | - <a-form-item label="知识库" class="form-item"> | 27 | + <a-form-item label="知识库" class="form-item required" name="knowledgeId"> |
| 28 | <a-select | 28 | <a-select |
| 29 | v-model:value="formState.knowledgeId" | 29 | v-model:value="formState.knowledgeId" |
| 30 | placeholder="请选择知识库" | 30 | placeholder="请选择知识库" |
| @@ -35,7 +35,7 @@ | @@ -35,7 +35,7 @@ | ||
| 35 | </a-form-item> | 35 | </a-form-item> |
| 36 | 36 | ||
| 37 | <!-- 按钮(多选) --> | 37 | <!-- 按钮(多选) --> |
| 38 | - <a-form-item label="功能按钮" class="form-item"> | 38 | + <a-form-item label="功能按钮" class="form-item required" name="buttonIds"> |
| 39 | <a-select | 39 | <a-select |
| 40 | v-model:value="formState.buttonIds" | 40 | v-model:value="formState.buttonIds" |
| 41 | mode="multiple" | 41 | mode="multiple" |
| @@ -50,11 +50,11 @@ | @@ -50,11 +50,11 @@ | ||
| 50 | </a-form-item> | 50 | </a-form-item> |
| 51 | 51 | ||
| 52 | <!-- 提示词 --> | 52 | <!-- 提示词 --> |
| 53 | - <a-form-item label="提示词" class="form-item"> | 53 | + <a-form-item label="提示词" class="form-item required" name="prompt"> |
| 54 | <a-textarea | 54 | <a-textarea |
| 55 | v-model:value="formState.prompt" | 55 | v-model:value="formState.prompt" |
| 56 | placeholder="请输入提示词,例如:你是一个专业的AI助手..." | 56 | placeholder="请输入提示词,例如:你是一个专业的AI助手..." |
| 57 | - :rows="4" | 57 | + :rows="9" |
| 58 | :maxlength="500" | 58 | :maxlength="500" |
| 59 | show-count | 59 | show-count |
| 60 | /> | 60 | /> |
| @@ -80,7 +80,7 @@ | @@ -80,7 +80,7 @@ | ||
| 80 | <script lang="ts" setup> | 80 | <script lang="ts" setup> |
| 81 | import { reactive, ref, onMounted } from 'vue'; | 81 | import { reactive, ref, onMounted } from 'vue'; |
| 82 | import { getConfigData, saveConfig } from './Chatsetting.api'; | 82 | import { getConfigData, saveConfig } from './Chatsetting.api'; |
| 83 | - import { message } from 'ant-design-vue'; | 83 | + import { FormInstance, message } from 'ant-design-vue'; |
| 84 | import { SaveOutlined, UndoOutlined } from '@ant-design/icons-vue'; | 84 | import { SaveOutlined, UndoOutlined } from '@ant-design/icons-vue'; |
| 85 | 85 | ||
| 86 | // 表单数据结构 | 86 | // 表单数据结构 |
| @@ -93,6 +93,25 @@ | @@ -93,6 +93,25 @@ | ||
| 93 | prompt: '', | 93 | prompt: '', |
| 94 | }); | 94 | }); |
| 95 | 95 | ||
| 96 | + const formRef = ref<FormInstance>(); // 添加表单引用 | ||
| 97 | + | ||
| 98 | + const rules = reactive({ | ||
| 99 | + embeddingId: [{ required: true, message: '请选择向量模型', trigger: 'change' }], | ||
| 100 | + llmId: [{ required: true, message: '请选择大语言模型', trigger: 'change' }], | ||
| 101 | + knowledgeId: [{ required: true, message: '请选择知识库', trigger: 'change' }], | ||
| 102 | + buttonIds: [ | ||
| 103 | + { | ||
| 104 | + required: true, | ||
| 105 | + message: '请至少选择一个按钮', | ||
| 106 | + trigger: 'change', | ||
| 107 | + type: 'array', | ||
| 108 | + }, | ||
| 109 | + ], | ||
| 110 | + prompt: [ | ||
| 111 | + { required: true, message: '请输入提示词', trigger: 'blur' }, | ||
| 112 | + { max: 500, message: '提示词长度不能超过500个字符', trigger: 'blur' }, | ||
| 113 | + ], | ||
| 114 | + }); | ||
| 96 | // 选项数据 | 115 | // 选项数据 |
| 97 | const options = reactive({ | 116 | const options = reactive({ |
| 98 | embeddingOptions: [], | 117 | embeddingOptions: [], |
| @@ -114,7 +133,11 @@ | @@ -114,7 +133,11 @@ | ||
| 114 | // 填充表单 | 133 | // 填充表单 |
| 115 | Object.keys(formState).forEach((key) => { | 134 | Object.keys(formState).forEach((key) => { |
| 116 | if (data.airagChatsettingConfig[key] !== undefined) { | 135 | if (data.airagChatsettingConfig[key] !== undefined) { |
| 136 | + if (key === 'buttonIds') { | ||
| 137 | + formState[key] = data.airagChatsettingConfig[key] || []; | ||
| 138 | + } else { | ||
| 117 | formState[key] = data.airagChatsettingConfig[key]; | 139 | formState[key] = data.airagChatsettingConfig[key]; |
| 140 | + } | ||
| 118 | console.log('shuju' + formState[key]); | 141 | console.log('shuju' + formState[key]); |
| 119 | } | 142 | } |
| 120 | }); | 143 | }); |
| @@ -134,6 +157,12 @@ | @@ -134,6 +157,12 @@ | ||
| 134 | 157 | ||
| 135 | // 保存配置 | 158 | // 保存配置 |
| 136 | const saveConfigData = async () => { | 159 | const saveConfigData = async () => { |
| 160 | + try { | ||
| 161 | + await formRef.value?.validateFields(); | ||
| 162 | + } catch (error) { | ||
| 163 | + message.error('请填写所有必填字段'); | ||
| 164 | + return; | ||
| 165 | + } | ||
| 137 | const params = { | 166 | const params = { |
| 138 | ...formState, | 167 | ...formState, |
| 139 | id: formState.id || '', | 168 | id: formState.id || '', |
| @@ -163,7 +192,7 @@ | @@ -163,7 +192,7 @@ | ||
| 163 | 192 | ||
| 164 | // 重置表单 | 193 | // 重置表单 |
| 165 | const resetForm = () => { | 194 | const resetForm = () => { |
| 166 | - loadConfig(); | 195 | + formRef.value?.resetFields(); |
| 167 | }; | 196 | }; |
| 168 | 197 | ||
| 169 | // 初始化加载 | 198 | // 初始化加载 |
| @@ -174,7 +203,7 @@ | @@ -174,7 +203,7 @@ | ||
| 174 | 203 | ||
| 175 | <style lang="less" scoped> | 204 | <style lang="less" scoped> |
| 176 | .config-card { | 205 | .config-card { |
| 177 | - max-width: 800px; | 206 | + max-width: 1800px; |
| 178 | margin: 20px auto; | 207 | margin: 20px auto; |
| 179 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); | 208 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| 180 | 209 |
| 1 | <template> | 1 | <template> |
| 2 | <div> | 2 | <div> |
| 3 | + <div class="knowledge-header" v-if="currentKnowledge"> | ||
| 4 | + <div class="header-container"> | ||
| 5 | + <h2 class="knowledge-title">{{ currentKnowledge.name }} - 文档列表</h2> | ||
| 6 | + <a-button type="link" @click="goBack" class="back-button"> <Icon icon="ant-design:arrow-left-outlined" /> 返回知识库列表 </a-button> | ||
| 7 | + </div> | ||
| 8 | + </div> | ||
| 3 | <!--引用表格--> | 9 | <!--引用表格--> |
| 4 | <BasicTable @register="registerTable" :rowSelection="rowSelection"> | 10 | <BasicTable @register="registerTable" :rowSelection="rowSelection"> |
| 5 | <!--插槽:table标题--> | 11 | <!--插槽:table标题--> |
| @@ -24,7 +30,7 @@ | @@ -24,7 +30,7 @@ | ||
| 24 | </a-button> | 30 | </a-button> |
| 25 | </a-dropdown> | 31 | </a-dropdown> |
| 26 | <!-- 高级查询 --> | 32 | <!-- 高级查询 --> |
| 27 | -<!-- <super-query :config="superQueryConfig" @search="handleSuperQuery" />--> | 33 | + <!-- <super-query :config="superQueryConfig" @search="handleSuperQuery" />--> |
| 28 | </template> | 34 | </template> |
| 29 | <!--操作栏--> | 35 | <!--操作栏--> |
| 30 | <template #action="{ record }"> | 36 | <template #action="{ record }"> |
| @@ -49,10 +55,43 @@ | @@ -49,10 +55,43 @@ | ||
| 49 | import { useUserStore } from '/@/store/modules/user'; | 55 | import { useUserStore } from '/@/store/modules/user'; |
| 50 | import JUploadButton from '@/components/Button/src/JUploadButton.vue'; | 56 | import JUploadButton from '@/components/Button/src/JUploadButton.vue'; |
| 51 | import { columns as defaultColumns } from './Test.data'; | 57 | import { columns as defaultColumns } from './Test.data'; |
| 58 | + import { useRoute, useRouter } from 'vue-router'; | ||
| 52 | const queryParam = reactive<any>({}); | 59 | const queryParam = reactive<any>({}); |
| 53 | const checkedKeys = ref<Array<string | number>>([]); | 60 | const checkedKeys = ref<Array<string | number>>([]); |
| 54 | const userStore = useUserStore(); | 61 | const userStore = useUserStore(); |
| 55 | - // 添加知识库名称映射 | 62 | + |
| 63 | + const route = useRoute(); | ||
| 64 | + const router = useRouter(); | ||
| 65 | + | ||
| 66 | + // 当前知识库信息 | ||
| 67 | + const currentKnowledge = ref<{ id: string; name: string } | null>(null); | ||
| 68 | + | ||
| 69 | + // 在onMounted中添加知识库ID处理 | ||
| 70 | + onMounted(() => { | ||
| 71 | + // 从路由参数获取知识库ID | ||
| 72 | + const knowledgeId = route.query.knowledgeId as string; | ||
| 73 | + const knowledgeName = route.query.knowledgeName as string; | ||
| 74 | + | ||
| 75 | + console.log('ceshi' + knowledgeId); | ||
| 76 | + if (knowledgeId && knowledgeName) { | ||
| 77 | + currentKnowledge.value = { | ||
| 78 | + id: knowledgeId, | ||
| 79 | + name: knowledgeName, | ||
| 80 | + }; | ||
| 81 | + | ||
| 82 | + // 设置查询参数 | ||
| 83 | + queryParam.knowledgeId = knowledgeId; | ||
| 84 | + reload(); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + loadKnowledgeMap(); | ||
| 88 | + }); | ||
| 89 | + | ||
| 90 | + // 返回知识库列表 | ||
| 91 | + function goBack() { | ||
| 92 | + router.push('/super/airag/aiknowledge/AiKnowledgeBaseList'); | ||
| 93 | + } | ||
| 94 | + // 知识库名称映射 | ||
| 56 | const knowledgeMap = ref<Record<string, string>>({}); | 95 | const knowledgeMap = ref<Record<string, string>>({}); |
| 57 | 96 | ||
| 58 | // 加载知识库列表 | 97 | // 加载知识库列表 |
| @@ -142,6 +181,9 @@ | @@ -142,6 +181,9 @@ | ||
| 142 | fixed: 'right', | 181 | fixed: 'right', |
| 143 | }, | 182 | }, |
| 144 | beforeFetch: (params) => { | 183 | beforeFetch: (params) => { |
| 184 | + if (currentKnowledge.value?.id) { | ||
| 185 | + params.knowledgeId = currentKnowledge.value.id; | ||
| 186 | + } | ||
| 145 | // 处理知识库查询参数 | 187 | // 处理知识库查询参数 |
| 146 | if (params.knowledgeId) { | 188 | if (params.knowledgeId) { |
| 147 | // 直接使用对象而不是JSON字符串 | 189 | // 直接使用对象而不是JSON字符串 |
| @@ -269,4 +311,69 @@ | @@ -269,4 +311,69 @@ | ||
| 269 | :deep(.ant-input-number) { | 311 | :deep(.ant-input-number) { |
| 270 | width: 100%; | 312 | width: 100%; |
| 271 | } | 313 | } |
| 314 | + .knowledge-header { | ||
| 315 | + margin-bottom: 16px; | ||
| 316 | + padding: 16px; | ||
| 317 | + background: #fff; | ||
| 318 | + border-radius: 8px; | ||
| 319 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); | ||
| 320 | + | ||
| 321 | + .header-container { | ||
| 322 | + display: flex; | ||
| 323 | + justify-content: space-between; | ||
| 324 | + align-items: center; | ||
| 325 | + | ||
| 326 | + .knowledge-title { | ||
| 327 | + margin: 0; | ||
| 328 | + font-size: 18px; | ||
| 329 | + font-weight: 600; | ||
| 330 | + color: #1f2329; | ||
| 331 | + flex: 1; | ||
| 332 | + white-space: nowrap; | ||
| 333 | + overflow: hidden; | ||
| 334 | + text-overflow: ellipsis; | ||
| 335 | + padding-right: 16px; | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + .back-button { | ||
| 339 | + display: flex; | ||
| 340 | + align-items: center; | ||
| 341 | + color: #1890ff; | ||
| 342 | + font-weight: 500; | ||
| 343 | + padding: 4px 8px; | ||
| 344 | + transition: all 0.3s; | ||
| 345 | + flex-shrink: 0; | ||
| 346 | + | ||
| 347 | + &:hover { | ||
| 348 | + color: #40a9ff; | ||
| 349 | + background: rgba(24, 144, 255, 0.1); | ||
| 350 | + border-radius: 4px; | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + i { | ||
| 354 | + margin-right: 4px; | ||
| 355 | + font-size: 14px; | ||
| 356 | + } | ||
| 357 | + } | ||
| 358 | + } | ||
| 359 | + } | ||
| 360 | + | ||
| 361 | + @media (max-width: 768px) { | ||
| 362 | + .knowledge-header { | ||
| 363 | + .header-container { | ||
| 364 | + flex-direction: column; | ||
| 365 | + align-items: flex-start; | ||
| 366 | + | ||
| 367 | + .knowledge-title { | ||
| 368 | + margin-bottom: 12px; | ||
| 369 | + padding-right: 0; | ||
| 370 | + width: 100%; | ||
| 371 | + } | ||
| 372 | + | ||
| 373 | + .back-button { | ||
| 374 | + align-self: flex-end; | ||
| 375 | + } | ||
| 376 | + } | ||
| 377 | + } | ||
| 378 | + } | ||
| 272 | </style> | 379 | </style> |
-
请 注册 或 登录 后发表评论