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> <template>
<div <div :class="[
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" 'fixed top-0 right-0 h-full z-50 bg-white shadow-lg transform transition-transform duration-300 ease-in-out',
ref="chatContainer"> '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"> <div class="flex flex-col gap-y-5 flex-1 pb-2">
<li v-for="chatElement in messages" :key="chatElement.content" <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']"> :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> </script>
<style lang="css"> <style lang="css">

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<template> <template>
<aside id="logo-sidebar" <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 " 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"> aria-label="Sidebar" tabindex="-1" data-drawer-backdrop="true">
<div class="h-full px-3 pb-4 overflow-y-auto bg-white "> <div class="h-full px-3 pb-4 overflow-y-auto bg-white ">
<ul class="space-y-2 font-medium"> <ul class="space-y-2 font-medium">
<li v-for="item in menuItems" :key="item.path"> <li v-for="item in menuItems" :key="item.path">

View File

@@ -23,11 +23,15 @@
@click.prevent="handleSearch">搜索</button> @click.prevent="handleSearch">搜索</button>
</div> </div>
</form> </form>
<button @click="handleUpsertDepartmentClick()" <Button :handleClick="() => handleUpsertDepartmentClick()" :isLoading="false" :abortable="false"
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" submitContent="新增部门" size="sm" class="w-full sm:w-auto">
type="button"> <template #icon>
新增部门 <svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
</button> 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>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg"> <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"> <table class="w-full text-sm text-left rtl:text-right text-gray-500">
@@ -108,6 +112,7 @@
<script setup lang="ts"> <script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue"; import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import DepartmentUpsertModal from "@/components/DepartmentUpsertModal.vue"; import DepartmentUpsertModal from "@/components/DepartmentUpsertModal.vue";
import DepartmentDeleteModal from "@/components/PopupModal.vue"; import DepartmentDeleteModal from "@/components/PopupModal.vue";
import TablePagination from "@/components/TablePagination.vue"; import TablePagination from "@/components/TablePagination.vue";

View File

@@ -24,11 +24,15 @@
</div> </div>
</form> </form>
<!-- Create Modal toggle --> <!-- Create Modal toggle -->
<button @click="handleUpsertPositionClick()" <Button :handleClick="() => handleUpsertPositionClick()" :isLoading="false" :abortable="false"
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" submitContent="新增岗位" size="sm" class="w-full sm:w-auto">
type="button"> <template #icon>
新增岗位 <svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
</button> 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>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg"> <div class="relative overflow-x-auto shadow-md sm:rounded-lg">
@@ -105,6 +109,7 @@
<script setup lang="ts"> <script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue"; import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import PositionDeleteModal from "@/components/PopupModal.vue"; import PositionDeleteModal from "@/components/PopupModal.vue";
import PositionUpsertModal from "@/components/PositionUpsertModal.vue"; import PositionUpsertModal from "@/components/PositionUpsertModal.vue";
import TablePagination from "@/components/TablePagination.vue"; import TablePagination from "@/components/TablePagination.vue";

View File

@@ -24,11 +24,15 @@
</div> </div>
</form> </form>
<!-- Create Modal toggle --> <!-- Create Modal toggle -->
<button @click="handleUpsertRoleClick(undefined)" <Button :handleClick="() => handleUpsertRoleClick(undefined)" :isLoading="false" :abortable="false"
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" submitContent="新增角色" size="sm" class="w-full sm:w-auto">
type="button"> <template #icon>
新增角色 <svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
</button> 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>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg"> <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"> <table class="w-full text-sm text-left rtl:text-right text-gray-500">
@@ -116,6 +120,7 @@
<script setup lang="ts"> <script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue"; import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import RoleDeleteModal from "@/components/PopupModal.vue"; import RoleDeleteModal from "@/components/PopupModal.vue";
import RoleUpsertModal from "@/components/RoleUpsertModal.vue"; import RoleUpsertModal from "@/components/RoleUpsertModal.vue";
import TablePagination from "@/components/TablePagination.vue"; import TablePagination from "@/components/TablePagination.vue";

View File

@@ -24,11 +24,15 @@
</div> </div>
</form> </form>
<!-- Create Modal toggle --> <!-- Create Modal toggle -->
<button @click="handleUpsertUserClick(undefined)" <Button :handleClick="() => handleUpsertUserClick(undefined)" :isLoading="false" :abortable="false"
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" submitContent="新增用户" size="sm" class="w-full sm:w-auto">
type="button"> <template #icon>
新增用户 <svg class="w-4 h-4 me-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
</button> 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>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg"> <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"> <table class="w-full text-sm text-left rtl:text-right text-gray-500">
@@ -150,6 +154,7 @@
<script setup lang="ts"> <script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue"; import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import UserDeleteModal from "@/components/PopupModal.vue"; import UserDeleteModal from "@/components/PopupModal.vue";
import SortIcon from "@/components/SortIcon.vue"; import SortIcon from "@/components/SortIcon.vue";
import TablePagination from "@/components/TablePagination.vue"; import TablePagination from "@/components/TablePagination.vue";