This commit is contained in:
ccmjga
2025-06-02 15:18:33 +08:00
parent 2f73915844
commit 7cd734bffd
8 changed files with 85 additions and 42 deletions

View File

@@ -1,7 +1,17 @@
<template>
<div
class="h-[calc(100vh-3.5rem)] flex flex-col box-border p-3 overflow-y-auto w-80 overflow-x-hidden border-gray-200 border-l"
ref="chatContainer">
<div :class="[
'fixed top-0 right-0 h-full z-50 bg-white shadow-lg transform transition-transform duration-300 ease-in-out',
'flex flex-col box-border overflow-y-auto w-full sm:w-96',
isVisible ? 'translate-x-0' : 'translate-x-full'
]" ref="chatContainer">
<div class="flex items-center justify-between p-3 border-b">
<h2 class="text-lg font-semibold text-gray-900">助手</h2>
<button @click="closeAssistant" class="text-gray-500 hover:text-gray-700">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div class="flex flex-col gap-y-5 flex-1 pb-2">
<li v-for="chatElement in messages" :key="chatElement.content"
:class="['flex items-start gap-2.5', chatElement.isUser ? 'flex-row-reverse max-w-full break-words' : 'flex-row']">
@@ -319,6 +329,16 @@ onMounted(async () => {
},
);
});
const props = defineProps<{
isVisible: boolean;
}>();
const emit = defineEmits(['close']);
const closeAssistant = () => {
emit('close');
};
</script>
<style lang="css">

View File

