Compare commits

...

4 Commits

Author SHA1 Message Date
6387a5798b 优化展示 2025-11-25 22:37:00 +08:00
cc9a85e85a 屏蔽 ResizeObserver 报错 2025-11-25 22:22:35 +08:00
cb4da5effa 增加参考价格 2025-11-25 20:38:51 +08:00
dd243ad2e7 更新swagger 2025-11-25 20:28:32 +08:00
11 changed files with 555 additions and 15 deletions

View File

@@ -2722,6 +2722,18 @@
],
"summary": "获取原料列表",
"parameters": [
{
"type": "number",
"description": "参考价格最大值",
"name": "max_reference_price",
"in": "query"
},
{
"type": "number",
"description": "参考价格最小值",
"name": "min_reference_price",
"in": "query"
},
{
"type": "string",
"description": "按原料名称模糊查询",
@@ -2793,7 +2805,7 @@
"summary": "创建原料",
"parameters": [
{
"description": "原料信息",
"description": "原料信息,包含名称、描述和参考价格",
"name": "rawMaterial",
"in": "body",
"required": true,
@@ -2895,7 +2907,7 @@
"required": true
},
{
"description": "更新后的原料信息",
"description": "更新后的原料信息,包含名称、描述和参考价格",
"name": "rawMaterial",
"in": "body",
"required": true,
@@ -3262,6 +3274,215 @@
}
}
},
"/api/v1/inventory/stock/adjust": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "手动调整指定原料的库存量。",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"库存管理"
],
"summary": "调整原料库存",
"parameters": [
{
"description": "库存调整请求",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.StockAdjustmentRequest"
}
}
],
"responses": {
"200": {
"description": "业务码为200代表调整成功",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.StockLogResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/inventory/stock/current": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "获取所有原料的当前库存列表,支持分页和过滤。",
"produces": [
"application/json"
],
"tags": [
"库存管理"
],
"summary": "获取当前库存列表",
"parameters": [
{
"type": "string",
"description": "排序字段, 例如 \"stock DESC\"",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "page_size",
"in": "query"
},
{
"type": "string",
"description": "按原料名称模糊查询",
"name": "raw_material_name",
"in": "query"
}
],
"responses": {
"200": {
"description": "业务码为200代表成功获取列表",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListCurrentStockResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/inventory/stock/logs": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "获取原料库存变动历史记录,支持分页、过滤和时间范围查询。",
"produces": [
"application/json"
],
"tags": [
"库存管理"
],
"summary": "获取库存变动日志",
"parameters": [
{
"type": "string",
"description": "结束时间 (RFC3339格式)",
"name": "end_time",
"in": "query"
},
{
"type": "string",
"description": "排序字段",
"name": "order_by",
"in": "query"
},
{
"type": "integer",
"description": "页码",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页数量",
"name": "page_size",
"in": "query"
},
{
"type": "integer",
"description": "按原料ID精确查询",
"name": "raw_material_id",
"in": "query"
},
{
"type": "array",
"items": {
"enum": [
"采购入库",
"饲喂出库",
"变质出库",
"售卖出库",
"杂用领取",
"手动盘点",
"发酵出库",
"发酵入库"
],
"type": "string"
},
"collectionFormat": "csv",
"description": "按来源类型查询",
"name": "source_types",
"in": "query"
},
{
"type": "string",
"description": "开始时间 (RFC3339格式, e.g., \"2023-01-01T00:00:00Z\")",
"name": "start_time",
"in": "query"
}
],
"responses": {
"200": {
"description": "业务码为200代表成功获取列表",
"schema": {
"allOf": [
{
"$ref": "#/definitions/controller.Response"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/dto.ListStockLogResponse"
}
}
}
]
}
}
}
}
},
"/api/v1/monitor/device-command-logs": {
"get": {
"security": [
@@ -3443,6 +3664,7 @@
},
{
"enum": [
7,
-1,
0,
1,
@@ -3452,12 +3674,12 @@
5,
-1,
5,
6,
7
6
],
"type": "integer",
"format": "int32",
"x-enum-varnames": [
"_numLevels",
"DebugLevel",
"InfoLevel",
"WarnLevel",
@@ -3467,8 +3689,7 @@
"FatalLevel",
"_minLevel",
"_maxLevel",
"InvalidLevel",
"_numLevels"
"InvalidLevel"
],
"name": "level",
"in": "query"
@@ -6999,6 +7220,10 @@
"description": "原料名称",
"type": "string",
"maxLength": 100
},
"reference_price": {
"description": "参考价格(kg/元)",
"type": "number"
}
}
},
@@ -7057,6 +7282,27 @@
}
}
},
"dto.CurrentStockResponse": {
"type": "object",
"properties": {
"last_updated": {
"description": "最后更新时间",
"type": "string"
},
"raw_material_id": {
"description": "原料ID",
"type": "integer"
},
"raw_material_name": {
"description": "原料名称",
"type": "string"
},
"stock": {
"description": "当前库存量, 单位: g",
"type": "number"
}
}
},
"dto.DeleteDeviceThresholdAlarmDTO": {
"type": "object",
"required": [
@@ -7254,6 +7500,20 @@
}
}
},
"dto.ListCurrentStockResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.CurrentStockResponse"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListDeviceCommandLogResponse": {
"type": "object",
"properties": {
@@ -7535,6 +7795,20 @@
}
}
},
"dto.ListStockLogResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.StockLogResponse"
}
},
"pagination": {
"$ref": "#/definitions/dto.PaginationDTO"
}
}
},
"dto.ListTaskExecutionLogResponse": {
"type": "object",
"properties": {
@@ -8491,6 +8765,10 @@
"items": {
"$ref": "#/definitions/dto.RawMaterialNutrientDTO"
}
},
"reference_price": {
"description": "参考价格(kg/元)",
"type": "number"
}
}
},
@@ -8847,6 +9125,63 @@
}
}
},
"dto.StockAdjustmentRequest": {
"type": "object",
"required": [
"change_amount",
"raw_material_id"
],
"properties": {
"change_amount": {
"description": "变动数量, 正数为入库, 负数为出库, 单位: g",
"type": "number"
},
"raw_material_id": {
"description": "要调整的原料ID",
"type": "integer"
},
"remarks": {
"description": "备注",
"type": "string",
"maxLength": 255
}
}
},
"dto.StockLogResponse": {
"type": "object",
"properties": {
"after_quantity": {
"type": "number"
},
"before_quantity": {
"type": "number"
},
"change_amount": {
"type": "number"
},
"happened_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"raw_material_id": {
"type": "integer"
},
"raw_material_name": {
"type": "string"
},
"remarks": {
"type": "string"
},
"source_id": {
"type": "integer"
},
"source_type": {
"$ref": "#/definitions/models.StockLogSourceType"
}
}
},
"dto.SubPlanResponse": {
"type": "object",
"properties": {
@@ -9465,6 +9800,10 @@
"description": "原料名称",
"type": "string",
"maxLength": 100
},
"reference_price": {
"description": "参考价格(kg/元)",
"type": "number"
}
}
},
@@ -10066,6 +10405,43 @@
"FatalLevel"
]
},
"models.StockLogSourceType": {
"type": "string",
"enum": [
"采购入库",
"饲喂出库",
"变质出库",
"售卖出库",
"杂用领取",
"手动盘点",
"发酵出库",
"发酵入库"
],
"x-enum-comments": {
"StockLogSourceFermentEnd": "发酵料产出,作为新原料计入库存",
"StockLogSourceFermentStart": "原料投入发酵,从库存中扣除"
},
"x-enum-descriptions": [
"",
"",
"",
"",
"",
"",
"原料投入发酵,从库存中扣除",
"发酵料产出,作为新原料计入库存"
],
"x-enum-varnames": [
"StockLogSourcePurchase",
"StockLogSourceFeeding",
"StockLogSourceDeteriorate",
"StockLogSourceSale",
"StockLogSourceMiscellaneous",
"StockLogSourceManual",
"StockLogSourceFermentStart",
"StockLogSourceFermentEnd"
]
},
"models.TaskType": {
"type": "string",
"enum": [
@@ -10142,6 +10518,7 @@
"type": "integer",
"format": "int32",
"enum": [
7,
-1,
0,
1,
@@ -10151,10 +10528,10 @@
5,
-1,
5,
6,
7
6
],
"x-enum-varnames": [
"_numLevels",
"DebugLevel",
"InfoLevel",
"WarnLevel",
@@ -10164,8 +10541,7 @@
"FatalLevel",
"_minLevel",
"_maxLevel",
"InvalidLevel",
"_numLevels"
"InvalidLevel"
]
}
},

View File

@@ -239,6 +239,7 @@ import {PaginationDTO, Response} from '../enums';
* @property {string} name
* @property {string} description
* @property {Array<RawMaterialNutrientDTO>} raw_material_nutrients
* @property {number} [reference_price] - 参考价格(kg/元)
*/
/**
@@ -251,6 +252,8 @@ import {PaginationDTO, Response} from '../enums';
* @typedef {object} RawMaterialsParams
* @property {string} [name] - 按原料名称模糊查询
* @property {string} [nutrient_name] - 按营养名称模糊查询
* @property {number} [max_reference_price] - 参考价格最大值
* @property {number} [min_reference_price] - 参考价格最小值
* @property {string} [order_by] - 排序字段,例如 "id DESC"
* @property {number} [page]
* @property {number} [page_size]
@@ -260,12 +263,14 @@ import {PaginationDTO, Response} from '../enums';
* @typedef {object} CreateRawMaterialRequest
* @property {string} name - 原料名称
* @property {string} [description] - 描述
* @property {number} [reference_price] - 参考价格(kg/元)
*/
/**
* @typedef {object} UpdateRawMaterialRequest
* @property {string} name - 原料名称
* @property {string} [description] - 描述
* @property {number} [reference_price] - 参考价格(kg/元)
*/
// --- Recipe ---

View File

@@ -5,6 +5,7 @@ import { AlarmApi } from './alarm.js'; // 导入告警API
import { HealthApi } from './health.js'; // 导入健康检查API
import { DeviceTemplateApi } from './deviceTemplate.js'; // 导入设备模板API
import { FeedApi } from './feed.js'; // 导入饲料管理API
import { InventoryApi } from './inventory.js'; // 导入库存管理API
/**
* API客户端
@@ -19,6 +20,7 @@ export class ApiClient {
this.alarms = AlarmApi; // 添加告警API
this.deviceTemplates = DeviceTemplateApi; // 添加设备模板API
this.feeds = FeedApi; // 添加饲料管理API
this.inventory = InventoryApi; // 添加库存管理API
}
}

104
src/api/inventory.js Normal file
View File

@@ -0,0 +1,104 @@
import http from '../utils/http';
import { PaginationDTO, Response, StockLogSourceType } from '../enums';
// --- Typedefs for Inventory Management ---
/**
* @typedef {object} StockAdjustmentRequest
* @property {number} change_amount - 变动数量, 正数为入库, 负数为出库, 单位: g
* @property {number} raw_material_id - 要调整的原料ID
* @property {string} [remarks] - 备注
*/
/**
* @typedef {object} StockLogResponse
* @property {number} after_quantity
* @property {number} before_quantity
* @property {number} change_amount
* @property {string} happened_at
* @property {number} id
* @property {number} raw_material_id
* @property {string} raw_material_name
* @property {string} remarks
* @property {number} source_id
* @property {StockLogSourceType} source_type
*/
/**
* @typedef {object} ListStockLogResponse
* @property {Array<StockLogResponse>} list
* @property {PaginationDTO} pagination
*/
/**
* @typedef {object} CurrentStockResponse
* @property {string} last_updated - 最后更新时间
* @property {number} raw_material_id - 原料ID
* @property {string} raw_material_name - 原料名称
* @property {number} stock - 当前库存量, 单位: g
*/
/**
* @typedef {object} ListCurrentStockResponse
* @property {Array<CurrentStockResponse>} list
* @property {PaginationDTO} pagination
*/
/**
* @typedef {object} AdjustStockParams
* @property {StockAdjustmentRequest} request - 库存调整请求
*/
/**
* @typedef {object} GetCurrentStockListParams
* @property {string} [order_by] - 排序字段, 例如 "stock DESC"
* @property {number} [page] - 页码
* @property {number} [page_size] - 每页数量
* @property {string} [raw_material_name] - 按原料名称模糊查询
*/
/**
* @typedef {object} GetStockLogListParams
* @property {string} [end_time] - 结束时间 (RFC3339格式)
* @property {string} [order_by] - 排序字段
* @property {number} [page] - 页码
* @property {number} [page_size] - 每页数量
* @property {number} [raw_material_id] - 按原料ID精确查询
* @property {Array<StockLogSourceType>} [source_types] - 按来源类型查询
* @property {string} [start_time] - 开始时间 (RFC3339格式, e.g., "2023-01-01T00:00:00Z")
*/
// --- API Functions ---
/**
* 调整原料库存
* @param {StockAdjustmentRequest} data - 库存调整请求
* @returns {Promise<Response<StockLogResponse>>}
*/
export const adjustStock = (data) => {
return http.post('/api/v1/inventory/stock/adjust', data);
};
/**
* 获取当前库存列表
* @param {GetCurrentStockListParams} params - 查询参数
* @returns {Promise<Response<ListCurrentStockResponse>>}
*/
export const getCurrentStockList = (params) => {
return http.get('/api/v1/inventory/stock/current', { params });
};
/**
* 获取库存变动日志
* @param {GetStockLogListParams} params - 查询参数
* @returns {Promise<Response<ListStockLogResponse>>}
*/
export const getStockLogList = (params) => {
return http.get('/api/v1/inventory/stock/logs', { params });
};
export const InventoryApi = {
adjustStock,
getCurrentStockList,
getStockLogList,
};

View File

@@ -40,7 +40,7 @@
</template>
</el-table-column>
<!-- 移除 ID -->
<el-table-column prop="name" label="营养名称"></el-table-column>
<el-table-column prop="name" label="营养名称" width="250"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">

View File

@@ -46,7 +46,7 @@
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="年龄阶段名称"></el-table-column>
<el-table-column prop="name" label="年龄阶段名称" width="150"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">

View File

@@ -95,7 +95,7 @@
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="品种名称"></el-table-column>
<el-table-column prop="name" label="品种名称" width="150"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">

View File

@@ -1,8 +1,11 @@
<template>
<el-form :model="formData" :rules="rules" ref="formRef" label-width="100px">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="150px">
<el-form-item label="原料名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入原料名称"></el-input>
</el-form-item>
<el-form-item label="参考价格(kg/元)" prop="reference_price">
<el-input-number v-model="formData.reference_price" :min="0" :precision="2" :step="0.01" controls-position="right" style="width: 100%;"></el-input-number>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input
v-model="formData.description"
@@ -34,6 +37,7 @@ export default {
default: () => ({
name: '',
description: '',
reference_price: 0, // 新增参考价格默认值
}),
},
},
@@ -43,6 +47,7 @@ export default {
const formData = reactive({
name: '',
description: '',
reference_price: 0, // 新增参考价格
});
// 监听 initialData 变化,用于编辑模式下初始化表单
@@ -52,6 +57,7 @@ export default {
if (newVal) {
formData.name = newVal.name || '';
formData.description = newVal.description || '';
formData.reference_price = newVal.reference_price || 0; // 初始化参考价格
}
},
{ immediate: true, deep: true }
@@ -62,6 +68,11 @@ export default {
{ required: true, message: '请输入原料名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' },
],
reference_price: [
{ required: true, message: '请输入参考价格', trigger: 'change' },
{ type: 'number', message: '参考价格必须是数字', trigger: 'change' },
{ min: 0, type: 'number', message: '参考价格不能小于0', trigger: 'change' },
],
};
const submitForm = () => {
@@ -85,6 +96,7 @@ export default {
// 手动重置 formData因为 resetFields 不会重置未绑定 prop 的字段
formData.name = '';
formData.description = '';
formData.reference_price = 0; // 重置参考价格
};
return {

View File

@@ -38,7 +38,8 @@
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="原料名称"></el-table-column>
<el-table-column prop="name" label="原料名称" width="150"></el-table-column>
<el-table-column prop="reference_price" label="参考价格(kg/元)" width="180"></el-table-column>
<el-table-column prop="description" label="描述"></el-table-column>
<el-table-column label="操作">
<template #default="scope">

View File

@@ -64,6 +64,21 @@ export const LogChangeType = {
CORRECTION: '盘点校正',
};
/**
* 库存变动来源类型
* @enum {string}
*/
export const StockLogSourceType = {
PURCHASE: '采购入库',
FEEDING: '饲喂出库',
DETERIORATE: '变质出库',
SALE: '售卖出库',
MISCELLANEOUS: '杂用领取',
MANUAL: '手动盘点',
FERMENT_START: '发酵出库', // 原料投入发酵,从库存中扣除
FERMENT_END: '发酵入库', // 发酵料产出,作为新原料计入库存
};
/**
* 用药原因
* @enum {string}
@@ -263,6 +278,7 @@ export const ZapcoreLevel = {
PANIC: 4,
FATAL: 5,
INVALID: 6,
NUM_LEVELS: 7, // 新增的级别数量
};
/**

View File

@@ -1,3 +1,27 @@
// 全局屏蔽 ResizeObserver 错误
// 这是一个常见的 workaround用于处理某些环境下 ResizeObserver 导致的循环限制错误。
// 它通过包装 ResizeObserver 的回调函数来捕获并阻止错误向上冒泡,但不会修复根本原因。
if (typeof window !== 'undefined' && window.ResizeObserver) {
const originalResizeObserver = window.ResizeObserver;
window.ResizeObserver = class ResizeObserver extends originalResizeObserver {
constructor(callback) {
super((entries, observer) => {
// 使用 requestAnimationFrame 延迟执行回调,以避免 ResizeObserver loop limit exceeded 错误
window.requestAnimationFrame(() => {
try {
callback(entries, observer);
} catch (error) {
// 捕获 ResizeObserver 错误,并打印到控制台,防止应用崩溃
console.error('ResizeObserver 错误被捕获:', error);
// 如果需要,可以在这里选择断开观察者,以防止进一步的错误
// observer.disconnect();
}
});
});
}
};
}
import {createApp} from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';