This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FPGA_WebLab/src/views/User/BoardTableManager.ts

545 lines
16 KiB
TypeScript

import type {
ColumnDef,
ColumnFiltersState,
ExpandedState,
SortingState,
VisibilityState,
} from "@tanstack/vue-table";
import {
getCoreRowModel,
getExpandedRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useVueTable,
} from "@tanstack/vue-table";
import { h, ref, computed, version } from "vue";
import { createInjectionState } from "@vueuse/core";
import type { BoardData } from "../../utils/BoardManager";
import { useBoardManager } from "../../utils/BoardManager";
import { useDialogStore } from "@/stores/dialog";
const [useProvideBoardTableManager, useBoardTableManager] =
createInjectionState(() => {
// 从BoardManager获取数据和方法
const boardManager = useBoardManager()!;
const dialog = useDialogStore();
// 编辑状态
const isEditMode = ref(false);
// 表格状态管理
const sorting = ref<SortingState>([]);
const columnFilters = ref<ColumnFiltersState>([]);
const columnVisibility = ref<VisibilityState>({
// 默认隐藏端口、ID、状态列和板卡名称列
port: false,
id: false,
status: false,
version: false,
});
const rowSelection = ref({});
const expanded = ref<ExpandedState>({});
// 表格列定义
const columns: ColumnDef<BoardData>[] = [
{
id: "select",
header: ({ table }) =>
h("input", {
type: "checkbox",
class: "checkbox",
checked:
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() ? "indeterminate" : false),
onChange: (event: Event) =>
table.toggleAllPageRowsSelected(
!!(event.target as HTMLInputElement).checked,
),
}),
cell: ({ row }) =>
h("input", {
type: "checkbox",
class: "checkbox",
checked: row.getIsSelected(),
onChange: (event: Event) =>
row.toggleSelected(!!(event.target as HTMLInputElement).checked),
}),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "boardName",
header: "板卡名称",
cell: ({ row }) => {
const device = row.original;
return isEditMode.value
? h("input", {
type: "text",
class: "input input-sm w-full",
value: device.boardName,
onInput: (e: Event) => {
device.boardName = (e.target as HTMLInputElement).value;
},
})
: h("span", { class: "font-medium" }, device.boardName);
},
enableHiding: true,
},
{
accessorKey: "devAddr",
header: "IP 地址",
cell: ({ row }) => {
const device = row.original;
return isEditMode.value
? h("input", {
type: "text",
class: "input input-sm w-full",
value: device.ipAddr,
onInput: (e: Event) => {
device.ipAddr = (e.target as HTMLInputElement).value;
},
})
: h("span", { class: "font-medium" }, device.ipAddr);
},
},
{
accessorKey: "port",
header: "端口",
cell: ({ row }) => {
const device = row.original;
return isEditMode.value
? h("input", {
type: "number",
class: "input input-sm w-full",
value: device.port,
onInput: (e: Event) => {
device.port = parseInt((e.target as HTMLInputElement).value);
},
})
: h("span", { class: "font-mono" }, device.port.toString());
},
enableHiding: true,
},
{
accessorKey: "id",
header: "设备ID",
cell: ({ row }) =>
h("span", { class: "font-mono text-xs" }, row.original.id),
enableHiding: true,
},
{
accessorKey: "status",
header: "状态",
cell: ({ row }) => {
const device = row.original;
const statusText = device.status === 0 ? "忙碌" : "可用";
const statusClass =
device.status === 0 ? "badge-warning" : "badge-success";
return h(
"span",
{
class: `badge ${statusClass} min-w-15`,
},
statusText,
);
},
enableHiding: true,
},
{
accessorKey: "version",
header: "版本号",
cell: ({ row }) =>
h("span", { class: "font-mono" }, row.original.firmVersion),
},
{
accessorKey: "defaultBitstream",
header: "默认启动位流",
cell: ({ row }) => {
const device = row.original;
return h(
"select",
{
class: "select select-bordered select-sm w-full",
value: device.defaultBitstream,
onChange: (e: Event) => {
device.defaultBitstream = (e.target as HTMLSelectElement).value;
},
},
[
h("option", { value: "黄金位流" }, "黄金位流"),
h("option", { value: "应用位流1" }, "应用位流1"),
h("option", { value: "应用位流2" }, "应用位流2"),
h("option", { value: "应用位流3" }, "应用位流3"),
],
);
},
},
{
id: "goldBitstream",
header: "黄金位流",
cell: ({ row }) => {
const device = row.original;
return h("input", {
type: "file",
class: "file-input file-input-primary file-input-sm",
onChange: (e: Event) =>
boardManager.handleFileChange(e, device, "goldBitstreamFile"),
});
},
},
{
id: "appBitstream1",
header: "应用位流1",
cell: ({ row }) => {
const device = row.original;
return h("input", {
type: "file",
class: "file-input file-input-secondary file-input-sm",
onChange: (e: Event) =>
boardManager.handleFileChange(e, device, "appBitstream1File"),
});
},
},
{
id: "appBitstream2",
header: "应用位流2",
cell: ({ row }) => {
const device = row.original;
return h("input", {
type: "file",
class: "file-input file-input-accent file-input-sm",
onChange: (e: Event) =>
boardManager.handleFileChange(e, device, "appBitstream2File"),
});
},
},
{
id: "appBitstream3",
header: "应用位流3",
cell: ({ row }) => {
const device = row.original;
return h("input", {
type: "file",
class: "file-input file-input-info file-input-sm",
onChange: (e: Event) =>
boardManager.handleFileChange(e, device, "appBitstream3File"),
});
},
},
{
id: "actions",
header: "操作",
cell: ({ row }) => {
const device = row.original;
// 根据编辑模式显示不同的按钮
if (isEditMode.value) {
return h(
"div",
{
class: ["flex gap-2", { "min-w-30": !isEditMode.value }],
},
[
h(
"button",
{
class: "btn btn-error btn-sm",
onClick: async () => {
const confirmed = confirm(
`确定要删除设备 ${device.ipAddr} 吗?`,
);
if (confirmed) {
await deleteBoard(device.id);
}
},
},
"删除",
),
],
);
} else {
return h("div", { class: "flex gap-2 min-w-30" }, [
h(
"button",
{
class: "btn btn-warning btn-sm",
onClick: () =>
uploadAndDownloadBitstreams(
device,
device.goldBitstreamFile,
device.appBitstream1File,
device.appBitstream2File,
device.appBitstream3File,
),
},
"固化",
),
h(
"button",
{
class: "btn btn-success btn-sm",
onClick: () =>
hotresetBitstream(
device,
boardManager.getSelectedBitstreamNum(
device.defaultBitstream,
),
),
},
"热启动",
),
]);
}
},
enableHiding: false,
},
];
// 创建表格实例
const table = useVueTable({
get data() {
return boardManager.boards.value;
},
get columns() {
return columns;
},
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getExpandedRowModel: getExpandedRowModel(),
onSortingChange: (updaterOrValue) => {
if (typeof updaterOrValue === "function") {
sorting.value = updaterOrValue(sorting.value);
} else {
sorting.value = updaterOrValue;
}
},
onColumnFiltersChange: (updaterOrValue) => {
if (typeof updaterOrValue === "function") {
columnFilters.value = updaterOrValue(columnFilters.value);
} else {
columnFilters.value = updaterOrValue;
}
},
onColumnVisibilityChange: (updaterOrValue) => {
if (typeof updaterOrValue === "function") {
columnVisibility.value = updaterOrValue(columnVisibility.value);
} else {
columnVisibility.value = updaterOrValue;
}
},
onRowSelectionChange: (updaterOrValue) => {
if (typeof updaterOrValue === "function") {
rowSelection.value = updaterOrValue(rowSelection.value);
} else {
rowSelection.value = updaterOrValue;
}
},
onExpandedChange: (updaterOrValue) => {
if (typeof updaterOrValue === "function") {
expanded.value = updaterOrValue(expanded.value);
} else {
expanded.value = updaterOrValue;
}
},
state: {
get sorting() {
return sorting.value;
},
get columnFilters() {
return columnFilters.value;
},
get columnVisibility() {
return columnVisibility.value;
},
get rowSelection() {
return rowSelection.value;
},
get expanded() {
return expanded.value;
},
},
});
// UI层的API封装方法 - 添加dialog提示
// 获取所有板卡信息
async function getAllBoards(): Promise<boolean> {
const result = await boardManager.getAllBoards();
if (result.success) {
dialog?.info("获取板卡信息成功");
} else {
dialog?.error(result.error || "获取板卡信息失败");
}
return result.success;
}
// 新增板卡
async function addBoard(name: string, ipAddr: string, port: number): Promise<boolean> {
const result = await boardManager.addBoard(name, ipAddr, port);
if (result.success) {
dialog?.info("新增板卡成功");
} else {
dialog?.error(result.error || "新增板卡失败");
}
return result.success;
}
// 删除板卡
async function deleteBoard(boardId: string): Promise<boolean> {
const result = await boardManager.deleteBoard(boardId);
if (result.success) {
dialog?.info("删除板卡成功");
} else {
dialog?.error(result.error || "删除板卡失败");
}
return result.success;
}
// 上传并固化位流
async function uploadAndDownloadBitstreams(
board: BoardData,
goldBitstream?: File,
appBitstream1?: File,
appBitstream2?: File,
appBitstream3?: File,
): Promise<boolean> {
const result = await boardManager.uploadAndDownloadBitstreams(
board,
goldBitstream,
appBitstream1,
appBitstream2,
appBitstream3,
);
if (result.success) {
dialog?.info("固化比特流成功");
} else {
dialog?.error(result.error || "固化比特流失败");
}
return result.success;
}
// 热启动位流
async function hotresetBitstream(board: BoardData, bitstreamNum: number): Promise<boolean> {
const result = await boardManager.hotresetBitstream(board, bitstreamNum);
if (result.success) {
dialog?.info("切换比特流成功");
} else {
dialog?.error(result.error || "切换比特流失败");
}
return result.success;
}
// 表格操作方法
const getSelectedRows = () => table.getFilteredSelectedRowModel().rows;
const getAllRows = () => table.getFilteredRowModel().rows;
const getColumnByKey = (key: string) => table.getColumn(key);
const getAllHideableColumns = () =>
table.getAllColumns().filter((column) => column.getCanHide());
const getHeaderGroups = () => table.getHeaderGroups();
const getRowModel = () => table.getRowModel();
const canPreviousPage = () => table.getCanPreviousPage();
const canNextPage = () => table.getCanNextPage();
const previousPage = () => table.previousPage();
const nextPage = () => table.nextPage();
// 编辑模式控制
const toggleEditMode = () => {
isEditMode.value = !isEditMode.value;
};
// 删除选中的实验板
const deleteSelectedBoards = async () => {
const selectedRows = getSelectedRows();
if (selectedRows.length === 0) {
dialog?.warn("请先选择要删除的实验板");
return false;
}
const boardNames = selectedRows
.map((row) => row.original.boardName || row.original.ipAddr)
.join("、");
const confirmed = confirm(
`确定要删除以下 ${selectedRows.length} 个实验板吗?\n${boardNames}`,
);
if (!confirmed) {
return false;
}
let successCount = 0;
let failCount = 0;
// 批量删除
for (const row of selectedRows) {
const board = row.original;
const result = await boardManager.deleteBoard(board.id);
if (result.success) {
successCount++;
} else {
failCount++;
}
}
// 清空选择状态
rowSelection.value = {};
// 显示结果提示
if (failCount === 0) {
dialog?.info(`成功删除 ${successCount} 个实验板`);
} else if (successCount === 0) {
dialog?.error(
`删除失败,共 ${failCount} 个实验板删除失败`,
);
} else {
dialog?.warn(
`部分删除成功:成功 ${successCount} 个,失败 ${failCount}`,
);
}
return successCount > 0;
};
return {
// 表格实例
table,
// 列定义
columns,
// 表格操作方法
getSelectedRows,
getAllRows,
getColumnByKey,
getAllHideableColumns,
getHeaderGroups,
getRowModel,
canPreviousPage,
canNextPage,
previousPage,
nextPage,
deleteSelectedBoards,
// 状态
sorting,
columnFilters,
columnVisibility,
rowSelection,
expanded,
// 编辑模式
isEditMode,
toggleEditMode,
// UI层封装的API方法
getAllBoards,
addBoard,
deleteBoard,
uploadAndDownloadBitstreams,
hotresetBitstream,
// BoardManager 的引用
boardManager,
};
});
export { useProvideBoardTableManager, useBoardTableManager };