refactor(developer-config): 移除开发者配置页面相关代码和文档
- 删除应用配置页面及相关组件,重构路由为 /developer/config/[id].vue - 移除开发者文档页面及其导航与样式实现 - 清理开发者侧功能完善工作日志文件 - 删除全局.gitignore配置文件,清理无用忽略规则 - 优化应用配置页面的参数读取和路由结构,解决刷新404问题 - 解决数据库配置唯一键冲突,调整保存逻辑避免重复插入 - 移除对后端配置加密字段的 secret 标记,修正加密异常问题
This commit is contained in:
465
app/pages/admin/supply/purchase.vue
Normal file
465
app/pages/admin/supply/purchase.vue
Normal file
@@ -0,0 +1,465 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ layout: 'admin' })
|
||||
|
||||
// 采购统计
|
||||
const purchaseStats = ref([
|
||||
{ label: '采购单总数', value: 156, icon: 'fa-file-alt', gradient: 'from-blue-500 to-purple-500', change: '+23%', up: true },
|
||||
{ label: '待审批', value: 12, icon: 'fa-clock', gradient: 'from-orange-500 to-yellow-500', change: '+3', up: false },
|
||||
{ label: '已完成', value: 138, icon: 'fa-check-circle', gradient: 'from-green-500 to-teal-500', change: '+18%', up: true },
|
||||
{ label: '采购总额', value: '89.5', unit: '万', icon: 'fa-wallet', gradient: 'from-pink-500 to-rose-500', change: '+15%', up: true },
|
||||
])
|
||||
|
||||
// 采购单列表
|
||||
const purchaseOrders = ref([
|
||||
{ id: 'PO-2026040901', supplier: '深圳市精密机械有限公司', material: '轴承组件 A型', quantity: 500, unit: '套', amount: 25000, status: 'pending', applicant: '张经理', date: '2026-04-09' },
|
||||
{ id: 'PO-2026040802', supplier: '上海五金工具厂', material: '数控刀具套装', quantity: 20, unit: '套', amount: 36000, status: 'approved', applicant: '李主管', date: '2026-04-08' },
|
||||
{ id: 'PO-2026040801', supplier: '东莞市金属材料公司', material: '铝合金板材', quantity: 200, unit: '张', amount: 48000, status: 'processing', applicant: '王经理', date: '2026-04-08' },
|
||||
{ id: 'PO-2026040703', supplier: '苏州液压设备厂', material: '液压缸体 B型', quantity: 50, unit: '件', amount: 75000, status: 'completed', applicant: '赵主管', date: '2026-04-07' },
|
||||
{ id: 'PO-2026040702', supplier: '广州润滑油脂公司', material: '工业润滑油', quantity: 100, unit: '桶', amount: 15000, status: 'completed', applicant: '张经理', date: '2026-04-07' },
|
||||
{ id: 'PO-2026040601', supplier: '深圳市精密机械有限公司', material: '密封圈组件', quantity: 1000, unit: '个', amount: 8000, status: 'completed', applicant: '李主管', date: '2026-04-06' },
|
||||
])
|
||||
|
||||
const statusMap: Record<string, { label: string; color: string; bg: string }> = {
|
||||
pending: { label: '待审批', color: 'text-orange-600', bg: 'bg-orange-100' },
|
||||
approved: { label: '已审批', color: 'text-blue-600', bg: 'bg-blue-100' },
|
||||
processing: { label: '执行中', color: 'text-purple-600', bg: 'bg-purple-100' },
|
||||
completed: { label: '已完成', color: 'text-green-600', bg: 'bg-green-100' },
|
||||
rejected: { label: '已驳回', color: 'text-red-600', bg: 'bg-red-100' },
|
||||
}
|
||||
|
||||
const statusFilter = ref('all')
|
||||
const searchKeyword = ref('')
|
||||
|
||||
const filteredList = computed(() => {
|
||||
return purchaseOrders.value.filter((item) => {
|
||||
const matchStatus = statusFilter.value === 'all' || item.status === statusFilter.value
|
||||
const matchSearch = !searchKeyword.value ||
|
||||
item.id.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
|
||||
item.supplier.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
|
||||
item.material.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
return matchStatus && matchSearch
|
||||
})
|
||||
})
|
||||
|
||||
// 供应商列表
|
||||
const suppliers = ref([
|
||||
{ name: '深圳市精密机械有限公司', contact: '陈经理', phone: '138****1234', items: 45, total: '120万', rating: 4.8 },
|
||||
{ name: '上海五金工具厂', contact: '李总', phone: '139****5678', items: 32, total: '85万', rating: 4.6 },
|
||||
{ name: '东莞市金属材料公司', contact: '王经理', phone: '137****9012', items: 28, total: '200万', rating: 4.9 },
|
||||
{ name: '苏州液压设备厂', contact: '张工', phone: '136****3456', items: 15, total: '95万', rating: 4.7 },
|
||||
])
|
||||
|
||||
const addFormVisible = ref(false)
|
||||
const addForm = reactive({
|
||||
supplier: '',
|
||||
material: '',
|
||||
quantity: '',
|
||||
unit: '',
|
||||
estimatedAmount: '',
|
||||
deliveryDate: '',
|
||||
remark: '',
|
||||
})
|
||||
|
||||
function handleAdd() {
|
||||
addFormVisible.value = false
|
||||
message.success('采购单创建成功')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="purchase-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 purchaseStats"
|
||||
: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 }}<span v-if="stat.unit" class="text-base text-gray-500 ml-1">{{ stat.unit }}</span>
|
||||
</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="pending">待审批</a-radio-button>
|
||||
<a-radio-button value="approved">已审批</a-radio-button>
|
||||
<a-radio-button value="processing">执行中</a-radio-button>
|
||||
<a-radio-button value="completed">已完成</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<a-input-search
|
||||
v-model:value="searchKeyword"
|
||||
placeholder="搜索采购单号、供应商、物料..."
|
||||
style="width: 300px"
|
||||
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-file-alt text-blue-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="140" />
|
||||
<a-table-column title="供应商" dataIndex="supplier" width="200">
|
||||
<template #default="{ text }">
|
||||
<span class="font-medium">{{ text }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="采购物料" dataIndex="material" width="150" />
|
||||
<a-table-column title="数量" dataIndex="quantity" width="100" align="center">
|
||||
<template #default="{ record }">
|
||||
{{ record.quantity }} {{ record.unit }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="金额(元)" dataIndex="amount" width="120" align="right">
|
||||
<template #default="{ text }">
|
||||
<span class="font-medium text-orange-600">¥{{ text.toLocaleString() }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<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="applicant" width="100" align="center" />
|
||||
<a-table-column title="申请日期" dataIndex="date" width="120" />
|
||||
<a-table-column title="操作" width="160" align="center" fixed="right">
|
||||
<template #default>
|
||||
<div class="flex items-center gap-2 justify-center">
|
||||
<a-button type="link" size="small" title="查看详情">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a-button>
|
||||
<a-button type="link" size="small" title="审批" v-if="statusFilter === 'pending'">
|
||||
<i class="fas fa-check"></i>
|
||||
</a-button>
|
||||
<a-button type="link" size="small" title="编辑">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 供应商管理 -->
|
||||
<div class="glass rounded-2xl p-6 mt-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="font-bold text-lg text-gray-800">
|
||||
<i class="fas fa-building text-green-500 mr-2"></i>
|
||||
供应商列表
|
||||
</h3>
|
||||
<a-button type="link">
|
||||
<i class="fas fa-plus mr-1"></i>添加供应商
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<a-row :gutter="[16, 16]">
|
||||
<a-col :xs="24" :sm="12" :lg="6" v-for="supplier in suppliers" :key="supplier.name">
|
||||
<div class="supplier-card glass rounded-xl p-4 card-hover">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center text-white">
|
||||
<i class="fas fa-building"></i>
|
||||
</div>
|
||||
<a-rate :default-value="supplier.rating" disabled size="small" />
|
||||
</div>
|
||||
<h4 class="font-bold text-gray-800 mb-1">{{ supplier.name }}</h4>
|
||||
<p class="text-sm text-gray-500 mb-2">{{ supplier.contact }} | {{ supplier.phone }}</p>
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-gray-500">合作物料:<span class="text-blue-600 font-medium">{{ supplier.items }}</span> 种</span>
|
||||
<span class="text-gray-500">累计:<span class="text-green-600 font-medium">{{ supplier.total }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<!-- 新建采购单弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="addFormVisible"
|
||||
title="新建采购单"
|
||||
@ok="handleAdd"
|
||||
ok-text="确认创建"
|
||||
cancel-text="取消"
|
||||
width="600px"
|
||||
>
|
||||
<a-form :model="addForm" layout="vertical">
|
||||
<a-form-item label="供应商" required>
|
||||
<a-select v-model:value="addForm.supplier" placeholder="请选择供应商">
|
||||
<a-select-option v-for="s in suppliers" :key="s.name" :value="s.name">{{ s.name }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="采购物料" required>
|
||||
<a-input v-model:value="addForm.material" placeholder="请输入物料名称" />
|
||||
</a-form-item>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="数量" required>
|
||||
<a-input-number v-model:value="addForm.quantity" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="单位">
|
||||
<a-input v-model:value="addForm.unit" placeholder="如:套、件、个" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="预估金额(元)">
|
||||
<a-input-number v-model:value="addForm.estimatedAmount" style="width: 100%" :min="0" />
|
||||
</a-form-item>
|
||||
<a-form-item label="期望交货日期">
|
||||
<a-date-picker v-model:value="addForm.deliveryDate" style="width: 100%" />
|
||||
</a-form-item>
|
||||
<a-form-item label="备注">
|
||||
<a-textarea v-model:value="addForm.remark" :rows="3" placeholder="请输入备注信息" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.purchase-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);
|
||||
}
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.rounded-2xl {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.rounded-xl {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-gray-800 {
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.text-gray-600 {
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.text-orange-600 {
|
||||
color: #ea580c;
|
||||
}
|
||||
|
||||
.text-blue-600 {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.text-green-600 {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.bg-gradient-to-br {
|
||||
background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));
|
||||
}
|
||||
</style>
|
||||
462
app/pages/admin/supply/warehouse.vue
Normal file
462
app/pages/admin/supply/warehouse.vue
Normal file
@@ -0,0 +1,462 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ layout: 'admin' })
|
||||
|
||||
// 仓储统计
|
||||
const warehouseStats = ref([
|
||||
{ label: '物料种类', value: 5230, icon: 'fa-boxes', gradient: 'from-blue-500 to-cyan-500', change: '+126', up: true },
|
||||
{ label: '库存总量', value: '8.5', unit: '万', icon: 'fa-warehouse', gradient: 'from-green-500 to-teal-500', change: '+1.2万', up: true },
|
||||
{ label: '待入库', value: 45, icon: 'fa-arrow-down', gradient: 'from-orange-500 to-yellow-500', change: '+12', up: false },
|
||||
{ label: '待出库', value: 28, icon: 'fa-truck', gradient: 'from-purple-500 to-pink-500', change: '-8', up: true },
|
||||
])
|
||||
|
||||
// 库存列表
|
||||
const inventoryList = ref([
|
||||
{ code: 'MAT-001', name: '轴承组件 A型', category: '标准件', warehouse: 'A区', location: 'A-01-03', stock: 1500, safeStock: 500, unit: '套', status: 'normal' },
|
||||
{ code: 'MAT-002', name: '铝合金板材', category: '原材料', warehouse: 'B区', location: 'B-02-05', stock: 320, safeStock: 200, unit: '张', status: 'normal' },
|
||||
{ code: 'MAT-003', name: '液压缸体 B型', category: '半成品', warehouse: 'C区', location: 'C-01-02', stock: 80, safeStock: 100, unit: '件', status: 'low' },
|
||||
{ code: 'MAT-004', name: '数控刀具套装', category: '工装', warehouse: 'D区', location: 'D-03-01', stock: 45, safeStock: 20, unit: '套', status: 'normal' },
|
||||
{ code: 'MAT-005', name: '密封圈组件', category: '标准件', warehouse: 'A区', location: 'A-02-01', stock: 2800, safeStock: 1000, unit: '个', status: 'normal' },
|
||||
{ code: 'MAT-006', name: '传动齿轮组 C型', category: '零部件', warehouse: 'C区', location: 'C-02-03', stock: 150, safeStock: 200, unit: '组', status: 'low' },
|
||||
])
|
||||
|
||||
const statusMap: Record<string, { label: string; color: string; bg: string }> = {
|
||||
normal: { label: '正常', color: 'text-green-600', bg: 'bg-green-100' },
|
||||
low: { label: '偏低', color: 'text-orange-600', bg: 'bg-orange-100' },
|
||||
out: { label: '缺货', color: 'text-red-600', bg: 'bg-red-100' },
|
||||
over: { label: '超储', color: 'text-blue-600', bg: 'bg-blue-100' },
|
||||
}
|
||||
|
||||
// 入库记录
|
||||
const inboundRecords = ref([
|
||||
{ id: 'IN-2026040901', material: '轴承组件 A型', quantity: 500, unit: '套', type: '采购入库', operator: '仓管员-张三', date: '2026-04-09 09:30', status: 'completed' },
|
||||
{ id: 'IN-2026040802', material: '铝合金板材', quantity: 200, unit: '张', type: '采购入库', operator: '仓管员-李四', date: '2026-04-08 14:20', status: 'completed' },
|
||||
{ id: 'IN-2026040801', material: '数控刀具套装', quantity: 20, unit: '套', type: '采购入库', operator: '仓管员-张三', date: '2026-04-08 10:15', status: 'completed' },
|
||||
{ id: 'IN-2026040703', material: '工业润滑油', quantity: 50, unit: '桶', type: '采购入库', operator: '仓管员-王五', date: '2026-04-07 16:45', status: 'completed' },
|
||||
])
|
||||
|
||||
// 出库记录
|
||||
const outboundRecords = ref([
|
||||
{ id: 'OUT-2026040901', material: '轴承组件 A型', quantity: 100, unit: '套', type: '生产领料', recipient: '1号车间', operator: '仓管员-张三', date: '2026-04-09 08:30', status: 'completed' },
|
||||
{ id: 'OUT-2026040902', material: '密封圈组件', quantity: 200, unit: '个', type: '生产领料', recipient: '2号车间', operator: '仓管员-李四', date: '2026-04-09 10:20', status: 'completed' },
|
||||
{ id: 'OUT-2026040801', material: '铝合金板材', quantity: 50, unit: '张', type: '生产领料', recipient: '3号车间', operator: '仓管员-王五', date: '2026-04-08 15:30', status: 'completed' },
|
||||
])
|
||||
|
||||
const activeTab = ref('inventory')
|
||||
const searchKeyword = ref('')
|
||||
|
||||
const filteredInventory = computed(() => {
|
||||
return inventoryList.value.filter((item) => {
|
||||
return !searchKeyword.value ||
|
||||
item.name.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
|
||||
item.code.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
|
||||
item.category.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
})
|
||||
})
|
||||
|
||||
// 入库表单
|
||||
const inboundVisible = ref(false)
|
||||
const inboundForm = reactive({
|
||||
material: '',
|
||||
quantity: '',
|
||||
unit: '',
|
||||
type: 'purchase',
|
||||
supplier: '',
|
||||
remark: '',
|
||||
})
|
||||
|
||||
function handleInbound() {
|
||||
inboundVisible.value = false
|
||||
message.success('入库登记成功')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="warehouse-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>
|
||||
<div class="flex gap-3">
|
||||
<a-button @click="inboundVisible = true">
|
||||
<template #icon><i class="fas fa-arrow-down mr-1"></i></template>
|
||||
入库登记
|
||||
</a-button>
|
||||
<a-button type="primary">
|
||||
<template #icon><i class="fas fa-arrow-up mr-1"></i></template>
|
||||
出库登记
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="grid grid-cols-4 gap-6 mb-6">
|
||||
<div
|
||||
v-for="stat in warehouseStats"
|
||||
: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 }}<span v-if="stat.unit" class="text-base text-gray-500 ml-1">{{ stat.unit }}</span>
|
||||
</h3>
|
||||
<p class="text-gray-500 text-sm">{{ stat.label }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<div class="glass rounded-2xl p-4 mb-6">
|
||||
<a-radio-group v-model:value="activeTab" button-style="solid">
|
||||
<a-radio-button value="inventory">
|
||||
<i class="fas fa-boxes mr-1"></i>库存查询
|
||||
</a-radio-button>
|
||||
<a-radio-button value="inbound">
|
||||
<i class="fas fa-arrow-down mr-1"></i>入库记录
|
||||
</a-radio-button>
|
||||
<a-radio-button value="outbound">
|
||||
<i class="fas fa-arrow-up mr-1"></i>出库记录
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-input-search
|
||||
v-model:value="searchKeyword"
|
||||
placeholder="搜索物料名称、编号..."
|
||||
style="width: 280px; float: right"
|
||||
allow-clear
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 库存列表 -->
|
||||
<div v-show="activeTab === 'inventory'" 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-boxes text-blue-500 mr-2"></i>
|
||||
库存列表
|
||||
</h3>
|
||||
<span class="text-sm text-gray-500">共 {{ filteredInventory.length }} 种物料</span>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:dataSource="filteredInventory"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
rowKey="code"
|
||||
:scroll="{ x: 1100 }"
|
||||
>
|
||||
<a-table-column title="物料编码" dataIndex="code" width="110" />
|
||||
<a-table-column title="物料名称" dataIndex="name" width="160">
|
||||
<template #default="{ text }">
|
||||
<span class="font-medium">{{ text }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="分类" dataIndex="category" width="100" />
|
||||
<a-table-column title="仓库" dataIndex="warehouse" width="80" align="center" />
|
||||
<a-table-column title="库位" dataIndex="location" width="100" align="center" />
|
||||
<a-table-column title="库存量" dataIndex="stock" width="120" align="right">
|
||||
<template #default="{ record }">
|
||||
<span class="font-medium">{{ record.stock.toLocaleString() }} {{ record.unit }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="安全库存" dataIndex="safeStock" width="100" align="right">
|
||||
<template #default="{ record }">
|
||||
<span class="text-gray-500">{{ record.safeStock }} {{ record.unit }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<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="操作" width="120" align="center" fixed="right">
|
||||
<template #default>
|
||||
<div class="flex items-center gap-2 justify-center">
|
||||
<a-button type="link" size="small" title="详情">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a-button>
|
||||
<a-button type="link" size="small" title="调整">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 入库记录 -->
|
||||
<div v-show="activeTab === 'inbound'" 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-arrow-down text-green-500 mr-2"></i>
|
||||
入库记录
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:dataSource="inboundRecords"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
rowKey="id"
|
||||
>
|
||||
<a-table-column title="入库单号" dataIndex="id" width="140" />
|
||||
<a-table-column title="物料名称" dataIndex="material" width="160">
|
||||
<template #default="{ text }">
|
||||
<span class="font-medium">{{ text }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="数量" width="120" align="center">
|
||||
<template #default="{ record }">
|
||||
<span class="text-green-600 font-medium">+{{ record.quantity }} {{ record.unit }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="入库类型" dataIndex="type" width="120">
|
||||
<template #default="{ text }">
|
||||
<a-tag :color="text === '采购入库' ? 'blue' : 'purple'">{{ text }}</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="操作员" dataIndex="operator" width="120" />
|
||||
<a-table-column title="入库时间" dataIndex="date" width="160" />
|
||||
<a-table-column title="状态" width="100" align="center">
|
||||
<template #default>
|
||||
<a-tag color="success">已完成</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 出库记录 -->
|
||||
<div v-show="activeTab === 'outbound'" 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-arrow-up text-orange-500 mr-2"></i>
|
||||
出库记录
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<a-table
|
||||
:dataSource="outboundRecords"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
rowKey="id"
|
||||
>
|
||||
<a-table-column title="出库单号" dataIndex="id" width="140" />
|
||||
<a-table-column title="物料名称" dataIndex="material" width="160">
|
||||
<template #default="{ text }">
|
||||
<span class="font-medium">{{ text }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="数量" width="120" align="center">
|
||||
<template #default="{ record }">
|
||||
<span class="text-orange-600 font-medium">-{{ record.quantity }} {{ record.unit }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="出库类型" dataIndex="type" width="120">
|
||||
<template #default="{ text }">
|
||||
<a-tag :color="text === '生产领料' ? 'orange' : 'cyan'">{{ text }}</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="领用部门" dataIndex="recipient" width="120" />
|
||||
<a-table-column title="操作员" dataIndex="operator" width="120" />
|
||||
<a-table-column title="出库时间" dataIndex="date" width="160" />
|
||||
<a-table-column title="状态" width="100" align="center">
|
||||
<template #default>
|
||||
<a-tag color="success">已完成</a-tag>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 入库登记弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="inboundVisible"
|
||||
title="入库登记"
|
||||
@ok="handleInbound"
|
||||
ok-text="确认入库"
|
||||
cancel-text="取消"
|
||||
>
|
||||
<a-form :model="inboundForm" layout="vertical">
|
||||
<a-form-item label="物料" required>
|
||||
<a-select v-model:value="inboundForm.material" placeholder="请选择物料">
|
||||
<a-select-option v-for="item in inventoryList" :key="item.code" :value="item.name">
|
||||
{{ item.name }} ({{ item.code }})
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="数量" required>
|
||||
<a-input-number v-model:value="inboundForm.quantity" style="width: 100%" :min="1" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="单位">
|
||||
<a-input v-model:value="inboundForm.unit" placeholder="如:套、件、个" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item label="入库类型">
|
||||
<a-radio-group v-model:value="inboundForm.type">
|
||||
<a-radio value="purchase">采购入库</a-radio>
|
||||
<a-radio value="return">退货入库</a-radio>
|
||||
<a-radio value="transfer">调拨入库</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="供应商" v-if="inboundForm.type === 'purchase'">
|
||||
<a-input v-model:value="inboundForm.supplier" placeholder="请输入供应商名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="备注">
|
||||
<a-textarea v-model:value="inboundForm.remark" :rows="2" placeholder="请输入备注信息" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.warehouse-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);
|
||||
}
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.text-2xl {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.text-3xl {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.rounded-2xl {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.gap-6 {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-gray-800 {
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.text-green-600 {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.text-orange-600 {
|
||||
color: #ea580c;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user