mirror of https://github.com/veypi/OneAuth.git
update crud and env.js
parent
ae463e0723
commit
e83f2da265
@ -1,33 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<style>
|
||||
.body {}
|
||||
|
||||
.menu-item {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="menu-item">
|
||||
<a :href="`/app/main?id=${id}`">应用简介</a>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a :href="`/app/user?id=${id}`">用户管理</a>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a :href="`/app/auth?id=${id}`">权限管理</a>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a :href="`/app/settings?id=${id}`">应用设置</a>
|
||||
</div>
|
||||
</body>
|
||||
<script setup>
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
id = params.get('id')
|
||||
id = $router.params.id
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
@ -1,630 +1,260 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>多字段表格管理界面</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'LucideIcons';
|
||||
src: url(https://cdn.jsdelivr.net/npm/lucide-static@latest/font/Lucide.ttf) format('truetype');
|
||||
}
|
||||
.lucide {
|
||||
font-family: 'LucideIcons';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
line-height: 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
body {
|
||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
.modal-backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
.modal-content {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 10px;
|
||||
body {}
|
||||
|
||||
|
||||
.table-column {
|
||||
flex-grow: 1;
|
||||
max-width: 30%;
|
||||
border-bottom: 2px solid black;
|
||||
background-color: var(--color-background, #fff);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e1;
|
||||
border-radius: 10px;
|
||||
|
||||
.sticky-column {
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
|
||||
.header-key {
|
||||
border-bottom: 2px solid black;
|
||||
padding: 0 0.5rem;
|
||||
font-size: 1.2rem;
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="p-6 md:p-10">
|
||||
<div class="max-w-7xl mx-auto bg-white p-6 rounded-lg shadow-lg">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 mb-6">客户信息管理</h1>
|
||||
|
||||
<div class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4">
|
||||
<div class="relative w-full md:w-1/3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索姓名或记录编号..."
|
||||
!value="searchQuery"
|
||||
@input="searchQuery = $event.target.value"
|
||||
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
/>
|
||||
<span class="lucide absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400">search</span>
|
||||
</div>
|
||||
<button
|
||||
@click="openModal('addEditModal', 'add')"
|
||||
class="w-full md:w-auto bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg flex items-center justify-center gap-2 transition duration-150 ease-in-out"
|
||||
>
|
||||
<span class="lucide">plus</span> 新增记录
|
||||
</button>
|
||||
</div>
|
||||
.table-value {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 0.5rem;
|
||||
height: 3rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 border border-gray-200 rounded-lg">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
记录编号
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
姓名
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
状态
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
创建日期
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
操作
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr v-for="(record, index) in filteredRecords" :key="index">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ record.id }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">{{ record.name }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full"
|
||||
:class="{
|
||||
'bg-green-100 text-green-800': record.status === 'active',
|
||||
'bg-yellow-100 text-yellow-800': record.status === 'pending',
|
||||
'bg-red-100 text-red-800': record.status === 'inactive',
|
||||
}"
|
||||
>
|
||||
{{ record.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">{{ record.createdAt }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium space-x-2">
|
||||
<button
|
||||
@click="openModal('viewModal', record)"
|
||||
title="查看"
|
||||
class="text-blue-600 hover:text-blue-800 transition duration-150 ease-in-out"
|
||||
>
|
||||
<span class="lucide">eye</span>
|
||||
</button>
|
||||
<button
|
||||
@click="openModal('addEditModal', 'edit', record)"
|
||||
title="编辑"
|
||||
class="text-yellow-600 hover:text-yellow-800 transition duration-150 ease-in-out"
|
||||
>
|
||||
<span class="lucide">pencil</span>
|
||||
</button>
|
||||
<button
|
||||
@click="openModal('deleteConfirmModal', record)"
|
||||
title="删除"
|
||||
class="text-red-600 hover:text-red-800 transition duration-150 ease-in-out"
|
||||
>
|
||||
<span class="lucide">trash-2</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
.table-value[odd='1'] {
|
||||
background-color: color-mix(in srgb, var(--color-background, #fff), #888 10%);
|
||||
}
|
||||
|
||||
<div class="mt-6 flex justify-center">
|
||||
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
|
||||
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
||||
<span class="sr-only">Previous</span>
|
||||
<span class="lucide h-5 w-5">chevron-left</span>
|
||||
</a>
|
||||
<a href="#" aria-current="page" class="z-10 bg-blue-50 border-blue-500 text-blue-600 relative inline-flex items-center px-4 py-2 border text-sm font-medium">
|
||||
1
|
||||
</a>
|
||||
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium">
|
||||
2
|
||||
</a>
|
||||
<span class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700">
|
||||
...
|
||||
</span>
|
||||
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium">
|
||||
10
|
||||
</a>
|
||||
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
||||
<span class="sr-only">Next</span>
|
||||
<span class="lucide h-5 w-5">chevron-right</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
.table-btn {}
|
||||
|
||||
<!-- 查看弹窗 -->
|
||||
<div id="viewModal" class="fixed inset-0 z-50 hidden items-center justify-center p-4 modal-backdrop">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-3xl max-h-[90vh] overflow-hidden flex flex-col modal-content transform scale-95">
|
||||
<div class="flex justify-between items-center p-4 border-b border-gray-200">
|
||||
<h2 class="text-xl font-semibold text-gray-800">记录详情 ({{ currentRecord.id }})</h2>
|
||||
<button @click="closeModal('viewModal')" class="text-gray-400 hover:text-gray-600">
|
||||
<span class="lucide text-2xl">x</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 overflow-y-auto">
|
||||
<!-- 显示当前记录详细信息 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-4">
|
||||
<fieldset class="border border-gray-300 p-4 rounded-md md:col-span-2">
|
||||
<legend class="text-lg font-medium text-gray-700 px-2">基本信息</legend>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mt-2">
|
||||
<div><strong class="text-gray-600">ID:</strong> <span class="text-gray-800">{{ currentRecord.id }}</span></div>
|
||||
<div><strong class="text-gray-600">记录编号:</strong> <span class="text-gray-800">{{ currentRecord.recordNumber }}</span></div>
|
||||
<div><strong class="text-gray-600">状态:</strong> <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full" :class="statusClass(currentRecord.status)">{{ currentRecord.status }}</span></div>
|
||||
<div><strong class="text-gray-600">创建日期:</strong> <span class="text-gray-800">{{ currentRecord.createdAt }}</span></div>
|
||||
<div><strong class="text-gray-600">创建人:</strong> <span class="text-gray-800">{{ currentRecord.createdBy }}</span></div>
|
||||
<div><strong class="text-gray-600">最后修改日期:</strong> <span class="text-gray-800">{{ currentRecord.lastModifiedAt }}</span></div>
|
||||
<div><strong class="text-gray-600">修改人:</strong> <span class="text-gray-800">{{ currentRecord.lastModifiedBy }}</span></div>
|
||||
<div><strong class="text-gray-600">类别:</strong> <span class="text-gray-800">{{ currentRecord.category }}</span></div>
|
||||
<div><strong class="text-gray-600">类型:</strong> <span class="text-gray-800">{{ currentRecord.type }}</span></div>
|
||||
<div><strong class="text-gray-600">优先级:</strong> <span class="text-gray-800">{{ currentRecord.priority }}</span></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
.dialog {
|
||||
min-height: 50vh;
|
||||
max-height: 80vh;
|
||||
overflow: auto;
|
||||
width: 50vw;
|
||||
background-color: var(--color-background, #fff);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
<fieldset class="border border-gray-300 p-4 rounded-md md:col-span-2">
|
||||
<legend class="text-lg font-medium text-gray-700 px-2">联系信息</legend>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mt-2">
|
||||
<div><strong class="text-gray-600">姓名:</strong> <span class="text-gray-800">{{ currentRecord.fullName }}</span></div>
|
||||
<div><strong class="text-gray-600">邮箱:</strong> <span class="text-gray-800">{{ currentRecord.email }}</span></div>
|
||||
<div><strong class="text-gray-600">电话:</strong> <span class="text-gray-800">{{ currentRecord.phone }}</span></div>
|
||||
<div class="sm:col-span-2"><strong class="text-gray-600">地址1:</strong> <span class="text-gray-800">{{ currentRecord.address1 }}</span></div>
|
||||
<div><strong class="text-gray-600">地址2:</strong> <span class="text-gray-800">{{ currentRecord.address2 }}</span></div>
|
||||
<div><strong class="text-gray-600">城市:</strong> <span class="text-gray-800">{{ currentRecord.city }}</span></div>
|
||||
<div><strong class="text-gray-600">省份:</strong> <span class="text-gray-800">{{ currentRecord.province }}</span></div>
|
||||
<div><strong class="text-gray-600">邮编:</strong> <span class="text-gray-800">{{ currentRecord.postalCode }}</span></div>
|
||||
<div><strong class="text-gray-600">国家:</strong> <span class="text-gray-800">{{ currentRecord.country }}</span></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="border border-gray-300 p-4 rounded-md md:col-span-2">
|
||||
<legend class="text-lg font-medium text-gray-700 px-2">其他信息</legend>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mt-2">
|
||||
<div><strong class="text-gray-600">开始日期:</strong> <span class="text-gray-800">{{ currentRecord.startDate }}</span></div>
|
||||
<div><strong class="text-gray-600">结束日期:</strong> <span class="text-gray-800">{{ currentRecord.endDate }}</span></div>
|
||||
<div><strong class="text-gray-600">金额:</strong> <span class="text-gray-800">{{ currentRecord.amount }}</span></div>
|
||||
<div><strong class="text-gray-600">数量:</strong> <span class="text-gray-800">{{ currentRecord.quantity }}</span></div>
|
||||
<div class="sm:col-span-2 md:col-span-3"><strong class="text-gray-600">描述:</strong> <p class="text-gray-800 mt-1">{{ currentRecord.description }}</p></div>
|
||||
<div class="sm:col-span-2 md:col-span-3"><strong class="text-gray-600">备注:</strong> <p class="text-gray-800 mt-1">{{ currentRecord.notes }}</p></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end p-4 border-t border-gray-200 bg-gray-50">
|
||||
<button @click="closeModal('viewModal')" class="bg-gray-500 hover:bg-gray-600 text-white font-semibold py-2 px-4 rounded-lg transition duration-150 ease-in-out">
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
::-webkit-scrollbar,
|
||||
::-webkit-scrollbar-track {
|
||||
width: 0.25rem;
|
||||
height: 0.25rem;
|
||||
background: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<div id="addEditModal" class="fixed inset-0 z-50 hidden items-center justify-center p-4 modal-backdrop">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col modal-content transform scale-95">
|
||||
<div class="flex justify-between items-center p-4 border-b border-gray-200">
|
||||
<h2 id="modalTitle" class="text-xl font-semibold text-gray-800">{{ isEditMode ? '编辑记录' : '新增记录' }}</h2>
|
||||
<button @click="closeModal('addEditModal')" class="text-gray-400 hover:text-gray-600">
|
||||
<span class="lucide text-2xl">x</span>
|
||||
</button>
|
||||
</div>
|
||||
<form class="flex-grow overflow-y-auto p-6 space-y-6">
|
||||
<fieldset class="border border-gray-300 p-4 rounded-md">
|
||||
<legend class="text-lg font-medium text-gray-700 px-2">基本信息</legend>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mt-2">
|
||||
<div>
|
||||
<label for="recordNumber" class="block text-sm font-medium text-gray-700">记录编号</label>
|
||||
<input
|
||||
type="text"
|
||||
id="recordNumber"
|
||||
name="recordNumber"
|
||||
!value="currentRecord.recordNumber"
|
||||
readonly
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-100 focus:outline-none sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="status" class="block text-sm font-medium text-gray-700">状态 *</label>
|
||||
<select
|
||||
id="status"
|
||||
name="status"
|
||||
!value="currentRecord.status"
|
||||
@change="currentRecord.status = $event.target.value"
|
||||
required
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
>
|
||||
<option value="active">活动</option>
|
||||
<option value="pending">待定</option>
|
||||
<option value="inactive">非活动</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="category" class="block text-sm font-medium text-gray-700">类别</label>
|
||||
<input
|
||||
type="text"
|
||||
id="category"
|
||||
name="category"
|
||||
!value="currentRecord.category"
|
||||
@input="currentRecord.category = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="type" class="block text-sm font-medium text-gray-700">类型</label>
|
||||
<input
|
||||
type="text"
|
||||
id="type"
|
||||
name="type"
|
||||
!value="currentRecord.type"
|
||||
@input="currentRecord.type = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="priority" class="block text-sm font-medium text-gray-700">优先级</label>
|
||||
<select
|
||||
id="priority"
|
||||
name="priority"
|
||||
!value="currentRecord.priority"
|
||||
@change="currentRecord.priority = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
>
|
||||
<option value="high">高</option>
|
||||
<option value="medium">中</option>
|
||||
<option value="low">低</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
transform: translateX(10px);
|
||||
}
|
||||
|
||||
<fieldset class="border border-gray-300 p-4 rounded-md">
|
||||
<legend class="text-lg font-medium text-gray-700 px-2">联系信息</legend>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mt-2">
|
||||
<div>
|
||||
<label for="fullName" class="block text-sm font-medium text-gray-700">姓名 *</label>
|
||||
<input
|
||||
type="text"
|
||||
id="fullName"
|
||||
name="fullName"
|
||||
!value="currentRecord.fullName"
|
||||
@input="currentRecord.fullName = $event.target.value"
|
||||
required
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700">邮箱</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
!value="currentRecord.email"
|
||||
@input="currentRecord.email = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="phone" class="block text-sm font-medium text-gray-700">电话</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
!value="currentRecord.phone"
|
||||
@input="currentRecord.phone = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label for="address1" class="block text-sm font-medium text-gray-700">地址1</label>
|
||||
<input
|
||||
type="text"
|
||||
id="address1"
|
||||
name="address1"
|
||||
!value="currentRecord.address1"
|
||||
@input="currentRecord.address1 = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="address2" class="block text-sm font-medium text-gray-700">地址2</label>
|
||||
<input
|
||||
type="text"
|
||||
id="address2"
|
||||
name="address2"
|
||||
!value="currentRecord.address2"
|
||||
@input="currentRecord.address2 = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="city" class="block text-sm font-medium text-gray-700">城市</label>
|
||||
<input
|
||||
type="text"
|
||||
id="city"
|
||||
name="city"
|
||||
!value="currentRecord.city"
|
||||
@input="currentRecord.city = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="province" class="block text-sm font-medium text-gray-700">省份</label>
|
||||
<input
|
||||
type="text"
|
||||
id="province"
|
||||
name="province"
|
||||
!value="currentRecord.province"
|
||||
@input="currentRecord.province = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="postalCode" class="block text-sm font-medium text-gray-700">邮编</label>
|
||||
<input
|
||||
type="text"
|
||||
id="postalCode"
|
||||
name="postalCode"
|
||||
!value="currentRecord.postalCode"
|
||||
@input="currentRecord.postalCode = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="country" class="block text-sm font-medium text-gray-700">国家</label>
|
||||
<input
|
||||
type="text"
|
||||
id="country"
|
||||
name="country"
|
||||
!value="currentRecord.country"
|
||||
@input="currentRecord.country = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
.keysearch {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 0.375rem;
|
||||
outline: none;
|
||||
transition: border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
<fieldset class="border border-gray-300 p-4 rounded-md">
|
||||
<legend class="text-lg font-medium text-gray-700 px-2">其他信息</legend>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mt-2">
|
||||
<div>
|
||||
<label for="startDate" class="block text-sm font-medium text-gray-700">开始日期</label>
|
||||
<input
|
||||
type="date"
|
||||
id="startDate"
|
||||
name="startDate"
|
||||
!value="currentRecord.startDate"
|
||||
@input="currentRecord.startDate = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="endDate" class="block text-sm font-medium text-gray-700">结束日期</label>
|
||||
<input
|
||||
type="date"
|
||||
id="endDate"
|
||||
name="endDate"
|
||||
!value="currentRecord.endDate"
|
||||
@input="currentRecord.endDate = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="amount" class="block text-sm font-medium text-gray-700">金额</label>
|
||||
<input
|
||||
type="number"
|
||||
id="amount"
|
||||
name="amount"
|
||||
step="0.01"
|
||||
!value="currentRecord.amount"
|
||||
@input="currentRecord.amount = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="quantity" class="block text-sm font-medium text-gray-700">数量</label>
|
||||
<input
|
||||
type="number"
|
||||
id="quantity"
|
||||
name="quantity"
|
||||
!value="currentRecord.quantity"
|
||||
@input="currentRecord.quantity = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div class="sm:col-span-2 md:col-span-3">
|
||||
<label for="description" class="block text-sm font-medium text-gray-700">描述</label>
|
||||
<textarea
|
||||
id="description"
|
||||
name="description"
|
||||
rows="3"
|
||||
!value="currentRecord.description"
|
||||
@input="currentRecord.description = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2 md:col-span-3">
|
||||
<label for="notes" class="block text-sm font-medium text-gray-700">备注</label>
|
||||
<textarea
|
||||
id="notes"
|
||||
name="notes"
|
||||
rows="3"
|
||||
!value="currentRecord.notes"
|
||||
@input="currentRecord.notes = $event.target.value"
|
||||
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<div class="flex justify-end p-4 border-t border-gray-200 bg-gray-50">
|
||||
<button @click="closeModal('addEditModal')" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-semibold py-2 px-4 rounded-lg mr-2 transition duration-150 ease-in-out">
|
||||
取消
|
||||
</button>
|
||||
<button @click="saveRecord" class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg transition duration-150 ease-in-out">
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
.keysearch:focus {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 删除确认弹窗 -->
|
||||
<div id="deleteConfirmModal" class="fixed inset-0 z-50 hidden items-center justify-center p-4 modal-backdrop">
|
||||
<div class="bg-white rounded-lg shadow-xl w-full max-w-md modal-content transform scale-95">
|
||||
<div class="flex justify-between items-center p-4 border-b border-gray-200">
|
||||
<h2 class="text-lg font-semibold text-gray-800">确认删除</h2>
|
||||
<button @click="closeModal('deleteConfirmModal')" class="text-gray-400 hover:text-gray-600">
|
||||
<span class="lucide text-2xl">x</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-sm text-gray-600">您确定要删除记录 "{{ currentRecord.recordNumber }}" 吗?此操作无法撤销。</p>
|
||||
</div>
|
||||
<div class="flex justify-end p-4 border-t border-gray-200 bg-gray-50">
|
||||
<button @click="closeModal('deleteConfirmModal')" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-semibold py-2 px-4 rounded-lg mr-2 transition duration-150 ease-in-out">
|
||||
取消
|
||||
</button>
|
||||
<button @click="confirmDelete" class="bg-red-600 hover:bg-red-700 text-white font-semibold py-2 px-4 rounded-lg transition duration-150 ease-in-out">
|
||||
确认删除
|
||||
</button>
|
||||
<body>
|
||||
<div class="flex justify-evenly overflow-x-auto">
|
||||
<div v-if='!key.hidden' class="table-column" :class="{'sticky-column':index===0}" style="min-height: 21rem;left:0"
|
||||
v-for='(key,index) in keys'>
|
||||
<div class="header-key">{{key.label||key.name}}</div>
|
||||
<div class="table-value" :odd='index%2' v-for='(row, index) in data'>
|
||||
<vslot :name='key.name' v='row,index' :style="key.style">
|
||||
<div refu='input' v:value='row[key.name]' :type="key.type==='textarea'?'text':key.type"
|
||||
:required='key.required' :validate='key.validate' :opts='key.opts'
|
||||
v-if='editable && !key.disabled && row._enable'>
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ (key.field?key.field(row):row[key.name]) || ' '}}
|
||||
</div>
|
||||
</vslot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-column sticky-column" style="right:0;min-width: 8rem;">
|
||||
<vslot name='_key' class="header-key">
|
||||
<div refu='dropdown' class="w-full">
|
||||
<div refu='icon' class="text-2xl" name='ecs'></div>
|
||||
<div vslot='menu'>
|
||||
<div class="dropdown-item" @click='show(0)'>创建</div>
|
||||
<div class="dropdown-item" @click='show(1)'>高级检索</div>
|
||||
<div class="dropdown-item" @click='show(2)'>智能导入</div>
|
||||
</div>
|
||||
</div>
|
||||
</vslot>
|
||||
<div class="table-value" :odd='index%2' v-for='(row, index) in data'>
|
||||
<vslot class="w-full flex justify-center gap-2 text-xl" name='_addon' v='row,index'>
|
||||
<div refu='icon' name='edit-square' color='#78c' v-if='!row._enable' @click='row._enable=true'></div>
|
||||
<div refu='icon' name='save' color='#ff3300' v-else @click='wrap(1, row)'></div>
|
||||
<div refu='icon' name='delete' color='#f66' v-if='!row._enable' @click='wrap(3, row)'></div>
|
||||
<div refu='icon' name='close' color='#aaa' v-else @click='delete row._enable'></div>
|
||||
</vslot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 px-4 select-none h-12">
|
||||
<input !value='listOpts.keyword' @input.delay1s='search' class="keysearch" placeholder="简单检索" />
|
||||
<div refu='icon' name='left' class="ml-auto" @click='wrap(0,-1)'></div>
|
||||
<div>{{listOpts.page}}</div>
|
||||
<div refu='icon' name='right' class="mr-auto" @click='wrap(0,1)'></div>
|
||||
<div class="">总计{{total}}条数据</div>
|
||||
</div>
|
||||
<div refu='dialog' v:show='showFlag'>
|
||||
<div class="dialog">
|
||||
<vslot v-if='showMode==0' name='create' v='keys,oncreate'>
|
||||
<table-create :keys='keys' :oncreate='oncreate'></table-create>
|
||||
</vslot>
|
||||
<table-setting :keys='keys' :opts='listOpts' v-else-if='showMode==1' :apply='()=>wrap(0)'>
|
||||
</table-setting>
|
||||
<div v-else-if class="w-full flex flex-col flex-grow items-center gap-4" style=" height: calc(100% - 0px);">
|
||||
<textarea class="w-full bg-gray-200 flex-grow p-4" placeholder="请输入文本内容或者拖入文件" !value='ai_content'
|
||||
@input='ai_content=$event.target.value' style="resize:vertical;"></textarea>
|
||||
<div refu='btn' class="mx-auto" size='lg' :click='ai'>智能识别</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script setup>
|
||||
searchQuery = '';
|
||||
records = [
|
||||
{ id: 1, recordNumber: 'REC-001', name: '张三', status: 'active', createdAt: '2024-01-15', category: '重要客户', type: '企业' },
|
||||
{ id: 2, recordNumber: 'REC-002', name: '李四', status: 'pending', createdAt: '2024-02-20', category: '普通客户', type: '个人' },
|
||||
{ id: 3, recordNumber: 'REC-003', name: '王五', status: 'inactive', createdAt: '2024-03-10', category: '潜在客户', type: '企业' },
|
||||
];
|
||||
currentRecord = {};
|
||||
isEditMode = false;
|
||||
|
||||
filteredRecords = () => {
|
||||
return records.filter((record) =>
|
||||
record.recordNumber.includes(searchQuery) || record.name.includes(searchQuery)
|
||||
);
|
||||
};
|
||||
|
||||
openModal = (modalId, mode, recordData) => {
|
||||
const modal = document.getElementById(modalId);
|
||||
if (!modal) return;
|
||||
|
||||
if (mode === 'add') {
|
||||
isEditMode = false;
|
||||
currentRecord = { recordNumber: '自动生成', status: 'active', category: '', type: '' };
|
||||
} else if (mode === 'edit') {
|
||||
isEditMode = true;
|
||||
currentRecord = { ...recordData };
|
||||
} else if (mode === 'view') {
|
||||
currentRecord = { ...recordData };
|
||||
showFlag = false
|
||||
showMode = 0
|
||||
loading = false
|
||||
keys = []
|
||||
data = []
|
||||
host = window.location.origin
|
||||
api = ''
|
||||
editable = false
|
||||
ai_content = ''
|
||||
show = (m) => {
|
||||
showMode = m
|
||||
showFlag = true
|
||||
}
|
||||
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const backdrop = modal.querySelector('.modal-backdrop') || modal;
|
||||
const content = modal.querySelector('.modal-content');
|
||||
backdrop.style.opacity = '1';
|
||||
if (content) content.style.transform = 'scale(1)';
|
||||
});
|
||||
|
||||
document.body.style.overflow = 'hidden';
|
||||
};
|
||||
|
||||
closeModal = (modalId) => {
|
||||
const modal = document.getElementById(modalId);
|
||||
if (!modal) return;
|
||||
|
||||
const backdrop = modal.querySelector('.modal-backdrop') || modal;
|
||||
const content = modal.querySelector('.modal-content');
|
||||
backdrop.style.opacity = '0';
|
||||
if (content) content.style.transform = 'scale(0.95)';
|
||||
|
||||
setTimeout(() => {
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
|
||||
if (!document.querySelector('.modal-backdrop:not(.hidden)')) {
|
||||
document.body.style.overflow = 'auto';
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
saveRecord = () => {
|
||||
if (isEditMode) {
|
||||
const index = records.findIndex((record) => record.id === currentRecord.id);
|
||||
records[index] = { ...currentRecord };
|
||||
update = async (row) => {
|
||||
if (api) {
|
||||
return await $env.api.Patch(host + api + '/' + row.id, row)
|
||||
}
|
||||
}
|
||||
total = 0
|
||||
listOpts = {
|
||||
page: 1,
|
||||
page_size: 10,
|
||||
keyword: '',
|
||||
keywords: {},
|
||||
sort_by: 'created_at',
|
||||
order: 'desc',
|
||||
}
|
||||
next = async (opts) => {
|
||||
if (api) {
|
||||
opts = Object.assign({}, opts)
|
||||
if (opts.keywords && Object.keys(opts.keywords).length > 0) {
|
||||
opts.keywords = JSON.stringify(opts.keywords)
|
||||
} else {
|
||||
currentRecord.id = records.length + 1;
|
||||
records.push({ ...currentRecord });
|
||||
delete opts.keywords
|
||||
}
|
||||
return await $axios.get(host + api, opts)
|
||||
}
|
||||
return []
|
||||
}
|
||||
create = async (data) => {
|
||||
if (api) {
|
||||
return await $axios.post(host + api, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
del = async (row) => {
|
||||
if (api) {
|
||||
return await $axios.delete(host + api + '/' + row.id, row)
|
||||
}
|
||||
}
|
||||
search = (e) => {
|
||||
listOpts.keyword = e.target.value
|
||||
wrap(0)
|
||||
}
|
||||
oncreate = async (d) => {
|
||||
await wrap(2, d)
|
||||
}
|
||||
wrap = async (mode, props) => {
|
||||
try {
|
||||
if (mode === 0) {
|
||||
if (typeof props === 'number') {
|
||||
listOpts.page += props
|
||||
}
|
||||
let max = Math.ceil(total / listOpts.page_size)
|
||||
if (listOpts.page < 1) {
|
||||
listOpts.page = 1
|
||||
return
|
||||
} else if (listOpts.page > max && max > 0) {
|
||||
listOpts.page = max
|
||||
return
|
||||
}
|
||||
let data = await $data.next(listOpts)
|
||||
if (Array.isArray(data)) {
|
||||
$data.data = data
|
||||
} else {
|
||||
$data.data = data.items
|
||||
$data.total = data.total
|
||||
}
|
||||
} else if (mode === 1) {
|
||||
await update(props)
|
||||
props._enable = false
|
||||
} else if (mode === 2) {
|
||||
let res = await create(props)
|
||||
showFlag = false
|
||||
$data.data.push(res)
|
||||
} else if (mode == 3) {
|
||||
await del(props)
|
||||
$data.data = $data.data.filter((d) => d.id !== data.id)
|
||||
}
|
||||
showFlag = false
|
||||
} catch (e) {
|
||||
$data.onerr(e)
|
||||
}
|
||||
console.log('done')
|
||||
}
|
||||
onerr = (e) => {
|
||||
console.error(e)
|
||||
}
|
||||
closeModal('addEditModal');
|
||||
};
|
||||
|
||||
confirmDelete = () => {
|
||||
records = records.filter((record) => record.id !== currentRecord.id);
|
||||
closeModal('deleteConfirmModal');
|
||||
};
|
||||
|
||||
statusClass = (status) => {
|
||||
if (status === 'active') return 'bg-green-100 text-green-800';
|
||||
if (status === 'pending') return 'bg-yellow-100 text-yellow-800';
|
||||
if (status === 'inactive') return 'bg-red-100 text-red-800';
|
||||
};
|
||||
</script>
|
||||
<script>
|
||||
// 初始化:确保所有弹窗开始时都是隐藏的,并设置初始状态
|
||||
const modals = document.querySelectorAll('.modal-backdrop');
|
||||
modals.forEach((modal) => {
|
||||
modal.classList.add('hidden');
|
||||
modal.style.opacity = '0';
|
||||
const content = modal.querySelector('.modal-content');
|
||||
if (content) {
|
||||
content.style.transform = 'scale(0.95)';
|
||||
}
|
||||
});
|
||||
|
||||
// 点击弹窗外部区域关闭 (可选)
|
||||
window.addEventListener('click', (event) => {
|
||||
const modals = document.querySelectorAll('.modal-backdrop');
|
||||
modals.forEach((modal) => {
|
||||
if (event.target === modal) {
|
||||
closeModal(modal.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
if ($data.data.length === 0 && $data.api) {
|
||||
wrap(0)
|
||||
}
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
import routes from './routes.js'
|
||||
import token from './token.js'
|
||||
|
||||
export default ($env) => {
|
||||
console.log($env, $vyes.root)
|
||||
token.setBaseUrl($env.root)
|
||||
token.wrapAxios($env.$axios)
|
||||
$env.$global.token = token
|
||||
$env.$global.user = token.body()
|
||||
|
||||
|
||||
$env.$router.addRoutes(routes)
|
||||
$env.$router.beforeEnter = async (to, from, next) => {
|
||||
if (to.meta && to.meta.auth) {
|
||||
if (token.isExpired()) {
|
||||
await token.refresh()
|
||||
}
|
||||
if (token.isExpired()) {
|
||||
token.logout(to.path)
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
$env.$axios.interceptors.response.use(function(response) {
|
||||
if (response?.data) {
|
||||
return response.data
|
||||
}
|
||||
return response;
|
||||
}, function(error) {
|
||||
let data = error.response ? error.response.data : error.response
|
||||
return Promise.reject(data.message || data);
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Auth Layout</title>
|
||||
<style>
|
||||
.layout-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
user-select: none;
|
||||
height: 60px;
|
||||
background: #409EFF;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 200px;
|
||||
border-right: 1px solid #ddd;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.menu a {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
display: block;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menu a:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
|
||||
.menu a[active] {
|
||||
background: #409EFF;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.footer {
|
||||
height: 40px;
|
||||
background: #f5f5f5;
|
||||
border-top: 1px solid #ddd;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="height: 100%;width: 100%;margin: 0;">
|
||||
<div class="layout-container">
|
||||
<header class="header">
|
||||
<div class="header-title">
|
||||
<a href="@/">首页</a>
|
||||
</div>
|
||||
<a class="ml-auto" href="/">应用权限管理</a>
|
||||
<!-- 新增跳转到/home的链接 -->
|
||||
<div refu='ico' class="ml-auto" style="margin-left: auto;"></div>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="main-container">
|
||||
<vslot v='user' class="menu" name='menu'>
|
||||
<a :href="`/app/${id}`">应用简介</a>
|
||||
<a :href="`/app/${id}/user`">用户管理</a>
|
||||
<a :href="`/app/${id}/auth`">权限管理</a>
|
||||
<a :href="`/app/${id}/settings`">应用设置</a>
|
||||
</vslot>
|
||||
|
||||
<vslot class="content">
|
||||
</vslot>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
Copyright © 2025 veypi. All Rights Reserved..
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
<script setup>
|
||||
id = $router.params.id;
|
||||
try {
|
||||
$env.app = await $axios.get(`/api/app/${id}`)
|
||||
document.title = `${app.name} - 项目主页`
|
||||
} catch (e) {
|
||||
$router.push('/')
|
||||
console.log(e)
|
||||
}
|
||||
</script>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in New Issue