更新授权文件管理页面
This commit is contained in:
parent
e93000b0c2
commit
cbfd253720
|
|
@ -5,13 +5,6 @@
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<h2 class="text-2xl font-semibold">设备列表</h2>
|
<h2 class="text-2xl font-semibold">设备列表</h2>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<button
|
|
||||||
class="px-4 py-2 rounded flex items-center gap-2"
|
|
||||||
style="border: 1px solid #D9D9D9; color: rgba(0, 0, 0, 0.85)"
|
|
||||||
>
|
|
||||||
<Upload :size="16" />
|
|
||||||
BOM导入
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 rounded flex items-center gap-2"
|
class="px-4 py-2 rounded flex items-center gap-2"
|
||||||
style="border: 1px solid #D9D9D9; color: rgba(0, 0, 0, 0.85)"
|
style="border: 1px solid #D9D9D9; color: rgba(0, 0, 0, 0.85)"
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,19 @@ import {
|
||||||
Plus,
|
Plus,
|
||||||
Trash2,
|
Trash2,
|
||||||
GripVertical,
|
GripVertical,
|
||||||
|
X,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const showDrawer = ref(false)
|
||||||
|
const drawerForm = ref({
|
||||||
|
name: '',
|
||||||
|
code: '',
|
||||||
|
authFile: '',
|
||||||
|
configFile: '',
|
||||||
|
firmwareVersion: '',
|
||||||
|
status: '在产' as '在产' | '停产',
|
||||||
|
})
|
||||||
|
|
||||||
interface ChecklistItem {
|
interface ChecklistItem {
|
||||||
id: number
|
id: number
|
||||||
text: string
|
text: string
|
||||||
|
|
@ -209,9 +220,11 @@ const boardVersionData = [
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h3 class="text-lg font-semibold">型号列表</h3>
|
<h3 class="text-lg font-semibold">型号列表</h3>
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 rounded text-white"
|
class="px-4 py-2 rounded text-white flex items-center gap-2"
|
||||||
:style="{ backgroundColor: '#1890FF' }"
|
:style="{ backgroundColor: '#1890FF' }"
|
||||||
|
@click="showDrawer = true"
|
||||||
>
|
>
|
||||||
|
<Plus :size="16" />
|
||||||
新增型号
|
新增型号
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -369,47 +382,161 @@ const boardVersionData = [
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Board Version Requirements -->
|
|
||||||
<div class="bg-white rounded-lg" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
|
<!-- New Model Drawer -->
|
||||||
<div class="p-6 border-b" :style="{ borderColor: '#F0F0F0' }">
|
<div v-if="showDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showDrawer = false">
|
||||||
<h3 class="text-lg font-semibold">板卡版本要求</h3>
|
<div class="bg-white w-[480px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
|
||||||
|
<!-- Drawer Header -->
|
||||||
|
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
|
||||||
|
<h3 class="text-lg font-semibold">新增设备型号</h3>
|
||||||
|
<button @click="showDrawer = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
|
||||||
|
<X :size="18" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="w-full">
|
<!-- Drawer Body -->
|
||||||
<thead :style="{ backgroundColor: '#FAFAFA' }">
|
<div class="flex-1 overflow-y-auto p-6">
|
||||||
<tr>
|
<div class="space-y-5">
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">板卡类型</th>
|
<div>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">要求固件版本</th>
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">型号名称 <span style="color: #FF4D4F">*</span></label>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">校验规则</th>
|
<input v-model="drawerForm.name" type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如:GD30 高密度电法仪" />
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">操作</th>
|
</div>
|
||||||
</tr>
|
|
||||||
</thead>
|
<div>
|
||||||
<tbody>
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">型号编码 <span style="color: #FF4D4F">*</span></label>
|
||||||
<tr
|
<select v-model="drawerForm.code" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
v-for="(board, index) in boardVersionData"
|
<option value="">请选择编码</option>
|
||||||
:key="index"
|
<option>GD10-2025</option>
|
||||||
class="border-b"
|
<option>GD20-2025</option>
|
||||||
:style="{ borderColor: '#F0F0F0' }"
|
<option>GD30-2025</option>
|
||||||
>
|
<option>GT20-2025</option>
|
||||||
<td class="px-6 py-4">{{ board.boardType }}</td>
|
<option>GM10-2025</option>
|
||||||
<td class="px-6 py-4">
|
</select>
|
||||||
<span
|
</div>
|
||||||
class="px-2 py-1 rounded text-xs"
|
|
||||||
:style="{ backgroundColor: '#F0F2F5', color: 'rgba(0, 0, 0, 0.85)' }"
|
<div>
|
||||||
>
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定授权文件</label>
|
||||||
{{ board.requiredVersion }}
|
<select v-model="drawerForm.authFile" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
</span>
|
<option value="">请选择授权文件</option>
|
||||||
</td>
|
<option>auth_gd10_v1.0.lic</option>
|
||||||
<td class="px-6 py-4" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ board.validationRule }}</td>
|
<option>auth_gd20_v2.0.lic</option>
|
||||||
<td class="px-6 py-4">
|
<option>auth_gd30_v2.3.lic</option>
|
||||||
<div class="flex items-center gap-3">
|
<option>auth_gt20_v1.8.lic</option>
|
||||||
<button class="text-sm" :style="{ color: '#1890FF' }">编辑</button>
|
<option>auth_gm10_v1.5.lic</option>
|
||||||
<button class="text-sm" :style="{ color: '#1890FF' }">测试</button>
|
</select>
|
||||||
|
<div class="text-xs mt-1" style="color: rgba(0,0,0,0.45)">授权文件按型号绑定,设备激活时自动下载</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定配置文件</label>
|
||||||
|
<select v-model="drawerForm.configFile" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
|
<option value="">请选择配置文件</option>
|
||||||
|
<option>config_gd10_v1.0.json</option>
|
||||||
|
<option>config_gd20_v1.2.json</option>
|
||||||
|
<option>config_gd30_v1.5.json</option>
|
||||||
|
<option>config_gt20_v1.2.json</option>
|
||||||
|
<option>config_gm10_v1.0.json</option>
|
||||||
|
</select>
|
||||||
|
<div class="text-xs mt-1" style="color: rgba(0,0,0,0.45)">配置文件包含发射参数、采集参数、网络参数等</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定主机固件版本</label>
|
||||||
|
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
|
<option value="">请选择主机固件版本</option>
|
||||||
|
<option>v2.3.5</option>
|
||||||
|
<option>v2.3.0</option>
|
||||||
|
<option>v2.2.0</option>
|
||||||
|
<option>v2.1.0</option>
|
||||||
|
<option>v1.8.2</option>
|
||||||
|
<option>v1.5.1</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定主协板固件版本</label>
|
||||||
|
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
|
<option value="">请选择主协板固件版本</option>
|
||||||
|
<option>v2.3.5</option>
|
||||||
|
<option>v2.3.0</option>
|
||||||
|
<option>v2.2.0</option>
|
||||||
|
<option>v2.1.0</option>
|
||||||
|
<option>v1.8.2</option>
|
||||||
|
<option>v1.5.1</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定发射板固件版本</label>
|
||||||
|
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
|
<option value="">请选择发射板固件版本</option>
|
||||||
|
<option>v2.3.5</option>
|
||||||
|
<option>v2.3.0</option>
|
||||||
|
<option>v2.2.0</option>
|
||||||
|
<option>v2.1.0</option>
|
||||||
|
<option>v1.8.2</option>
|
||||||
|
<option>v1.5.1</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定采集板固件版本</label>
|
||||||
|
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
|
<option value="">请选择采集板固件版本</option>
|
||||||
|
<option>v2.3.5</option>
|
||||||
|
<option>v2.3.0</option>
|
||||||
|
<option>v2.2.0</option>
|
||||||
|
<option>v2.1.0</option>
|
||||||
|
<option>v1.8.2</option>
|
||||||
|
<option>v1.5.1</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">状态</label>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<label class="flex items-center gap-2 cursor-pointer text-sm">
|
||||||
|
<input type="radio" v-model="drawerForm.status" value="在产" style="accent-color: #1890FF" />
|
||||||
|
<span>在产</span>
|
||||||
|
</label>
|
||||||
|
<label class="flex items-center gap-2 cursor-pointer text-sm">
|
||||||
|
<input type="radio" v-model="drawerForm.status" value="停产" style="accent-color: #1890FF" />
|
||||||
|
<span>停产</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Preview -->
|
||||||
|
<div class="p-4 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
|
||||||
|
<div class="text-sm font-medium mb-3">绑定预览</div>
|
||||||
|
<div class="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span>授权文件</span>
|
||||||
|
<span :style="{ color: drawerForm.authFile ? '#52C41A' : '#FF4D4F' }">{{ drawerForm.authFile || '未绑定' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span>配置文件</span>
|
||||||
|
<span :style="{ color: drawerForm.configFile ? '#52C41A' : '#FF4D4F' }">{{ drawerForm.configFile || '未绑定' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span>固件版本</span>
|
||||||
|
<span :style="{ color: drawerForm.firmwareVersion ? '#52C41A' : '#FF4D4F' }">{{ drawerForm.firmwareVersion || '未绑定' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Drawer Footer -->
|
||||||
|
<div class="flex items-center justify-end gap-3 p-5 border-t" style="border-color: #F0F0F0">
|
||||||
|
<button class="px-4 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showDrawer = false">取消</button>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 rounded text-white text-sm"
|
||||||
|
:style="{ backgroundColor: drawerForm.name && drawerForm.code ? '#1890FF' : '#D9D9D9' }"
|
||||||
|
:disabled="!drawerForm.name || !drawerForm.code"
|
||||||
|
@click="showDrawer = false"
|
||||||
|
>确认创建</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,54 @@ import {
|
||||||
Camera,
|
Camera,
|
||||||
Upload,
|
Upload,
|
||||||
Check,
|
Check,
|
||||||
|
Download,
|
||||||
|
X,
|
||||||
|
FileSpreadsheet,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const showImportDialog = ref(false)
|
||||||
|
const importFile = ref<File | null>(null)
|
||||||
|
|
||||||
|
const showPhotoDialog = ref(false)
|
||||||
|
const photoDialogItemId = ref(0)
|
||||||
|
const photoFiles = ref<{ name: string; url: string }[]>([])
|
||||||
|
const photoNote = ref('')
|
||||||
|
|
||||||
|
const openPhotoDialog = (itemId: number) => {
|
||||||
|
photoDialogItemId.value = itemId
|
||||||
|
photoFiles.value = []
|
||||||
|
photoNote.value = ''
|
||||||
|
showPhotoDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPhotoSelect = (e: Event) => {
|
||||||
|
const target = e.target as HTMLInputElement
|
||||||
|
if (target.files) {
|
||||||
|
for (const f of Array.from(target.files)) {
|
||||||
|
photoFiles.value.push({ name: f.name, url: URL.createObjectURL(f) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const removePhoto = (index: number) => {
|
||||||
|
photoFiles.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmPhotos = () => {
|
||||||
|
const item = checklistItems.value.find((i: any) => i.id === photoDialogItemId.value)
|
||||||
|
if (item) {
|
||||||
|
item.photos = photoFiles.value.length
|
||||||
|
item.completed = true
|
||||||
|
}
|
||||||
|
showPhotoDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFileChange = (e: Event) => {
|
||||||
|
const target = e.target as HTMLInputElement
|
||||||
|
if (target.files && target.files.length > 0) importFile.value = target.files[0]
|
||||||
|
}
|
||||||
|
|
||||||
const selectedModel = ref('GD30')
|
const selectedModel = ref('GD30')
|
||||||
const checklistItems = ref([
|
const checklistItems = ref([
|
||||||
{ id: 1, text: '主板安装及固定', completed: true, photos: 3, needPhoto: true, versionCheck: true, versionMatch: true },
|
{ id: 1, text: '主板安装及固定', completed: true, photos: 3, needPhoto: true, versionCheck: true, versionMatch: true },
|
||||||
|
|
@ -124,8 +170,7 @@ const toggleChecklistItem = (id: number) => {
|
||||||
type="text"
|
type="text"
|
||||||
class="w-full px-3 py-2 border rounded"
|
class="w-full px-3 py-2 border rounded"
|
||||||
:style="{ borderColor: '#D9D9D9', backgroundColor: '#FAFAFA' }"
|
:style="{ borderColor: '#D9D9D9', backgroundColor: '#FAFAFA' }"
|
||||||
value="张工程师"
|
value="张工"
|
||||||
readonly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -167,6 +212,7 @@ const toggleChecklistItem = (id: number) => {
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 rounded flex items-center gap-2"
|
class="px-4 py-2 rounded flex items-center gap-2"
|
||||||
:style="{ border: '1px solid #D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
:style="{ border: '1px solid #D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
||||||
|
@click="showImportDialog = true"
|
||||||
>
|
>
|
||||||
<Upload :size="16" />
|
<Upload :size="16" />
|
||||||
导入
|
导入
|
||||||
|
|
@ -220,9 +266,6 @@ const toggleChecklistItem = (id: number) => {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 border-t" :style="{ borderColor: '#F0F0F0' }">
|
|
||||||
<button class="text-sm" :style="{ color: '#1890FF' }">+ 添加物料</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Assembly Checklist Card -->
|
<!-- Assembly Checklist Card -->
|
||||||
|
|
@ -281,18 +324,20 @@ const toggleChecklistItem = (id: number) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span
|
<button
|
||||||
v-if="item.needPhoto && item.completed && item.photos > 0"
|
v-if="item.needPhoto && item.completed && item.photos > 0"
|
||||||
class="px-3 py-1 rounded text-xs flex items-center gap-1"
|
class="px-3 py-1 rounded text-xs flex items-center gap-1 cursor-pointer"
|
||||||
:style="{ backgroundColor: '#E6F7FF', color: '#1890FF' }"
|
:style="{ backgroundColor: '#E6F7FF', color: '#1890FF' }"
|
||||||
|
@click="openPhotoDialog(item.id)"
|
||||||
>
|
>
|
||||||
<Camera :size="12" />
|
<Camera :size="12" />
|
||||||
已上传 {{ item.photos }}张
|
已上传 {{ item.photos }}张
|
||||||
</span>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="item.needPhoto && !item.completed"
|
v-if="item.needPhoto && !item.completed"
|
||||||
class="px-3 py-1 rounded text-xs flex items-center gap-1"
|
class="px-3 py-1 rounded text-xs flex items-center gap-1"
|
||||||
:style="{ backgroundColor: '#1890FF', color: '#fff' }"
|
:style="{ backgroundColor: '#1890FF', color: '#fff' }"
|
||||||
|
@click="openPhotoDialog(item.id)"
|
||||||
>
|
>
|
||||||
<Camera :size="12" />
|
<Camera :size="12" />
|
||||||
拍照上传
|
拍照上传
|
||||||
|
|
@ -327,5 +372,143 @@ const toggleChecklistItem = (id: number) => {
|
||||||
提交
|
提交
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Photo Upload Dialog -->
|
||||||
|
<div v-if="showPhotoDialog" class="fixed inset-0 z-50 flex items-center justify-center" style="background-color: rgba(0,0,0,0.45)">
|
||||||
|
<div class="bg-white rounded-lg w-[560px] max-h-[80vh] flex flex-col" style="box-shadow: 0 4px 12px rgba(0,0,0,0.15)">
|
||||||
|
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
|
||||||
|
<h3 class="text-lg font-semibold">上传照片</h3>
|
||||||
|
<button @click="showPhotoDialog = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
|
||||||
|
<X :size="18" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 overflow-y-auto flex-1">
|
||||||
|
<!-- Photo grid -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="text-sm font-medium mb-2">照片({{ photoFiles.length }}张)</div>
|
||||||
|
<div class="flex flex-wrap gap-3">
|
||||||
|
<!-- Uploaded photos -->
|
||||||
|
<div v-for="(photo, i) in photoFiles" :key="i" class="relative w-24 h-24 rounded-lg overflow-hidden" style="border: 1px solid #F0F0F0">
|
||||||
|
<img :src="photo.url" :alt="photo.name" class="w-full h-full object-cover" />
|
||||||
|
<button
|
||||||
|
class="absolute top-1 right-1 w-5 h-5 rounded-full flex items-center justify-center"
|
||||||
|
style="background-color: rgba(0,0,0,0.5); color: #fff"
|
||||||
|
@click="removePhoto(i)"
|
||||||
|
>
|
||||||
|
<X :size="12" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Add button -->
|
||||||
|
<div
|
||||||
|
class="w-24 h-24 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-colors hover:border-blue-400"
|
||||||
|
style="border: 2px dashed #D9D9D9; color: rgba(0,0,0,0.45)"
|
||||||
|
@click="($refs.photoInput as HTMLInputElement)?.click()"
|
||||||
|
>
|
||||||
|
<Camera :size="24" />
|
||||||
|
<span class="text-xs mt-1">添加照片</span>
|
||||||
|
</div>
|
||||||
|
<input ref="photoInput" type="file" accept="image/*" multiple class="hidden" @change="onPhotoSelect" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Note -->
|
||||||
|
<div>
|
||||||
|
<div class="text-sm font-medium mb-2">备注</div>
|
||||||
|
<textarea
|
||||||
|
v-model="photoNote"
|
||||||
|
class="w-full px-3 py-2 border rounded text-sm"
|
||||||
|
style="border-color: #D9D9D9; min-height: 80px; resize: vertical"
|
||||||
|
placeholder="输入备注信息(可选)"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end gap-3 p-5 border-t" style="border-color: #F0F0F0">
|
||||||
|
<button class="px-4 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showPhotoDialog = false">取消</button>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 rounded text-white text-sm"
|
||||||
|
:style="{ backgroundColor: photoFiles.length > 0 ? '#1890FF' : '#D9D9D9' }"
|
||||||
|
:disabled="photoFiles.length === 0"
|
||||||
|
@click="confirmPhotos"
|
||||||
|
>确认上传({{ photoFiles.length }}张)</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Import Excel Dialog -->
|
||||||
|
<div v-if="showImportDialog" class="fixed inset-0 z-50 flex items-center justify-center" style="background-color: rgba(0,0,0,0.45)">
|
||||||
|
<div class="bg-white rounded-lg w-[520px]" style="box-shadow: 0 4px 12px rgba(0,0,0,0.15)">
|
||||||
|
<!-- Dialog Header -->
|
||||||
|
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
|
||||||
|
<h3 class="text-lg font-semibold">导入BOM清单</h3>
|
||||||
|
<button @click="showImportDialog = false; importFile = null" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
|
||||||
|
<X :size="18" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dialog Body -->
|
||||||
|
<div class="p-6">
|
||||||
|
<!-- Step 1: Download Template -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<div class="text-sm font-medium mb-2">第一步:下载导入模板</div>
|
||||||
|
<div class="p-4 rounded-lg flex items-center justify-between" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<FileSpreadsheet :size="24" style="color: #52C41A" />
|
||||||
|
<div>
|
||||||
|
<div class="text-sm">BOM导入模板.xlsx</div>
|
||||||
|
<div class="text-xs" style="color: rgba(0,0,0,0.45)">包含物料编码、物料名称、SN号、规格型号等字段</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="px-3 py-1.5 rounded text-sm flex items-center gap-1" style="border: 1px solid #1890FF; color: #1890FF">
|
||||||
|
<Download :size="14" />
|
||||||
|
下载模板
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 2: Upload File -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<div class="text-sm font-medium mb-2">第二步:上传Excel文件</div>
|
||||||
|
<div
|
||||||
|
class="p-8 rounded-lg text-center cursor-pointer transition-colors"
|
||||||
|
:style="{
|
||||||
|
border: importFile ? '2px solid #52C41A' : '2px dashed #D9D9D9',
|
||||||
|
backgroundColor: importFile ? '#F6FFED' : '#FAFAFA',
|
||||||
|
}"
|
||||||
|
@click="($refs.fileInput as HTMLInputElement)?.click()"
|
||||||
|
>
|
||||||
|
<input ref="fileInput" type="file" accept=".xlsx,.xls,.csv" class="hidden" @change="onFileChange" />
|
||||||
|
<template v-if="importFile">
|
||||||
|
<FileSpreadsheet :size="32" style="color: #52C41A; margin: 0 auto 8px" />
|
||||||
|
<div class="text-sm" style="color: #52C41A">{{ importFile.name }}</div>
|
||||||
|
<div class="text-xs mt-1" style="color: rgba(0,0,0,0.45)">点击重新选择文件</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<Upload :size="32" style="color: #D9D9D9; margin: 0 auto 8px" />
|
||||||
|
<div class="text-sm" style="color: rgba(0,0,0,0.65)">点击或拖拽文件到此处上传</div>
|
||||||
|
<div class="text-xs mt-1" style="color: rgba(0,0,0,0.45)">支持 .xlsx、.xls、.csv 格式</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tips -->
|
||||||
|
<div class="p-3 rounded text-xs" style="background-color: #FFFBE6; border: 1px solid #FFE58F; color: #D46B08">
|
||||||
|
提示:请确保Excel文件格式与模板一致,物料编码和SN号为必填字段。导入后可在BOM清单中编辑。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dialog Footer -->
|
||||||
|
<div class="flex items-center justify-end gap-3 p-5 border-t" style="border-color: #F0F0F0">
|
||||||
|
<button class="px-4 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showImportDialog = false; importFile = null">取消</button>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 rounded text-white text-sm"
|
||||||
|
:style="{ backgroundColor: importFile ? '#1890FF' : '#D9D9D9' }"
|
||||||
|
:disabled="!importFile"
|
||||||
|
@click="showImportDialog = false; importFile = null"
|
||||||
|
>确认导入</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,205 +1,155 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Plus, Upload, Download, Info } from 'lucide-vue-next'
|
import { ref, computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { Plus, Download, Info, X } from 'lucide-vue-next'
|
||||||
|
|
||||||
const router = useRouter()
|
const showDrawer = ref(false)
|
||||||
|
const selectedModel = ref('GD-10 Supreme')
|
||||||
|
const expiryType = ref('永久')
|
||||||
|
const expiryDate = ref('')
|
||||||
|
|
||||||
interface License {
|
interface AuthItem { name: string,gd10: boolean, gd20: boolean, gd30: boolean }
|
||||||
sn: string
|
const authItems: AuthItem[] = [
|
||||||
licenseId: string
|
{ name: '1D SP',gd10: true,gd20: true, gd30: true },
|
||||||
status: '已激活' | '已生成' | '待激活'
|
{ name: '2D SP',gd10: true, gd20: true, gd30: true },
|
||||||
generationDate: string
|
{ name: '3D SP',gd10: false, gd20: true, gd30: true },
|
||||||
expiryDate: string
|
{ name: '1D VES',gd10: true, gd20: true, gd30: true },
|
||||||
|
{ name: '2D ERT',gd10: true, gd20: true, gd30: true },
|
||||||
|
{ name: '3D ERT',gd10: false, gd20: true, gd30: true },
|
||||||
|
{ name: '1D IP',gd10: true, gd20: true, gd30: true },
|
||||||
|
{ name: '2D IP' ,gd10: true, gd20: true, gd30: true },
|
||||||
|
{ name: '3D IP',gd10: false, gd20: true, gd30: true },
|
||||||
|
{ name: '跨孔(Cross-Hole)',gd10: false, gd20: true, gd30: true },
|
||||||
|
{ name: '水上(Marine)',gd10: false, gd20: true, gd30: true },
|
||||||
|
]
|
||||||
|
const checkedItems = ref(new Set<string>())
|
||||||
|
|
||||||
|
const availableItems = computed(() =>
|
||||||
|
authItems.map(item => {
|
||||||
|
let preset = false
|
||||||
|
if (selectedModel.value === 'GD-10 Supreme') preset = item.gd10
|
||||||
|
else if (selectedModel.value === 'GD-20 Supreme') preset = item.gd20
|
||||||
|
else preset = item.gd30
|
||||||
|
return { ...item, preset }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const selectedCount = computed(() => checkedItems.value.size)
|
||||||
|
const totalAvailable = computed(() => authItems.length)
|
||||||
|
const toggleItem = (name: string) => { checkedItems.value.has(name) ? checkedItems.value.delete(name) : checkedItems.value.add(name) }
|
||||||
|
const selectAll = () => { authItems.forEach(i => checkedItems.value.add(i.name)) }
|
||||||
|
const clearAll = () => { checkedItems.value.clear() }
|
||||||
|
const openDrawer = () => {
|
||||||
|
checkedItems.value.clear()
|
||||||
|
// Auto-check items that match the selected model
|
||||||
|
authItems.forEach(item => {
|
||||||
|
let preset = false
|
||||||
|
if (selectedModel.value === 'GD-10 Supreme') preset = item.gd10
|
||||||
|
else if (selectedModel.value === 'GD-20 Supreme') preset = item.gd20
|
||||||
|
else preset = item.gd30
|
||||||
|
if (preset) checkedItems.value.add(item.name)
|
||||||
|
})
|
||||||
|
showDrawer.value = true
|
||||||
|
}
|
||||||
|
const onModelChange = () => {
|
||||||
|
checkedItems.value.clear()
|
||||||
|
authItems.forEach(item => {
|
||||||
|
let preset = false
|
||||||
|
if (selectedModel.value === 'GD-10 Supreme') preset = item.gd10
|
||||||
|
else if (selectedModel.value === 'GD-20 Supreme') preset = item.gd20
|
||||||
|
else preset = item.gd30
|
||||||
|
if (preset) checkedItems.value.add(item.name)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface License { licenseId: string; model: string; status: '已发布' | '草稿' | '已停用'; modules: string; expiry: string; createdDate: string }
|
||||||
const licenses: License[] = [
|
const licenses: License[] = [
|
||||||
{
|
{ licenseId: 'LIC-GD10-Supreme-v1.0', model: 'GD-10 Supreme', status: '已发布', modules: '1D SP, 2D SP, 1D VES, 2D ERT, 1D IP, 2D IP', expiry: '永久', createdDate: '2025-02-01' },
|
||||||
sn: 'GD30-2025-000001',
|
{ licenseId: 'LIC-GD20-Supreme-v1.0', model: 'GD-20 Supreme', status: '已发布', modules: '全部模块', expiry: '永久', createdDate: '2025-02-05' },
|
||||||
licenseId: 'LIC-2025-0001',
|
{ licenseId: 'LIC-GD30-Supreme-v1.0', model: 'GD-30 Supreme', status: '草稿', modules: '全部模块(不含水上)', expiry: '1年', createdDate: '2025-02-08' },
|
||||||
status: '已激活',
|
|
||||||
generationDate: '2025-02-01',
|
|
||||||
expiryDate: '2026-02-01',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sn: 'GT20-2025-000045',
|
|
||||||
licenseId: 'LIC-2025-0045',
|
|
||||||
status: '已生成',
|
|
||||||
generationDate: '2025-02-05',
|
|
||||||
expiryDate: '2026-02-05',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sn: 'GTXD-2025-000023',
|
|
||||||
licenseId: 'LIC-2025-0023',
|
|
||||||
status: '待激活',
|
|
||||||
generationDate: '2025-02-08',
|
|
||||||
expiryDate: '2026-02-08',
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
const getStatusStyle = (s: License['status']) => {
|
||||||
const getStatusStyle = (status: License['status']) => {
|
if (s === '已发布') return { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' }
|
||||||
switch (status) {
|
if (s === '草稿') return { backgroundColor: '#FFFBE6', color: '#FAAD14', border: '1px solid #FFE58F' }
|
||||||
case '已激活':
|
return { backgroundColor: '#FAFAFA', color: 'rgba(0,0,0,0.45)', border: '1px solid #D9D9D9' }
|
||||||
return {
|
|
||||||
backgroundColor: '#F6FFED',
|
|
||||||
color: '#52C41A',
|
|
||||||
border: '1px solid #B7EB8F',
|
|
||||||
}
|
|
||||||
case '已生成':
|
|
||||||
return {
|
|
||||||
backgroundColor: '#E6F7FF',
|
|
||||||
color: '#1890FF',
|
|
||||||
border: '1px solid #91D5FF',
|
|
||||||
}
|
|
||||||
case '待激活':
|
|
||||||
return {
|
|
||||||
backgroundColor: '#FFFBE6',
|
|
||||||
color: '#FAAD14',
|
|
||||||
border: '1px solid #FFE58F',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<!-- Page Header -->
|
<!-- Header -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<h2 class="text-2xl font-semibold">授权管理</h2>
|
<h2 class="text-2xl font-semibold">授权管理</h2>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<button
|
<button class="px-4 py-2 rounded flex items-center gap-2" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)">
|
||||||
class="px-4 py-2 rounded flex items-center gap-2"
|
<Download :size="16" /> 导出
|
||||||
:style="{ border: '1px solid #D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
|
||||||
>
|
|
||||||
<Upload :size="16" />
|
|
||||||
批量生成
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button class="px-4 py-2 rounded text-white flex items-center gap-2" style="background-color: #1890FF" @click="openDrawer">
|
||||||
class="px-4 py-2 rounded flex items-center gap-2"
|
<Plus :size="16" /> 选择授权项生成
|
||||||
:style="{ border: '1px solid #D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
|
||||||
>
|
|
||||||
<Download :size="16" />
|
|
||||||
导出
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="px-4 py-2 rounded text-white flex items-center gap-2"
|
|
||||||
:style="{ backgroundColor: '#1890FF' }"
|
|
||||||
@click="router.push('/licenses/generate')"
|
|
||||||
>
|
|
||||||
<Plus :size="16" />
|
|
||||||
选择授权项生成
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm" :style="{ color: 'rgba(0, 0, 0, 0.45)' }">管理设备授权文件</p>
|
<p class="text-sm" style="color: rgba(0,0,0,0.45)">管理设备授权文件</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Info Banner -->
|
<!-- Info Banner -->
|
||||||
<div
|
<div class="mb-6 p-4 rounded-lg flex items-start gap-3" style="background-color: #E6FFFB; border: 1px solid #87E8DE">
|
||||||
class="mb-6 p-4 rounded-lg flex items-start gap-3"
|
<Info :size="20" style="color: #13C2C2; flex-shrink: 0; margin-top: 2px" />
|
||||||
:style="{ backgroundColor: '#E6FFFB', border: '1px solid #87E8DE' }"
|
<div style="color: #006D75">
|
||||||
>
|
|
||||||
<Info :size="20" :style="{ color: '#13C2C2', flexShrink: 0, marginTop: '2px' }" />
|
|
||||||
<div :style="{ color: '#006D75' }">
|
|
||||||
<div class="font-medium mb-1">授权说明</div>
|
<div class="font-medium mb-1">授权说明</div>
|
||||||
<div class="text-sm">
|
<div class="text-sm">授权文件按设备型号管理,每个型号对应一套授权模块配置。点击"选择授权项生成"可按型号勾选功能模块并生成授权文件,设备在APP激活时自动下载对应型号的授权文件。</div>
|
||||||
授权文件按型号关联,点击选择授权项生成可勾选功能模块和有效期。生产装配阶段有配置无授权,授权在出厂阶段生成。
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter Card -->
|
<!-- Filter -->
|
||||||
<div class="bg-white p-6 rounded-lg mb-6" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
|
<div class="bg-white p-6 rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
|
||||||
<div class="grid grid-cols-4 gap-4">
|
<div class="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm mb-2" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.65)">设备型号</label>
|
||||||
设备SN号
|
<select class="w-full px-3 py-2 border rounded" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
</label>
|
<option>全部</option><option>GD-10 Supreme</option><option>GD-20 Supreme</option><option>GD-30 Supreme</option>
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="w-full px-3 py-2 border rounded"
|
|
||||||
:style="{ borderColor: '#D9D9D9' }"
|
|
||||||
placeholder="输入设备SN号搜索"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label class="block text-sm mb-2" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">
|
|
||||||
授权状态
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
class="w-full px-3 py-2 border rounded"
|
|
||||||
:style="{ borderColor: '#D9D9D9', backgroundColor: '#fff' }"
|
|
||||||
>
|
|
||||||
<option>全部</option>
|
|
||||||
<option>已激活</option>
|
|
||||||
<option>已生成</option>
|
|
||||||
<option>待激活</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm mb-2" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.65)">状态</label>
|
||||||
设备型号
|
<select class="w-full px-3 py-2 border rounded" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
</label>
|
<option>全部</option><option>已发布</option><option>草稿</option><option>已停用</option>
|
||||||
<select
|
|
||||||
class="w-full px-3 py-2 border rounded"
|
|
||||||
:style="{ borderColor: '#D9D9D9', backgroundColor: '#fff' }"
|
|
||||||
>
|
|
||||||
<option>全部</option>
|
|
||||||
<option>GD30 高密度电法仪</option>
|
|
||||||
<option>GT20 瞬变电磁仪</option>
|
|
||||||
<option>GM10 大地电磁仪</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-end">
|
<div class="flex items-end">
|
||||||
<button
|
<button class="w-full px-4 py-2 rounded text-white" style="background-color: #1890FF">查询</button>
|
||||||
class="w-full px-4 py-2 rounded text-white"
|
|
||||||
:style="{ backgroundColor: '#1890FF' }"
|
|
||||||
>
|
|
||||||
查询
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- License List -->
|
<!-- License Table -->
|
||||||
<div class="bg-white rounded-lg mb-6" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
|
<div class="bg-white rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="w-full">
|
<table class="w-full">
|
||||||
<thead :style="{ backgroundColor: '#FAFAFA' }">
|
<thead style="background-color: #FAFAFA">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">设备SN号</th>
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">授权文件ID</th>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">授权ID</th>
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">适配型号</th>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">状态</th>
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">授权模块</th>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">生成日期</th>
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">有效期</th>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">到期日期</th>
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">创建日期</th>
|
||||||
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">操作</th>
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">状态</th>
|
||||||
|
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr
|
<tr v-for="(lic, i) in licenses" :key="i" class="border-b" style="border-color: #F0F0F0">
|
||||||
v-for="(license, index) in licenses"
|
<td class="px-6 py-4" style="color: #1890FF">{{ lic.licenseId }}</td>
|
||||||
:key="index"
|
<td class="px-6 py-4">{{ lic.model }}</td>
|
||||||
class="border-b"
|
<td class="px-6 py-4 text-sm" style="color: rgba(0,0,0,0.65); max-width: 200px">{{ lic.modules }}</td>
|
||||||
:style="{ borderColor: '#F0F0F0' }"
|
<td class="px-6 py-4" style="color: rgba(0,0,0,0.65)">{{ lic.expiry }}</td>
|
||||||
>
|
<td class="px-6 py-4" style="color: rgba(0,0,0,0.65)">{{ lic.createdDate }}</td>
|
||||||
<td class="px-6 py-4">{{ license.sn }}</td>
|
<td class="px-6 py-4"><span class="px-2 py-1 rounded text-xs" :style="getStatusStyle(lic.status)">{{ lic.status }}</span></td>
|
||||||
<td class="px-6 py-4" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ license.licenseId }}</td>
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<span
|
|
||||||
class="px-2 py-1 rounded text-xs"
|
|
||||||
:style="getStatusStyle(license.status)"
|
|
||||||
>
|
|
||||||
{{ license.status }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ license.generationDate }}</td>
|
|
||||||
<td class="px-6 py-4" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ license.expiryDate }}</td>
|
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<button class="text-sm" :style="{ color: '#1890FF' }">详情</button>
|
<button class="text-sm" style="color: #1890FF">详情</button>
|
||||||
<button class="text-sm" :style="{ color: '#1890FF' }">下载</button>
|
<button class="text-sm" style="color: #1890FF">下载</button>
|
||||||
<button class="text-sm" :style="{ color: '#1890FF' }">续期</button>
|
<button v-if="lic.status === '草稿'" class="text-sm" style="color: #52C41A">发布</button>
|
||||||
<button class="text-sm" :style="{ color: '#FF4D4F' }">撤销</button>
|
<button v-if="lic.status === '已发布'" class="text-sm" style="color: #FF4D4F">停用</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -209,42 +159,92 @@ const getStatusStyle = (status: License['status']) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
<div class="bg-white p-4 rounded-lg flex items-center justify-between" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
|
<div class="bg-white p-4 rounded-lg flex items-center justify-between" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
|
||||||
<div class="text-sm" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">
|
<div class="text-sm" style="color: rgba(0,0,0,0.65)">显示 1-10 / 共 156 条</div>
|
||||||
显示 1-10 / 共 156 条
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0,0,0,0.45)" disabled>上一页</button>
|
||||||
class="px-3 py-1 rounded border"
|
<button class="px-3 py-1 rounded" style="background-color: #1890FF; color: #fff">1</button>
|
||||||
:style="{ borderColor: '#D9D9D9', color: 'rgba(0, 0, 0, 0.45)' }"
|
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">2</button>
|
||||||
disabled
|
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">3</button>
|
||||||
>
|
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">下一页</button>
|
||||||
上一页
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Authorization Generate Drawer -->
|
||||||
|
<div v-if="showDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showDrawer = false">
|
||||||
|
<div class="bg-white w-[640px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
|
||||||
|
<h3 class="text-lg font-semibold">选择授权项生成</h3>
|
||||||
|
<button @click="showDrawer = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
|
||||||
|
<X :size="18" />
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<div class="flex-1 overflow-y-auto p-6">
|
||||||
|
<!-- Model & Expiry -->
|
||||||
|
<div class="grid grid-cols-2 gap-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">设备型号 <span style="color: #FF4D4F">*</span></label>
|
||||||
|
<select v-model="selectedModel" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff" @change="onModelChange()">
|
||||||
|
<option>GD-10 Supreme</option>
|
||||||
|
<option>GD-20 Supreme</option>
|
||||||
|
<option>GD-30 Supreme</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">授权有效期</label>
|
||||||
|
<select v-model="expiryType" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
|
||||||
|
<option>永久</option><option>1年</option><option>2年</option><option>自定义</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div v-if="expiryType === '自定义'">
|
||||||
|
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">到期日期</label>
|
||||||
|
<input type="date" v-model="expiryDate" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auth Items Table -->
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="text-sm font-medium">功能授权项</span>
|
||||||
|
<span class="text-xs" style="color: rgba(0,0,0,0.45)">已选 {{ selectedCount }} / {{ totalAvailable }} 项</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<button class="text-xs" style="color: #1890FF" @click="selectAll">全选</button>
|
||||||
|
<button class="text-xs" style="color: rgba(0,0,0,0.45)" @click="clearAll">清空</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead style="background-color: #FAFAFA">
|
||||||
|
<tr>
|
||||||
|
<th class="px-3 py-2 text-left" style="width: 36px"></th>
|
||||||
|
<th class="px-3 py-2 text-left">功能授权项名称</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, i) in availableItems" :key="i" class="border-b" style="border-color: #F0F0F0">
|
||||||
|
<td class="px-3 py-2">
|
||||||
|
<input type="checkbox" class="w-3.5 h-3.5" style="accent-color: #1890FF" :checked="checkedItems.has(item.name)" @change="toggleItem(item.name)" />
|
||||||
|
</td>
|
||||||
|
<td class="px-3 py-2">{{ item.name }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="flex items-center justify-end gap-3 p-5 border-t" style="border-color: #F0F0F0">
|
||||||
|
<button class="px-4 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showDrawer = false">取消</button>
|
||||||
<button
|
<button
|
||||||
class="px-3 py-1 rounded"
|
class="px-4 py-2 rounded text-white text-sm"
|
||||||
:style="{ backgroundColor: '#1890FF', color: '#fff' }"
|
:style="{ backgroundColor: selectedCount > 0 ? '#52C41A' : '#D9D9D9' }"
|
||||||
>
|
:disabled="selectedCount === 0"
|
||||||
1
|
@click="showDrawer = false"
|
||||||
</button>
|
>生成授权文件({{ selectedCount }}项)</button>
|
||||||
<button
|
</div>
|
||||||
class="px-3 py-1 rounded border"
|
|
||||||
:style="{ borderColor: '#D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
|
||||||
>
|
|
||||||
2
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="px-3 py-1 rounded border"
|
|
||||||
:style="{ borderColor: '#D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
|
||||||
>
|
|
||||||
3
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="px-3 py-1 rounded border"
|
|
||||||
:style="{ borderColor: '#D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
|
|
||||||
>
|
|
||||||
下一页
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue