商品表优化

This commit is contained in:
liu
2026-08-05 14:10:20 +08:00
parent 38338a8833
commit dfa471bf22
9 changed files with 54 additions and 14 deletions
@@ -22,7 +22,7 @@ use Modules\AnnoRoute\Attribute\RequestAttribute;
use Modules\Common\Http\Controllers\BaseController; use Modules\Common\Http\Controllers\BaseController;
/** /**
* 商品档案管理(A1 商品列表 / A2 价格矩阵与批量调价 / 多等级价格体系) * 商品档案管理
*/ */
#[RequestAttribute('/product/goods', 'product.goods')] #[RequestAttribute('/product/goods', 'product.goods')]
class ProductController extends BaseController class ProductController extends BaseController
+24 -4
View File
@@ -2,11 +2,13 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\SystemTool\Models\SysFileModel;
/** /**
* 商品档案模型(品名/规格包规/供应商/等级/多等级价格体系) * 商品档案模型(品名/规格包规/供应商/等级/多等级价格体系)
@@ -16,9 +18,9 @@ class ProductModel extends Model
use SoftDeletes, HasFactory; use SoftDeletes, HasFactory;
/** 状态:下架 */ /** 状态:下架 */
public const STATUS_OFF = 0; public const int STATUS_OFF = 0;
/** 状态:上架 */ /** 状态:上架 */
public const STATUS_ON = 1; public const int STATUS_ON = 1;
protected $table = 'product'; protected $table = 'product';
protected $primaryKey = 'id'; protected $primaryKey = 'id';
@@ -28,10 +30,12 @@ class ProductModel extends Model
'supplier_id', 'supplier_id',
'name', 'name',
'spec', 'spec',
'grade',
'unit', 'unit',
'image', 'images',
'content',
'sort', 'sort',
'shelf_life',
'stock',
'status', 'status',
'remark', 'remark',
]; ];
@@ -39,10 +43,26 @@ class ProductModel extends Model
protected $casts = [ protected $casts = [
'category_id' => 'integer', 'category_id' => 'integer',
'supplier_id' => 'integer', 'supplier_id' => 'integer',
'shelf_life' => 'integer',
'stock' => 'integer',
'sort' => 'integer', 'sort' => 'integer',
'status' => 'integer', 'status' => 'integer',
]; ];
public function images(): Attribute
{
return Attribute::make(
get: function($value){
if (empty($value)) {
return collect();
}
$ids = explode(',', $value);
return SysFileModel::whereIn('id', $ids)->get();
},
set: fn ($value) => is_array($value) ? implode(',', $value) : $value,
);
}
/** /**
* 所属分类 * 所属分类
*/ */
@@ -36,7 +36,7 @@ return new class extends Migration
$table->string('name', 100)->comment('品名'); $table->string('name', 100)->comment('品名');
$table->string('spec', 100)->default('')->comment('规格/包规'); $table->string('spec', 100)->default('')->comment('规格/包规');
$table->string('unit', 20)->default('斤')->comment('计价单位(斤/件/箱等)'); $table->string('unit', 20)->default('斤')->comment('计价单位(斤/件/箱等)');
$table->string('image', 255)->default('')->comment('商品图片'); $table->string('images', 255)->default('')->comment('商品图片');
$table->text('content')->default('')->comment('商品图文详情'); $table->text('content')->default('')->comment('商品图文详情');
$table->integer('sort')->default(0)->comment('排序'); $table->integer('sort')->default(0)->comment('排序');
$table->integer('shelf_life')->default(0)->comment('保质期'); $table->integer('shelf_life')->default(0)->comment('保质期');
+2 -1
View File
@@ -14,13 +14,14 @@
"@ant-design/x": "^2.7.0", "@ant-design/x": "^2.7.0",
"@ant-design/x-markdown": "^2.7.0", "@ant-design/x-markdown": "^2.7.0",
"@ant-design/x-sdk": "^2.7.0", "@ant-design/x-sdk": "^2.7.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-react": "^1.0.6",
"antd": "^6.0.0", "antd": "^6.0.0",
"antd-img-crop": "^4.27.0", "antd-img-crop": "^4.27.0",
"axios": "^1.15.2", "axios": "^1.15.2",
"dayjs": "^1.11.18", "dayjs": "^1.11.18",
"echarts-for-react": "^3.0.2", "echarts-for-react": "^3.0.2",
"i18next": "^25.4.2", "i18next": "^25.4.2",
"ifanrx-react-ueditor": "^2.3.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
@@ -32,9 +32,11 @@ import type { RangePickerProps } from "antd/es/date-picker";
import type { CheckboxGroupProps } from 'antd/es/checkbox'; import type { CheckboxGroupProps } from 'antd/es/checkbox';
import IconSelector from '@/components/XinFormField/IconSelector'; import IconSelector from '@/components/XinFormField/IconSelector';
import ImageUploader from '@/components/XinFormField/ImageUploader'; import ImageUploader from '@/components/XinFormField/ImageUploader';
import RichTextEditor from '@/components/XinFormField/RichTextEditor';
import UserSelector from '@/components/XinFormField/UserSelector'; import UserSelector from '@/components/XinFormField/UserSelector';
import type { IconSelectProps } from '@/components/XinFormField/IconSelector/typings'; import type { IconSelectProps } from '@/components/XinFormField/IconSelector/typings';
import type { ImageUploaderProps } from '@/components/XinFormField/ImageUploader/typings'; import type { ImageUploaderProps } from '@/components/XinFormField/ImageUploader/typings';
import type { RichTextEditorProps } from '@/components/XinFormField/RichTextEditor/typings';
import type { UserSelectorProps } from '@/components/XinFormField/UserSelector/typings'; import type { UserSelectorProps } from '@/components/XinFormField/UserSelector/typings';
import type {ReactNode} from "react"; import type {ReactNode} from "react";
@@ -121,6 +123,9 @@ export default function FieldRender<T>(props: FieldsRenderProps<T>) {
case 'image': case 'image':
dom = <ImageUploader {...fieldProps as ImageUploaderProps} />; dom = <ImageUploader {...fieldProps as ImageUploaderProps} />;
break; break;
case 'richText':
dom = <RichTextEditor {...fieldProps as RichTextEditorProps} />;
break;
case 'icon': case 'icon':
dom = <IconSelector {...fieldProps as IconSelectProps} />; dom = <IconSelector {...fieldProps as IconSelectProps} />;
break; break;
@@ -22,6 +22,7 @@ import type { CheckboxGroupProps } from 'antd/es/checkbox';
import {type Key, type ReactNode} from 'react'; import {type Key, type ReactNode} from 'react';
import type { IconSelectProps } from '@/components/XinFormField/IconSelector/typings'; import type { IconSelectProps } from '@/components/XinFormField/IconSelector/typings';
import type { ImageUploaderProps } from '@/components/XinFormField/ImageUploader/typings'; import type { ImageUploaderProps } from '@/components/XinFormField/ImageUploader/typings';
import type { RichTextEditorProps } from '@/components/XinFormField/RichTextEditor/typings';
import type { UserSelectorProps } from '@/components/XinFormField/UserSelector/typings'; import type { UserSelectorProps } from '@/components/XinFormField/UserSelector/typings';
/** /**
@@ -52,6 +53,7 @@ export type FieldValue =
| 'quarter' // 季度选择 | 'quarter' // 季度选择
| 'year' // 年选择 | 'year' // 年选择
| 'image' // 图片上传 | 'image' // 图片上传
| 'richText' // 富文本编辑器
| 'color' // 颜色选择 | 'color' // 颜色选择
| 'icon' // 图标选择 | 'icon' // 图标选择
| 'user' // 用户选择 | 'user' // 用户选择
@@ -84,6 +86,7 @@ export interface FieldPropsMap {
quarter: DatePickerProps; quarter: DatePickerProps;
year: DatePickerProps; year: DatePickerProps;
image: ImageUploaderProps; image: ImageUploaderProps;
richText: RichTextEditorProps;
color: ColorPickerProps; color: ColorPickerProps;
icon: IconSelectProps; icon: IconSelectProps;
user: UserSelectorProps; user: UserSelectorProps;
+5
View File
@@ -30,4 +30,9 @@ export default {
"xin.form.imageUploader.error.dimensionExceeded": "Image dimensions cannot exceed {{maxWidth}}x{{maxHeight}}px!", "xin.form.imageUploader.error.dimensionExceeded": "Image dimensions cannot exceed {{maxWidth}}x{{maxHeight}}px!",
"xin.form.imageUploader.error.loadFailed": "Failed to load image!", "xin.form.imageUploader.error.loadFailed": "Failed to load image!",
"xin.form.imageUploader.error.readFailed": "Failed to read image!", "xin.form.imageUploader.error.readFailed": "Failed to read image!",
// RichTextEditor Component
"xin.form.richText.error.notImage": "Only image files can be uploaded!",
"xin.form.richText.error.sizeExceeded": "Image size cannot exceed {{maxSize}}MB!",
"xin.form.richText.error.uploadFailed": "Image upload failed!",
} }
+5
View File
@@ -30,4 +30,9 @@ export default {
"xin.form.imageUploader.error.dimensionExceeded": "图片尺寸不能超过 {{maxWidth}}x{{maxHeight}}px", "xin.form.imageUploader.error.dimensionExceeded": "图片尺寸不能超过 {{maxWidth}}x{{maxHeight}}px",
"xin.form.imageUploader.error.loadFailed": "图片加载失败!", "xin.form.imageUploader.error.loadFailed": "图片加载失败!",
"xin.form.imageUploader.error.readFailed": "图片读取失败!", "xin.form.imageUploader.error.readFailed": "图片读取失败!",
// RichTextEditor 组件
"xin.form.richText.error.notImage": "只能上传图片文件!",
"xin.form.richText.error.sizeExceeded": "图片大小不能超过 {{maxSize}}MB",
"xin.form.richText.error.uploadFailed": "图片上传失败!",
}; };
+8 -7
View File
@@ -165,6 +165,10 @@ const ProductGoodsPage: React.FC = () => {
title: '商品名称', title: '商品名称',
dataIndex: 'name', dataIndex: 'name',
valueType: 'text', valueType: 'text',
colProps: { span: 24 },
fieldProps: {
styles: { input: { height: 52, fontSize: 22 } }
},
required: true, required: true,
rules: [{ required: true, message: '请输入商品名称' }], rules: [{ required: true, message: '请输入商品名称' }],
}, },
@@ -174,12 +178,6 @@ const ProductGoodsPage: React.FC = () => {
valueType: 'text', valueType: 'text',
hideInSearch: true, hideInSearch: true,
}, },
{
title: '等级',
dataIndex: 'grade',
valueType: 'text',
hideInSearch: true,
},
{ {
title: '单位', title: '单位',
dataIndex: 'unit', dataIndex: 'unit',
@@ -347,7 +345,10 @@ const ProductGoodsPage: React.FC = () => {
rowProps: { gutter: 20 }, rowProps: { gutter: 20 },
layout: 'vertical', layout: 'vertical',
}, },
modalProps: { width: 800 }, modalProps: {
width: 800,
styles: {container: {height: '80vh', overflowY: "auto" }}
},
}; };
return ( return (