商品表优化
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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!",
|
||||||
}
|
}
|
||||||
@@ -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": "图片上传失败!",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
Reference in New Issue
Block a user