- 删除应用配置页面及相关组件,重构路由为 /developer/config/[id].vue - 移除开发者文档页面及其导航与样式实现 - 清理开发者侧功能完善工作日志文件 - 删除全局.gitignore配置文件,清理无用忽略规则 - 优化应用配置页面的参数读取和路由结构,解决刷新404问题 - 解决数据库配置唯一键冲突,调整保存逻辑避免重复插入 - 移除对后端配置加密字段的 secret 标记,修正加密异常问题
618 lines
18 KiB
Vue
618 lines
18 KiB
Vue
<script setup lang="ts">
|
||
definePageMeta({ layout: 'admin' })
|
||
|
||
// 设备统计数据
|
||
const equipmentStats = ref([
|
||
{ label: '设备总数', value: 128, icon: 'fa-cogs', color: 'blue', gradient: 'from-blue-500 to-purple-500', change: '+12%', up: true },
|
||
{ label: '运行中', value: 96, icon: 'fa-play-circle', color: 'green', gradient: 'from-green-500 to-teal-500', change: '+8%', up: true },
|
||
{ label: '待机中', value: 24, icon: 'fa-pause-circle', color: 'orange', gradient: 'from-orange-500 to-yellow-500', change: '-5%', up: false },
|
||
{ label: '故障告警', value: 8, icon: 'fa-exclamation-triangle', color: 'red', gradient: 'from-red-500 to-pink-500', change: '+2', up: false },
|
||
])
|
||
|
||
// 设备列表
|
||
const equipmentList = ref([
|
||
{ id: 'EQ-001', name: 'CNC加工中心 01', model: 'CNC-850', location: '1号车间 A区', status: 'running', utilization: 92, temp: 45, vibration: 0.8, lastMaintenance: '2026-04-01', nextMaintenance: '2026-05-01' },
|
||
{ id: 'EQ-002', name: 'CNC加工中心 02', model: 'CNC-850', location: '1号车间 A区', status: 'running', utilization: 88, temp: 43, vibration: 0.7, lastMaintenance: '2026-04-02', nextMaintenance: '2026-05-02' },
|
||
{ id: 'EQ-003', name: 'CNC加工中心 03', model: 'CNC-650', location: '1号车间 B区', status: 'warning', utilization: 0, temp: 78, vibration: 2.5, lastMaintenance: '2026-03-15', nextMaintenance: '2026-04-15' },
|
||
{ id: 'EQ-004', name: '数控铣床 01', model: 'XK-500', location: '2号车间', status: 'idle', utilization: 0, temp: 28, vibration: 0.3, lastMaintenance: '2026-03-28', nextMaintenance: '2026-04-28' },
|
||
{ id: 'EQ-005', name: '数控铣床 02', model: 'XK-500', location: '2号车间', status: 'running', utilization: 75, temp: 38, vibration: 0.6, lastMaintenance: '2026-04-03', nextMaintenance: '2026-05-03' },
|
||
{ id: 'EQ-006', name: '冲压机 01', model: 'CP-200T', location: '3号车间', status: 'running', utilization: 85, temp: 42, vibration: 1.2, lastMaintenance: '2026-03-20', nextMaintenance: '2026-04-20' },
|
||
{ id: 'EQ-007', name: '激光切割机 01', model: 'JC-3000', location: '3号车间', status: 'idle', utilization: 0, temp: 25, vibration: 0.2, lastMaintenance: '2026-04-05', nextMaintenance: '2026-05-05' },
|
||
{ id: 'EQ-008', name: '空压机 01', model: 'KY-50', location: '动力站房', status: 'running', utilization: 68, temp: 55, vibration: 1.5, lastMaintenance: '2026-03-10', nextMaintenance: '2026-05-10' },
|
||
])
|
||
|
||
// 筛选状态
|
||
const statusFilter = ref('all')
|
||
const searchKeyword = ref('')
|
||
|
||
// 筛选后的列表
|
||
const filteredList = computed(() => {
|
||
return equipmentList.value.filter((item) => {
|
||
const matchStatus = statusFilter.value === 'all' || item.status === statusFilter.value
|
||
const matchSearch = !searchKeyword.value ||
|
||
item.name.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
|
||
item.id.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||
return matchStatus && matchSearch
|
||
})
|
||
})
|
||
|
||
const statusMap: Record<string, { label: string; color: string; bg: string }> = {
|
||
running: { label: '运行中', color: 'text-green-600', bg: 'bg-green-100' },
|
||
idle: { label: '待机', color: 'text-orange-600', bg: 'bg-orange-100' },
|
||
warning: { label: '告警', color: 'text-red-600', bg: 'bg-red-100' },
|
||
offline: { label: '离线', color: 'text-gray-600', bg: 'bg-gray-100' },
|
||
}
|
||
|
||
// 设备详情
|
||
const selectedEquipment = ref<typeof equipmentList.value[0] | null>(null)
|
||
const detailVisible = ref(false)
|
||
|
||
function showDetail(item: typeof equipmentList.value[0]) {
|
||
selectedEquipment.value = item
|
||
detailVisible.value = true
|
||
}
|
||
|
||
// 维保记录
|
||
const maintenanceRecords = ref([
|
||
{ date: '2026-04-01', type: '例行保养', technician: '李师傅', cost: 500, status: '完成' },
|
||
{ date: '2026-03-01', type: '例行保养', technician: '李师傅', cost: 480, status: '完成' },
|
||
{ date: '2026-02-01', type: '更换配件', technician: '王师傅', cost: 1200, status: '完成' },
|
||
])
|
||
|
||
// 设备效率趋势(模拟数据)
|
||
const efficiencyTrend = ref([85, 88, 82, 90, 92, 88, 91, 89, 93, 92])
|
||
const days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日', '周一', '周二', '今天']
|
||
|
||
// 添加设备表单
|
||
const addFormVisible = ref(false)
|
||
const addForm = reactive({
|
||
name: '',
|
||
model: '',
|
||
location: '',
|
||
supplier: '',
|
||
purchaseDate: '',
|
||
warrantyEnd: '',
|
||
})
|
||
|
||
function handleAdd() {
|
||
// 模拟添加
|
||
addFormVisible.value = false
|
||
message.success('设备添加成功')
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="equipment-page">
|
||
<!-- 页面标题 -->
|
||
<div class="page-header mb-6">
|
||
<div>
|
||
<h2 class="text-2xl font-bold text-gray-800">设备管理</h2>
|
||
<p class="text-gray-500 mt-1">实时监控设备状态,优化生产效率</p>
|
||
</div>
|
||
<a-button type="primary" @click="addFormVisible = true">
|
||
<template #icon><i class="fas fa-plus mr-1"></i></template>
|
||
添加设备
|
||
</a-button>
|
||
</div>
|
||
|
||
<!-- 统计卡片 -->
|
||
<div class="grid grid-cols-4 gap-6 mb-6">
|
||
<div
|
||
v-for="stat in equipmentStats"
|
||
:key="stat.label"
|
||
class="glass rounded-2xl p-6 card-hover cursor-pointer"
|
||
>
|
||
<div class="flex items-center justify-between mb-4">
|
||
<div
|
||
class="w-12 h-12 rounded-xl flex items-center justify-center text-white"
|
||
:class="`bg-gradient-to-br ${stat.gradient}`"
|
||
>
|
||
<i :class="`fas ${stat.icon}`"></i>
|
||
</div>
|
||
<span
|
||
class="text-sm font-medium"
|
||
:class="stat.up ? 'text-green-500' : 'text-red-500'"
|
||
>
|
||
<i :class="stat.up ? 'fas fa-arrow-up' : 'fas fa-arrow-down'"></i>
|
||
{{ stat.change }}
|
||
</span>
|
||
</div>
|
||
<h3 class="text-3xl font-bold text-gray-800 mb-1">{{ stat.value }}</h3>
|
||
<p class="text-gray-500 text-sm">{{ stat.label }}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 筛选栏 -->
|
||
<div class="glass rounded-2xl p-4 mb-6">
|
||
<div class="flex items-center justify-between gap-4">
|
||
<div class="flex items-center gap-3">
|
||
<span class="text-gray-600 font-medium">状态筛选:</span>
|
||
<a-radio-group v-model:value="statusFilter" button-style="solid">
|
||
<a-radio-button value="all">全部</a-radio-button>
|
||
<a-radio-button value="running">运行中</a-radio-button>
|
||
<a-radio-button value="idle">待机</a-radio-button>
|
||
<a-radio-button value="warning">告警</a-radio-button>
|
||
</a-radio-group>
|
||
</div>
|
||
<a-input-search
|
||
v-model:value="searchKeyword"
|
||
placeholder="搜索设备名称或编号..."
|
||
style="width: 280px"
|
||
allow-clear
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 设备列表 -->
|
||
<div class="glass rounded-2xl p-6">
|
||
<div class="flex items-center justify-between mb-4">
|
||
<h3 class="font-bold text-lg text-gray-800">
|
||
<i class="fas fa-list text-purple-500 mr-2"></i>
|
||
设备列表
|
||
</h3>
|
||
<span class="text-sm text-gray-500">共 {{ filteredList.length }} 台设备</span>
|
||
</div>
|
||
|
||
<a-table
|
||
:dataSource="filteredList"
|
||
:pagination="{ pageSize: 10 }"
|
||
rowKey="id"
|
||
:scroll="{ x: 1200 }"
|
||
>
|
||
<a-table-column title="设备编号" dataIndex="id" width="100" />
|
||
<a-table-column title="设备名称" dataIndex="name" width="180">
|
||
<template #default="{ record }">
|
||
<div class="flex items-center gap-2">
|
||
<div
|
||
class="w-8 h-8 rounded-lg flex items-center justify-center text-white text-xs"
|
||
:class="statusMap[record.status]?.bg.replace('100', '500') || 'bg-gray-500'"
|
||
>
|
||
<i class="fas fa-cog"></i>
|
||
</div>
|
||
<span class="font-medium">{{ record.name }}</span>
|
||
</div>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="型号" dataIndex="model" width="100" />
|
||
<a-table-column title="位置" dataIndex="location" width="120" />
|
||
<a-table-column title="状态" dataIndex="status" width="100" align="center">
|
||
<template #default="{ text }">
|
||
<span
|
||
:class="statusMap[text]?.color"
|
||
class="px-2 py-1 rounded-lg text-sm font-medium"
|
||
:class="statusMap[text]?.bg"
|
||
>
|
||
{{ statusMap[text]?.label }}
|
||
</span>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="利用率" dataIndex="utilization" width="140">
|
||
<template #default="{ record }">
|
||
<div class="flex items-center gap-2">
|
||
<a-progress
|
||
:percent="record.utilization"
|
||
:status="record.utilization > 80 ? 'success' : 'active'"
|
||
:showInfo="false"
|
||
size="small"
|
||
:stroke-color="record.utilization > 80 ? '#10b981' : '#6366f1'"
|
||
style="width: 80px"
|
||
/>
|
||
<span class="text-sm text-gray-600">{{ record.utilization }}%</span>
|
||
</div>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="温度(°C)" dataIndex="temp" width="100" align="center">
|
||
<template #default="{ text, record }">
|
||
<span :class="text > 60 ? 'text-red-500 font-medium' : 'text-gray-600'">
|
||
{{ text }}
|
||
</span>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="振动(mm/s)" dataIndex="vibration" width="110" align="center">
|
||
<template #default="{ text }">
|
||
<span :class="text > 2 ? 'text-red-500 font-medium' : 'text-gray-600'">
|
||
{{ text }}
|
||
</span>
|
||
</template>
|
||
</a-table-column>
|
||
<a-table-column title="下次保养" dataIndex="nextMaintenance" width="120" />
|
||
<a-table-column title="操作" width="120" align="center" fixed="right">
|
||
<template #default="{ record }">
|
||
<div class="flex items-center gap-2 justify-center">
|
||
<a-button type="link" size="small" @click="showDetail(record)">
|
||
<i class="fas fa-eye"></i>
|
||
</a-button>
|
||
<a-button type="link" size="small">
|
||
<i class="fas fa-edit"></i>
|
||
</a-button>
|
||
</div>
|
||
</template>
|
||
</a-table-column>
|
||
</a-table>
|
||
</div>
|
||
|
||
<!-- 设备详情抽屉 -->
|
||
<a-drawer
|
||
v-model:open="detailVisible"
|
||
:title="selectedEquipment?.name"
|
||
width="500"
|
||
placement="right"
|
||
>
|
||
<template v-if="selectedEquipment">
|
||
<!-- 基本信息 -->
|
||
<div class="mb-6">
|
||
<h4 class="font-bold text-gray-800 mb-3 flex items-center gap-2">
|
||
<i class="fas fa-info-circle text-blue-500"></i>
|
||
基本信息
|
||
</h4>
|
||
<a-descriptions :column="2" bordered size="small">
|
||
<a-descriptions-item label="设备编号">{{ selectedEquipment.id }}</a-descriptions-item>
|
||
<a-descriptions-item label="设备型号">{{ selectedEquipment.model }}</a-descriptions-item>
|
||
<a-descriptions-item label="安装位置">{{ selectedEquipment.location }}</a-descriptions-item>
|
||
<a-descriptions-item label="当前状态">
|
||
<span
|
||
:class="statusMap[selectedEquipment.status]?.color"
|
||
class="px-2 py-0.5 rounded text-sm font-medium"
|
||
:class="statusMap[selectedEquipment.status]?.bg"
|
||
>
|
||
{{ statusMap[selectedEquipment.status]?.label }}
|
||
</span>
|
||
</a-descriptions-item>
|
||
</a-descriptions>
|
||
</div>
|
||
|
||
<!-- 实时监控 -->
|
||
<div class="mb-6">
|
||
<h4 class="font-bold text-gray-800 mb-3 flex items-center gap-2">
|
||
<i class="fas fa-chart-line text-green-500"></i>
|
||
实时监控
|
||
</h4>
|
||
<div class="grid grid-cols-3 gap-3">
|
||
<div class="bg-gray-50 rounded-xl p-4 text-center">
|
||
<div class="text-2xl font-bold text-blue-600">{{ selectedEquipment.utilization }}%</div>
|
||
<div class="text-xs text-gray-500 mt-1">设备利用率</div>
|
||
</div>
|
||
<div class="bg-gray-50 rounded-xl p-4 text-center">
|
||
<div
|
||
class="text-2xl font-bold"
|
||
:class="selectedEquipment.temp > 60 ? 'text-red-600' : 'text-orange-600'"
|
||
>
|
||
{{ selectedEquipment.temp }}°C
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">当前温度</div>
|
||
</div>
|
||
<div class="bg-gray-50 rounded-xl p-4 text-center">
|
||
<div
|
||
class="text-2xl font-bold"
|
||
:class="selectedEquipment.vibration > 2 ? 'text-red-600' : 'text-purple-600'"
|
||
>
|
||
{{ selectedEquipment.vibration }}
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">振动值</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 效率趋势图 -->
|
||
<div class="mb-6">
|
||
<h4 class="font-bold text-gray-800 mb-3 flex items-center gap-2">
|
||
<i class="fas fa-chart-area text-purple-500"></i>
|
||
效率趋势
|
||
</h4>
|
||
<div class="h-32 flex items-end gap-2">
|
||
<div
|
||
v-for="(value, idx) in efficiencyTrend"
|
||
:key="idx"
|
||
class="flex-1 flex flex-col items-center"
|
||
>
|
||
<div
|
||
class="w-full bg-gradient-to-t from-purple-500 to-purple-300 rounded-t-lg transition-all hover:opacity-80"
|
||
:style="{ height: value + '%' }"
|
||
></div>
|
||
<span class="text-xs text-gray-400 mt-1">{{ days[idx] }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 维保记录 -->
|
||
<div>
|
||
<h4 class="font-bold text-gray-800 mb-3 flex items-center gap-2">
|
||
<i class="fas fa-wrench text-orange-500"></i>
|
||
维保记录
|
||
</h4>
|
||
<a-timeline>
|
||
<a-timeline-item
|
||
v-for="record in maintenanceRecords"
|
||
:key="record.date"
|
||
:color="record.status === '完成' ? 'green' : 'gray'"
|
||
>
|
||
<div class="flex justify-between">
|
||
<span class="font-medium">{{ record.type }}</span>
|
||
<span class="text-gray-500 text-sm">{{ record.date }}</span>
|
||
</div>
|
||
<div class="text-sm text-gray-500">
|
||
技师:{{ record.technician }} | 费用:¥{{ record.cost }}
|
||
</div>
|
||
</a-timeline-item>
|
||
</a-timeline>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="mt-6 flex gap-3">
|
||
<a-button type="primary" block>发起保养</a-button>
|
||
<a-button block>导出报表</a-button>
|
||
</div>
|
||
</template>
|
||
</a-drawer>
|
||
|
||
<!-- 添加设备弹窗 -->
|
||
<a-modal
|
||
v-model:open="addFormVisible"
|
||
title="添加新设备"
|
||
@ok="handleAdd"
|
||
ok-text="确认添加"
|
||
cancel-text="取消"
|
||
>
|
||
<a-form :model="addForm" layout="vertical">
|
||
<a-form-item label="设备名称" required>
|
||
<a-input v-model:value="addForm.name" placeholder="请输入设备名称" />
|
||
</a-form-item>
|
||
<a-form-item label="设备型号" required>
|
||
<a-input v-model:value="addForm.model" placeholder="请输入设备型号" />
|
||
</a-form-item>
|
||
<a-form-item label="安装位置" required>
|
||
<a-input v-model:value="addForm.location" placeholder="请输入安装位置" />
|
||
</a-form-item>
|
||
<a-form-item label="供应商">
|
||
<a-input v-model:value="addForm.supplier" placeholder="请输入供应商" />
|
||
</a-form-item>
|
||
<a-form-item label="采购日期">
|
||
<a-date-picker v-model:value="addForm.purchaseDate" style="width: 100%" />
|
||
</a-form-item>
|
||
<a-form-item label="保修截止日期">
|
||
<a-date-picker v-model:value="addForm.warrantyEnd" style="width: 100%" />
|
||
</a-form-item>
|
||
</a-form>
|
||
</a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.equipment-page {
|
||
padding: 0;
|
||
}
|
||
|
||
.page-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.glass {
|
||
background: rgba(255, 255, 255, 0.85);
|
||
backdrop-filter: blur(20px);
|
||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.card-hover {
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.card-hover:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.grid {
|
||
display: grid;
|
||
}
|
||
|
||
.grid-cols-4 {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
}
|
||
|
||
@media (max-width: 1200px) {
|
||
.grid-cols-4 {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.grid-cols-4 {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.mb-6 {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.mb-4 {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.mb-3 {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.mt-1 {
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.mr-2 {
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.mr-1 {
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.mt-6 {
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.text-2xl {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.text-3xl {
|
||
font-size: 30px;
|
||
}
|
||
|
||
.text-lg {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.text-sm {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.text-xs {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.rounded-2xl {
|
||
border-radius: 16px;
|
||
}
|
||
|
||
.p-6 {
|
||
padding: 24px;
|
||
}
|
||
|
||
.p-4 {
|
||
padding: 16px;
|
||
}
|
||
|
||
.gap-6 {
|
||
gap: 24px;
|
||
}
|
||
|
||
.gap-4 {
|
||
gap: 16px;
|
||
}
|
||
|
||
.gap-3 {
|
||
gap: 12px;
|
||
}
|
||
|
||
.gap-2 {
|
||
gap: 8px;
|
||
}
|
||
|
||
.flex {
|
||
display: flex;
|
||
}
|
||
|
||
.grid {
|
||
display: grid;
|
||
}
|
||
|
||
.items-center {
|
||
align-items: center;
|
||
}
|
||
|
||
.justify-between {
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.text-gray-800 {
|
||
color: #1f2937;
|
||
}
|
||
|
||
.text-gray-600 {
|
||
color: #4b5563;
|
||
}
|
||
|
||
.text-gray-500 {
|
||
color: #6b7280;
|
||
}
|
||
|
||
.text-gray-400 {
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.text-blue-600 {
|
||
color: #2563eb;
|
||
}
|
||
|
||
.text-green-600 {
|
||
color: #16a34a;
|
||
}
|
||
|
||
.text-orange-600 {
|
||
color: #ea580c;
|
||
}
|
||
|
||
.text-red-600 {
|
||
color: #dc2626;
|
||
}
|
||
|
||
.text-purple-600 {
|
||
color: #9333ea;
|
||
}
|
||
|
||
.bg-gray-50 {
|
||
background-color: #f9fafb;
|
||
}
|
||
|
||
.bg-green-100 {
|
||
background-color: #dcfce7;
|
||
}
|
||
|
||
.bg-orange-100 {
|
||
background-color: #ffedd5;
|
||
}
|
||
|
||
.bg-red-100 {
|
||
background-color: #fee2e2;
|
||
}
|
||
|
||
.font-bold {
|
||
font-weight: 700;
|
||
}
|
||
|
||
.font-medium {
|
||
font-weight: 500;
|
||
}
|
||
|
||
.from-blue-500 {
|
||
--tw-gradient-from: #3b82f6;
|
||
}
|
||
|
||
.to-purple-500 {
|
||
--tw-gradient-to: #a855f7;
|
||
}
|
||
|
||
.from-green-500 {
|
||
--tw-gradient-from: #22c55e;
|
||
}
|
||
|
||
.to-teal-500 {
|
||
--tw-gradient-to: #14b8a6;
|
||
}
|
||
|
||
.from-orange-500 {
|
||
--tw-gradient-from: #f97316;
|
||
}
|
||
|
||
.to-yellow-500 {
|
||
--tw-gradient-to: #eab308;
|
||
}
|
||
|
||
.from-red-500 {
|
||
--tw-gradient-from: #ef4444;
|
||
}
|
||
|
||
.to-pink-500 {
|
||
--tw-gradient-to: #ec4899;
|
||
}
|
||
|
||
.rounded-xl {
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.rounded-lg {
|
||
border-radius: 8px;
|
||
}
|
||
</style>
|