|
|
|
@ -12,69 +12,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
<div v-show="!isLeftPanelCollapsed">
|
|
|
|
<div v-show="!isLeftPanelCollapsed">
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
<el-form :model="userQueryParams" ref="userQueryFormRef" :inline="true" label-width="80px">
|
|
|
|
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="80px">
|
|
|
|
<el-form-item label="用户姓名" prop="userName">
|
|
|
|
<el-form-item label="用户昵称" prop="nickName">
|
|
|
|
<el-input
|
|
|
|
<el-input
|
|
|
|
v-model="userQueryParams.userName"
|
|
|
|
v-model="queryParams.nickName"
|
|
|
|
placeholder="请输入用户姓名进行模糊搜索"
|
|
|
|
placeholder="请输入用户昵称"
|
|
|
|
clearable
|
|
|
|
clearable
|
|
|
|
style="width: 200px"
|
|
|
|
style="width: 200px"
|
|
|
|
@keyup.enter="handleUserQuery"
|
|
|
|
@keyup.enter="getList"
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item label="AI模型" prop="aiModel">
|
|
|
|
<el-form-item label="AI模型" prop="modelId">
|
|
|
|
<el-select v-model="userQueryParams.aiModel" placeholder="请选择AI模型" clearable style="width: 200px">
|
|
|
|
<el-select v-model="queryParams.modelId" placeholder="请选择AI模型" clearable>
|
|
|
|
<el-option label="GPT-3.5" value="gpt-3.5" />
|
|
|
|
<el-option
|
|
|
|
<el-option label="GPT-4" value="gpt-4" />
|
|
|
|
v-for="dict in aiModelList"
|
|
|
|
<el-option label="Claude-3" value="claude-3" />
|
|
|
|
:key="dict.modelId"
|
|
|
|
<el-option label="DeepSeek" value="deepseek" />
|
|
|
|
:label="dict.modelName"
|
|
|
|
<el-option label="通义千问" value="qwen" />
|
|
|
|
:value="dict.modelId"
|
|
|
|
|
|
|
|
/>
|
|
|
|
</el-select>
|
|
|
|
</el-select>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item>
|
|
|
|
<el-form-item>
|
|
|
|
<el-button type="primary" icon="Search" @click="handleUserQuery">搜索</el-button>
|
|
|
|
<el-button type="primary" icon="Search" @click="getList">搜索</el-button>
|
|
|
|
<el-button icon="Refresh" @click="resetUserQuery">重置</el-button>
|
|
|
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form>
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 用户列表 -->
|
|
|
|
<!-- 用户列表 -->
|
|
|
|
<el-table
|
|
|
|
<el-table
|
|
|
|
v-loading="userLoading"
|
|
|
|
v-loading="loading"
|
|
|
|
:data="userPaginatedList"
|
|
|
|
:data="tokenUsageList"
|
|
|
|
@row-click="handleUserSelect"
|
|
|
|
@row-click="handleUserSelect"
|
|
|
|
highlight-current-row
|
|
|
|
highlight-current-row
|
|
|
|
:row-class-name="getUserRowClass"
|
|
|
|
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<el-table-column label="用户姓名" align="center" prop="userName" />
|
|
|
|
<el-table-column label="用户" align="center" prop="nickName" v-if="columns[2].visible"/>
|
|
|
|
<el-table-column label="AI模型" align="center" prop="aiModel" />
|
|
|
|
<el-table-column label="AI模型" align="center" prop="modelName" v-if="columns[4].visible"/>
|
|
|
|
<el-table-column label="提问Token" align="center" prop="promptTokens">
|
|
|
|
<el-table-column label="提问Token" align="center" prop="promptToken" v-if="columns[5].visible"/>
|
|
|
|
<template #default="scope">
|
|
|
|
<el-table-column label="回复Token" align="center" prop="completionToken" v-if="columns[6].visible"/>
|
|
|
|
<span class="token-count">{{ scope.row.promptTokens.toLocaleString() }}</span>
|
|
|
|
<el-table-column label="总Token" align="center" prop="totalToken" v-if="columns[7].visible"/>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
<el-table-column label="回答Token" align="center" prop="completionTokens">
|
|
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
|
|
<span class="token-count">{{ scope.row.completionTokens.toLocaleString() }}</span>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
<el-table-column label="总Token" align="center" prop="totalTokens">
|
|
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
|
|
<span class="token-count total">{{ scope.row.totalTokens.toLocaleString() }}</span>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
</el-table>
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 用户分页 -->
|
|
|
|
<!-- 用户分页 -->
|
|
|
|
<el-pagination
|
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
|
|
|
v-show="userTotal > 0"
|
|
|
|
v-model:limit="queryParams.pageSize" @pagination="getList"/>
|
|
|
|
class="mt-4"
|
|
|
|
|
|
|
|
:total="userTotal"
|
|
|
|
|
|
|
|
v-model:current-page="userQueryParams.pageNum"
|
|
|
|
|
|
|
|
v-model:page-size="userQueryParams.pageSize"
|
|
|
|
|
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
|
|
|
@size-change="handleUserQuery"
|
|
|
|
|
|
|
|
@current-change="handleUserQuery"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</el-card>
|
|
|
|
</el-card>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
@ -94,74 +74,256 @@
|
|
|
|
<template #header>
|
|
|
|
<template #header>
|
|
|
|
<div class="card-header">
|
|
|
|
<div class="card-header">
|
|
|
|
<span>Token使用详情记录</span>
|
|
|
|
<span>Token使用详情记录</span>
|
|
|
|
<span v-if="selectedUser" class="selected-user">当前用户:{{ selectedUser.userName }}</span>
|
|
|
|
<span v-if="selectedUser" class="selected-user">当前用户:{{
|
|
|
|
|
|
|
|
selectedUser.nickName
|
|
|
|
|
|
|
|
}},当前模型:{{ selectedUser.modelName }}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
<el-form :model="detailQueryParams" ref="detailQueryFormRef" :inline="true" label-width="80px">
|
|
|
|
<el-form :model="detailQueryParams" ref="detailQueryFormRef" :inline="true" label-width="80px">
|
|
|
|
<el-form-item label="使用类型" prop="usageType">
|
|
|
|
<el-form-item label="使用类型" prop="messageDetailType">
|
|
|
|
<el-select v-model="detailQueryParams.usageType" placeholder="请选择使用类型" clearable style="width: 200px">
|
|
|
|
<el-select v-model='detailQueryParams.messageDetailType' placeholder='请选择使用类型' clearable>
|
|
|
|
<el-option label="AI问答" value="ai_chat" />
|
|
|
|
<el-option v-for='dict in ai_chat_message_detail_type' :key='dict.value' :label='dict.label' :value='dict.value'/>
|
|
|
|
<el-option label="AI知识库" value="ai_knowledge" />
|
|
|
|
|
|
|
|
<el-option label="AI生成SQL" value="ai_sql" />
|
|
|
|
|
|
|
|
<el-option label="AI智能填报" value="ai_form" />
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
</el-select>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
<el-form-item>
|
|
|
|
<el-form-item>
|
|
|
|
<el-button type="primary" icon="Search" @click="handleDetailQuery">搜索</el-button>
|
|
|
|
<el-button type="primary" icon="Search" @click="getDetailList">搜索</el-button>
|
|
|
|
<el-button icon="Refresh" @click="resetDetailQuery">重置</el-button>
|
|
|
|
<el-button icon="Refresh" @click="resetDetailQuery">重置</el-button>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form-item>
|
|
|
|
</el-form>
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 详情列表 -->
|
|
|
|
<!-- 详情列表 -->
|
|
|
|
<el-table v-loading="detailLoading" :data="detailPaginatedList">
|
|
|
|
<el-table v-loading="detailLoading" :data="detailList">
|
|
|
|
<el-table-column label="用户姓名" align="center" prop="userName" />
|
|
|
|
<el-table-column label="用户姓名" align="center" prop="nickName"/>
|
|
|
|
<el-table-column label="使用类型" align="center" prop="usageType">
|
|
|
|
<el-table-column label="使用类型" align="center" prop="messageDetailType">
|
|
|
|
<template #default="scope">
|
|
|
|
<template #default="scope">
|
|
|
|
<el-tag :type="getUsageTypeTagType(scope.row.usageType)">
|
|
|
|
<dict-tag :options='ai_chat_message_detail_type' :value='scope.row.messageDetailType'/>
|
|
|
|
{{ getUsageTypeLabel(scope.row.usageType) }}
|
|
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="AI模型" align="center" prop="aiModel" />
|
|
|
|
<el-table-column label="AI模型" align="center" prop="modelName"/>
|
|
|
|
<el-table-column label="会话名称" align="center" prop="sessionName" :show-overflow-tooltip="true" />
|
|
|
|
<el-table-column label="提问内容" align="center" prop="questionContent" :show-overflow-tooltip="true"/>
|
|
|
|
<el-table-column label="提问Token" align="center" prop="promptTokens">
|
|
|
|
<el-table-column label="回复内容" align="center" prop="answerContent" :show-overflow-tooltip="true"/>
|
|
|
|
|
|
|
|
<el-table-column label="提问Token" align="center" prop="promptToken">
|
|
|
|
<template #default="scope">
|
|
|
|
<template #default="scope">
|
|
|
|
<span class="token-count">{{ scope.row.promptTokens.toLocaleString() }}</span>
|
|
|
|
<span class="token-count">{{ scope.row.promptToken?.toLocaleString() }}</span>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="回答Token" align="center" prop="completionTokens">
|
|
|
|
<el-table-column label="回答Token" align="center" prop="completionToken">
|
|
|
|
<template #default="scope">
|
|
|
|
<template #default="scope">
|
|
|
|
<span class="token-count">{{ scope.row.completionTokens.toLocaleString() }}</span>
|
|
|
|
<span class="token-count">{{ scope.row.completionToken?.toLocaleString() }}</span>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="总Token" align="center" prop="totalTokens">
|
|
|
|
<el-table-column label="总Token" align="center" prop="totalToken">
|
|
|
|
<template #default="scope">
|
|
|
|
<template #default="scope">
|
|
|
|
<span class="token-count total">{{ scope.row.totalTokens.toLocaleString() }}</span>
|
|
|
|
<span class="token-count total">{{ scope.row.totalToken?.toLocaleString() }}</span>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table>
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 详情分页 -->
|
|
|
|
<!-- 详情分页 -->
|
|
|
|
<el-pagination
|
|
|
|
<pagination v-show="detailTotal > 0" :total="detailTotal" v-model:page="detailQueryParams.pageNum"
|
|
|
|
v-show="detailTotal > 0"
|
|
|
|
v-model:limit="detailQueryParams.pageSize" @pagination="getDetailList"/>
|
|
|
|
class="mt-4"
|
|
|
|
|
|
|
|
:total="detailTotal"
|
|
|
|
|
|
|
|
v-model:current-page="detailQueryParams.pageNum"
|
|
|
|
|
|
|
|
v-model:page-size="detailQueryParams.pageSize"
|
|
|
|
|
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
|
|
|
|
@size-change="handleDetailQuery"
|
|
|
|
|
|
|
|
@current-change="handleDetailQuery"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
</el-card>
|
|
|
|
</el-card>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup name="TokenUsageDetails">
|
|
|
|
<script setup name="TokenUsage" lang="ts">
|
|
|
|
import { ref, reactive, onMounted, computed } from 'vue';
|
|
|
|
import {ref, reactive, onMounted, computed} from 'vue';
|
|
|
|
|
|
|
|
import {listTokenUsage, getAiChatMessageDetailList, getTokenUsage} from '@/api/ai/record/tokenUsage';
|
|
|
|
|
|
|
|
import {TokenUsageVO, TokenUsageQuery, TokenUsageForm} from '@/api/ai/record/tokenUsage/types';
|
|
|
|
|
|
|
|
import {getAiModelList} from '@/api/ai/base/aiModel';
|
|
|
|
|
|
|
|
import {AiModelVO} from '@/api/ai/base/aiModel/types';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const {proxy} = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
|
|
const {ai_chat_message_detail_type} = toRefs<any>(proxy?.useDict('ai_chat_message_detail_type'));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const selectedUser = ref(null); // 当前选中的用户
|
|
|
|
|
|
|
|
const tokenUsageList = ref<TokenUsageVO[]>([]);
|
|
|
|
|
|
|
|
const buttonLoading = ref(false);
|
|
|
|
|
|
|
|
const loading = ref(true);
|
|
|
|
|
|
|
|
const showSearch = ref(true);
|
|
|
|
|
|
|
|
const ids = ref<Array<string | number>>([]);
|
|
|
|
|
|
|
|
const single = ref(true);
|
|
|
|
|
|
|
|
const multiple = ref(true);
|
|
|
|
|
|
|
|
const total = ref(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const queryFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
|
|
const tokenUsageFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const dialog = reactive<DialogOption>({
|
|
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
|
|
title: ''
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 列显隐信息
|
|
|
|
|
|
|
|
const columns = ref<FieldOption[]>([
|
|
|
|
|
|
|
|
{key: 0, label: `主键`, visible: true},
|
|
|
|
|
|
|
|
{key: 1, label: `租户ID`, visible: true},
|
|
|
|
|
|
|
|
{key: 2, label: `用户`, visible: true},
|
|
|
|
|
|
|
|
{key: 3, label: `待结算token`, visible: true},
|
|
|
|
|
|
|
|
{key: 4, label: `模型ID,关联ai_model`, visible: true},
|
|
|
|
|
|
|
|
{key: 5, label: `提问token数量`, visible: true},
|
|
|
|
|
|
|
|
{key: 6, label: `回复token数量`, visible: true},
|
|
|
|
|
|
|
|
{key: 7, label: `累计使用token`, visible: true},
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const initFormData: TokenUsageForm = {
|
|
|
|
|
|
|
|
tokenUsageId: undefined,
|
|
|
|
|
|
|
|
userId: undefined,
|
|
|
|
|
|
|
|
token: undefined,
|
|
|
|
|
|
|
|
modelId: undefined,
|
|
|
|
|
|
|
|
promptToken: undefined,
|
|
|
|
|
|
|
|
completionToken: undefined,
|
|
|
|
|
|
|
|
totalToken: undefined
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const data = reactive<PageData<TokenUsageForm, TokenUsageQuery>>({
|
|
|
|
|
|
|
|
form: {...initFormData},
|
|
|
|
|
|
|
|
queryParams: {
|
|
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
|
|
tokenUsageId: undefined,
|
|
|
|
|
|
|
|
userId: undefined,
|
|
|
|
|
|
|
|
token: undefined,
|
|
|
|
|
|
|
|
modelId: undefined,
|
|
|
|
|
|
|
|
promptToken: undefined,
|
|
|
|
|
|
|
|
completionToken: undefined,
|
|
|
|
|
|
|
|
totalToken: undefined,
|
|
|
|
|
|
|
|
params: {}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
rules: {
|
|
|
|
|
|
|
|
tokenUsageId: [
|
|
|
|
|
|
|
|
{required: true, message: "主键不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
userId: [
|
|
|
|
|
|
|
|
{required: true, message: "用户不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
token: [
|
|
|
|
|
|
|
|
{required: true, message: "待结算token不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
modelId: [
|
|
|
|
|
|
|
|
{required: true, message: "模型ID,关联ai_model不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
promptToken: [
|
|
|
|
|
|
|
|
{required: true, message: "提问token数量不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
completionToken: [
|
|
|
|
|
|
|
|
{required: true, message: "回复token数量不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
totalToken: [
|
|
|
|
|
|
|
|
{required: true, message: "累计使用token不能为空", trigger: "blur"}
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const {queryParams, form, rules} = toRefs(data);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const aiModelList = ref<AiModelVO[]>([]);
|
|
|
|
|
|
|
|
/** 查询AI模型列表 */
|
|
|
|
|
|
|
|
const getAIModelList = async () => {
|
|
|
|
|
|
|
|
const res = await getAiModelList({});
|
|
|
|
|
|
|
|
aiModelList.value = res.data;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 查询用户token使用详情列表 */
|
|
|
|
|
|
|
|
const getList = async () => {
|
|
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
|
|
const res = await listTokenUsage(queryParams.value);
|
|
|
|
|
|
|
|
tokenUsageList.value = res.rows;
|
|
|
|
|
|
|
|
total.value = res.total;
|
|
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 取消按钮 */
|
|
|
|
|
|
|
|
const cancel = () => {
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
dialog.visible = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 表单重置 */
|
|
|
|
|
|
|
|
const reset = () => {
|
|
|
|
|
|
|
|
form.value = {...initFormData};
|
|
|
|
|
|
|
|
tokenUsageFormRef.value?.resetFields();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 搜索按钮操作 */
|
|
|
|
|
|
|
|
const handleQuery = () => {
|
|
|
|
|
|
|
|
queryParams.value.pageNum = 1;
|
|
|
|
|
|
|
|
getList();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 重置按钮操作 */
|
|
|
|
|
|
|
|
const resetQuery = () => {
|
|
|
|
|
|
|
|
queryFormRef.value?.resetFields();
|
|
|
|
|
|
|
|
handleQuery();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDetailQuery = () => {
|
|
|
|
|
|
|
|
detailQueryParams.pageNum = 1;
|
|
|
|
|
|
|
|
getDetailList();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 重置按钮操作 */
|
|
|
|
|
|
|
|
const resetDetailQuery = () => {
|
|
|
|
|
|
|
|
detailQueryFormRef.value?.resetFields();
|
|
|
|
|
|
|
|
selectedUser.value = null;
|
|
|
|
|
|
|
|
detailQueryParams.userId = undefined;
|
|
|
|
|
|
|
|
detailQueryParams.modelId = undefined;
|
|
|
|
|
|
|
|
handleDetailQuery();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 多选框选中数据 */
|
|
|
|
|
|
|
|
const handleSelectionChange = (selection: TokenUsageVO[]) => {
|
|
|
|
|
|
|
|
ids.value = selection.map(item => item.tokenUsageId);
|
|
|
|
|
|
|
|
single.value = selection.length != 1;
|
|
|
|
|
|
|
|
multiple.value = !selection.length;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ================= 右侧详情记录逻辑 =================
|
|
|
|
|
|
|
|
const detailLoading = ref(false);
|
|
|
|
|
|
|
|
const detailList = ref([]);
|
|
|
|
|
|
|
|
const detailTotal = ref(0);
|
|
|
|
|
|
|
|
const detailQueryFormRef = ref();
|
|
|
|
|
|
|
|
const detailQueryParams = reactive({
|
|
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
|
|
messageDetailType: '',
|
|
|
|
|
|
|
|
createBy: null, // 当前选中的用户ID
|
|
|
|
|
|
|
|
modelId: null
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取详情列表数据
|
|
|
|
|
|
|
|
const getDetailList = async () => {
|
|
|
|
|
|
|
|
detailLoading.value = true;
|
|
|
|
|
|
|
|
const res = await getAiChatMessageDetailList(detailQueryParams);
|
|
|
|
|
|
|
|
detailList.value = res.rows;
|
|
|
|
|
|
|
|
detailTotal.value = res.total;
|
|
|
|
|
|
|
|
detailLoading.value = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ================= 生命周期 =================
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
|
|
getAIModelList();
|
|
|
|
|
|
|
|
getList();
|
|
|
|
|
|
|
|
getDetailList();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用户选择操作
|
|
|
|
|
|
|
|
const handleUserSelect = (row) => {
|
|
|
|
|
|
|
|
selectedUser.value = row;
|
|
|
|
|
|
|
|
detailQueryParams.userId = row.userId;
|
|
|
|
|
|
|
|
detailQueryParams.modelId = row.modelId;
|
|
|
|
|
|
|
|
getDetailList();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ================= MOCK DATA =================
|
|
|
|
// ================= MOCK DATA =================
|
|
|
|
// 模拟用户总览数据
|
|
|
|
// 模拟用户总览数据
|
|
|
|
@ -372,135 +534,12 @@ const getUsageTypeTagType = (usageType) => {
|
|
|
|
* 获取用户行样式类名
|
|
|
|
* 获取用户行样式类名
|
|
|
|
* @param {object} param0
|
|
|
|
* @param {object} param0
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
const getUserRowClass = ({ row }) => {
|
|
|
|
const getUserRowClass = ({row}) => {
|
|
|
|
return selectedUser.value && row.id === selectedUser.value.id ? 'selected-row' : '';
|
|
|
|
return selectedUser.value && row.id === selectedUser.value.id ? 'selected-row' : '';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// ================= 工具函数 END =================
|
|
|
|
// ================= 工具函数 END =================
|
|
|
|
|
|
|
|
|
|
|
|
// ================= 左侧用户总览逻辑 =================
|
|
|
|
|
|
|
|
const userLoading = ref(false);
|
|
|
|
|
|
|
|
const userList = ref([]);
|
|
|
|
|
|
|
|
const userTotal = ref(0);
|
|
|
|
|
|
|
|
const userQueryFormRef = ref();
|
|
|
|
|
|
|
|
const userQueryParams = reactive({
|
|
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
|
|
userName: '',
|
|
|
|
|
|
|
|
aiModel: '',
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算属性:用户列表过滤和分页
|
|
|
|
|
|
|
|
const userFilteredList = computed(() => {
|
|
|
|
|
|
|
|
let list = userList.value;
|
|
|
|
|
|
|
|
if (userQueryParams.userName) {
|
|
|
|
|
|
|
|
list = list.filter(item => item.userName.includes(userQueryParams.userName));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userQueryParams.aiModel) {
|
|
|
|
|
|
|
|
list = list.filter(item => item.aiModel === userQueryParams.aiModel);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const userPaginatedList = computed(() => {
|
|
|
|
|
|
|
|
userTotal.value = userFilteredList.value.length;
|
|
|
|
|
|
|
|
const start = (userQueryParams.pageNum - 1) * userQueryParams.pageSize;
|
|
|
|
|
|
|
|
const end = start + userQueryParams.pageSize;
|
|
|
|
|
|
|
|
return userFilteredList.value.slice(start, end);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取用户列表数据
|
|
|
|
|
|
|
|
const getUserList = async () => {
|
|
|
|
|
|
|
|
userLoading.value = true;
|
|
|
|
|
|
|
|
const res = await mockApiCall(mockUserList);
|
|
|
|
|
|
|
|
userList.value = res;
|
|
|
|
|
|
|
|
handleUserQuery();
|
|
|
|
|
|
|
|
userLoading.value = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用户搜索按钮操作
|
|
|
|
|
|
|
|
const handleUserQuery = () => {
|
|
|
|
|
|
|
|
userQueryParams.pageNum = 1;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用户重置按钮操作
|
|
|
|
|
|
|
|
const resetUserQuery = () => {
|
|
|
|
|
|
|
|
userQueryFormRef.value.resetFields();
|
|
|
|
|
|
|
|
handleUserQuery();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// ================= 左侧用户总览逻辑 END =================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ================= 右侧详情记录逻辑 =================
|
|
|
|
|
|
|
|
const detailLoading = ref(false);
|
|
|
|
|
|
|
|
const detailList = ref([]);
|
|
|
|
|
|
|
|
const detailTotal = ref(0);
|
|
|
|
|
|
|
|
const detailQueryFormRef = ref();
|
|
|
|
|
|
|
|
const detailQueryParams = reactive({
|
|
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
|
|
usageType: '',
|
|
|
|
|
|
|
|
userId: null, // 当前选中的用户ID
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const selectedUser = ref(null); // 当前选中的用户
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 计算属性:详情列表过滤和分页
|
|
|
|
|
|
|
|
const detailFilteredList = computed(() => {
|
|
|
|
|
|
|
|
let list = detailList.value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 根据选中的用户过滤
|
|
|
|
|
|
|
|
if (detailQueryParams.userId) {
|
|
|
|
|
|
|
|
list = list.filter(item => item.userId === detailQueryParams.userId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 根据使用类型过滤
|
|
|
|
|
|
|
|
if (detailQueryParams.usageType) {
|
|
|
|
|
|
|
|
list = list.filter(item => item.usageType === detailQueryParams.usageType);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const detailPaginatedList = computed(() => {
|
|
|
|
|
|
|
|
detailTotal.value = detailFilteredList.value.length;
|
|
|
|
|
|
|
|
const start = (detailQueryParams.pageNum - 1) * detailQueryParams.pageSize;
|
|
|
|
|
|
|
|
const end = start + detailQueryParams.pageSize;
|
|
|
|
|
|
|
|
return detailFilteredList.value.slice(start, end);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取详情列表数据
|
|
|
|
|
|
|
|
const getDetailList = async () => {
|
|
|
|
|
|
|
|
detailLoading.value = true;
|
|
|
|
|
|
|
|
const res = await mockApiCall(mockDetailList);
|
|
|
|
|
|
|
|
detailList.value = res;
|
|
|
|
|
|
|
|
handleDetailQuery();
|
|
|
|
|
|
|
|
detailLoading.value = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 详情搜索按钮操作
|
|
|
|
|
|
|
|
const handleDetailQuery = () => {
|
|
|
|
|
|
|
|
detailQueryParams.pageNum = 1;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 详情重置按钮操作
|
|
|
|
|
|
|
|
const resetDetailQuery = () => {
|
|
|
|
|
|
|
|
detailQueryFormRef.value.resetFields();
|
|
|
|
|
|
|
|
detailQueryParams.userId = selectedUser.value ? selectedUser.value.id : null;
|
|
|
|
|
|
|
|
handleDetailQuery();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 用户选择操作
|
|
|
|
|
|
|
|
const handleUserSelect = (row) => {
|
|
|
|
|
|
|
|
selectedUser.value = row;
|
|
|
|
|
|
|
|
detailQueryParams.userId = row.id;
|
|
|
|
|
|
|
|
handleDetailQuery();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// ================= 右侧详情记录逻辑 END =================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ================= 生命周期 =================
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
|
|
getUserList();
|
|
|
|
|
|
|
|
getDetailList();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
<style scoped>
|
|
|
|
|