enterprise-saa-s-dashboard-.../src/app/pages/DeviceModelManagement.vue

620 lines
27 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { ref } from 'vue'
import {
Info,
Server,
CheckCircle2,
AlertTriangle,
Layers,
Plus,
Trash2,
GripVertical,
X,
} from 'lucide-vue-next'
const showDrawer = ref(false)
const drawerForm = ref({
name: '',
code: '',
authFile: '',
configFile: '',
firmwareVersion: '',
status: '在产' as '在产' | '停产',
})
interface ChecklistItem {
id: number
text: string
type: 'text' | 'bool'
required: boolean
note: string
}
interface ChecklistData {
[key: string]: ChecklistItem[]
}
const activeTab = ref('GD30')
const stats = [
{ label: '型号总数', value: '12', icon: Server, color: '#4a7c59' },
{ label: '在产型号', value: '8', icon: CheckCircle2, color: '#52C41A' },
{ label: '停产型号', value: '3', icon: AlertTriangle, color: '#FAAD14' },
{ label: '关联设备总数', value: '5,234', icon: Layers, color: '#722ED1' },
]
const modelData = [
{
name: 'GD30 高密度电法仪',
code: 'GD30-2024',
authFile: 'auth_gd30_v2.3.lic',
configFile: 'config_gd30_v1.5.json',
firmwareVersion: 'v2.3.5',
deviceCount: 2456,
status: '在产' as const,
},
{
name: 'GT20 瞬变电磁仪',
code: 'GT20-2023',
authFile: 'auth_gt20_v1.8.lic',
configFile: 'config_gt20_v1.2.json',
firmwareVersion: 'v1.8.2',
deviceCount: 1823,
status: '在产' as const,
},
{
name: 'GM10 大地电磁仪',
code: 'GM10-2022',
authFile: 'auth_gm10_v1.5.lic',
configFile: 'config_gm10_v1.0.json',
firmwareVersion: 'v1.5.1',
deviceCount: 955,
status: '停产' as const,
},
]
const checklistData = ref<ChecklistData>({
GD30: [
{ id: 1, text: '主板SN扫码绑定主机', type: 'text', required: true, note: '唯一SN' },
{ id: 2, text: '采集板SN录入', type: 'text', required: true, note: '1/6/12通道' },
{ id: 3, text: '发射板SN录入', type: 'text', required: true, note: '' },
{ id: 4, text: '内置升压模块检查', type: 'bool', required: true, note: '' },
{ id: 5, text: 'GPS/北斗检测', type: 'bool', required: true, note: '授时正常' },
{ id: 6, text: '电池安装与容量检测', type: 'bool', required: true, note: '' },
{ id: 7, text: '输入电压12~48V测试', type: 'bool', required: true, note: '' },
{ id: 8, text: '接收电压精度校验', type: 'bool', required: true, note: '按型号量程' },
{ id: 9, text: '自电补偿±10V', type: 'bool', required: true, note: '' },
{ id: 10, text: '输入阻抗≥100MΩ', type: 'bool', required: true, note: '' },
{ id: 11, text: '恒压/恒流模式', type: 'bool', required: true, note: '' },
{ id: 12, text: '最大发射电流达标', type: 'bool', required: true, note: '6A/10A/10A' },
{ id: 13, text: '脉冲宽度配置', type: 'bool', required: true, note: '' },
{ id: 14, text: '调级输出电压', type: 'bool', required: true, note: '100~600V' },
{ id: 15, text: '系统启动正常', type: 'bool', required: true, note: '' },
{ id: 16, text: '采集APP连接', type: 'bool', required: true, note: '' },
{ id: 17, text: 'Geometa账号配置', type: 'bool', required: true, note: '' },
{ id: 18, text: '授权文件校验', type: 'bool', required: true, note: '' },
{ id: 19, text: 'USB/WiFi/网口/SD', type: 'bool', required: true, note: '' },
{ id: 20, text: 'IP66防护与密封', type: 'bool', required: true, note: '' },
{ id: 21, text: '过流/过压/短路保护', type: 'bool', required: true, note: '' },
{ id: 22, text: '出厂装箱核对', type: 'bool', required: true, note: '' },
],
GT20: [
{ id: 1, text: '主板SN扫码绑定主机', type: 'text', required: true, note: '唯一SN' },
{ id: 2, text: '采集板SN录入', type: 'text', required: true, note: '' },
{ id: 3, text: 'GPS/北斗检测', type: 'bool', required: true, note: '授时正常' },
{ id: 4, text: '系统启动正常', type: 'bool', required: true, note: '' },
{ id: 5, text: '整体功能测试', type: 'bool', required: true, note: '' },
],
GM10: [
{ id: 1, text: '主板SN扫码绑定主机', type: 'text', required: true, note: '唯一SN' },
{ id: 2, text: '传感器模块连接', type: 'bool', required: true, note: '' },
{ id: 3, text: '接口板安装', type: 'bool', required: true, note: '' },
{ id: 4, text: '线缆整理', type: 'bool', required: true, note: '' },
{ id: 5, text: '系统初始化检测', type: 'bool', required: true, note: '' },
],
})
const editingCell = ref<{ model: string; id: number; field: string } | null>(null)
const updateChecklistItem = (model: string, id: number, field: keyof ChecklistItem, value: string | boolean) => {
checklistData.value = {
...checklistData.value,
[model]: checklistData.value[model].map((item: ChecklistItem) =>
item.id === id ? { ...item, [field]: value } : item
),
}
}
const addChecklistItem = (model: string) => {
const items = checklistData.value[model]
const newId = items.length > 0 ? Math.max(...items.map((i: ChecklistItem) => i.id)) + 1 : 1
checklistData.value = {
...checklistData.value,
[model]: [...items, { id: newId, text: '新检查项', type: 'bool' as const, required: true, note: '' }],
}
}
const deleteChecklistItem = (model: string, id: number) => {
checklistData.value = {
...checklistData.value,
[model]: checklistData.value[model].filter((item: ChecklistItem) => item.id !== id).map((item: ChecklistItem, i: number) => ({ ...item, id: i + 1 })),
}
}
const handleEditBlur = (e: Event, model: string, id: number) => {
updateChecklistItem(model, id, 'text', (e.target as HTMLInputElement).value)
editingCell.value = null
}
const handleEditKeydown = (e: KeyboardEvent) => {
if (e.key === 'Enter') (e.target as HTMLInputElement).blur()
}
const boardVersionData = [
{
boardType: '主板',
requiredVersion: 'v2.3.x',
validationRule: '版本号前缀必须为 v2.3',
status: 'active',
},
{
boardType: '采集板',
requiredVersion: 'v1.8.x',
validationRule: '版本号前缀必须为 v1.8',
status: 'active',
},
{
boardType: '发射板',
requiredVersion: 'v1.5.x',
validationRule: '版本号前缀必须为 v1.5',
status: 'active',
},
]
// Checklist template drawer state
const showChecklistDrawer = ref(false)
const newTplModel = ref('GD30')
const newTplItems = ref<{ text: string }[]>([{ text: '' }])
const openChecklistDrawer = () => {
newTplModel.value = 'GD30'
newTplItems.value = [{ text: '' }]
showChecklistDrawer.value = true
}
const addTplItem = () => {
newTplItems.value.push({ text: '' })
}
const removeTplItem = (index: number) => {
newTplItems.value.splice(index, 1)
}
</script>
<template>
<div class="p-6">
<!-- Page Header -->
<div class="mb-6">
<h2 class="text-2xl font-semibold mb-1">设备型号管理</h2>
<p class="text-sm" :style="{ color: 'rgba(0, 0, 0, 0.45)' }">管理设备型号及相关配置</p>
</div>
<!-- Info Banner -->
<div
class="mb-6 p-4 rounded-lg flex items-start gap-3"
:style="{ backgroundColor: '#eef5f0', border: '1px solid #a3c4ad' }"
>
<Info :size="20" :style="{ color: '#4a7c59', flexShrink: 0, marginTop: '2px' }" />
<div :style="{ color: '#2d5a3d' }">
型号管理是平台核心枢纽每个型号关联授权文件配置文件和固件版本
</div>
</div>
<!-- Stat Cards -->
<div class="grid grid-cols-4 gap-6 mb-6">
<div
v-for="(stat, index) in stats"
:key="index"
class="bg-white p-6 rounded-lg"
:style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }"
>
<div class="flex items-center justify-between">
<div>
<div class="text-sm mb-2" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ stat.label }}</div>
<div class="text-3xl font-semibold">{{ stat.value }}</div>
</div>
<div
class="w-12 h-12 rounded-lg flex items-center justify-center"
:style="{ backgroundColor: `${stat.color}15` }"
>
<component :is="stat.icon" :size="24" :style="{ color: stat.color }" />
</div>
</div>
</div>
</div>
<!-- Model Table -->
<div class="bg-white rounded-lg mb-6" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
<div class="p-6 border-b" :style="{ borderColor: '#F0F0F0' }">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold">型号列表</h3>
<button
class="px-4 py-2 rounded text-white flex items-center gap-2"
:style="{ backgroundColor: '#4a7c59' }"
@click="showDrawer = true"
>
<Plus :size="16" />
新增型号
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead :style="{ backgroundColor: '#FAFAFA' }">
<tr>
<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>
</thead>
<tbody>
<tr
v-for="(model, index) in modelData"
:key="index"
class="border-b"
:style="{ borderColor: '#F0F0F0' }"
>
<td class="px-6 py-4">{{ model.name }}</td>
<td class="px-6 py-4" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ model.code }}</td>
<td class="px-6 py-4">
<router-link to="/licenses" class="text-sm" style="color: #4a7c59">{{ model.authFile }}</router-link>
</td>
<td class="px-6 py-4">
<router-link to="/config-files" class="text-sm" style="color: #4a7c59">{{ model.configFile }}</router-link>
</td>
<td class="px-6 py-4">
<router-link to="/firmware" class="text-sm" style="color: #4a7c59">{{ model.firmwareVersion }}</router-link>
</td>
<td class="px-6 py-4">{{ model.deviceCount.toLocaleString() }}</td>
<td class="px-6 py-4">
<span
class="px-2 py-1 rounded text-xs"
:style="{
backgroundColor: model.status === '在产' ? '#F6FFED' : '#FFFBE6',
color: model.status === '在产' ? '#52C41A' : '#FAAD14',
border: `1px solid ${model.status === '在产' ? '#B7EB8F' : '#FFE58F'}`,
}"
>
{{ model.status }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<button class="text-sm" :style="{ color: '#4a7c59' }">编辑</button>
<router-link to="/licenses" class="text-sm" style="color: #4a7c59">授权</router-link>
<router-link to="/config-files" class="text-sm" style="color: #4a7c59">配置</router-link>
<router-link to="/firmware" class="text-sm" style="color: #4a7c59">固件</router-link>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Assembly Checklist -->
<div class="bg-white rounded-lg mb-6" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
<div class="p-6 border-b" :style="{ borderColor: '#F0F0F0' }">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold">装配Checklist模板</h3>
<button
class="px-4 py-2 rounded text-white flex items-center gap-2"
:style="{ backgroundColor: '#4a7c59' }"
@click="openChecklistDrawer"
>
<Plus :size="16" />
新增模版
</button>
</div>
</div>
<!-- Tabs -->
<div class="flex border-b" :style="{ borderColor: '#F0F0F0' }">
<button
v-for="model in Object.keys(checklistData)"
:key="model"
@click="activeTab = model"
class="px-6 py-3 text-sm font-medium transition-colors"
:style="{
color: activeTab === model ? '#4a7c59' : 'rgba(0, 0, 0, 0.65)',
borderBottom: activeTab === model ? '2px solid #4a7c59' : 'none',
marginBottom: activeTab === model ? '-1px' : '0',
}"
>
{{ model }}
</button>
</div>
<!-- Checklist Content -->
<div class="p-6">
<table class="w-full">
<thead :style="{ backgroundColor: '#FAFAFA' }">
<tr>
<th class="px-3 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)', width: '50px' }">序号</th>
<th class="px-3 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">项目名称</th>
</tr>
</thead>
<tbody>
<tr
v-for="item in checklistData[activeTab]"
:key="item.id"
class="border-b"
:style="{ borderColor: '#F0F0F0' }"
>
<td class="px-3 py-3 text-sm" :style="{ color: 'rgba(0, 0, 0, 0.45)' }">
<div class="flex items-center gap-1">
<GripVertical :size="14" :style="{ color: '#D9D9D9' }" />
{{ item.id }}
</div>
</td>
<td class="px-3 py-3 text-sm">
<input
v-if="editingCell?.model === activeTab && editingCell?.id === item.id && editingCell?.field === 'text'"
autofocus
class="w-full px-2 py-1 border rounded text-sm"
:style="{ borderColor: '#4a7c59' }"
:value="item.text"
@blur="handleEditBlur($event, activeTab, item.id)"
@keydown="handleEditKeydown"
/>
<span
v-else
class="cursor-pointer hover:text-blue-500"
@click="editingCell = { model: activeTab, id: item.id, field: 'text' }"
>
{{ item.text }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- New Model 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-[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>
<!-- Drawer Body -->
<div class="flex-1 overflow-y-auto p-6">
<div class="space-y-5">
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">型号名称 <span style="color: #FF4D4F">*</span></label>
<input v-model="drawerForm.name" type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如GD30 高密度电法仪" />
</div>
<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="drawerForm.code" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择编码</option>
<option>GD10-2025</option>
<option>GD20-2025</option>
<option>GD30-2025</option>
<option>GT20-2025</option>
<option>GM10-2025</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定授权文件</label>
<select v-model="drawerForm.authFile" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择授权文件</option>
<option>auth_gd10_v1.0.lic</option>
<option>auth_gd20_v2.0.lic</option>
<option>auth_gd30_v2.3.lic</option>
<option>auth_gt20_v1.8.lic</option>
<option>auth_gm10_v1.5.lic</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.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: #4a7c59" />
<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: #4a7c59" />
<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 ? '#4a7c59' : '#D9D9D9' }"
:disabled="!drawerForm.name || !drawerForm.code"
@click="showDrawer = false"
>确认创建</button>
</div>
</div>
</div>
<!-- Checklist Template Drawer -->
<div v-if="showChecklistDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showChecklistDrawer = false">
<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">新增Checklist模版</h3>
<button @click="showChecklistDrawer = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
<X :size="18" />
</button>
</div>
<!-- Drawer Body -->
<div class="flex-1 overflow-y-auto p-6">
<div class="space-y-5">
<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="newTplModel" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="GD30">GD30</option>
<option value="GT20">GT20</option>
<option value="GM10">GM10</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">检查项列表</label>
<div class="space-y-3">
<div
v-for="(item, index) in newTplItems"
:key="index"
class="flex items-center gap-2"
>
<span class="text-sm w-6 text-center flex-shrink-0" style="color: rgba(0,0,0,0.45)">{{ index + 1 }}</span>
<input
v-model="item.text"
type="text"
class="flex-1 px-3 py-2 border rounded text-sm"
style="border-color: #D9D9D9"
placeholder="请输入检查项名称"
/>
<button
@click="removeTplItem(index)"
class="p-1 rounded hover:bg-red-50 text-gray-400 hover:text-red-500 transition-colors flex-shrink-0"
:disabled="newTplItems.length <= 1"
:style="{ opacity: newTplItems.length <= 1 ? 0.3 : 1 }"
>
<Trash2 :size="15" />
</button>
</div>
</div>
<button
@click="addTplItem"
class="mt-3 px-4 py-2 text-sm flex items-center gap-1 rounded transition-colors w-full justify-center"
:style="{ color: '#4a7c59', border: '1px dashed #4a7c59' }"
>
<Plus :size="14" />
添加检查项
</button>
</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="showChecklistDrawer = false">取消</button>
<button
class="px-4 py-2 rounded text-white text-sm"
:style="{ backgroundColor: '#4a7c59' }"
@click="showChecklistDrawer = false"
>确认生成</button>
</div>
</div>
</div>
</div>
</template>