mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-04-05 11:47:32 +00:00
Compare commits
7 Commits
dev
...
f0a237fdf3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0a237fdf3 | ||
|
|
13d240342c | ||
|
|
8e20f561a4 | ||
|
|
b759275cf3 | ||
|
|
5f0f0fbd14 | ||
|
|
7088712560 | ||
|
|
2c302315b1 |
BIN
assets/group.png
BIN
assets/group.png
Binary file not shown.
|
Before Width: | Height: | Size: 576 KiB After Width: | Height: | Size: 530 KiB |
@@ -102,14 +102,14 @@ tasks.jacocoTestReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jacoco {
|
jacoco {
|
||||||
toolVersion = "0.8.12"
|
toolVersion = "0.8.13"
|
||||||
reportsDirectory.set(layout.buildDirectory.dir("reports/jacoco"))
|
reportsDirectory.set(layout.buildDirectory.dir("reports/jacoco"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pmd {
|
pmd {
|
||||||
sourceSets = listOf(java.sourceSets.findByName("main"))
|
sourceSets = listOf(java.sourceSets.findByName("main"))
|
||||||
isConsoleOutput = true
|
isConsoleOutput = true
|
||||||
toolVersion = "7.9.0"
|
toolVersion = "7.15.0"
|
||||||
rulesMinimumPriority.set(5)
|
rulesMinimumPriority.set(5)
|
||||||
ruleSetFiles = files("pmd-rules.xml")
|
ruleSetFiles = files("pmd-rules.xml")
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ spotless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
googleJavaFormat("1.25.2").reflowLongStrings()
|
googleJavaFormat("1.28.0").reflowLongStrings()
|
||||||
formatAnnotations()
|
formatAnnotations()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ public class LoggingAspect {
|
|||||||
return processWithLogging(joinPoint, aopLog);
|
return processWithLogging(joinPoint, aopLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Around("execution(* com.zl.mjga.service..*(..))")
|
// @Around("execution(* com.zl.mjga.service..*(..))")
|
||||||
// public Object logService(ProceedingJoinPoint joinPoint) throws Throwable {
|
// public Object logService(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
// AopLog aopLog = new AopLog();
|
// AopLog aopLog = new AopLog();
|
||||||
// return processWithLogging(joinPoint, aopLog);
|
// return processWithLogging(joinPoint, aopLog);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private Object processWithLogging(ProceedingJoinPoint joinPoint, AopLog aopLog) throws Throwable {
|
private Object processWithLogging(ProceedingJoinPoint joinPoint, AopLog aopLog) throws Throwable {
|
||||||
if (shouldSkipLogging(joinPoint) || !isUserAuthenticated()) {
|
if (shouldSkipLogging(joinPoint) || !isUserAuthenticated()) {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.zl.mjga.dto.aoplog;
|
package com.zl.mjga.dto.aoplog;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|||||||
@@ -47,14 +47,16 @@ public class AopLogRepository extends AopLogDao {
|
|||||||
.where(buildConditions(queryDto));
|
.where(buildConditions(queryDto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SelectConditionStep<Record> selectByWithoutReturnValue(AopLogQueryDto queryDto) {
|
public SelectConditionStep<Record> selectByWithoutReturnValue(AopLogQueryDto queryDto) {
|
||||||
return ctx()
|
return ctx()
|
||||||
.select(AOP_LOG.asterisk().except(AOP_LOG.RETURN_VALUE, AOP_LOG.METHOD_ARGS), USER.USERNAME, DSL.count().over().as("total_count"))
|
.select(
|
||||||
.from(AOP_LOG)
|
AOP_LOG.asterisk().except(AOP_LOG.RETURN_VALUE, AOP_LOG.METHOD_ARGS),
|
||||||
.leftJoin(USER)
|
USER.USERNAME,
|
||||||
.on(AOP_LOG.USER_ID.eq(USER.ID))
|
DSL.count().over().as("total_count"))
|
||||||
.where(buildConditions(queryDto));
|
.from(AOP_LOG)
|
||||||
|
.leftJoin(USER)
|
||||||
|
.on(AOP_LOG.USER_ID.eq(USER.ID))
|
||||||
|
.where(buildConditions(queryDto));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Condition buildConditions(AopLogQueryDto queryDto) {
|
private Condition buildConditions(AopLogQueryDto queryDto) {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class SignE2ETest {
|
|||||||
.uri("/auth/sign-up")
|
.uri("/auth/sign-up")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.bodyValue(
|
.bodyValue(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"username": "test_5fab32c22a3e",
|
"username": "test_5fab32c22a3e",
|
||||||
"password": "test_eab28b939ba1"
|
"password": "test_eab28b939ba1"
|
||||||
@@ -75,7 +75,7 @@ public class SignE2ETest {
|
|||||||
.uri("/auth/sign-in")
|
.uri("/auth/sign-in")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.bodyValue(
|
.bodyValue(
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"username": "test_5fab32c22a3e",
|
"username": "test_5fab32c22a3e",
|
||||||
"password": "test_eab28b939ba1"
|
"password": "test_eab28b939ba1"
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
# LoggingAspect generateCurlCommand 方法单元测试
|
|
||||||
|
|
||||||
## 测试概述
|
|
||||||
|
|
||||||
本测试文件 `LoggingAspectCurlGenerationTest.java` 专门针对 `LoggingAspect` 类中的 `generateCurlCommand` 方法进行全面的单元测试,验证该方法在各种场景下生成 curl 命令的正确性。
|
|
||||||
|
|
||||||
## 测试架构
|
|
||||||
|
|
||||||
测试采用 **嵌套测试类** 的结构,按功能模块组织:
|
|
||||||
|
|
||||||
### 1. GET 请求测试 (`GetRequestTests`)
|
|
||||||
- ✅ 基本 GET 请求 - 无查询参数
|
|
||||||
- ✅ GET 请求 - 包含查询参数
|
|
||||||
- ✅ GET 请求 - HTTPS 协议
|
|
||||||
- ✅ GET 请求 - 自定义端口
|
|
||||||
|
|
||||||
### 2. POST 请求测试 (`PostRequestTests`)
|
|
||||||
- ✅ POST 请求 - JSON 请求体
|
|
||||||
- ✅ POST 请求 - 空 JSON 请求体
|
|
||||||
- ✅ POST 请求 - 包含单引号的 JSON
|
|
||||||
|
|
||||||
### 3. PUT 和 PATCH 请求测试 (`PutAndPatchRequestTests`)
|
|
||||||
- ✅ PUT 请求 - JSON 请求体
|
|
||||||
- ✅ PATCH 请求 - JSON 请求体
|
|
||||||
|
|
||||||
### 4. 表单数据请求测试 (`FormDataRequestTests`)
|
|
||||||
- ✅ POST 请求 - 表单数据
|
|
||||||
- ✅ POST 请求 - 多值表单参数
|
|
||||||
- ✅ POST 请求 - 空表单数据
|
|
||||||
|
|
||||||
### 5. 请求头处理测试 (`HeaderProcessingTests`)
|
|
||||||
- ✅ 包含常规请求头
|
|
||||||
- ✅ 跳过特定请求头
|
|
||||||
- ✅ 验证跳过的请求头不会出现在 curl 命令中
|
|
||||||
|
|
||||||
### 6. 异常情况测试 (`ExceptionHandlingTests`)
|
|
||||||
- ✅ 读取请求体时发生 IOException
|
|
||||||
- ✅ 请求参数为 null
|
|
||||||
- ✅ 请求方法为 null
|
|
||||||
- ✅ 服务器信息为 null
|
|
||||||
|
|
||||||
### 7. 边界用例测试 (`BoundaryTests`)
|
|
||||||
- ✅ 最小化 GET 请求
|
|
||||||
- ✅ 复杂查询参数 - 包含特殊字符
|
|
||||||
- ✅ DELETE 请求 - 不应包含请求体
|
|
||||||
- ✅ HTTPS 请求 - 标准端口 443
|
|
||||||
- ✅ JSON 请求体为 null
|
|
||||||
|
|
||||||
## 测试覆盖的功能点
|
|
||||||
|
|
||||||
### 核心功能验证
|
|
||||||
1. **HTTP 方法处理**: GET, POST, PUT, PATCH, DELETE
|
|
||||||
2. **URL 构建**: 协议、主机名、端口、路径、查询参数
|
|
||||||
3. **请求头处理**: 包含/排除特定请求头
|
|
||||||
4. **请求体处理**: JSON、表单数据、空请求体
|
|
||||||
5. **异常处理**: 各种异常情况的优雅处理
|
|
||||||
|
|
||||||
### 特殊场景验证
|
|
||||||
1. **端口处理**: 标准端口省略,非标准端口包含
|
|
||||||
2. **字符转义**: JSON 中的单引号转义
|
|
||||||
3. **空值处理**: null 值的安全处理
|
|
||||||
4. **多值参数**: 表单中同名参数的多个值
|
|
||||||
|
|
||||||
## 测试技术特点
|
|
||||||
|
|
||||||
### 使用的测试技术
|
|
||||||
- **JUnit 5**: 现代化的测试框架
|
|
||||||
- **Mockito**: Mock 对象和行为验证
|
|
||||||
- **AssertJ**: 流畅的断言 API
|
|
||||||
- **嵌套测试**: 清晰的测试组织结构
|
|
||||||
|
|
||||||
### Mock 策略
|
|
||||||
- Mock `HttpServletRequest` 对象模拟各种 HTTP 请求场景
|
|
||||||
- Mock 依赖服务避免外部依赖
|
|
||||||
- 精确控制测试数据和行为
|
|
||||||
|
|
||||||
### 断言策略
|
|
||||||
- 验证生成的 curl 命令包含预期内容
|
|
||||||
- 验证不应包含的内容确实被排除
|
|
||||||
- 验证异常情况的错误消息
|
|
||||||
|
|
||||||
## 运行测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 运行所有 generateCurlCommand 相关测试
|
|
||||||
./gradlew test --tests "com.zl.mjga.unit.LoggingAspectCurlGenerationTest"
|
|
||||||
|
|
||||||
# 运行特定测试类别
|
|
||||||
./gradlew test --tests "com.zl.mjga.unit.LoggingAspectCurlGenerationTest\$GetRequestTests"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 测试价值
|
|
||||||
|
|
||||||
这套测试确保了 `generateCurlCommand` 方法在各种复杂场景下都能正确工作,为 AOP 日志功能的 curl 命令生成提供了可靠的质量保证。通过全面的测试覆盖,可以:
|
|
||||||
|
|
||||||
1. **防止回归**: 代码修改时及时发现问题
|
|
||||||
2. **文档作用**: 测试用例本身就是最好的使用文档
|
|
||||||
3. **重构支持**: 安全地进行代码重构
|
|
||||||
4. **质量保证**: 确保功能在各种边界条件下正常工作
|
|
||||||
|
|
||||||
## 测试结果
|
|
||||||
|
|
||||||
所有 **24 个测试用例** 均通过,覆盖了 `generateCurlCommand` 方法的所有主要功能和边界情况。
|
|
||||||
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
@@ -18,6 +18,7 @@ coverage
|
|||||||
/cypress/screenshots/
|
/cypress/screenshots/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
|
.vscode
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
7
frontend/.vscode/extensions.json
vendored
7
frontend/.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"Vue.volar",
|
|
||||||
"vitest.explorer",
|
|
||||||
"ms-playwright.playwright"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
18
frontend/.vscode/settings.json
vendored
18
frontend/.vscode/settings.json
vendored
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"explorer.fileNesting.enabled": true,
|
|
||||||
"explorer.fileNesting.patterns": {
|
|
||||||
"tsconfig.json": "tsconfig.*.json, env.d.ts",
|
|
||||||
"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
|
|
||||||
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig"
|
|
||||||
},
|
|
||||||
"files.associations": {
|
|
||||||
"*.css": "tailwindcss"
|
|
||||||
},
|
|
||||||
"editor.quickSuggestions": {
|
|
||||||
"strings": "on"
|
|
||||||
},
|
|
||||||
"tailwindCSS.classAttributes": ["class", "ui"],
|
|
||||||
"tailwindCSS.experimental.classRegex": [
|
|
||||||
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
22
frontend/package-lock.json
generated
22
frontend/package-lock.json
generated
@@ -21,7 +21,7 @@
|
|||||||
"marked": "^15.0.12",
|
"marked": "^15.0.12",
|
||||||
"openapi-fetch": "^0.13.5",
|
"openapi-fetch": "^0.13.5",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"tailwindcss": "^4.0.14",
|
"tailwindcss": "^4.1.11",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
@@ -1832,6 +1832,12 @@
|
|||||||
"tailwindcss": "4.1.6"
|
"tailwindcss": "4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/node/node_modules/tailwindcss": {
|
||||||
|
"version": "4.1.6",
|
||||||
|
"resolved": "http://mirrors.tencent.com/npm/tailwindcss/-/tailwindcss-4.1.6.tgz",
|
||||||
|
"integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@tailwindcss/oxide": {
|
"node_modules/@tailwindcss/oxide": {
|
||||||
"version": "4.1.6",
|
"version": "4.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz",
|
||||||
@@ -2079,6 +2085,12 @@
|
|||||||
"vite": "^5.2.0 || ^6"
|
"vite": "^5.2.0 || ^6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/vite/node_modules/tailwindcss": {
|
||||||
|
"version": "4.1.6",
|
||||||
|
"resolved": "http://mirrors.tencent.com/npm/tailwindcss/-/tailwindcss-4.1.6.tgz",
|
||||||
|
"integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.4.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||||
@@ -3655,7 +3667,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/flowbite": {
|
"node_modules/flowbite": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-3.1.2.tgz",
|
"resolved": "http://mirrors.tencent.com/npm/flowbite/-/flowbite-3.1.2.tgz",
|
||||||
"integrity": "sha512-MkwSgbbybCYgMC+go6Da5idEKUFfMqc/AmSjm/2ZbdmvoKf5frLPq/eIhXc9P+rC8t9boZtUXzHDgt5whZ6A/Q==",
|
"integrity": "sha512-MkwSgbbybCYgMC+go6Da5idEKUFfMqc/AmSjm/2ZbdmvoKf5frLPq/eIhXc9P+rC8t9boZtUXzHDgt5whZ6A/Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5705,9 +5717,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.1.6",
|
"version": "4.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz",
|
"resolved": "http://mirrors.tencent.com/npm/tailwindcss/-/tailwindcss-4.1.11.tgz",
|
||||||
"integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==",
|
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
"marked": "^15.0.12",
|
"marked": "^15.0.12",
|
||||||
"openapi-fetch": "^0.13.5",
|
"openapi-fetch": "^0.13.5",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"tailwindcss": "^4.0.14",
|
"tailwindcss": "^4.1.11",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|||||||
BIN
frontend/public/mjga.png
Normal file
BIN
frontend/public/mjga.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -443,13 +443,7 @@ onMounted(async () => {
|
|||||||
const $userDeleteModalElement: HTMLElement | null =
|
const $userDeleteModalElement: HTMLElement | null =
|
||||||
document.querySelector("#user-delete-modal");
|
document.querySelector("#user-delete-modal");
|
||||||
if ($userDeleteModalElement) {
|
if ($userDeleteModalElement) {
|
||||||
userDeleteModal.value = new Modal(
|
userDeleteModal.value = new Modal($userDeleteModalElement, {});
|
||||||
$userDeleteModalElement,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
id: "user-delete-modal",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const $departmentDeleteModalElement: HTMLElement | null =
|
const $departmentDeleteModalElement: HTMLElement | null =
|
||||||
document.querySelector("#department-delete-modal");
|
document.querySelector("#department-delete-modal");
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center items-center my-4">
|
<div class="flex justify-center items-center my-4">
|
||||||
<a class="group relative w-full sm:w-2/3 md:w-1/2 lg:w-2/5 xl:w-1/3 max-w-xs overflow-hidden rounded-lg border border-gray-200 shadow-lg transition-all duration-300 hover:shadow-xl hover:scale-[1.02]"
|
<a class="group relative w-full max-w-xs overflow-hidden rounded-lg border border-gray-200 shadow-lg transition-all duration-300 hover:shadow-xl hover:scale-[1.02]"
|
||||||
:href="href" target="_blank">
|
:href="href" target="_blank">
|
||||||
<div class="absolute top-0 right-0 bg-blue-600 text-white text-xs px-2 py-1 rounded-bl-lg z-10">{{ label }}</div>
|
<div class="absolute top-0 right-0 bg-blue-600 text-white text-xs px-2 py-1 rounded-bl-lg z-10">{{ label }}</div>
|
||||||
<img :src="imageSrc" :alt="imageAlt"
|
<img :src="imageSrc" :alt="imageAlt"
|
||||||
class="w-full h-auto transition-transform duration-500 group-hover:scale-105">
|
class="w-full h-52 transition-transform duration-500 group-hover:scale-105">
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-3 transform transition-all duration-300">
|
class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-3 transform transition-all duration-300">
|
||||||
<span class="text-white text-sm font-medium group-hover:underline flex items-center">
|
<span class="text-white text-sm font-medium group-hover:underline flex items-center">
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="text-sm pl-0.5 pr-2 font-medium">Star</span>
|
<span class="text-sm pl-0.5 pr-2 font-medium">Star</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-sm py-0.5 px-2 font-medium">0.2k</span>
|
<span class="text-sm py-0.5 px-2 font-medium">0.3k</span>
|
||||||
</a>
|
</a>
|
||||||
<button class="cursor-pointer pt-1" @click="changeAssistantVisible">
|
<button class="cursor-pointer pt-1" @click="changeAssistantVisible">
|
||||||
<AiChatIcon />
|
<AiChatIcon />
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Modal, initFlowbite } from "flowbite";
|
import { initFlowbite } from "flowbite";
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
|
|
||||||
export type ModalSize =
|
export type ModalSize =
|
||||||
@@ -72,7 +72,6 @@ const maxWidthClass = computed(() => {
|
|||||||
return sizes[props.size || "md"];
|
return sizes[props.size || "md"];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 确保Flowbite初始化,这对于PopupModal的正常工作至关重要
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initFlowbite();
|
initFlowbite();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -111,19 +111,23 @@ export const useAiChat = () => {
|
|||||||
const searchAction = async (message: string) => {
|
const searchAction = async (message: string) => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
try {
|
try {
|
||||||
const { data } = await client.POST("/ai/action/search", {
|
|
||||||
body: message,
|
|
||||||
});
|
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
content: data?.action
|
content: "",
|
||||||
? "搜索到功能,请您执行。"
|
|
||||||
: "未搜索到指定功能,请告诉我更加准确的信息。",
|
|
||||||
type: "action",
|
type: "action",
|
||||||
isUser: false,
|
isUser: false,
|
||||||
username: "知路智能体",
|
username: "知路智能体",
|
||||||
command: data?.action,
|
command: undefined,
|
||||||
});
|
});
|
||||||
return data;
|
const { data } = await client.POST("/ai/action/search", {
|
||||||
|
body: message,
|
||||||
|
});
|
||||||
|
messages.value[messages.value.length - 1].content = data?.action
|
||||||
|
? "搜索到功能,请您执行。"
|
||||||
|
: "未搜索到指定功能,请告诉我更加准确的信息。";
|
||||||
|
messages.value[messages.value.length - 1].command = data?.action;
|
||||||
|
} catch (error) {
|
||||||
|
messages.value.pop();
|
||||||
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
|
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
|
||||||
<PromotionBanner href="https://www.bilibili.com/cheese/play/ss198449120" imageSrc="/ai-tdd.png"
|
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||||
imageAlt="ai-tdd-tutorial" label="官方教程" text="无幻觉式 AI 编程方法论" />
|
<PromotionBanner href="https://www.bilibili.com/cheese/play/ss198449120" imageSrc="/ai-tdd.png"
|
||||||
|
imageAlt="ai-tdd-tutorial" label="官方教程" text="无幻觉式 AI 编程方法论" />
|
||||||
|
<PromotionBanner href="https://www.mjga.cc" imageSrc="/mjga.png" imageAlt="后端脚手架" label="后端脚手架"
|
||||||
|
text="国内唯一可选配组件和元信息的脚手架" />
|
||||||
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<Breadcrumbs :names="['用户管理']" />
|
<Breadcrumbs :names="['用户管理']" />
|
||||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">用户管理</h1>
|
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl">用户管理</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user