mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-04-08 22:47:36 +00:00
add ai delete
This commit is contained in:
@@ -5,6 +5,8 @@ import com.zl.mjga.dto.PageResponseDto;
|
|||||||
import com.zl.mjga.dto.ai.LlmQueryDto;
|
import com.zl.mjga.dto.ai.LlmQueryDto;
|
||||||
import com.zl.mjga.dto.ai.LlmVm;
|
import com.zl.mjga.dto.ai.LlmVm;
|
||||||
import com.zl.mjga.exception.BusinessException;
|
import com.zl.mjga.exception.BusinessException;
|
||||||
|
import com.zl.mjga.repository.DepartmentRepository;
|
||||||
|
import com.zl.mjga.repository.UserRepository;
|
||||||
import com.zl.mjga.service.AiChatService;
|
import com.zl.mjga.service.AiChatService;
|
||||||
import com.zl.mjga.service.EmbeddingService;
|
import com.zl.mjga.service.EmbeddingService;
|
||||||
import com.zl.mjga.service.LlmService;
|
import com.zl.mjga.service.LlmService;
|
||||||
@@ -16,8 +18,11 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jooq.generated.mjga.enums.LlmCodeEnum;
|
import org.jooq.generated.mjga.enums.LlmCodeEnum;
|
||||||
import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
||||||
|
import org.jooq.generated.mjga.tables.pojos.Department;
|
||||||
|
import org.jooq.generated.mjga.tables.pojos.User;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@@ -34,6 +39,8 @@ public class AiController {
|
|||||||
private final AiChatService aiChatService;
|
private final AiChatService aiChatService;
|
||||||
private final LlmService llmService;
|
private final LlmService llmService;
|
||||||
private final EmbeddingService embeddingService;
|
private final EmbeddingService embeddingService;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final DepartmentRepository departmentRepository;
|
||||||
|
|
||||||
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
public Flux<String> chat(Principal principal, @RequestBody String userMessage) {
|
public Flux<String> chat(Principal principal, @RequestBody String userMessage) {
|
||||||
@@ -72,4 +79,27 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
return embeddingService.searchAction(message);
|
return embeddingService.searchAction(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).WRITE_USER_ROLE_PERMISSION)")
|
||||||
|
@DeleteMapping("/action/user")
|
||||||
|
void deleteUser(@RequestParam String username, Principal principal) {
|
||||||
|
if (StringUtils.equals(username, principal.getName())) {
|
||||||
|
throw new BusinessException("不能删除当前登录用户");
|
||||||
|
}
|
||||||
|
User fetched = userRepository.fetchOneByUsername(username);
|
||||||
|
if (fetched == null) {
|
||||||
|
throw new BusinessException("该用户不存在");
|
||||||
|
}
|
||||||
|
userRepository.deleteByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).WRITE_USER_ROLE_PERMISSION)")
|
||||||
|
@DeleteMapping("/action/department")
|
||||||
|
void deleteDepartment(@RequestParam String name) {
|
||||||
|
Department department = departmentRepository.fetchOneByName(name);
|
||||||
|
if (department == null) {
|
||||||
|
throw new BusinessException("该部门不存在");
|
||||||
|
}
|
||||||
|
departmentRepository.deleteByName(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class IdentityAccessController {
|
|||||||
return identityAccessService.queryUniqueUserWithRolePermission(userId);
|
return identityAccessService.queryUniqueUserWithRolePermission(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).DELETE_USER_ROLE_PERMISSION)")
|
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).WRITE_USER_ROLE_PERMISSION)")
|
||||||
@DeleteMapping("/user")
|
@DeleteMapping("/user")
|
||||||
void deleteUser(@RequestParam Long userId) {
|
void deleteUser(@RequestParam Long userId) {
|
||||||
if (userId == 1) {
|
if (userId == 1) {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import lombok.Getter;
|
|||||||
@Getter
|
@Getter
|
||||||
public enum Actions {
|
public enum Actions {
|
||||||
CREATE_USER("CREATE_USER", "创建用户"),
|
CREATE_USER("CREATE_USER", "创建用户"),
|
||||||
CREATE_DEPARTMENT("CREATE_DEPARTMENT", "创建部门");
|
CREATE_DEPARTMENT("CREATE_DEPARTMENT", "创建部门"),
|
||||||
|
DELETE_USER("DELETE_USER", "删除用户"),
|
||||||
|
DELETE_DEPARTMENT("DELETE_DEPARTMENT", "删除部门");
|
||||||
public static final String INDEX_KEY = "action";
|
public static final String INDEX_KEY = "action";
|
||||||
private final String code;
|
private final String code;
|
||||||
private final String content;
|
private final String content;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.jooq.generated.mjga.tables.daos.DepartmentDao;
|
|||||||
import org.jooq.impl.DSL;
|
import org.jooq.impl.DSL;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public class DepartmentRepository extends DepartmentDao {
|
public class DepartmentRepository extends DepartmentDao {
|
||||||
@@ -95,4 +96,9 @@ public class DepartmentRepository extends DepartmentDao {
|
|||||||
.innerJoin(USER.department())
|
.innerJoin(USER.department())
|
||||||
.where(USER.ID.eq(userId));
|
.where(USER.ID.eq(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteByName(String name) {
|
||||||
|
ctx().deleteFrom(DEPARTMENT).where(DEPARTMENT.NAME.eq(name)).execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ public class LlmRepository extends AiLlmConfigDao {
|
|||||||
public Result<Record> pageFetchBy(PageRequestDto pageRequestDto, LlmQueryDto llmQueryDto) {
|
public Result<Record> pageFetchBy(PageRequestDto pageRequestDto, LlmQueryDto llmQueryDto) {
|
||||||
return ctx()
|
return ctx()
|
||||||
.select(
|
.select(
|
||||||
AI_LLM_CONFIG.asterisk(),
|
AI_LLM_CONFIG.asterisk(), DSL.count().over().as("total_llm").convertFrom(Long::valueOf))
|
||||||
DSL.count().over().as("total_llm").convertFrom(Long::valueOf))
|
|
||||||
.from(AI_LLM_CONFIG)
|
.from(AI_LLM_CONFIG)
|
||||||
.where(
|
.where(
|
||||||
StringUtils.isNotEmpty(llmQueryDto.name())
|
StringUtils.isNotEmpty(llmQueryDto.name())
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class EmbeddingService {
|
|||||||
EmbeddingSearchRequest embeddingSearchRequest =
|
EmbeddingSearchRequest embeddingSearchRequest =
|
||||||
EmbeddingSearchRequest.builder()
|
EmbeddingSearchRequest.builder()
|
||||||
.queryEmbedding(zhipuEmbeddingModel.embed(message).content())
|
.queryEmbedding(zhipuEmbeddingModel.embed(message).content())
|
||||||
.minScore(0.89)
|
.minScore(0.89)
|
||||||
.build();
|
.build();
|
||||||
EmbeddingSearchResult<TextSegment> embeddingSearchResult =
|
EmbeddingSearchResult<TextSegment> embeddingSearchResult =
|
||||||
zhiPuEmbeddingStore.search(embeddingSearchRequest);
|
zhiPuEmbeddingStore.search(embeddingSearchRequest);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.zl.mjga.service;
|
package com.zl.mjga.service;
|
||||||
|
|
||||||
|
import static org.jooq.generated.mjga.Tables.AI_LLM_CONFIG;
|
||||||
|
|
||||||
import com.zl.mjga.dto.PageRequestDto;
|
import com.zl.mjga.dto.PageRequestDto;
|
||||||
import com.zl.mjga.dto.PageResponseDto;
|
import com.zl.mjga.dto.PageResponseDto;
|
||||||
import com.zl.mjga.dto.ai.LlmQueryDto;
|
import com.zl.mjga.dto.ai.LlmQueryDto;
|
||||||
@@ -18,8 +20,6 @@ import org.jooq.generated.mjga.tables.pojos.AiLlmConfig;
|
|||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import static org.jooq.generated.mjga.Tables.AI_LLM_CONFIG;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -44,11 +44,13 @@ public class LlmService {
|
|||||||
if (records.isEmpty()) {
|
if (records.isEmpty()) {
|
||||||
return PageResponseDto.empty();
|
return PageResponseDto.empty();
|
||||||
}
|
}
|
||||||
List<LlmVm> llmVms = records.map((record) -> {
|
List<LlmVm> llmVms =
|
||||||
LlmVm into = record.into(LlmVm.class);
|
records.map(
|
||||||
into.setType(record.get(AI_LLM_CONFIG.TYPE).getLiteral());
|
(record) -> {
|
||||||
return into;
|
LlmVm into = record.into(LlmVm.class);
|
||||||
});
|
into.setType(record.get(AI_LLM_CONFIG.TYPE).getLiteral());
|
||||||
|
return into;
|
||||||
|
});
|
||||||
Long totalLlm = records.get(0).getValue("total_llm", Long.class);
|
Long totalLlm = records.get(0).getValue("total_llm", Long.class);
|
||||||
return new PageResponseDto<>(totalLlm, llmVms);
|
return new PageResponseDto<>(totalLlm, llmVms);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1102,6 +1102,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/ai/action/user": {
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"ai-controller"
|
||||||
|
],
|
||||||
|
"operationId": "deleteUser_1",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "query",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/ai/action/department": {
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"ai-controller"
|
||||||
|
],
|
||||||
|
"operationId": "deleteDepartment_1",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "query",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
|||||||
72
frontend/src/api/types/schema.d.ts
vendored
72
frontend/src/api/types/schema.d.ts
vendored
@@ -532,6 +532,38 @@ export interface paths {
|
|||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/ai/action/user": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete: operations["deleteUser_1"];
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
|
"/ai/action/department": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete: operations["deleteDepartment_1"];
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export type webhooks = Record<string, never>;
|
export type webhooks = Record<string, never>;
|
||||||
export interface components {
|
export interface components {
|
||||||
@@ -1693,4 +1725,44 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
deleteUser_1: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description OK */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content?: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
deleteDepartment_1: {
|
||||||
|
parameters: {
|
||||||
|
query: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description OK */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content?: never;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
51
frontend/src/components/InputButton.vue
Normal file
51
frontend/src/components/InputButton.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<form class="max-w-xs mb-4">
|
||||||
|
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only ">Search</label>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
|
||||||
|
<svg class="w-4 h-4 text-gray-500 " aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||||
|
viewBox="0 0 20 20">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input type="search" id="default-search" v-model="bindInput"
|
||||||
|
:class="['block w-full ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500', size === 'sm' ? 'p-2.5' : size === 'md' ? 'p-3' : 'p-4']"
|
||||||
|
:placeholder="placeholder" required />
|
||||||
|
<button type="submit"
|
||||||
|
:class="['text-white absolute end-2.5 font-medium rounded-lg text-sm', size === 'sm' ? 'text-xs px-1.5 py-1.5 bottom-2' : size === 'md' ? 'text-sm px-4 py-2 bottom-2.5' : 'text-base px-4 py-2', bgColor] "
|
||||||
|
@click.prevent="handleSubmitClick(bindInput)">{{ content }}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const {
|
||||||
|
placeholder,
|
||||||
|
content,
|
||||||
|
handleSubmit,
|
||||||
|
size = "md",
|
||||||
|
bgColor = "bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 ",
|
||||||
|
} = defineProps<{
|
||||||
|
placeholder?: string;
|
||||||
|
content: string;
|
||||||
|
handleSubmit: (input: string) => void;
|
||||||
|
size: "sm" | "md" | "lg";
|
||||||
|
bgColor: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const bindInput = ref<string>();
|
||||||
|
|
||||||
|
const handleSubmitClick = (input?: string) => {
|
||||||
|
const userSchema = z
|
||||||
|
.string({
|
||||||
|
message: "输入的内容不能为空",
|
||||||
|
})
|
||||||
|
.nonempty();
|
||||||
|
const result = userSchema.parse(input);
|
||||||
|
handleSubmit(result);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -43,7 +43,7 @@ defineProps<{
|
|||||||
title: string;
|
title: string;
|
||||||
id: string;
|
id: string;
|
||||||
closeModal: () => void;
|
closeModal: () => void;
|
||||||
onSubmit: (event: Event) => Promise<void>;
|
onSubmit: () => Promise<void>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
28
frontend/src/composables/ai/useAiAction.ts
Normal file
28
frontend/src/composables/ai/useAiAction.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import client from "../../api/client";
|
||||||
|
|
||||||
|
export const useAiAction = () => {
|
||||||
|
const deleteUserByUsername = async (username: string) => {
|
||||||
|
await client.DELETE("/ai/action/user", {
|
||||||
|
params: {
|
||||||
|
query: {
|
||||||
|
username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteDepartmentByName = async (name: string) => {
|
||||||
|
await client.DELETE("/ai/action/department", {
|
||||||
|
params: {
|
||||||
|
query: {
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
deleteUserByUsername,
|
||||||
|
deleteDepartmentByName,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -17,12 +17,21 @@
|
|||||||
<div class="markdown-content markdown-body text-base font-normal py-2.5 text-gray-900 "
|
<div class="markdown-content markdown-body text-base font-normal py-2.5 text-gray-900 "
|
||||||
v-html="renderMarkdown(chatElement.content)">
|
v-html="renderMarkdown(chatElement.content)">
|
||||||
</div>
|
</div>
|
||||||
<button v-if="chatElement.type === 'action' && chatElement.command" type="button"
|
<button
|
||||||
@click="commandActionMap[chatElement.command!]"
|
v-if="chatElement.type === 'action' && (chatElement.command === 'CREATE_USER' || chatElement.command === 'CREATE_DEPARTMENT')"
|
||||||
|
type="button" @click="commandActionMap[chatElement.command!]"
|
||||||
class="px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
class="px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
||||||
{{
|
{{
|
||||||
commandContentMap[chatElement.command!]
|
commandContentMap[chatElement.command!]
|
||||||
}}</button>
|
}}</button>
|
||||||
|
<InputButton
|
||||||
|
bgColor="bg-red-700 hover:bg-red-800 focus:ring-red-300 text-white focus:ring-4 focus:outline-none"
|
||||||
|
size="sm" :content="commandContentMap[chatElement.command!]" :handleSubmit="handleDeleteUserClick"
|
||||||
|
v-if="chatElement.command === 'DELETE_USER'" />
|
||||||
|
<InputButton
|
||||||
|
bgColor="bg-red-700 hover:bg-red-800 focus:ring-red-300 text-white focus:ring-4 focus:outline-none"
|
||||||
|
size="sm" :content="commandContentMap[chatElement.command!]" :handleSubmit="handleDeleteDepartmentClick"
|
||||||
|
v-if="chatElement.command === 'DELETE_DEPARTMENT'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -54,6 +63,24 @@
|
|||||||
帮我创建用户?
|
帮我创建用户?
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button @click.prevent="() => handleSendClick('删除用户', true)" class=" inline-flex items-center justify-center p-0.5
|
||||||
|
mb-2 me-2 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-purple-500
|
||||||
|
to-pink-500 group-hover:from-purple-500 group-hover:to-pink-500 hover:text-white dark:text-white focus:ring-4
|
||||||
|
focus:outline-none focus:ring-purple-200 cursor-pointer dark:focus:ring-purple-800">
|
||||||
|
<span
|
||||||
|
class="px-3 py-2 text-xs transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-transparent group-hover:dark:bg-transparent">
|
||||||
|
删除用户
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button @click.prevent="() => handleSendClick('删除部门', true)" class=" inline-flex items-center justify-center p-0.5
|
||||||
|
mb-2 me-2 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-purple-500
|
||||||
|
to-pink-500 group-hover:from-purple-500 group-hover:to-pink-500 hover:text-white dark:text-white focus:ring-4
|
||||||
|
focus:outline-none focus:ring-purple-200 cursor-pointer dark:focus:ring-purple-800">
|
||||||
|
<span
|
||||||
|
class="px-3 py-2 text-xs transition-all ease-in duration-75 bg-white dark:bg-gray-900 rounded-md group-hover:bg-transparent group-hover:dark:bg-transparent">
|
||||||
|
删除部门
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
<button @click.prevent="() => handleSendClick('请帮我创建部门', true)" class="inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-purple-500 to-pink-500 group-hover:from-purple-500 group-hover:to-pink-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-purple-200
|
<button @click.prevent="() => handleSendClick('请帮我创建部门', true)" class="inline-flex items-center justify-center p-0.5 mb-2 me-2 overflow-hidden font-medium text-gray-900 rounded-lg group bg-gradient-to-br from-purple-500 to-pink-500 group-hover:from-purple-500 group-hover:to-pink-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-purple-200
|
||||||
cursor-pointer dark:focus:ring-purple-800">
|
cursor-pointer dark:focus:ring-purple-800">
|
||||||
<span
|
<span
|
||||||
@@ -99,11 +126,19 @@
|
|||||||
userUpsertModal!.hide();
|
userUpsertModal!.hide();
|
||||||
}">
|
}">
|
||||||
</UserUpsertModal>
|
</UserUpsertModal>
|
||||||
|
<UserDeleteModal :id="'user-delete-modal'" :closeModal="() => {
|
||||||
|
currentDeleteUsername = undefined
|
||||||
|
userDeleteModal!.hide();
|
||||||
|
}" :onSubmit="handleDeleteUserSubmit" title="确定删除该用户吗" content="删除用户"></UserDeleteModal>
|
||||||
<DepartmentUpsertModal :id="'department-upsert-modal'" :onSubmit="handleUpsertDepartmentSubmit" :closeModal="() => {
|
<DepartmentUpsertModal :id="'department-upsert-modal'" :onSubmit="handleUpsertDepartmentSubmit" :closeModal="() => {
|
||||||
availableDepartments = undefined
|
availableDepartments = undefined
|
||||||
departmentUpsertModal!.hide();
|
departmentUpsertModal!.hide();
|
||||||
}" :availableDepartments="availableDepartments">
|
}" :availableDepartments="availableDepartments">
|
||||||
</DepartmentUpsertModal>
|
</DepartmentUpsertModal>
|
||||||
|
<DepartmentDeleteModal :id="'department-delete-modal'" :closeModal="() => {
|
||||||
|
currentDeleteDepartmentName = undefined
|
||||||
|
departmentDeleteModal!.hide();
|
||||||
|
}" :onSubmit="handleDeleteDepartmentSubmit" title="确定删除该部门吗" content="删除部门"></DepartmentDeleteModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -124,6 +159,10 @@ import type { UserUpsertSubmitModel } from "../types/user";
|
|||||||
import { useDepartmentQuery } from "@/composables/department/useDepartmentQuery";
|
import { useDepartmentQuery } from "@/composables/department/useDepartmentQuery";
|
||||||
import { useDepartmentUpsert } from "@/composables/department/useDepartmentUpsert";
|
import { useDepartmentUpsert } from "@/composables/department/useDepartmentUpsert";
|
||||||
import type { DepartmentUpsertModel } from "@/types/department";
|
import type { DepartmentUpsertModel } from "@/types/department";
|
||||||
|
import UserDeleteModal from "@/components/PopupModal.vue";
|
||||||
|
import { useAiAction } from "@/composables/ai/useAiAction";
|
||||||
|
import DepartmentDeleteModal from "@/components/PopupModal.vue";
|
||||||
|
import InputButton from "@/components/InputButton.vue";
|
||||||
|
|
||||||
const { messages, chat, isLoading, cancel, actionChat } = useAiChat();
|
const { messages, chat, isLoading, cancel, actionChat } = useAiChat();
|
||||||
const { user } = useUserStore();
|
const { user } = useUserStore();
|
||||||
@@ -135,6 +174,11 @@ const alertStore = useAlertStore();
|
|||||||
const isCommandMode = ref(false);
|
const isCommandMode = ref(false);
|
||||||
const userUpsert = useUserUpsert();
|
const userUpsert = useUserUpsert();
|
||||||
const departmentUpsert = useDepartmentUpsert();
|
const departmentUpsert = useDepartmentUpsert();
|
||||||
|
const userDeleteModal = ref<ModalInterface>();
|
||||||
|
const { deleteUserByUsername, deleteDepartmentByName } = useAiAction();
|
||||||
|
const departmentDeleteModal = ref<ModalInterface>();
|
||||||
|
const currentDeleteUsername = ref<string>();
|
||||||
|
const currentDeleteDepartmentName = ref<string>();
|
||||||
|
|
||||||
const { availableDepartments, fetchAvailableDepartments } =
|
const { availableDepartments, fetchAvailableDepartments } =
|
||||||
useDepartmentQuery();
|
useDepartmentQuery();
|
||||||
@@ -147,11 +191,19 @@ const commandActionMap: Record<string, () => void> = {
|
|||||||
fetchAvailableDepartments();
|
fetchAvailableDepartments();
|
||||||
departmentUpsertModal.value?.show();
|
departmentUpsertModal.value?.show();
|
||||||
},
|
},
|
||||||
|
DELETE_USER: () => {
|
||||||
|
userDeleteModal.value?.show();
|
||||||
|
},
|
||||||
|
DELETE_DEPARTMENT: () => {
|
||||||
|
departmentDeleteModal.value?.show();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const commandContentMap: Record<string, string> = {
|
const commandContentMap: Record<string, string> = {
|
||||||
CREATE_USER: "创建用户",
|
CREATE_USER: "创建用户",
|
||||||
CREATE_DEPARTMENT: "创建部门",
|
CREATE_DEPARTMENT: "创建部门",
|
||||||
|
DELETE_USER: "删除用户",
|
||||||
|
DELETE_DEPARTMENT: "删除部门",
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleMode = () => {
|
const toggleMode = () => {
|
||||||
@@ -184,6 +236,20 @@ const renderMarkdown = (content: string | undefined) => {
|
|||||||
// console.log('处理后HTML:', renderMarkdown(newVal[newVal.length - 1]));
|
// console.log('处理后HTML:', renderMarkdown(newVal[newVal.length - 1]));
|
||||||
// }, { deep: true });
|
// }, { deep: true });
|
||||||
|
|
||||||
|
const handleDeleteUserClick = (input: string) => {
|
||||||
|
currentDeleteUsername.value = input;
|
||||||
|
nextTick(() => {
|
||||||
|
userDeleteModal.value?.show();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteDepartmentClick = (input: string) => {
|
||||||
|
currentDeleteDepartmentName.value = input;
|
||||||
|
nextTick(() => {
|
||||||
|
departmentDeleteModal.value?.show();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleUpsertUserSubmit = async (data: UserUpsertSubmitModel) => {
|
const handleUpsertUserSubmit = async (data: UserUpsertSubmitModel) => {
|
||||||
await userUpsert.upsertUser(data);
|
await userUpsert.upsertUser(data);
|
||||||
userUpsertModal.value?.hide();
|
userUpsertModal.value?.hide();
|
||||||
@@ -204,6 +270,24 @@ const handleUpsertDepartmentSubmit = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteUserSubmit = async () => {
|
||||||
|
await deleteUserByUsername(currentDeleteUsername.value!);
|
||||||
|
userDeleteModal.value?.hide();
|
||||||
|
alertStore.showAlert({
|
||||||
|
content: "操作成功",
|
||||||
|
level: "success",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteDepartmentSubmit = async () => {
|
||||||
|
await deleteDepartmentByName(currentDeleteDepartmentName.value!);
|
||||||
|
departmentDeleteModal.value?.hide();
|
||||||
|
alertStore.showAlert({
|
||||||
|
content: "操作成功",
|
||||||
|
level: "success",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
messages,
|
messages,
|
||||||
async () => {
|
async () => {
|
||||||
@@ -266,6 +350,24 @@ onMounted(async () => {
|
|||||||
id: "user-upsert-modal",
|
id: "user-upsert-modal",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
const $userDeleteModalElement: HTMLElement | null =
|
||||||
|
document.querySelector("#user-delete-modal");
|
||||||
|
userDeleteModal.value = new Modal(
|
||||||
|
$userDeleteModalElement,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
id: "user-delete-modal",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const $departmentDeleteModalElement: HTMLElement | null =
|
||||||
|
document.querySelector("#department-delete-modal");
|
||||||
|
departmentDeleteModal.value = new Modal(
|
||||||
|
$departmentDeleteModalElement,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
id: "department-delete-modal",
|
||||||
|
},
|
||||||
|
);
|
||||||
const $departmentUpsertModalElement: HTMLElement | null =
|
const $departmentUpsertModalElement: HTMLElement | null =
|
||||||
document.querySelector("#department-upsert-modal");
|
document.querySelector("#department-upsert-modal");
|
||||||
departmentUpsertModal.value = new Modal(
|
departmentUpsertModal.value = new Modal(
|
||||||
|
|||||||
@@ -114,8 +114,8 @@
|
|||||||
<span>编辑</span>
|
<span>编辑</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="flex items-center justify-center whitespace-nowrap gap-x-1
|
<button class="flex items-center justify-center whitespace-nowrap gap-x-1
|
||||||
bg-red-700 hover:bg-red-800 focus:outline-none focus:ring-red-300
|
bg-red-700 hover:bg-red-800 focus:ring-red-300
|
||||||
text-white focus:ring-4 focus:outline-nonefont-medium rounded-lg text-sm px-4 py-2.5"
|
text-white focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-4 py-2.5"
|
||||||
@click="handleDeleteUserClick(user)" type="button">
|
@click="handleDeleteUserClick(user)" type="button">
|
||||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
|
|||||||
Reference in New Issue
Block a user