fix tablebutton

This commit is contained in:
Chuck1sn
2025-06-15 17:54:33 +08:00
parent fe3ebdf931
commit 1995985750
10 changed files with 1930 additions and 2008 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -61,8 +61,10 @@
<option :value="'search'">搜索模式</option>
<option :value="'chat'">询问模式</option>
</select>
<Button :abortable="true" :isLoading="isLoading" :loadingContent="'中止'" :submitContent="'发送'"
:handleClick="() => handleSendClick(inputMessage, commandMode)" />
<TableButton variant="primary" :isLoading="isLoading" :abortable="true"
@click="() => handleSendClick(inputMessage, commandMode)">
{{ isLoading ? '中止' : '发送' }}
</TableButton>
</div>
</div>
@@ -91,6 +93,7 @@
import InputButton from "@/components/InputButton.vue";
import UserDeleteModal from "@/components/PopupModal.vue";
import DepartmentDeleteModal from "@/components/PopupModal.vue";
import TableButton from "@/components/TableButton.vue";
import LoadingIcon from "@/components/icons/LoadingIcon.vue";
import { useAiAction } from "@/composables/ai/useAiAction";
import { useDepartmentQuery } from "@/composables/department/useDepartmentQuery";
@@ -103,7 +106,6 @@ import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { marked } from "marked";
import { nextTick, onMounted, onUnmounted, ref, watch } from "vue";
import { z } from "zod";
import Button from "../components/Button.vue";
import DepartmentUpsertModal from "../components/DepartmentUpsertModal.vue";
import UserUpsertModal from "../components/UserUpsertModal.vue";
import { useAiChat } from "../composables/ai/useAiChat";

View File

@@ -1,96 +0,0 @@
<template>
<button :disabled="disabled" @click="handleClick" type="button" :class="[
'text-white',
'focus:ring-4',
'focus:outline-none',
'focus:ring-blue-300',
'font-medium',
'rounded-lg',
'text-center',
'inline-flex',
'items-center',
'justify-center',
isLoading && !abortable ? 'bg-blue-400 cursor-not-allowed' : 'bg-blue-700 hover:bg-blue-800',
sizeClasses
]">
<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, useSlots } from "vue";
import LoadingIcon from "./icons/LoadingIcon.vue";
import StopIcon from "./icons/StopIcon.vue";
const props = defineProps<{
loadingContent?: string;
submitContent: string;
isLoading: boolean;
abortable: boolean;
handleClick: (event: Event) => void;
size?: "xs" | "sm" | "md" | "lg" | "xl";
iconOnly?: boolean;
}>();
const slots = useSlots();
const sizeClasses = computed(() => {
if (props.iconOnly && slots.icon) {
switch (props.size) {
case "xs":
return "p-1.5";
case "sm":
return "p-2";
case "lg":
return "p-3";
case "xl":
return "p-3.5";
default:
return "p-2.5";
}
}
switch (props.size) {
case "xs":
return "text-xs px-3 py-1.5";
case "sm":
return "text-sm px-3.5 py-2";
case "lg":
return "text-base px-5 py-3";
case "xl":
return "text-base px-6 py-3.5";
default:
return "text-sm px-4 py-2.5";
}
});
const iconSizeClasses = computed(() => {
switch (props.size) {
case "xs":
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.5 h-4.5";
}
});
const loadingStyle = computed<string>(() => {
return "";
});
const disabled = computed(() => {
return !props.abortable && props.isLoading;
});
</script>

View File

