Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions frontend/eslint.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// https://eslint.org/docs/latest/use/configure/configuration-files-new

import eslint from "@eslint/js";
import pluginVue from "eslint-plugin-vue";
import globals from "globals";
// TypeScript支持
import * as typescriptEslint from "typescript-eslint";
// Vue支持
import pluginVue from "eslint-plugin-vue";
import vueParser from "vue-eslint-parser";
import globals from "globals";
// 代码风格与格式化
import configPrettier from "eslint-config-prettier";
import prettierPlugin from "eslint-plugin-prettier";

// 解析自动导入配置
import fs from "node:fs";
Expand Down Expand Up @@ -59,6 +63,7 @@ export default [
"**/*.min.*",
"**/auto-imports.d.ts",
"**/components.d.ts",
"**/types/**/*.d.ts",
],
},

Expand Down Expand Up @@ -135,6 +140,7 @@ export default [
sourceType: "module",
parser: typescriptEslint.parser,
extraFileExtensions: [".vue"],
tsconfigRootDir: __dirname,
},
},
rules: {
Expand Down Expand Up @@ -177,8 +183,10 @@ export default [
languageOptions: {
parser: typescriptEslint.parser,
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
ecmaVersion: "latest",
sourceType: "module",
project: "./tsconfig.json",
tsconfigRootDir: __dirname,
},
},
rules: {
Expand Down Expand Up @@ -215,5 +223,15 @@ export default [
},

// Prettier 集成(必须放在最后)
configPrettier,
{
plugins: {
prettier: prettierPlugin, // 将 Prettier 的输出作为 ESLint 的问题来报告
},
rules: {
...configPrettier.rules,
"prettier/prettier": ["error", {}, { usePrettierrc: true }],
"arrow-body-style": "off",
"prefer-arrow-callback": "off",
},
},
];
11 changes: 8 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "fastapi-admin",
"name": "fastapiadmin",
"description": "Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板",
"version": "2.0.0",
"version": "2.2.0",
"private": true,
"type": "module",
"scripts": {
"i": "pnpm install",
"dev": "vite",
"prod": "vite --mode prod",
"build": "vite build",
"build": "vue-tsc --noEmit & vite build",
"build:pro": "pnpm vite build --mode pro",
"build:gitee": "pnpm vite build --mode gitee",
"build:dev": "pnpm vite build --mode dev",
Expand Down Expand Up @@ -112,6 +112,11 @@
"eslint-plugin-vue": "^10.4.0",
"fs-extra": "^11.2.0",
"husky": "^9.1.7",
"lint-staged": "^15.5.2",
"postcss": "^8.5.6",
"postcss-html": "^1.8.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.6.2",
"sass": "^1.89.2",
"stylelint": "^16.25.0",
"stylelint-config-html": "^1.1.0",
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,36 @@
class="wh-full"
>
<router-view />

<!-- AI 助手 -->
<AiAssistant v-if="enableAiAssistant" />
</el-watermark>
</el-config-provider>
</template>

<script setup lang="ts">
import { useAppStore, useSettingsStore } from "@/store";
import { useAppStore, useSettingsStore, useUserStore } from "@/store";
import { defaultSettings } from "@/settings";
import { ThemeMode } from "@/enums/settings/theme.enum";
import { ComponentSize } from "@/enums/settings/layout.enum";
import AiAssistant from "@/components/AiAssistant/index.vue";

const appStore = useAppStore();
const settingsStore = useSettingsStore();
const userStore = useUserStore();

const locale = computed(() => appStore.locale);
const size = computed(() => appStore.size as ComponentSize);
const showWatermark = computed(() => settingsStore.showWatermark);

// 只有在启用 AI 助手且用户已登录时才显示
// 使用 userInfo 作为响应式依赖,当用户退出登录时会自动更新
const enableAiAssistant = computed(() => {
const isEnabled = settingsStore.enableAiAssistant;
const isLoggedIn = userStore.basicInfo && Object.keys(userStore.basicInfo).length > 0;
return isEnabled && isLoggedIn;
});

// 明亮/暗黑主题水印字体颜色适配
const fontColor = computed(() => {
return settingsStore.theme === ThemeMode.DARK ? "rgba(255, 255, 255, .15)" : "rgba(0, 0, 0, .15)";
Expand Down
180 changes: 180 additions & 0 deletions frontend/src/api/ai/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import request from "@/utils/request";

/**
* AI 命令请求参数
*/
export interface AiCommandRequest {
/** 用户输入的自然语言命令 */
command: string;
/** 当前页面路由(用于上下文) */
currentRoute?: string;
/** 当前激活的组件名称 */
currentComponent?: string;
/** 额外上下文信息 */
context?: Record<string, any>;
}

/**
* 函数调用参数
*/
export interface FunctionCall {
/** 函数名称 */
name: string;
/** 函数描述 */
description?: string;
/** 参数对象 */
arguments: Record<string, any>;
}

/**
* AI 命令解析响应
*/
export interface AiCommandResponse {
/** 解析日志ID(用于关联执行记录) */
parseLogId?: string;
/** 是否成功解析 */
success: boolean;
/** 解析后的函数调用列表 */
functionCalls: FunctionCall[];
/** AI 的理解和说明 */
explanation?: string;
/** 置信度 (0-1) */
confidence?: number;
/** 错误信息 */
error?: string;
/** 原始 LLM 响应(用于调试) */
rawResponse?: string;
}

/**
* AI 命令执行请求
*/
export interface AiExecuteRequest {
/** 关联的解析日志ID */
parseLogId?: string;
/** 原始命令(用于审计) */
originalCommand?: string;
/** 要执行的函数调用 */
functionCall: FunctionCall;
/** 确认模式:auto=自动执行, manual=需要用户确认 */
confirmMode?: "auto" | "manual";
/** 用户确认标志 */
userConfirmed?: boolean;
/** 幂等性令牌(防止重复执行) */
idempotencyKey?: string;
/** 当前页面路由 */
currentRoute?: string;
}

/**
* AI 命令执行响应
*/
export interface AiExecuteResponse {
/** 是否执行成功 */
success: boolean;
/** 执行结果数据 */
data?: any;
/** 执行结果说明 */
message?: string;
/** 影响的记录数 */
affectedRows?: number;
/** 错误信息 */
error?: string;
/** 记录ID(用于追踪) */
recordId?: string;
/** 需要用户确认 */
requiresConfirmation?: boolean;
/** 确认提示信息 */
confirmationPrompt?: string;
}

export interface AiCommandRecordPageQuery extends PageQuery {
keywords?: string;
executeStatus?: number;
parseStatus?: number;
userId?: number;
aiProvider?: string;
aiModel?: string;
functionName?: string;
createTime?: [string, string];
}

export interface AiCommandRecordVO {
id: string;
userId: number;
username: string;
originalCommand: string;
aiProvider?: string;
aiModel?: string;
parseStatus?: number;
functionCalls?: string;
explanation?: string;
confidence?: number;
parseErrorMessage?: string;
inputTokens?: number;
outputTokens?: number;
parseDurationMs?: number;
functionName?: string;
functionArguments?: string;
executeStatus?: number;
executeErrorMessage?: string;
ipAddress?: string;
createTime?: string;
updateTime?: string;
}

/**
* AI 命令 API
*/
class AiCommandApi {
/**
* 解析自然语言命令
*
* @param data 命令请求参数
* @returns 解析结果
*/
static parseCommand(data: AiCommandRequest): Promise<AiCommandResponse> {
return request<any, AiCommandResponse>({
url: "/api/v1/ai/command/parse",
method: "post",
data,
});
}

/**
* 执行已解析的命令
*
* @param data 执行请求参数
* @returns 执行结果数据(成功时返回,失败时抛出异常)
*/
static executeCommand(data: AiExecuteRequest): Promise<any> {
return request<any, any>({
url: "/api/v1/ai/command/execute",
method: "post",
data,
});
}

/**
* 获取命令记录分页列表
*/
static getCommandRecordPage(queryParams: AiCommandRecordPageQuery) {
return request<any, PageResult<AiCommandRecordVO[]>>({
url: "/api/v1/ai/command/records",
method: "get",
params: queryParams,
});
}

/**
* 撤销命令执行(如果支持)
*/
static rollbackCommand(logId: string) {
return request({
url: `/api/v1/ai/command/rollback/${logId}`,
method: "post",
});
}
}

export default AiCommandApi;
1 change: 1 addition & 0 deletions frontend/src/assets/icons/ai.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 63 additions & 0 deletions frontend/src/assets/images/login-bg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading