优化设备采购界面搜索

This commit is contained in:
Mr.j 2025-08-05 21:41:13 +08:00
parent 749b24a17f
commit 60deb39de8
7 changed files with 2294 additions and 1946 deletions

View File

@ -2,6 +2,10 @@
*
*/
export interface EquipmentListReq {
/** 最低价格 */
minPrice?: number
/** 最高价格 */
maxPrice?: number
/** 设备名称 */
equipmentName?: string
/** 设备类型 */
@ -74,6 +78,8 @@ export interface EquipmentListReq {
orderDirection?: string
/** 页码 */
page?: number
/** 库存条码 */
inventoryBarcode?: string
}
/**

View File

@ -83,7 +83,7 @@ const storeSetup = () => {
// 合并路由
const setRoutes = (data: RouteRecordRaw[]) => {
// 合并路由并排序
routes.value = [...constantRoutes, ...systemRoutes].concat(data)
routes.value = [...constantRoutes, ...systemRoutes, ...data]
.sort((a, b) => (a.meta?.sort ?? 0) - (b.meta?.sort ?? 0))
asyncRoutes.value = data
}
@ -181,8 +181,8 @@ const storeSetup = () => {
}]
// 使用已转换的数据生成路由
const asyncRoutes = formatAsyncRoutes(data as unknown as RouteItem[])
// 合并systemRoutes中的路由
const allRoutes = [...asyncRoutes, ...systemRoutes]
// 合并路由,避免重复
const allRoutes = [...asyncRoutes]
const flatRoutes = flatMultiLevelRoutes(cloneDeep(allRoutes))
setRoutes(allRoutes)
return flatRoutes

View File

@ -0,0 +1,210 @@
# 设备采购模块
## 功能概述
设备采购模块是一个完整的企业设备采购管理系统,提供设备采购的全生命周期管理,包括采购申请、订单管理、供应商管理、设备入库等功能。
## 主要功能
### 1. 采购记录管理
- **新增采购记录**:支持完整的设备采购信息录入
- **编辑采购记录**:修改已存在的采购记录信息
- **查看采购记录**:查看采购记录的详细信息
- **删除采购记录**:删除不需要的采购记录
### 2. 搜索功能
- **多条件搜索**:支持按设备名称、型号、供应商、状态等条件搜索
- **时间范围搜索**:支持按采购时间、入库时间、启用时间范围搜索
- **价格范围搜索**:支持按价格范围搜索
- **状态搜索**:支持按设备状态、位置状态、健康状态搜索
### 3. 数据展示
- **统计卡片**:显示采购总数、待处理、已完成、采购总额等统计信息
- **表格展示**:分页展示采购记录列表
- **状态标签**:使用不同颜色的标签显示设备状态
- **价格格式化**:自动格式化价格显示
### 4. 数据导出
- **Excel导出**支持将采购记录导出为Excel文件
- **筛选导出**:支持按搜索条件导出数据
## 技术架构
### 前端技术栈
- **Vue 3**使用Composition API
- **TypeScript**:提供类型安全
- **Arco Design Vue**UI组件库
- **Vite**:构建工具
### 后端技术栈
- **Spring Boot**:后端框架
- **MyBatis Plus**ORM框架
- **MySQL**:数据库
- **Swagger**API文档
### 数据模型
- **EquipmentEntity**:设备实体类
- **EquipmentReq**:设备请求类
- **EquipmentResp**:设备响应类
- **EquipmentListReq**:设备列表查询请求类
## 文件结构
```
procurement/
├── index.vue # 主页面
├── components/
│ ├── ProcurementSearch.vue # 搜索组件
│ └── ProcurementModal.vue # 弹窗组件
├── test.vue # 测试页面
└── README.md # 说明文档
```
## API接口
### 1. 分页查询
- **接口**`GET /equipment/procurement/page`
- **参数**EquipmentListReq
- **返回**PageResult<EquipmentResp>
### 2. 新增采购
- **接口**`POST /equipment/procurement`
- **参数**EquipmentReq
- **返回**Result<null>
### 3. 更新采购
- **接口**`PUT /equipment/procurement/{equipmentId}`
- **参数**EquipmentReq
- **返回**Result<null>
### 4. 删除采购
- **接口**`DELETE /equipment/procurement/{equipmentId}`
- **参数**equipmentId
- **返回**Result<null>
### 5. 获取详情
- **接口**`GET /equipment/procurement/detail/{equipmentId}`
- **参数**equipmentId
- **返回**Result<EquipmentResp>
### 6. 导出数据
- **接口**`GET /equipment/procurement/export`
- **参数**EquipmentListReq
- **返回**Blob
## 使用说明
### 1. 访问页面
在浏览器中访问设备采购模块页面。
### 2. 查看数据
页面会自动加载采购记录列表,显示统计信息和数据表格。
### 3. 搜索数据
点击"搜索采购"按钮,在弹出的搜索弹窗中输入搜索条件,点击"搜索"按钮。
### 4. 新增记录
点击"新增采购"按钮,在弹出的表单中填写设备采购信息,点击"确定"按钮。
### 5. 编辑记录
在表格中点击"编辑"按钮,在弹出的表单中修改信息,点击"确定"按钮。
### 6. 删除记录
在表格中点击"删除"按钮,确认删除操作。
### 7. 导出数据
点击"导出"按钮选择保存位置下载Excel文件。
## 字段说明
### 基本信息
- **设备名称**:设备的名称
- **设备类型**:设备的分类(检测设备、安防设备、办公设备、车辆等)
- **设备型号**:设备的具体型号
- **序列号**:设备的唯一序列号
- **品牌**:设备的品牌
- **资产编号**:设备的资产编号
- **配置规格**:设备的配置规格和参数
### 采购信息
- **采购订单**:采购订单号
- **供应商**:供应商名称
- **数量**:采购数量
- **单价**:设备单价
- **总价**:设备总价
- **采购价格**:采购价格
- **当前净值**:设备的当前净值
- **采购时间**:采购时间
- **入库时间**:入库时间
- **启用时间**:启用时间
- **预计报废时间**:预计报废时间
- **折旧方法**:折旧方法(直线折旧、余额递减、年数总和)
- **折旧年限**:折旧年限
- **残值**:设备残值
- **保修截止日期**:保修截止日期
### 状态信息
- **设备状态**:设备状态(正常、维修中、已报废、闲置、丢失)
- **使用状态**:使用状态(空闲中、使用中)
- **位置状态**:位置状态(库存中、使用中、维修中、已报废、外借中、丢失、闲置)
- **健康状态**:健康状态(优秀、良好、一般、较差、危险)
- **负责人**:设备负责人
- **维护人员**:维护人员
- **物理位置**:设备的物理位置
- **库存条码**:库存条码
- **上次维护日期**:上次维护日期
- **下次维护日期**:下次维护日期
### 其他信息
- **次户号**:次户号
- **盘点依据**:盘点依据
- **动态记录**:动态记录信息
- **资产备注**:资产备注信息
## 注意事项
1. **数据验证**:所有必填字段都需要填写,系统会进行数据验证
2. **权限控制**:不同用户可能有不同的操作权限
3. **数据安全**:敏感数据会进行加密处理
4. **性能优化**:大量数据时会进行分页处理
5. **错误处理**:系统会显示友好的错误提示
## 开发说明
### 1. 开发环境
- Node.js 16+
- Vue 3.3+
- TypeScript 5.0+
### 2. 安装依赖
```bash
npm install
```
### 3. 启动开发服务器
```bash
npm run dev
```
### 4. 构建生产版本
```bash
npm run build
```
### 5. 代码规范
- 使用ESLint进行代码检查
- 使用Prettier进行代码格式化
- 遵循Vue 3 Composition API最佳实践
## 更新日志
### v1.0.0 (2025-01-XX)
- 初始版本发布
- 实现基本的CRUD功能
- 实现搜索和导出功能
- 实现统计信息展示
- 实现响应式设计
## 联系方式
如有问题或建议,请联系开发团队。

View File

@ -0,0 +1,865 @@
<template>
<a-modal
:visible="visible"
:title="getModalTitle()"
width="1200px"
:confirm-loading="loading"
:ok-button-props="{ disabled: !isFormValid || isView }"
@cancel="handleCancel"
@ok="handleSubmit"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
auto-label-width
>
<!-- 标签页导航 -->
<div class="tab-navigation">
<div
v-for="tab in tabs"
:key="tab.key"
class="tab-item" :class="[{ active: activeTab === tab.key }]"
@click="activeTab = tab.key"
>
{{ tab.label }}
</div>
</div>
<!-- 标签页内容 -->
<div class="tab-content">
<!-- 基本信息 -->
<div v-show="activeTab === 'basic'">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="设备名称" field="equipmentName">
<a-input
v-model="formData.equipmentName"
placeholder="请输入设备名称"
:disabled="isView"
show-word-limit
:max-length="200"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="设备类型" field="equipmentType">
<a-select
v-model="formData.equipmentType"
:options="equipmentTypeOptions"
placeholder="请选择设备类型"
:disabled="isView"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="设备型号" field="equipmentModel">
<a-input
v-model="formData.equipmentModel"
placeholder="请输入设备型号"
:disabled="isView"
show-word-limit
:max-length="200"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="序列号" field="equipmentSn">
<a-input
v-model="formData.equipmentSn"
placeholder="请输入序列号"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="品牌" field="brand">
<a-input
v-model="formData.brand"
placeholder="请输入品牌"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="资产编号" field="assetCode">
<a-input
v-model="formData.assetCode"
placeholder="请输入资产编号"
:disabled="isView"
show-word-limit
:max-length="50"
/>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="配置规格" field="specification">
<a-textarea
v-model="formData.specification"
placeholder="请输入配置规格参数"
:disabled="isView"
:rows="3"
show-word-limit
:max-length="500"
/>
</a-form-item>
</div>
<!-- 采购信息 -->
<div v-show="activeTab === 'procurement'">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="采购订单" field="purchaseOrder">
<a-input
v-model="formData.purchaseOrder"
placeholder="请输入采购订单号"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="供应商" field="supplierName">
<a-input
v-model="formData.supplierName"
placeholder="请输入供应商名称"
:disabled="isView"
show-word-limit
:max-length="200"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="数量" field="quantity">
<a-input-number
v-model="formData.quantity"
placeholder="请输入数量"
:min="1"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="单价" field="unitPrice">
<a-input-number
v-model="formData.unitPrice"
placeholder="请输入单价"
:precision="2"
:min="0"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="总价" field="totalPrice">
<a-input-number
v-model="formData.totalPrice"
placeholder="请输入总价"
:precision="2"
:min="0"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="采购价格" field="purchasePrice">
<a-input-number
v-model="formData.purchasePrice"
placeholder="请输入采购价格"
:precision="2"
:min="0"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="当前净值" field="currentNetValue">
<a-input-number
v-model="formData.currentNetValue"
placeholder="请输入当前净值"
:precision="2"
:min="0"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="采购时间" field="purchaseTime">
<a-date-picker
v-model="formData.purchaseTime"
placeholder="请选择采购时间"
:disabled="isView"
show-time
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="入库时间" field="inStockTime">
<a-date-picker
v-model="formData.inStockTime"
placeholder="请选择入库时间"
:disabled="isView"
show-time
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="启用时间" field="activationTime">
<a-date-picker
v-model="formData.activationTime"
placeholder="请选择启用时间"
:disabled="isView"
show-time
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="预计报废时间" field="expectedScrapTime">
<a-date-picker
v-model="formData.expectedScrapTime"
placeholder="请选择预计报废时间"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="折旧方法" field="depreciationMethod">
<a-select
v-model="formData.depreciationMethod"
placeholder="请选择折旧方法"
:disabled="isView"
>
<a-option value="straight_line">直线折旧</a-option>
<a-option value="declining_balance">余额递减</a-option>
<a-option value="sum_of_years">年数总和</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="折旧年限" field="depreciationYears">
<a-input-number
v-model="formData.depreciationYears"
placeholder="请输入折旧年限"
:min="1"
:max="50"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="残值" field="salvageValue">
<a-input-number
v-model="formData.salvageValue"
placeholder="请输入残值"
:precision="2"
:min="0"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="保修截止日期" field="warrantyExpireDate">
<a-date-picker
v-model="formData.warrantyExpireDate"
placeholder="请选择保修截止日期"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
</div>
<!-- 状态信息 -->
<div v-show="activeTab === 'status'">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="设备状态" field="equipmentStatus">
<a-select
v-model="formData.equipmentStatus"
:options="equipmentStatusOptions"
placeholder="请选择设备状态"
:disabled="isView"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="使用状态" field="useStatus">
<a-select
v-model="formData.useStatus"
:options="useStatusOptions"
placeholder="请选择使用状态"
:disabled="isView"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="位置状态" field="locationStatus">
<a-select
v-model="formData.locationStatus"
:options="locationStatusOptions"
placeholder="请选择位置状态"
:disabled="isView"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="健康状态" field="healthStatus">
<a-select
v-model="formData.healthStatus"
:options="healthStatusOptions"
placeholder="请选择健康状态"
:disabled="isView"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="负责人" field="responsiblePerson">
<a-input
v-model="formData.responsiblePerson"
placeholder="请输入负责人"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="维护人员" field="maintenancePerson">
<a-input
v-model="formData.maintenancePerson"
placeholder="请输入维护人员"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="物理位置" field="physicalLocation">
<a-input
v-model="formData.physicalLocation"
placeholder="请输入物理位置"
:disabled="isView"
show-word-limit
:max-length="200"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="库存条码" field="inventoryBarcode">
<a-input
v-model="formData.inventoryBarcode"
placeholder="请输入库存条码"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="上次维护日期" field="lastMaintenanceDate">
<a-date-picker
v-model="formData.lastMaintenanceDate"
placeholder="请选择上次维护日期"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="下次维护日期" field="nextMaintenanceDate">
<a-date-picker
v-model="formData.nextMaintenanceDate"
placeholder="请选择下次维护日期"
:disabled="isView"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
</div>
<!-- 其他信息 -->
<div v-show="activeTab === 'other'">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="次户号" field="accountNumber">
<a-input
v-model="formData.accountNumber"
placeholder="请输入次户号"
:disabled="isView"
show-word-limit
:max-length="100"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="盘点依据" field="inventoryBasis">
<a-input
v-model="formData.inventoryBasis"
placeholder="请输入盘点依据"
:disabled="isView"
show-word-limit
:max-length="200"
/>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="动态记录" field="dynamicRecord">
<a-textarea
v-model="formData.dynamicRecord"
placeholder="请输入动态记录"
:disabled="isView"
:rows="4"
show-word-limit
:max-length="1000"
/>
</a-form-item>
<a-form-item label="资产备注" field="assetRemark">
<a-textarea
v-model="formData.assetRemark"
placeholder="请输入资产备注"
:disabled="isView"
:rows="3"
show-word-limit
:max-length="500"
/>
</a-form-item>
</div>
</div>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
import { Message } from '@arco-design/web-vue'
import type { FormInstance } from '@arco-design/web-vue'
import { equipmentProcurementApi } from '@/apis/equipment/procurement'
import type { EquipmentResp, EquipmentReq } from '@/apis/equipment/type'
interface Props {
visible: boolean
procurementData?: EquipmentResp | null
mode: 'add' | 'edit' | 'view'
}
interface Emits {
(e: 'update:visible', value: boolean): void
(e: 'success'): void
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
procurementData: null,
mode: 'add',
})
const emit = defineEmits<Emits>()
const formRef = ref<FormInstance>()
const loading = ref(false)
const activeTab = ref('basic')
//
const tabs = [
{ key: 'basic', label: '基本信息' },
{ key: 'procurement', label: '采购信息' },
{ key: 'status', label: '状态信息' },
{ key: 'other', label: '其他信息' },
]
//
const formData = reactive<EquipmentReq>({
equipmentName: '',
equipmentModel: '',
equipmentType: '',
equipmentStatus: '',
useStatus: '',
equipmentSn: '',
assetCode: '',
brand: '',
specification: '',
locationStatus: '',
physicalLocation: '',
responsiblePerson: '',
healthStatus: '',
purchaseTime: '',
inStockTime: '',
activationTime: '',
expectedScrapTime: '',
actualScrapTime: '',
purchaseOrder: '',
supplierName: '',
purchasePrice: undefined,
currentNetValue: undefined,
depreciationMethod: '',
depreciationYears: undefined,
salvageValue: undefined,
warrantyExpireDate: '',
lastMaintenanceDate: '',
nextMaintenanceDate: '',
maintenancePerson: '',
inventoryBarcode: '',
assetRemark: '',
accountNumber: '',
quantity: 1,
unitPrice: undefined,
totalPrice: undefined,
inventoryBasis: '',
dynamicRecord: '',
})
//
const rules = {
equipmentName: [{ required: true, message: '请输入设备名称' }],
equipmentModel: [{ required: true, message: '请输入设备型号' }],
equipmentType: [{ required: true, message: '请选择设备类型' }],
equipmentSn: [{ required: true, message: '请输入设备序列号' }],
equipmentStatus: [{ required: true, message: '请选择设备状态' }],
useStatus: [{ required: true, message: '请选择使用状态' }],
purchaseOrder: [{ required: true, message: '请输入采购订单号' }],
supplierName: [{ required: true, message: '请输入供应商名称' }],
purchasePrice: [{ required: true, message: '请输入采购价格' }],
quantity: [{ required: true, message: '请输入数量' }],
unitPrice: [{ required: true, message: '请输入单价' }],
totalPrice: [{ required: true, message: '请输入总价' }],
}
//
const equipmentTypeOptions = [
{ label: '检测设备', value: 'detection' },
{ label: '安防设备', value: 'security' },
{ label: '办公设备', value: 'office' },
{ label: '车辆', value: 'car' },
{ label: '其他设备', value: 'other' },
]
const equipmentStatusOptions = [
{ label: '正常', value: 'normal' },
{ label: '维修中', value: 'repair' },
{ label: '已报废', value: 'scrap' },
{ label: '闲置', value: 'idle' },
{ label: '丢失', value: 'lost' },
]
const useStatusOptions = [
{ label: '空闲中', value: '0' },
{ label: '使用中', value: '1' },
]
const locationStatusOptions = [
{ label: '库存中', value: 'in_stock' },
{ label: '使用中', value: 'in_use' },
{ label: '维修中', value: 'repair' },
{ label: '已报废', value: 'scrapped' },
{ label: '外借中', value: 'on_loan' },
{ label: '丢失', value: 'lost' },
{ label: '闲置', value: 'idle' },
]
const healthStatusOptions = [
{ label: '优秀', value: 'excellent' },
{ label: '良好', value: 'good' },
{ label: '一般', value: 'normal' },
{ label: '较差', value: 'poor' },
{ label: '危险', value: 'critical' },
]
//
const isView = computed(() => props.mode === 'view')
const isFormValid = computed(() => {
return formData.equipmentName &&
formData.equipmentModel &&
formData.equipmentType &&
formData.equipmentSn
})
//
const getModalTitle = () => {
const titles = {
add: '新增采购记录',
edit: '编辑采购记录',
view: '查看采购记录',
}
return titles[props.mode]
}
//
watch(() => props.visible, (newVal) => {
if (newVal && props.procurementData) {
initFormData()
}
})
//
const initFormData = () => {
if (props.procurementData) {
Object.assign(formData, {
equipmentName: props.procurementData.equipmentName || '',
equipmentModel: props.procurementData.equipmentModel || '',
equipmentType: props.procurementData.equipmentType || '',
equipmentStatus: props.procurementData.equipmentStatus || '',
useStatus: props.procurementData.useStatus || '',
equipmentSn: props.procurementData.equipmentSn || '',
assetCode: props.procurementData.assetCode || '',
brand: props.procurementData.brand || '',
specification: props.procurementData.specification || '',
locationStatus: props.procurementData.locationStatus || '',
physicalLocation: props.procurementData.physicalLocation || '',
responsiblePerson: props.procurementData.responsiblePerson || '',
healthStatus: props.procurementData.healthStatus || '',
purchaseTime: props.procurementData.purchaseTime || '',
inStockTime: props.procurementData.inStockTime || '',
activationTime: props.procurementData.activationTime || '',
expectedScrapTime: props.procurementData.expectedScrapTime || '',
actualScrapTime: props.procurementData.actualScrapTime || '',
purchaseOrder: props.procurementData.purchaseOrder || '',
supplierName: props.procurementData.supplierName || '',
purchasePrice: props.procurementData.purchasePrice,
currentNetValue: props.procurementData.currentNetValue,
depreciationMethod: props.procurementData.depreciationMethod || '',
depreciationYears: props.procurementData.depreciationYears,
salvageValue: props.procurementData.salvageValue,
warrantyExpireDate: props.procurementData.warrantyExpireDate || '',
lastMaintenanceDate: props.procurementData.lastMaintenanceDate || '',
nextMaintenanceDate: props.procurementData.nextMaintenanceDate || '',
maintenancePerson: props.procurementData.maintenancePerson || '',
inventoryBarcode: props.procurementData.inventoryBarcode || '',
assetRemark: props.procurementData.assetRemark || '',
accountNumber: props.procurementData.accountNumber || '',
quantity: props.procurementData.quantity || 1,
unitPrice: props.procurementData.unitPrice,
totalPrice: props.procurementData.totalPrice,
inventoryBasis: props.procurementData.inventoryBasis || '',
dynamicRecord: props.procurementData.dynamicRecord || '',
})
} else {
resetForm()
}
}
//
const resetForm = () => {
Object.assign(formData, {
equipmentName: '',
equipmentModel: '',
equipmentType: '',
equipmentStatus: '',
useStatus: '',
equipmentSn: '',
assetCode: '',
brand: '',
specification: '',
locationStatus: '',
physicalLocation: '',
responsiblePerson: '',
healthStatus: '',
purchaseTime: '',
inStockTime: '',
activationTime: '',
expectedScrapTime: '',
actualScrapTime: '',
purchaseOrder: '',
supplierName: '',
purchasePrice: undefined,
currentNetValue: undefined,
depreciationMethod: '',
depreciationYears: undefined,
salvageValue: undefined,
warrantyExpireDate: '',
lastMaintenanceDate: '',
nextMaintenanceDate: '',
maintenancePerson: '',
inventoryBarcode: '',
assetRemark: '',
accountNumber: '',
quantity: 1,
unitPrice: undefined,
totalPrice: undefined,
inventoryBasis: '',
dynamicRecord: '',
})
formRef.value?.resetFields()
}
//
const handleSubmit = async () => {
try {
await formRef.value?.validate()
loading.value = true
if (props.mode === 'edit' && props.procurementData) {
await equipmentProcurementApi.update(props.procurementData.equipmentId, formData)
Message.success('更新成功')
} else {
await equipmentProcurementApi.add(formData)
Message.success('新增成功')
}
emit('success')
} catch (error: any) {
console.error('操作失败:', error)
Message.error(error?.message || '操作失败')
} finally {
loading.value = false
}
}
//
const handleCancel = () => {
emit('update:visible', false)
resetForm()
}
</script>
<style scoped lang="scss">
.tab-navigation {
display: flex;
border-bottom: 1px solid var(--color-border);
margin-bottom: 24px;
.tab-item {
padding: 12px 24px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-weight: 500;
color: var(--color-text-2);
&:hover {
color: var(--color-primary);
}
&.active {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
}
}
}
.tab-content {
.arco-form-item {
margin-bottom: 24px;
.arco-form-item-label {
font-weight: 500;
color: var(--color-text-1);
margin-bottom: 8px;
}
}
.arco-input,
.arco-select,
.arco-input-number,
.arco-date-picker {
border-radius: 6px;
border: 1px solid var(--color-border);
transition: all 0.2s ease;
&:hover {
border-color: var(--color-primary-light-3);
}
&:focus,
&.arco-input-focus,
&.arco-select-focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(var(--primary-6), 0.1);
}
}
.arco-textarea {
border-radius: 6px;
border: 1px solid var(--color-border);
transition: all 0.2s ease;
&:hover {
border-color: var(--color-primary-light-3);
}
&:focus,
&.arco-textarea-focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(var(--primary-6), 0.1);
}
}
}
//
@media (max-width: 768px) {
.tab-navigation {
.tab-item {
padding: 8px 16px;
font-size: 14px;
}
}
.tab-content {
.arco-row {
.arco-col {
margin-bottom: 16px;
}
}
}
}
</style>

View File

@ -1,26 +1,23 @@
<template>
<div class="procurement-search-container">
<!-- 搜索按钮 -->
<div class="search-trigger">
<a-button type="primary" @click="showSearchModal = true">
<template #icon>
<IconSearch />
</template>
搜索采购记录
搜索采购
</a-button>
</div>
<!-- 搜索弹窗 -->
<a-modal
v-model:visible="showSearchModal"
class="search-modal"
title="采购记录搜索"
width="1200px"
:footer="false"
@cancel="handleCancel"
width="1000px"
>
<a-form
ref="formRef"
:model="searchForm"
layout="vertical"
class="search-form"
>
<div class="search-content">
<a-form layout="vertical" :model="searchForm" class="search-form">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="设备名称">
@ -28,7 +25,15 @@
v-model="searchForm.equipmentName"
placeholder="请输入设备名称"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="设备型号">
<a-input
v-model="searchForm.equipmentModel"
placeholder="请输入设备型号"
allow-clear
/>
</a-form-item>
</a-col>
@ -36,91 +41,14 @@
<a-form-item label="设备类型">
<a-select
v-model="searchForm.equipmentType"
:options="equipmentTypeOptions"
placeholder="请选择设备类型"
allow-clear
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="设备状态">
<a-select
v-model="searchForm.equipmentStatus"
:options="equipmentStatusOptions"
placeholder="请选择设备状态"
allow-clear
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="位置状态">
<a-select
v-model="searchForm.locationStatus"
:options="locationStatusOptions"
placeholder="请选择位置状态"
allow-clear
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="健康状态">
<a-select
v-model="searchForm.healthStatus"
:options="healthStatusOptions"
placeholder="请选择健康状态"
allow-clear
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="使用状态">
<a-select
v-model="searchForm.useStatus"
:options="useStatusOptions"
placeholder="请选择使用状态"
allow-clear
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="次户号">
<a-input
v-model="searchForm.accountNumber"
placeholder="请输入次户号"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="设备序列号">
<a-input
v-model="searchForm.equipmentSn"
placeholder="请输入设备序列号"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="资产编号">
<a-input
v-model="searchForm.assetCode"
placeholder="请输入资产编号"
allow-clear
@input="debouncedSearch"
/>
>
<a-option value="detection">检测设备</a-option>
<a-option value="security">安防设备</a-option>
<a-option value="office">办公设备</a-option>
<a-option value="car">车辆</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
@ -132,73 +60,6 @@
v-model="searchForm.brand"
placeholder="请输入品牌"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="设备型号">
<a-input
v-model="searchForm.equipmentModel"
placeholder="请输入设备型号"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="规格型号">
<a-input
v-model="searchForm.specification"
placeholder="请输入规格型号"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="物理位置">
<a-input
v-model="searchForm.physicalLocation"
placeholder="请输入物理位置"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="负责人">
<a-input
v-model="searchForm.responsiblePerson"
placeholder="请输入负责人"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="维护人员">
<a-input
v-model="searchForm.maintenancePerson"
placeholder="请输入维护人员"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="采购订单">
<a-input
v-model="searchForm.purchaseOrder"
placeholder="请输入采购订单号"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
@ -208,18 +69,15 @@
v-model="searchForm.supplierName"
placeholder="请输入供应商名称"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="数量">
<a-input-number
v-model="searchForm.quantity"
placeholder="请输入数量"
:min="1"
style="width: 100%"
@change="debouncedSearch"
<a-form-item label="采购订单">
<a-input
v-model="searchForm.purchaseOrder"
placeholder="请输入采购订单号"
allow-clear
/>
</a-form-item>
</a-col>
@ -227,102 +85,196 @@
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="单价">
<a-input-number
v-model="searchForm.unitPrice"
placeholder="请输入单价"
:precision="2"
:min="0"
style="width: 100%"
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="总价">
<a-input-number
v-model="searchForm.totalPrice"
placeholder="请输入总价"
:precision="2"
:min="0"
style="width: 100%"
@change="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="盘点依据">
<a-input
v-model="searchForm.inventoryBasis"
placeholder="请输入盘点依据"
<a-form-item label="设备状态">
<a-select
v-model="searchForm.equipmentStatus"
placeholder="请选择设备状态"
allow-clear
>
<a-option value="normal">正常</a-option>
<a-option value="repair">维修中</a-option>
<a-option value="scrap">已报废</a-option>
<a-option value="idle">闲置</a-option>
<a-option value="lost">丢失</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="位置状态">
<a-select
v-model="searchForm.locationStatus"
placeholder="请选择位置状态"
allow-clear
>
<a-option value="in_stock">库存中</a-option>
<a-option value="in_use">使用中</a-option>
<a-option value="repair">维修中</a-option>
<a-option value="scrapped">已报废</a-option>
<a-option value="on_loan">外借中</a-option>
<a-option value="lost">丢失</a-option>
<a-option value="idle">闲置</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="健康状态">
<a-select
v-model="searchForm.healthStatus"
placeholder="请选择健康状态"
allow-clear
>
<a-option value="excellent">优秀</a-option>
<a-option value="good">良好</a-option>
<a-option value="normal">一般</a-option>
<a-option value="poor">较差</a-option>
<a-option value="critical">危险</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="负责人">
<a-input
v-model="searchForm.responsiblePerson"
placeholder="请输入负责人"
allow-clear
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="维护人员">
<a-input
v-model="searchForm.maintenancePerson"
placeholder="请输入维护人员"
allow-clear
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="物理位置">
<a-input
v-model="searchForm.physicalLocation"
placeholder="请输入物理位置"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="动态记录">
<a-col :span="8">
<a-form-item label="资产编号">
<a-input
v-model="searchForm.dynamicRecord"
placeholder="请输入动态记录"
v-model="searchForm.assetCode"
placeholder="请输入资产编号"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="资产备注">
<a-col :span="8">
<a-form-item label="设备序列号">
<a-input
v-model="searchForm.assetRemark"
placeholder="请输入资产备注"
v-model="searchForm.equipmentSn"
placeholder="请输入设备序列号"
allow-clear
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="库存条码">
<a-input
v-model="searchForm.inventoryBarcode"
placeholder="请输入库存条码"
allow-clear
@input="debouncedSearch"
/>
</a-form-item>
</a-col>
</a-row>
<!-- 时间范围搜索 -->
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="采购时间范围">
<a-range-picker
v-model="purchaseTimeRange"
v-model="searchForm.purchaseTimeRange"
show-time
placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="handleTimeRangeChange"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="入库时间范围">
<a-range-picker
v-model="inStockTimeRange"
v-model="searchForm.inStockTimeRange"
show-time
placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="handleTimeRangeChange"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="启用时间范围">
<a-range-picker
v-model="activationTimeRange"
v-model="searchForm.activationTimeRange"
show-time
placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="handleTimeRangeChange"
/>
</a-form-item>
</a-col>
</a-row>
<!-- 搜索操作按钮 -->
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="价格范围">
<a-input-number
v-model="searchForm.minPrice"
placeholder="最低价格"
:min="0"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="至">
<a-input-number
v-model="searchForm.maxPrice"
placeholder="最高价格"
:min="0"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="配置规格">
<a-input
v-model="searchForm.specification"
placeholder="请输入配置规格关键词"
allow-clear
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="24">
<a-form-item label="备注">
<a-input
v-model="searchForm.assetRemark"
placeholder="请输入备注关键词"
allow-clear
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
<template #footer>
<div class="search-actions">
<a-space>
<a-button :loading="loading" type="primary" @click="handleSearch">
<a-button type="primary" @click="handleSearch" :loading="loading">
<template #icon>
<IconSearch />
</template>
@ -334,272 +286,139 @@
</template>
重置
</a-button>
<a-button type="dashed" @click="handleTestSearch">
测试搜索
</a-button>
<a-button @click="handleCancel">
<a-button @click="showSearchModal = false">
取消
</a-button>
</a-space>
</div>
</a-form>
</div>
</template>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { IconRefresh, IconSearch } from '@arco-design/web-vue/es/icon'
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
import type { EquipmentListReq } from '@/apis/equipment/type'
interface Props {
loading?: boolean
}
// eslint-disable-next-line unused-imports/no-unused-vars
interface Emits {
(e: 'search', params: EquipmentListReq): void
(e: 'reset'): void
}
const props = withDefaults(defineProps<Props>(), {
loading: false,
})
const emit = defineEmits<{
search: [params: EquipmentListReq]
reset: []
}>()
const emit = defineEmits<Emits>()
//
const showSearchModal = ref(false)
const formRef = ref()
//
// eslint-disable-next-line ts/no-unsafe-function-type
const debounce = (func: Function, delay: number) => {
let timeoutId: NodeJS.Timeout
return (...args: any[]) => {
clearTimeout(timeoutId)
// eslint-disable-next-line prefer-spread
timeoutId = setTimeout(() => func.apply(null, args), delay)
}
}
const searchForm = reactive<EquipmentListReq>({
//
const searchForm = reactive({
equipmentName: '',
equipmentModel: '',
equipmentType: '',
equipmentStatus: '',
equipmentSn: '',
assetCode: '',
brand: '',
supplierName: '',
purchaseOrder: '',
equipmentStatus: '',
locationStatus: '',
healthStatus: '',
responsiblePerson: '',
useStatus: '',
equipmentModel: '',
specification: '',
physicalLocation: '',
supplierName: '',
purchaseOrder: '',
maintenancePerson: '',
accountNumber: '',
quantity: undefined,
unitPrice: undefined,
totalPrice: undefined,
inventoryBasis: '',
dynamicRecord: '',
physicalLocation: '',
assetCode: '',
equipmentSn: '',
inventoryBarcode: '',
purchaseTimeRange: [],
inStockTimeRange: [],
activationTimeRange: [],
minPrice: undefined,
maxPrice: undefined,
specification: '',
assetRemark: '',
//
purchaseTimeStart: '',
purchaseTimeEnd: '',
inStockTimeStart: '',
inStockTimeEnd: '',
activationTimeStart: '',
activationTimeEnd: '',
})
//
const purchaseTimeRange = ref<[string, string] | null>(null)
const inStockTimeRange = ref<[string, string] | null>(null)
const activationTimeRange = ref<[string, string] | null>(null)
const equipmentTypeOptions = [
{ label: '检测设备', value: 'detection' },
{ label: '安防设备', value: 'security' },
{ label: '办公设备', value: 'office' },
{ label: '车辆', value: 'car' },
{ label: '其他设备', value: 'other' },
]
const equipmentStatusOptions = [
{ label: '正常', value: 'normal' },
{ label: '维修中', value: 'repair' },
{ label: '保养中', value: 'maintain' },
{ label: '报废', value: 'scrap' },
]
const locationStatusOptions = [
{ label: '正常', value: 'normal' },
{ label: '备用', value: 'spare' },
{ label: '使用中', value: 'in_use' },
{ label: '维护中', value: 'maintenance' },
{ label: '维修中', value: 'repair' },
]
const healthStatusOptions = [
{ label: '待认证', value: 'pending' },
{ label: '已认证', value: 'certified' },
{ label: '已过期', value: 'expired' },
{ label: '正常', value: 'normal' },
{ label: '警告', value: 'warning' },
{ label: '严重', value: 'critical' },
]
const useStatusOptions = [
{ label: '空闲中', value: '0' },
{ label: '使用中', value: '1' },
]
//
const debouncedSearch = debounce(() => {
console.log('🔍 ProcurementSearch - 防抖搜索触发')
console.log('🔍 ProcurementSearch - 搜索表单数据:', searchForm)
emit('search', { ...searchForm })
}, 300)
//
const handleSearch = () => {
console.log('🔍 ProcurementSearch - 搜索按钮被点击')
console.log('🔍 ProcurementSearch - 原始搜索表单数据:', searchForm)
//
const processedParams = {
...searchForm,
//
quantity: searchForm.quantity ? Number(searchForm.quantity) : undefined,
unitPrice: searchForm.unitPrice ? Number(searchForm.unitPrice) : undefined,
totalPrice: searchForm.totalPrice ? Number(searchForm.totalPrice) : undefined,
//
equipmentName: searchForm.equipmentName?.trim() || undefined,
equipmentType: searchForm.equipmentType?.trim() || undefined,
supplierName: searchForm.supplierName?.trim() || undefined,
purchaseOrder: searchForm.purchaseOrder?.trim() || undefined,
accountNumber: searchForm.accountNumber?.trim() || undefined,
inventoryBasis: searchForm.inventoryBasis?.trim() || undefined,
dynamicRecord: searchForm.dynamicRecord?.trim() || undefined,
const params: EquipmentListReq = {
equipmentName: searchForm.equipmentName || undefined,
equipmentModel: searchForm.equipmentModel || undefined,
equipmentType: searchForm.equipmentType || undefined,
brand: searchForm.brand || undefined,
supplierName: searchForm.supplierName || undefined,
purchaseOrder: searchForm.purchaseOrder || undefined,
equipmentStatus: searchForm.equipmentStatus || undefined,
locationStatus: searchForm.locationStatus || undefined,
healthStatus: searchForm.healthStatus || undefined,
responsiblePerson: searchForm.responsiblePerson || undefined,
maintenancePerson: searchForm.maintenancePerson || undefined,
physicalLocation: searchForm.physicalLocation || undefined,
assetCode: searchForm.assetCode || undefined,
equipmentSn: searchForm.equipmentSn || undefined,
inventoryBarcode: searchForm.inventoryBarcode || undefined,
specification: searchForm.specification || undefined,
assetRemark: searchForm.assetRemark || undefined,
}
// undefinednull
const filteredParams = Object.fromEntries(
Object.entries(processedParams).filter(([_, value]) => value !== undefined && value !== null && value !== ''),
)
//
if (searchForm.purchaseTimeRange && searchForm.purchaseTimeRange.length === 2) {
params.purchaseTimeStart = searchForm.purchaseTimeRange[0]
params.purchaseTimeEnd = searchForm.purchaseTimeRange[1]
}
console.log('🔍 ProcurementSearch - 处理后的参数:', filteredParams)
console.log('🔍 ProcurementSearch - 参数类型检查:')
Object.entries(filteredParams).forEach(([key, value]) => {
console.log(` ${key}: ${value} (${typeof value})`)
})
if (searchForm.inStockTimeRange && searchForm.inStockTimeRange.length === 2) {
params.inStockTimeStart = searchForm.inStockTimeRange[0]
params.inStockTimeEnd = searchForm.inStockTimeRange[1]
}
emit('search', filteredParams)
if (searchForm.activationTimeRange && searchForm.activationTimeRange.length === 2) {
params.activationTimeStart = searchForm.activationTimeRange[0]
params.activationTimeEnd = searchForm.activationTimeRange[1]
}
//
if (searchForm.minPrice !== undefined) {
params.minPrice = searchForm.minPrice
}
if (searchForm.maxPrice !== undefined) {
params.maxPrice = searchForm.maxPrice
}
emit('search', params)
showSearchModal.value = false
}
//
const handleReset = () => {
console.log('🔄 ProcurementSearch - 重置按钮被点击')
console.log('🔄 ProcurementSearch - 重置前的表单数据:', { ...searchForm })
Object.keys(searchForm).forEach((key) => {
searchForm[key as keyof EquipmentListReq] = '' as any
Object.keys(searchForm).forEach(key => {
if (Array.isArray(searchForm[key])) {
searchForm[key] = []
} else {
searchForm[key] = ''
}
})
//
purchaseTimeRange.value = null
inStockTimeRange.value = null
activationTimeRange.value = null
console.log('🔄 ProcurementSearch - 重置后的表单数据:', { ...searchForm })
formRef.value?.resetFields()
emit('reset')
}
const handleCancel = () => {
showSearchModal.value = false
}
//
const handleTimeRangeChange = () => {
//
if (purchaseTimeRange.value) {
searchForm.purchaseTimeStart = purchaseTimeRange.value[0]
searchForm.purchaseTimeEnd = purchaseTimeRange.value[1]
} else {
searchForm.purchaseTimeStart = ''
searchForm.purchaseTimeEnd = ''
}
if (inStockTimeRange.value) {
searchForm.inStockTimeStart = inStockTimeRange.value[0]
searchForm.inStockTimeEnd = inStockTimeRange.value[1]
} else {
searchForm.inStockTimeStart = ''
searchForm.inStockTimeEnd = ''
}
if (activationTimeRange.value) {
searchForm.activationTimeStart = activationTimeRange.value[0]
searchForm.activationTimeEnd = activationTimeRange.value[1]
} else {
searchForm.activationTimeStart = ''
searchForm.activationTimeEnd = ''
}
debouncedSearch()
}
const handleTestSearch = () => {
console.log('🔍 ProcurementSearch - 测试搜索按钮被点击')
console.log('🔍 ProcurementSearch - 当前搜索表单数据:', searchForm)
console.log('🔍 ProcurementSearch - 当前搜索表单数据类型:', typeof searchForm)
console.log('🔍 ProcurementSearch - 当前搜索表单的键值对:')
Object.entries(searchForm).forEach(([key, value]) => {
console.log(` ${key}: ${value} (${typeof value})`)
})
emit('search', { ...searchForm })
showSearchModal.value = false
}
defineExpose({
reset: handleReset,
getSearchForm: () => ({ ...searchForm }),
showModal: () => { showSearchModal.value = true },
})
</script>
<style lang="scss" scoped>
.procurement-search-container {
.search-trigger {
margin-bottom: 16px;
}
}
.search-modal {
.arco-modal-header {
padding: 20px 24px 16px;
border-bottom: 1px solid var(--color-border);
.arco-modal-title {
font-size: 18px;
font-weight: 600;
color: var(--color-text-1);
}
}
.arco-modal-body {
padding: 24px;
}
}
.search-content {
.search-form {
<style scoped lang="scss">
.search-form {
.arco-form-item {
margin-bottom: 20px;
margin-bottom: 16px;
.arco-form-item-label {
font-weight: 500;
color: var(--color-text-1);
margin-bottom: 8px;
font-size: 14px;
}
}
@ -622,40 +441,51 @@ defineExpose({
}
}
.arco-input-inner {
padding: 8px 12px;
font-size: 14px;
}
.arco-select-view {
padding: 8px 12px;
font-size: 14px;
}
.arco-select-arrow {
color: var(--color-text-3);
}
.arco-input-inner::placeholder {
color: var(--color-text-3);
font-size: 14px;
}
.arco-input-clear-btn {
color: var(--color-text-3);
.arco-range-picker {
border-radius: 6px;
border: 1px solid var(--color-border);
transition: all 0.2s ease;
&:hover {
color: var(--color-text-2);
border-color: var(--color-primary-light-3);
}
&:focus,
&.arco-range-picker-focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(var(--primary-6), 0.1);
}
}
}
.search-actions {
display: flex;
justify-content: center;
padding-top: 16px;
border-top: 1px solid var(--color-border);
.arco-btn {
border-radius: 6px;
font-weight: 500;
min-width: 80px;
}
}
//
@media (max-width: 768px) {
.search-form {
.arco-row {
.arco-col {
margin-bottom: 8px;
}
}
}
.search-actions {
display: flex;
justify-content: center;
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid var(--color-border);
.arco-space {
width: 100%;
justify-content: space-between;
}
}
}
</style>

View File

@ -0,0 +1,136 @@
<template>
<div class="procurement-test">
<a-card title="设备采购模块测试" :bordered="false">
<template #extra>
<a-space>
<a-button type="primary" @click="testApi">
测试API
</a-button>
<a-button @click="testSearch">
测试搜索
</a-button>
<a-button @click="testAdd">
测试新增
</a-button>
</a-space>
</template>
<a-divider />
<div class="test-results">
<h3>测试结果</h3>
<a-textarea
v-model="testResults"
:rows="10"
placeholder="测试结果将显示在这里..."
readonly
/>
</div>
<a-divider />
<div class="test-params">
<h3>测试参数</h3>
<a-form layout="vertical">
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="设备名称">
<a-input v-model="testParams.equipmentName" placeholder="测试设备" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="设备型号">
<a-input v-model="testParams.equipmentModel" placeholder="测试型号" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="供应商">
<a-input v-model="testParams.supplierName" placeholder="测试供应商" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</a-card>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { Message } from '@arco-design/web-vue'
import { equipmentProcurementApi } from '@/apis/equipment/procurement'
defineOptions({ name: 'ProcurementTest' })
const testResults = ref('')
const testParams = reactive({
equipmentName: '测试设备',
equipmentModel: '测试型号',
supplierName: '测试供应商',
})
//
const addTestResult = (message: string) => {
const timestamp = new Date().toLocaleString()
testResults.value += `[${timestamp}] ${message}\n`
}
// API
const testApi = async () => {
try {
addTestResult('开始测试API...')
const params = {
page: 1,
pageSize: 10,
equipmentName: testParams.equipmentName,
equipmentModel: testParams.equipmentModel,
supplierName: testParams.supplierName,
}
addTestResult(`请求参数: ${JSON.stringify(params, null, 2)}`)
const response = await equipmentProcurementApi.page(params)
addTestResult(`API响应: ${JSON.stringify(response, null, 2)}`)
Message.success('API测试成功')
} catch (error: any) {
addTestResult(`API测试失败: ${error.message}`)
Message.error('API测试失败')
}
}
//
const testSearch = () => {
addTestResult('测试搜索功能...')
addTestResult(`搜索参数: ${JSON.stringify(testParams, null, 2)}`)
Message.info('搜索测试完成')
}
//
const testAdd = () => {
addTestResult('测试新增功能...')
addTestResult(`新增参数: ${JSON.stringify(testParams, null, 2)}`)
Message.info('新增测试完成')
}
</script>
<style scoped lang="scss">
.procurement-test {
.test-results {
margin-bottom: 24px;
h3 {
margin-bottom: 16px;
color: var(--color-text-1);
}
}
.test-params {
h3 {
margin-bottom: 16px;
color: var(--color-text-1);
}
}
}
</style>