新增管理员权限检查和模型选择逻辑优化
- 在用户 API 中新增 `checkIsSuperAdmin` 函数,用于检查用户是否为超级管理员。 - 更新模型选择器,非管理员用户只能选择内置模型并需配置 API Key,提升安全性和用户体验。 - 优化模型配置页面,动态显示操作按钮,确保管理员与普通用户的操作权限区分明确。
This commit is contained in:
@@ -69,3 +69,10 @@ export function deleteUser(ids: number[]) {
|
||||
data: { ids },
|
||||
});
|
||||
}
|
||||
|
||||
export function checkIsSuperAdmin() {
|
||||
return request({
|
||||
url: '/admin-go/api/v1/system/user/checkIsSuperAdmin',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
v-for="model in modelList"
|
||||
:key="model.id"
|
||||
class="model-card"
|
||||
:class="{ selected: selectedModel?.id === model.id, 'system-model': model.tenantId === 1 }"
|
||||
:class="{ selected: selectedModel?.id === model.id, 'builtin-model': !model.apiKey }"
|
||||
@click="handleSelectModel(model)"
|
||||
>
|
||||
<div class="model-card-header">
|
||||
<div class="model-type">{{ getModelTypeName(model.modelsType) }}</div>
|
||||
<div class="model-badges">
|
||||
<el-tag v-if="model.tenantId === 1" type="warning" size="small">系统模型</el-tag>
|
||||
<el-tag v-if="!model.apiKey" type="warning" size="small">内置模型</el-tag>
|
||||
<el-icon v-if="selectedModel?.id === model.id" class="check-icon" color="#67c23a"><CircleCheck /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,12 +62,12 @@
|
||||
<!-- 新建模型弹窗 -->
|
||||
<EditModule ref="editModuleRef" @refresh="handleRefresh" />
|
||||
|
||||
<!-- 系统模型 API Key 输入弹窗 -->
|
||||
<el-dialog v-model="apiKeyDialogVisible" title="配置系统模型" width="500px" :close-on-click-modal="false" append-to-body>
|
||||
<!-- 内置模型 API Key 输入弹窗 -->
|
||||
<el-dialog v-model="apiKeyDialogVisible" title="配置内置模型" width="500px" :close-on-click-modal="false" append-to-body>
|
||||
<el-alert type="info" :closable="false" style="margin-bottom: 16px">
|
||||
<template #title>
|
||||
<div style="line-height: 1.6">
|
||||
您选择的是系统模型,需要配置您自己的 API Key。<br />
|
||||
您选择的是内置模型,需要配置您自己的 API Key。<br />
|
||||
系统将为您创建一个模型副本。
|
||||
</div>
|
||||
</template>
|
||||
@@ -89,10 +89,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { ref, reactive, watch, onMounted } from 'vue';
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { Search, CircleCheck } from '@element-plus/icons-vue';
|
||||
import { getModelModuleList, addModelModule } from '/@/api/digitalHuman/modelConfig/modelModule';
|
||||
import { checkIsSuperAdmin } from '/@/api/system/user/index';
|
||||
import { getApiErrorMessage } from '/@/utils/request';
|
||||
import EditModule from '/@/views/digitalHuman/modelConfig/modelModule/component/editModule.vue';
|
||||
|
||||
@@ -147,8 +148,9 @@ const modelList = ref<ModelItem[]>([]);
|
||||
const loading = ref(false);
|
||||
const selectedModel = ref<ModelItem | null>(null);
|
||||
const editModuleRef = ref();
|
||||
const isSuperAdmin = ref(false); // 是否为管理员
|
||||
|
||||
// 系统模型 API Key 配置
|
||||
// 内置模型 API Key 配置
|
||||
const apiKeyDialogVisible = ref(false);
|
||||
const apiKeyFormRef = ref<FormInstance>();
|
||||
const apiKeyForm = reactive({
|
||||
@@ -162,6 +164,16 @@ const apiKeyRules: FormRules = {
|
||||
const creatingModel = ref(false);
|
||||
const systemModelToClone = ref<ModelItem | null>(null);
|
||||
|
||||
// 检查是否为管理员
|
||||
const checkAdminStatus = async () => {
|
||||
try {
|
||||
const res: any = await checkIsSuperAdmin();
|
||||
isSuperAdmin.value = res.data?.isSuperAdmin || false;
|
||||
} catch {
|
||||
isSuperAdmin.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
@@ -195,6 +207,7 @@ const fetchModelList = async () => {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
modelName: searchParams.modelName || undefined,
|
||||
modelType: 1, // 只获取推理模型
|
||||
};
|
||||
const res: any = await getModelModuleList(params);
|
||||
modelList.value = res.data?.list || [];
|
||||
@@ -218,15 +231,21 @@ const handlePageChange = () => {
|
||||
};
|
||||
|
||||
const handleSelectModel = (model: ModelItem) => {
|
||||
// 判断是否是系统模型(tenantId === 1)
|
||||
if (model.tenantId === 1) {
|
||||
// 系统模型,需要用户配置 API Key
|
||||
// 如果是管理员,直接选中任何模型,不需要配置 API Key
|
||||
if (isSuperAdmin.value) {
|
||||
selectedModel.value = model;
|
||||
return;
|
||||
}
|
||||
|
||||
// 非管理员:判断是否是内置模型(apiKey 为空)
|
||||
if (!model.apiKey) {
|
||||
// 内置模型,需要用户配置 API Key
|
||||
systemModelToClone.value = model;
|
||||
apiKeyForm.modelName = model.modelName;
|
||||
apiKeyForm.apiKey = '';
|
||||
apiKeyDialogVisible.value = true;
|
||||
} else {
|
||||
// 非系统模型,直接选中
|
||||
// 用户模型,直接选中
|
||||
selectedModel.value = model;
|
||||
}
|
||||
};
|
||||
@@ -239,7 +258,7 @@ const handleCreatePrivateModel = async () => {
|
||||
|
||||
creatingModel.value = true;
|
||||
|
||||
// 基于系统模型创建新模型(继承原模型的所有配置,只替换 apiKey)
|
||||
// 基于内置模型创建新模型(继承原模型的所有配置,只替换 apiKey)
|
||||
const systemModel = systemModelToClone.value;
|
||||
const createParams = {
|
||||
modelName: apiKeyForm.modelName,
|
||||
@@ -312,6 +331,10 @@ const handleClose = () => {
|
||||
apiKeyDialogVisible.value = false;
|
||||
systemModelToClone.value = null;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
checkAdminStatus();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -360,12 +383,12 @@ const handleClose = () => {
|
||||
background: #f0f9ff;
|
||||
}
|
||||
|
||||
.model-card.system-model {
|
||||
.model-card.builtin-model {
|
||||
border-color: #fbbf24;
|
||||
background: #fffbeb;
|
||||
}
|
||||
|
||||
.model-card.system-model:hover {
|
||||
.model-card.builtin-model:hover {
|
||||
border-color: #f59e0b;
|
||||
}
|
||||
|
||||
|
||||
@@ -1058,6 +1058,9 @@ const useWorkflow = async (workflow: WorkflowItem) => {
|
||||
// 切换到创作模式
|
||||
isCreationMode.value = true;
|
||||
currentWorkflowForCreation.value = res.data;
|
||||
// 从工作流进入,不禁用表单
|
||||
isFromWorkspace.value = false;
|
||||
currentSessionId.value = null; // 清空会话 ID
|
||||
|
||||
// 初始化创作表单的值
|
||||
Object.keys(creationFormValues).forEach((key) => delete creationFormValues[key]);
|
||||
@@ -1296,6 +1299,9 @@ const sendMessage = async () => {
|
||||
userInput.value = '';
|
||||
selectedFiles.value = [];
|
||||
selectedCreationSkill.value = null;
|
||||
|
||||
// 7. 重新获取工作空间数据
|
||||
await getList();
|
||||
} catch {
|
||||
// 接口错误由 request 全局提示后端 message
|
||||
} finally {
|
||||
|
||||
@@ -41,12 +41,12 @@
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||
<el-form-item label="访问类型" prop="isPrivate">
|
||||
<el-radio-group v-model="state.ruleForm.isPrivate" @change="onIsPrivateChange">
|
||||
<el-radio :label="0">私有</el-radio>
|
||||
<el-radio :label="1">公共</el-radio>
|
||||
<el-radio :label="0">本地模型</el-radio>
|
||||
<el-radio :label="1">服务商模型</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="state.ruleForm.isPrivate === 1" :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-col v-if="!props.isSuperAdmin && state.ruleForm.isPrivate === 1" :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
|
||||
<el-form-item label="API 密钥" prop="apiKey">
|
||||
<el-input v-model="state.ruleForm.apiKey" type="password" show-password placeholder="请输入 API 密钥字符串" clearable></el-input>
|
||||
</el-form-item>
|
||||
@@ -254,8 +254,12 @@ export type ModelTypeOption = { id: number | string; label: string };
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelTypes?: ModelTypeOption[];
|
||||
isSuperAdmin?: boolean;
|
||||
}>(),
|
||||
{ modelTypes: () => [] as ModelTypeOption[] }
|
||||
{
|
||||
modelTypes: () => [] as ModelTypeOption[],
|
||||
isSuperAdmin: false,
|
||||
}
|
||||
);
|
||||
|
||||
const modelTypeOptions = computed(() => props.modelTypes);
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
<div class="system-user-search mb15">
|
||||
<el-input v-model="state.tableData.param.modelName" size="default" placeholder="请输入模型名称" style="max-width: 180px" clearable>
|
||||
</el-input>
|
||||
<el-select v-model="state.tableData.param.modelType" size="default" placeholder="请选择模型类型" style="max-width: 180px" clearable class="ml10">
|
||||
<el-select
|
||||
v-model="state.tableData.param.modelType"
|
||||
size="default"
|
||||
placeholder="请选择模型类型"
|
||||
style="max-width: 180px"
|
||||
clearable
|
||||
class="ml10"
|
||||
>
|
||||
<el-option v-for="type in state.modelTypes" :key="type.id" :label="type.label" :value="type.id" />
|
||||
</el-select>
|
||||
<el-button size="default" type="primary" class="ml10" @click="getTableData">
|
||||
@@ -31,7 +38,7 @@
|
||||
<!-- <el-table-column prop="baseUrl" label="模型服务地址" show-overflow-tooltip width="200"></el-table-column> -->
|
||||
<el-table-column prop="isPrivate" label="访问类型" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isPrivate === 1 ? 'primary' : 'info'">{{ scope.row.isPrivate === 1 ? '公共' : '私有' }}</el-tag>
|
||||
<el-tag :type="scope.row.isPrivate === 1 ? 'primary' : 'info'">{{ scope.row.isPrivate === 1 ? '本地模型' : '服务商模型' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="httpMethod" label="请求方式" width="100"></el-table-column>
|
||||
@@ -45,27 +52,30 @@
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip width="160"></el-table-column>
|
||||
<el-table-column prop="updatedAt" label="修改时间" show-overflow-tooltip width="160"></el-table-column>
|
||||
<el-table-column label="操作" width="300" fixed="right">
|
||||
<el-table-column label="操作" :width="isSuperAdmin ? 150 : 300" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-button size="small" text type="primary" @click="onOpenEditModule('edit', scope.row)">修改</el-button>
|
||||
<el-button
|
||||
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) !== 1"
|
||||
size="small"
|
||||
text
|
||||
type="warning"
|
||||
@click="onSetChatModel(scope.row)"
|
||||
>
|
||||
设为会话模型
|
||||
</el-button>
|
||||
<el-tag
|
||||
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) === 1"
|
||||
type="success"
|
||||
effect="dark"
|
||||
size="default"
|
||||
>
|
||||
✓ 当前会话模型
|
||||
</el-tag>
|
||||
<!-- 非管理员才显示会话模型按钮 -->
|
||||
<template v-if="!isSuperAdmin">
|
||||
<el-button
|
||||
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) !== 1"
|
||||
size="small"
|
||||
text
|
||||
type="warning"
|
||||
@click="onSetChatModel(scope.row)"
|
||||
>
|
||||
设为会话模型
|
||||
</el-button>
|
||||
<el-tag
|
||||
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) === 1"
|
||||
type="success"
|
||||
effect="dark"
|
||||
size="default"
|
||||
>
|
||||
✓ 当前会话模型
|
||||
</el-tag>
|
||||
</template>
|
||||
<el-button size="small" text type="danger" @click="onRowDel(scope.row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -85,7 +95,7 @@
|
||||
>
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
<EditModule ref="editModuleRef" :model-types="state.modelTypes" @refresh="getTableData()" />
|
||||
<EditModule ref="editModuleRef" :model-types="state.modelTypes" :is-super-admin="isSuperAdmin" @refresh="getTableData()" />
|
||||
|
||||
<!-- 系统模型 API Key 输入弹窗 -->
|
||||
<el-dialog v-model="apiKeyDialogVisible" title="配置系统模型" width="500px" :close-on-click-modal="false">
|
||||
@@ -124,11 +134,13 @@ import {
|
||||
updateChatModel,
|
||||
addModelModule,
|
||||
} from '/@/api/digitalHuman/modelConfig/modelModule/index';
|
||||
import { checkIsSuperAdmin } from '/@/api/system/user/index';
|
||||
import { getApiErrorMessage } from '/@/utils/request';
|
||||
|
||||
const EditModule = defineAsyncComponent(() => import('/@/views/digitalHuman/modelConfig/modelModule/component/editModule.vue'));
|
||||
|
||||
const editModuleRef = ref();
|
||||
const isSuperAdmin = ref(false); // 是否为管理员
|
||||
const state = reactive({
|
||||
modelTypes: [] as Array<{ id: number | string; label: string }>,
|
||||
tableData: {
|
||||
@@ -158,6 +170,16 @@ const apiKeyRules: FormRules = {
|
||||
const creatingModel = ref(false);
|
||||
const systemModelToClone = ref<any>(null);
|
||||
|
||||
// 检查是否为管理员
|
||||
const checkAdminStatus = async () => {
|
||||
try {
|
||||
const res: any = await checkIsSuperAdmin();
|
||||
isSuperAdmin.value = res.data?.isSuperAdmin || false;
|
||||
} catch {
|
||||
isSuperAdmin.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 判断是否为推理模型(只有推理模型才能设置为会话模型)
|
||||
const isInferenceModel = (modelsType: number | string | undefined | null) => {
|
||||
if (modelsType === undefined || modelsType === null || modelsType === '') {
|
||||
@@ -322,6 +344,7 @@ const onHandleCurrentChange = (val: number) => {
|
||||
|
||||
// 页面加载时
|
||||
onMounted(async () => {
|
||||
await checkAdminStatus(); // 检查管理员状态
|
||||
await loadModelTypes();
|
||||
getTableData();
|
||||
});
|
||||
@@ -348,7 +371,7 @@ onMounted(async () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
|
||||
.el-button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user