feat: 添加管理员实验板管理界面
This commit is contained in:
@@ -1,119 +1,193 @@
|
||||
<template>
|
||||
<div class="bg-base-200 min-h-screen p-6">
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
|
||||
<button class="btn btn-ghost text-error hover:underline" @click="
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
|
||||
<button
|
||||
class="btn btn-ghost text-error hover:underline"
|
||||
@click="
|
||||
() => {
|
||||
isEditMode = !isEditMode;
|
||||
}
|
||||
">
|
||||
编辑
|
||||
</button>
|
||||
</div>
|
||||
"
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<h2 class="card-title mb-4">IP 地址列表</h2>
|
||||
<button class="btn btn-ghost" @click="">刷新</button>
|
||||
</div>
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<div class="flex flex-row justify-between items-center mb-4">
|
||||
<h2 class="card-title">IP 地址列表</h2>
|
||||
<button class="btn btn-ghost" @click="refreshData">刷新</button>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table w-full">
|
||||
<!-- 表头 -->
|
||||
<thead>
|
||||
<tr class="bg-base-300">
|
||||
<th class="w-50">IP 地址</th>
|
||||
<th class="w-30">版本号</th>
|
||||
<th class="w-50">默认启动位流</th>
|
||||
<th class="w-80">黄金位流</th>
|
||||
<th class="w-80">应用位流1</th>
|
||||
<th class="w-80">应用位流2</th>
|
||||
<th class="w-80">应用位流3</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- 搜索和列控制 -->
|
||||
<div class="flex items-center py-4 gap-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="筛选 IP 地址..."
|
||||
class="input input-bordered max-w-sm"
|
||||
:value="table.getColumn('devAddr')?.getFilterValue() as string"
|
||||
@input="
|
||||
table
|
||||
.getColumn('devAddr')
|
||||
?.setFilterValue(($event.target as HTMLInputElement).value)
|
||||
"
|
||||
/>
|
||||
|
||||
<!-- 表格内容 -->
|
||||
<tbody>
|
||||
<tr class="hover">
|
||||
<td class="font-medium">
|
||||
<input v-if="isEditMode" type="text" placeholder="Type here" class="input m-0" v-model="devAddr" />
|
||||
<span v-else>{{ devAddr }}</span>
|
||||
</td>
|
||||
<td>v1.2.3</td>
|
||||
<td>
|
||||
<select class="select select-bordered w-full max-w-xs" v-model="selectBitstream">
|
||||
<option selected>黄金位流</option>
|
||||
<option>应用位流1</option>
|
||||
<option>应用位流2</option>
|
||||
<option>应用位流3</option>
|
||||
</select>
|
||||
</td>
|
||||
<!-- 黄金位流上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<input type="file" class="file-input file-input-primary" @change="handleFileChange($event, 0)" />
|
||||
</div>
|
||||
</td>
|
||||
<!-- 应用位流1上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<input type="file" class="file-input file-input-secondary" @change="handleFileChange($event, 1)" />
|
||||
</div>
|
||||
</td>
|
||||
<!-- 应用位流2上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<input type="file" class="file-input file-input-accent" @change="handleFileChange($event, 2)" />
|
||||
</div>
|
||||
</td>
|
||||
<!-- 应用位流3上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<input type="file" class="file-input file-input-info" @change="handleFileChange($event, 3)" />
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<td class="flex gap-2">
|
||||
<button class="btn grow btn-warning" @click="
|
||||
uploadAndDownloadBitstreams(
|
||||
devAddr,
|
||||
goldBitstreamFile,
|
||||
appBitstream1File,
|
||||
appBitstream2File,
|
||||
appBitstream3File,
|
||||
<div class="dropdown dropdown-end ml-auto">
|
||||
<div tabindex="0" role="button" class="btn btn-outline">
|
||||
列显示
|
||||
<svg
|
||||
class="w-4 h-4 ml-2"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 9-7 7-7-7"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52"
|
||||
>
|
||||
<li
|
||||
v-for="column in table
|
||||
.getAllColumns()
|
||||
.filter((column) => column.getCanHide())"
|
||||
:key="column.id"
|
||||
>
|
||||
<label class="label cursor-pointer">
|
||||
<span class="label-text capitalize">{{ column.id }}</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm"
|
||||
:checked="column.getIsVisible()"
|
||||
@change="
|
||||
column.toggleVisibility(
|
||||
!!($event.target as HTMLInputElement).checked,
|
||||
)
|
||||
">
|
||||
固化
|
||||
</button>
|
||||
<button class="btn grow btn-success" @click="
|
||||
hotresetBitstream(devAddr, getSelectedBitstreamNum())
|
||||
">
|
||||
切换并热启动
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 bg-base-300 p-4 rounded-lg">
|
||||
<p class="text-sm opacity-80">
|
||||
<span class="font-semibold text-error">提示:</span>
|
||||
请谨慎操作FPGA固化和热启动功能,确保上传的位流文件无误,以避免设备损坏。
|
||||
</p>
|
||||
<!-- 表格 -->
|
||||
<div class="overflow-x-auto border border-base-300 rounded-lg">
|
||||
<table class="table w-full">
|
||||
<thead>
|
||||
<tr
|
||||
v-for="headerGroup in table.getHeaderGroups()"
|
||||
:key="headerGroup.id"
|
||||
class="bg-base-300"
|
||||
>
|
||||
<th v-for="header in headerGroup.headers" :key="header.id">
|
||||
<FlexRender
|
||||
v-if="!header.isPlaceholder"
|
||||
:render="header.column.columnDef.header"
|
||||
:props="header.getContext()"
|
||||
/>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-if="table.getRowModel().rows?.length">
|
||||
<template v-for="row in table.getRowModel().rows" :key="row.id">
|
||||
<tr
|
||||
class="hover"
|
||||
:class="{ 'bg-primary/10': row.getIsSelected() }"
|
||||
>
|
||||
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
|
||||
<FlexRender
|
||||
:render="cell.column.columnDef.cell"
|
||||
:props="cell.getContext()"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="row.getIsExpanded()">
|
||||
<td :colspan="row.getAllCells().length" class="bg-base-200">
|
||||
<div class="p-4">
|
||||
<pre class="text-sm">{{
|
||||
JSON.stringify(row.original, null, 2)
|
||||
}}</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
<tr v-else>
|
||||
<td
|
||||
:colspan="columns.length"
|
||||
class="h-24 text-center text-base-content/60"
|
||||
>
|
||||
暂无数据
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页控制 -->
|
||||
<div class="flex items-center justify-between py-4">
|
||||
<div class="text-sm text-base-content/60">
|
||||
已选择 {{ table.getFilteredSelectedRowModel().rows.length }} /
|
||||
{{ table.getFilteredRowModel().rows.length }} 行
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="btn btn-outline btn-sm"
|
||||
:disabled="!table.getCanPreviousPage()"
|
||||
@click="table.previousPage()"
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-outline btn-sm"
|
||||
:disabled="!table.getCanNextPage()"
|
||||
@click="table.nextPage()"
|
||||
>
|
||||
下一页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 bg-base-300 p-4 rounded-lg">
|
||||
<p class="text-sm opacity-80">
|
||||
<span class="font-semibold text-error">提示:</span>
|
||||
请谨慎操作FPGA固化和热启动功能,确保上传的位流文件无误,以避免设备损坏。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
ExpandedState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
} from "@tanstack/vue-table";
|
||||
import {
|
||||
FlexRender,
|
||||
getCoreRowModel,
|
||||
getExpandedRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useVueTable,
|
||||
} from "@tanstack/vue-table";
|
||||
import { isNull, isUndefined } from "lodash";
|
||||
import { ref } from "vue";
|
||||
import { RemoteUpdateClient } from "@/APIClient";
|
||||
import { h, ref } from "vue";
|
||||
import { RemoteUpdateClient } from "@/APIClient";
|
||||
import { useDialogStore } from "@/stores/dialog";
|
||||
import { Common } from "@/utils/Common";
|
||||
|
||||
@@ -122,52 +196,285 @@ const dialog = useDialogStore();
|
||||
// 编辑状态
|
||||
const isEditMode = ref(false);
|
||||
|
||||
// 选择热切换的比特流
|
||||
const selectBitstream = ref("黄金位流");
|
||||
|
||||
// 存储上传文件的信息
|
||||
const goldBitstreamFile = ref<File>();
|
||||
const appBitstream1File = ref<File>();
|
||||
const appBitstream2File = ref<File>();
|
||||
const appBitstream3File = ref<File>();
|
||||
|
||||
// 远程升级相关参数
|
||||
const devAddr = ref("192.168.1.100");
|
||||
const devPort = 1234;
|
||||
const remoteUpdater = new RemoteUpdateClient();
|
||||
|
||||
// 设备数据接口
|
||||
export interface DeviceData {
|
||||
id: string;
|
||||
devAddr: string;
|
||||
version: string;
|
||||
defaultBitstream: string;
|
||||
goldBitstreamFile?: File;
|
||||
appBitstream1File?: File;
|
||||
appBitstream2File?: File;
|
||||
appBitstream3File?: File;
|
||||
}
|
||||
|
||||
// 模拟数据,实际应该从API获取
|
||||
const data = ref<DeviceData[]>([
|
||||
{
|
||||
id: "1",
|
||||
devAddr: "192.168.1.100",
|
||||
version: "v1.2.3",
|
||||
defaultBitstream: "黄金位流",
|
||||
},
|
||||
]);
|
||||
|
||||
// 表格列定义
|
||||
const columns: ColumnDef<DeviceData>[] = [
|
||||
{
|
||||
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: "devAddr",
|
||||
header: "IP 地址",
|
||||
cell: ({ row }) => {
|
||||
const device = row.original;
|
||||
return isEditMode.value
|
||||
? h("input", {
|
||||
type: "text",
|
||||
class: "input input-sm w-full",
|
||||
value: device.devAddr,
|
||||
onInput: (e: Event) => {
|
||||
device.devAddr = (e.target as HTMLInputElement).value;
|
||||
},
|
||||
})
|
||||
: h("span", { class: "font-medium" }, device.devAddr);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "version",
|
||||
header: "版本号",
|
||||
cell: ({ row }) => row.getValue("version"),
|
||||
},
|
||||
{
|
||||
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) =>
|
||||
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) =>
|
||||
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) =>
|
||||
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) =>
|
||||
handleFileChange(e, device, "appBitstream3File"),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "操作",
|
||||
cell: ({ row }) => {
|
||||
const device = row.original;
|
||||
return h("div", { class: "flex gap-2 min-w-30" }, [
|
||||
h(
|
||||
"button",
|
||||
{
|
||||
class: "btn btn-warning btn-sm",
|
||||
onClick: () =>
|
||||
uploadAndDownloadBitstreams(
|
||||
device.devAddr,
|
||||
device.goldBitstreamFile,
|
||||
device.appBitstream1File,
|
||||
device.appBitstream2File,
|
||||
device.appBitstream3File,
|
||||
),
|
||||
},
|
||||
"固化",
|
||||
),
|
||||
h(
|
||||
"button",
|
||||
{
|
||||
class: "btn btn-success btn-sm",
|
||||
onClick: () =>
|
||||
hotresetBitstream(
|
||||
device.devAddr,
|
||||
getSelectedBitstreamNum(device.defaultBitstream),
|
||||
),
|
||||
},
|
||||
"热启动",
|
||||
),
|
||||
]);
|
||||
},
|
||||
enableHiding: false,
|
||||
},
|
||||
];
|
||||
|
||||
// TanStack Table 状态
|
||||
const sorting = ref<SortingState>([]);
|
||||
const columnFilters = ref<ColumnFiltersState>([]);
|
||||
const columnVisibility = ref<VisibilityState>({});
|
||||
const rowSelection = ref({});
|
||||
const expanded = ref<ExpandedState>({});
|
||||
|
||||
// 创建表格实例
|
||||
const table = useVueTable({
|
||||
get data() {
|
||||
return data.value;
|
||||
},
|
||||
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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 处理文件上传
|
||||
function handleFileChange(event: Event, bistreamNum: number) {
|
||||
function handleFileChange(
|
||||
event: Event,
|
||||
device: DeviceData,
|
||||
fileKey: keyof DeviceData,
|
||||
) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const file = target.files?.[0]; // 获取选中的第一个文件
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bistreamNum === 0) {
|
||||
goldBitstreamFile.value = file;
|
||||
} else if (bistreamNum === 1) {
|
||||
appBitstream1File.value = file;
|
||||
} else if (bistreamNum === 2) {
|
||||
appBitstream2File.value = file;
|
||||
} else if (bistreamNum === 3) {
|
||||
appBitstream3File.value = file;
|
||||
} else {
|
||||
goldBitstreamFile.value = file;
|
||||
const file = target.files?.[0];
|
||||
if (file) {
|
||||
(device as any)[fileKey] = file;
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedBitstreamNum(): number {
|
||||
if (selectBitstream.value == "黄金位流") {
|
||||
return 0;
|
||||
} else if (selectBitstream.value == "应用位流1") {
|
||||
return 1;
|
||||
} else if (selectBitstream.value == "应用位流2") {
|
||||
return 2;
|
||||
} else if (selectBitstream.value == "应用位流3") {
|
||||
return 3;
|
||||
}
|
||||
function getSelectedBitstreamNum(bitstreamName: string): number {
|
||||
if (bitstreamName === "黄金位流") return 0;
|
||||
if (bitstreamName === "应用位流1") return 1;
|
||||
if (bitstreamName === "应用位流2") return 2;
|
||||
if (bitstreamName === "应用位流3") return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -206,7 +513,7 @@ async function uploadAndDownloadBitstreams(
|
||||
const ret = await remoteUpdater.downloadMultiBitstreams(
|
||||
devAddr,
|
||||
devPort,
|
||||
getSelectedBitstreamNum(),
|
||||
getSelectedBitstreamNum(data.value[0].defaultBitstream),
|
||||
);
|
||||
if (ret != cnt) {
|
||||
dialog.warn("固化比特流出错");
|
||||
@@ -240,7 +547,14 @@ async function hotresetBitstream(devAddr: string, bitstreamNum: number) {
|
||||
|
||||
async function refreshData() {
|
||||
try {
|
||||
const ret = await remoteUpdater.getFirmwareVersion(devAddr.value, devPort);
|
||||
const ret = await remoteUpdater.getFirmwareVersion(
|
||||
data.value[0].devAddr,
|
||||
devPort,
|
||||
);
|
||||
// 更新版本信息
|
||||
if (ret) {
|
||||
data.value[0].version = String(ret);
|
||||
}
|
||||
} catch (e) {
|
||||
dialog.error("获取数据失败");
|
||||
console.error(e);
|
||||
|
||||
0
src/views/User/BoardManager.ts
Normal file
0
src/views/User/BoardManager.ts
Normal file
@@ -9,25 +9,40 @@
|
||||
<li id="2" @click="setActivePage">
|
||||
<a :class="{ 'menu-active': activePage === 2 }">Item 2</a>
|
||||
</li>
|
||||
<li id="100" @click="setActivePage">
|
||||
<li v-if="isAdmin" id="100" @click="setActivePage">
|
||||
<a :class="{ 'menu-active': activePage === 100 }">实验板控制台</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="divider divider-horizontal h-full"></div>
|
||||
<div class="card bg-base-200 w-300"></div>
|
||||
<div class="card bg-base-200 w-300 rounded-2xl p-7">
|
||||
<div v-if="activePage === 1">
|
||||
<h2 class="card-title">用户信息</h2>
|
||||
<p>这里是用户信息页面的内容。</p>
|
||||
</div>
|
||||
<div v-else-if="activePage === 100">
|
||||
<BoardControl />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BoardControl from "./BoardControl.vue";
|
||||
import { toNumber } from "lodash";
|
||||
import { ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
|
||||
const activePage = ref(1);
|
||||
const isAdmin = ref(false);
|
||||
|
||||
function setActivePage(event: Event) {
|
||||
const target = event.currentTarget as HTMLLinkElement;
|
||||
activePage.value = toNumber(target.id);
|
||||
}
|
||||
|
||||
onMounted(async ()=>{
|
||||
isAdmin.value = await AuthManager.verifyAdminAuth();
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user