@@ -1,5 +1,5 @@
<template>
<button :disabled="disabled" @click="handleClick" type="button" :class="[
<button :disabled="disabled" @click="handleClick" type="button" :class="[
'text-white',
'focus:ring-4',
'focus:outline-none',
@@ -12,18 +12,20 @@
isLoading && !abortable ? 'bg-blue-400 cursor-not-allowed' : 'bg-blue-700 hover:bg-blue-800',
sizeClasses
]">
<LoadingIcon v-if="isLoading && !abortable" :class="iconSizeClasses" />
<StopIcon v-else-if="isLoading && abortable" :class="iconSizeClasses" />
<span v-if="iconOnly && isLoading" class="sr-only">{{ loadingContent }}</span>
<span v-else-if="iconOnly && !isLoading" class="sr-only">{{ submitContent }}</span>
<template v-else>
{{ isLoading ? loadingContent : submitContent }}
</template>
</button>
<LoadingIcon v-if="isLoading && !abortable" :class="[iconSizeClasses, iconOnly ? '' : 'me-2']" />
<StopIcon v-else-if="isLoading && abortable" :class="[iconSizeClasses, iconOnly ? '' : 'me-2']" />
<slot v-else-if="!isLoading && $slots.icon" name="icon" :iconSizeClasses="iconSizeClasses"></slot>
<span v-if="iconOnly && isLoading" class="sr-only">{{ loadingContent }}</span>
<span v-else-if="iconOnly && !isLoading && !$slots.icon" class="sr-only">{{ submitContent }}</span>
<template v-else-if="!iconOnly">
{{ isLoading ? loadingContent : submitContent }}
</template>
</button>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { computed, useSlots } from "vue";
import LoadingIcon from "./icons/LoadingIcon.vue";
import StopIcon from "./icons/StopIcon.vue";
@@ -37,8 +39,10 @@ const props = defineProps<{
iconOnly?: boolean;
}>();
const slots = useSlots();
const sizeClasses = computed(() => {
if (props.iconOnly) {
if (props.iconOnly && slots.icon) {
switch (props.size) {
case 'xs': return 'p-1.5';
case 'sm': return 'p-2';
@@ -60,11 +64,11 @@ const sizeClasses = computed(() => {
const iconSizeClasses = computed(() => {
switch (props.size) {
case 'xs': return 'w-3 h-3';
case 'sm': return 'w-3.5 h-3.5';
case 'sm': return 'w-4 h-4';
case 'lg': return 'w-5 h-5';
case 'xl': return 'w-6 h-6';
default:
return 'w-4 h-4';
return 'w-5 h-5';
}
});

View File

@@ -1,14 +1,13 @@
<template>
<Headbar :changeAssistantVisible="changeAssistantVisible"></Headbar>
<Sidebar>
</Sidebar>
<Sidebar />
<div class="flex flex-row h-[calc(100vh-3.5rem)] mt-14">
<article class="flex-1 sm:ml-44 overflow-y-auto">
<RouterView></RouterView>
</article>
<Assistant v-if="isAssistantVisible"></Assistant>
<!-- Assistant is now fixed positioned and controlled by isAssistantVisible -->
</div>
<Assistant :isVisible="isAssistantVisible" @close="changeAssistantVisible"></Assistant>
</template>
<script setup lang="ts">

View File

@@ -1,7 +1,7 @@
<template>
<aside id="logo-sidebar"
class="fixed top-0 left-0 z-30 px-1 w-44 min-h-screen overflow-y-auto pt-20 transition-transform -translate-x-full bg-white border-r border-gray-200 sm:translate-x-0 "
aria-label="Sidebar">
class="fixed top-0 left-0 z-30 px-1 w-44 min-h-screen overflow-y-auto pt-20 transition-transform -translate-x-full bg-white border-r border-gray-200 sm:translate-x-0"
aria-label="Sidebar" tabindex="-1" data-drawer-backdrop="true">
<div class="h-full px-3 pb-4 overflow-y-auto bg-white ">
<ul class="space-y-2 font-medium">
<li v-for="item in menuItems" :key="item.path">

View File

@@ -23,11 +23,15 @@
@click.prevent="handleSearch">搜索</button>
</div>
</form>
<button @click="handleUpsertDepartmentClick()"
class="flex items-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
type="button">
新增部门
</button>
<Button :handleClick="() => handleUpsertDepartmentClick()" :isLoading="false" :abortable="false"
submitContent="新增部门" size="sm" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left rtl:text-right text-gray-500">
@@ -108,6 +112,7 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import DepartmentUpsertModal from "@/components/DepartmentUpsertModal.vue";
import DepartmentDeleteModal from "@/components/PopupModal.vue";
import TablePagination from "@/components/TablePagination.vue";

View File

@@ -24,11 +24,15 @@
</div>
</form>
<!-- Create Modal toggle -->
<button @click="handleUpsertPositionClick()"
class="flex items-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
type="button">
新增岗位
</button>
<Button :handleClick="() => handleUpsertPositionClick()" :isLoading="false" :abortable="false"
submitContent="新增岗位" size="sm" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
@@ -105,6 +109,7 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import PositionDeleteModal from "@/components/PopupModal.vue";
import PositionUpsertModal from "@/components/PositionUpsertModal.vue";
import TablePagination from "@/components/TablePagination.vue";

View File

@@ -24,11 +24,15 @@
</div>
</form>
<!-- Create Modal toggle -->
<button @click="handleUpsertRoleClick(undefined)"
class="flex items-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
type="button">
新增角色
</button>
<Button :handleClick="() => handleUpsertRoleClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增角色" size="sm" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left rtl:text-right text-gray-500">
@@ -116,6 +120,7 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import RoleDeleteModal from "@/components/PopupModal.vue";
import RoleUpsertModal from "@/components/RoleUpsertModal.vue";
import TablePagination from "@/components/TablePagination.vue";

View File

@@ -24,11 +24,15 @@
</div>
</form>
<!-- Create Modal toggle -->
<button @click="handleUpsertUserClick(undefined)"
class="flex items-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center"
type="button">
新增用户
</button>
<Button :handleClick="() => handleUpsertUserClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增用户" size="sm" class="w-full sm:w-auto">
<template #icon>
<svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</template>
</Button>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left rtl:text-right text-gray-500">
@@ -150,6 +154,7 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import UserDeleteModal from "@/components/PopupModal.vue";
import SortIcon from "@/components/SortIcon.vue";
import TablePagination from "@/components/TablePagination.vue";