@@ -1,20 +1,22 @@
<template>
<button :class="[
<button :class="[
'flex items-center justify-center gap-x-1 whitespace-nowrap font-medium rounded-lg focus:ring-4 focus:outline-none',
sizeClasses,
colorClasses,
disabled ? 'opacity-50 cursor-not-allowed' : '',
(disabled || (isLoading && !abortable)) ? 'opacity-50 cursor-not-allowed' : '',
className
]" :disabled="disabled" @click="handleClick" type="button">
<slot name="icon"></slot>
<span>
<slot></slot>
</span>
</button>
]" :disabled="disabled || (isLoading && !abortable)" @click="handleClick" type="button">
<StopIcon v-if="isLoading && abortable" :class="iconSizeClasses" />
<slot v-else name="icon"></slot>
<span>
<slot></slot>
</span>
</button>
</template>
<script setup lang="ts">
import { computed } from "vue";
import StopIcon from "./icons/StopIcon.vue";
export type ButtonVariant =
| "primary"
@@ -36,6 +38,10 @@ const props = defineProps<{
className?: string;
/** 是否为移动端尺寸 */
isMobile?: boolean;
/** 是否处于加载状态 */
isLoading?: boolean;
/** 是否可中止 */
abortable?: boolean;
}>();
const emit = defineEmits<{
@@ -82,9 +88,20 @@ const sizeClasses = computed(() => {
return sizes[props.size || "md"];
});
/** 图标尺寸样式映射 */
const iconSizeClasses = computed(() => {
const sizes: Record<ButtonSize, string> = {
xs: "w-3.5 h-3.5",
sm: "w-4 h-4",
md: "w-4.5 h-4.5",
lg: "w-5 h-5",
};
return sizes[props.size || "md"];
});
/** 处理点击事件 */
const handleClick = (event: MouseEvent) => {
if (!props.disabled) {
if (!props.disabled && !(props.isLoading && !props.abortable)) {
emit("click", event);
}
};

View File

@@ -3,7 +3,6 @@
'inline': true,
[textColor]: true,
[size]: true,
'me-2': true
}" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path d="M7 5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2H7Z" />
</svg>

View File

@@ -8,12 +8,12 @@
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertDepartmentClick()" :isLoading="false" :abortable="false"
submitContent="新增部门" class="w-full sm:w-auto">
<TableButton variant="primary" @click="handleUpsertDepartmentClick()" class="w-full sm:w-auto">
<template #icon>
<PlusIcon class="w-4 h-4 me-2" />
<PlusIcon class="w-4 h-4" />
</template>
</Button>
新增部门
</TableButton>
</template>
</TableFilterForm>
@@ -109,7 +109,7 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import DepartmentUpsertModal from "@/components/DepartmentUpsertModal.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import DepartmentDeleteModal from "@/components/PopupModal.vue";

View File

@@ -8,12 +8,12 @@
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertPermissionClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增权限" class="w-full sm:w-auto">
<TableButton variant="primary" @click="handleUpsertPermissionClick(undefined)" class="w-full sm:w-auto">
<template #icon>
<PlusIcon class="w-4 h-4 me-2" />
<PlusIcon class="w-4 h-4" />
</template>
</Button>
新增权限
</TableButton>
</template>
</TableFilterForm>
@@ -109,7 +109,7 @@
<script setup lang="ts">
import type { components } from "@/api/types/schema";
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import PermissionUpsertModal from "@/components/PermissionUpsertModal.vue";
import PermissionDeleteModal from "@/components/PopupModal.vue";

View File

@@ -8,12 +8,12 @@
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertPositionClick()" :isLoading="false" :abortable="false"
submitContent="新增岗位" class="w-full sm:w-auto">
<TableButton variant="primary" @click="handleUpsertPositionClick()" class="w-full sm:w-auto">
<template #icon>
<PlusIcon class="w-4 h-4 me-2" />
<PlusIcon class="w-4 h-4" />
</template>
</Button>
新增岗位
</TableButton>
</template>
</TableFilterForm>
@@ -99,10 +99,10 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import PositionDeleteModal from "@/components/PopupModal.vue";
import PositionUpsertModal from "@/components/PositionUpsertModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";

View File

@@ -8,12 +8,12 @@
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<Button :handleClick="() => handleUpsertRoleClick(undefined)" :isLoading="false" :abortable="false"
submitContent="新增角色" class="w-full sm:w-auto">
<TableButton variant="primary" @click="handleUpsertRoleClick(undefined)" class="w-full sm:w-auto">
<template #icon>
<PlusIcon class="w-4 h-4 me-2" />
<PlusIcon class="w-4 h-4" />
</template>
</Button>
新增角色
</TableButton>
</template>
</TableFilterForm>
@@ -123,10 +123,10 @@
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import Button from "@/components/Button.vue";
import MobileCardList from "@/components/MobileCardList.vue";
import RoleDeleteModal from "@/components/PopupModal.vue";
import RoleUpsertModal from "@/components/RoleUpsertModal.vue";
import TableButton from "@/components/TableButton.vue";
import TableFilterForm from "@/components/TableFilterForm.vue";
import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";

View File

@@ -10,7 +10,7 @@
<template #actions>
<TableButton variant="primary" @click="() => handleUpsertUserClick(undefined)">
<template #icon>
<PlusIcon class="w-4 h-4 me-2" />
<PlusIcon class="w-4 h-4" />
</template>
新增用户
</TableButton>
@@ -161,9 +161,10 @@
<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 TableButton from "@/components/TableButton.vue";
import TableFilterForm, {
type FilterItem,
} from "@/components/TableFilterForm.vue";
@@ -185,7 +186,6 @@ import { useRouter } from "vue-router";
import type { components } from "../api/types/schema";
import useAlertStore from "../composables/store/useAlertStore";
import { useUserUpsert } from "../composables/user/useUserUpsert";
import TableButton from "@/components/TableButton.vue";
const filterConfig: FilterItem[] = [
{