分类图片上传和用户等级图片上传
This commit is contained in:
@@ -7,12 +7,14 @@ use App\Http\Requests\Customer\CustomerLevelFormRequest;
|
|||||||
use App\Models\CustomerLevelModel;
|
use App\Models\CustomerLevelModel;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||||
use Modules\Common\Http\Controllers\BaseController;
|
use Modules\Common\Http\Controllers\BaseController;
|
||||||
|
use Modules\SystemTool\Services\SysFileService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户等级管理(同一商品按客户等级定价)
|
* 客户等级管理(同一商品按客户等级定价)
|
||||||
@@ -39,6 +41,20 @@ class CustomerLevelController extends BaseController
|
|||||||
return $this->success($data);
|
return $this->success($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 上传等级图片文件 */
|
||||||
|
#[PostRoute('/upload', 'create')]
|
||||||
|
public function uploadImage(Request $request, SysFileService $service): JsonResponse
|
||||||
|
{
|
||||||
|
$data = $request->validate(['file' => 'required|file']);
|
||||||
|
$result = $service->upload(
|
||||||
|
$data['file'],
|
||||||
|
8,
|
||||||
|
20,
|
||||||
|
Auth::id()
|
||||||
|
);
|
||||||
|
return $this->success($result);
|
||||||
|
}
|
||||||
|
|
||||||
/** 创建等级 */
|
/** 创建等级 */
|
||||||
#[PostRoute(authorize: 'create')]
|
#[PostRoute(authorize: 'create')]
|
||||||
public function create(CustomerLevelFormRequest $request): JsonResponse
|
public function create(CustomerLevelFormRequest $request): JsonResponse
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ use App\Http\Requests\Product\ProductCategoryFormRequest;
|
|||||||
use App\Models\ProductCategoryModel;
|
use App\Models\ProductCategoryModel;
|
||||||
use App\Models\ProductModel;
|
use App\Models\ProductModel;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
use Modules\AnnoRoute\Attribute\DeleteRoute;
|
||||||
use Modules\AnnoRoute\Attribute\GetRoute;
|
use Modules\AnnoRoute\Attribute\GetRoute;
|
||||||
use Modules\AnnoRoute\Attribute\PostRoute;
|
use Modules\AnnoRoute\Attribute\PostRoute;
|
||||||
use Modules\AnnoRoute\Attribute\PutRoute;
|
use Modules\AnnoRoute\Attribute\PutRoute;
|
||||||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||||||
use Modules\Common\Http\Controllers\BaseController;
|
use Modules\Common\Http\Controllers\BaseController;
|
||||||
|
use Modules\SystemTool\Services\SysFileService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品分类管理(多级分类:蔬菜/水果/其他)
|
* 商品分类管理(多级分类:蔬菜/水果/其他)
|
||||||
@@ -34,6 +37,20 @@ class ProductCategoryController extends BaseController
|
|||||||
return $this->success(ProductCategoryModel::getTreeData(onlyEnabled: true));
|
return $this->success(ProductCategoryModel::getTreeData(onlyEnabled: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 上传商品分类图片文件 */
|
||||||
|
#[PostRoute('/upload', 'create')]
|
||||||
|
public function uploadImage(Request $request, SysFileService $service): JsonResponse
|
||||||
|
{
|
||||||
|
$data = $request->validate(['file' => 'required|file']);
|
||||||
|
$result = $service->upload(
|
||||||
|
$data['file'],
|
||||||
|
9,
|
||||||
|
20,
|
||||||
|
Auth::id()
|
||||||
|
);
|
||||||
|
return $this->success($result);
|
||||||
|
}
|
||||||
|
|
||||||
/** 创建分类 */
|
/** 创建分类 */
|
||||||
#[PostRoute(authorize: 'create')]
|
#[PostRoute(authorize: 'create')]
|
||||||
public function create(ProductCategoryFormRequest $request): JsonResponse
|
public function create(ProductCategoryFormRequest $request): JsonResponse
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
namespace App\Http\Requests\Customer;
|
namespace App\Http\Requests\Customer;
|
||||||
|
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Validation\Rules\Exists;
|
||||||
use Modules\Common\Http\Requests\BaseFormRequest;
|
use Modules\Common\Http\Requests\BaseFormRequest;
|
||||||
|
use Modules\SystemTool\Models\SysFileModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户等级 创建/编辑 验证
|
* 客户等级 创建/编辑 验证
|
||||||
@@ -23,7 +25,7 @@ class CustomerLevelFormRequest extends BaseFormRequest
|
|||||||
'name' => ['required', 'string', 'max:50', $unique],
|
'name' => ['required', 'string', 'max:50', $unique],
|
||||||
'sort' => 'nullable|integer',
|
'sort' => 'nullable|integer',
|
||||||
'status' => 'nullable|integer|in:0,1',
|
'status' => 'nullable|integer|in:0,1',
|
||||||
'remark' => 'nullable|string|max:255',
|
'icon' => ['nullable', 'integer', new Exists(SysFileModel::class, 'id')],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ class CustomerLevelFormRequest extends BaseFormRequest
|
|||||||
'name.max' => '等级名称最长 50 个字符',
|
'name.max' => '等级名称最长 50 个字符',
|
||||||
'name.unique' => '等级名称已存在',
|
'name.unique' => '等级名称已存在',
|
||||||
'status.in' => '状态值不正确',
|
'status.in' => '状态值不正确',
|
||||||
|
'icon.exists' => '请重新上传图片'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
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\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Modules\SystemTool\Models\SysFileModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户等级模型(同一商品按客户等级定价)
|
* 客户等级模型(同一商品按客户等级定价)
|
||||||
@@ -14,9 +17,9 @@ class CustomerLevelModel extends Model
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
/** 状态:停用 */
|
/** 状态:停用 */
|
||||||
public const STATUS_DISABLED = 0;
|
public const int STATUS_DISABLED = 0;
|
||||||
/** 状态:正常 */
|
/** 状态:正常 */
|
||||||
public const STATUS_NORMAL = 1;
|
public const int STATUS_NORMAL = 1;
|
||||||
|
|
||||||
protected $table = 'customer_level';
|
protected $table = 'customer_level';
|
||||||
protected $primaryKey = 'id';
|
protected $primaryKey = 'id';
|
||||||
@@ -25,14 +28,34 @@ class CustomerLevelModel extends Model
|
|||||||
'name',
|
'name',
|
||||||
'sort',
|
'sort',
|
||||||
'status',
|
'status',
|
||||||
'remark',
|
'icon'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'sort' => 'integer',
|
'sort' => 'integer',
|
||||||
'status' => 'integer',
|
'status' => 'integer',
|
||||||
|
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||||
|
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $appends = ['icon_url'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联图标
|
||||||
|
*/
|
||||||
|
public function icon(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysFileModel::class, 'id', 'icon');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图标链接
|
||||||
|
public function icon_url(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => $this->icon->preview_url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 该等级下的门店
|
* 该等级下的门店
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
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\Relations\HasOne;
|
||||||
|
use Modules\SystemTool\Models\SysFileModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品分类模型(蔬菜/水果/其他,多级分类自关联)
|
* 商品分类模型(蔬菜/水果/其他,多级分类自关联)
|
||||||
@@ -12,9 +15,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
class ProductCategoryModel extends Model
|
class ProductCategoryModel extends Model
|
||||||
{
|
{
|
||||||
/** 状态:停用 */
|
/** 状态:停用 */
|
||||||
public const STATUS_DISABLED = 0;
|
public const int STATUS_DISABLED = 0;
|
||||||
/** 状态:正常 */
|
/** 状态:正常 */
|
||||||
public const STATUS_NORMAL = 1;
|
public const int STATUS_NORMAL = 1;
|
||||||
|
|
||||||
protected $table = 'product_category';
|
protected $table = 'product_category';
|
||||||
protected $primaryKey = 'id';
|
protected $primaryKey = 'id';
|
||||||
@@ -24,14 +27,36 @@ class ProductCategoryModel extends Model
|
|||||||
'name',
|
'name',
|
||||||
'sort',
|
'sort',
|
||||||
'status',
|
'status',
|
||||||
|
'icon'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'parent_id' => 'integer',
|
'parent_id' => 'integer',
|
||||||
'sort' => 'integer',
|
'sort' => 'integer',
|
||||||
'status' => 'integer',
|
'status' => 'integer',
|
||||||
|
'icon' => 'integer',
|
||||||
|
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||||
|
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $appends = ['icon_url'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联图标
|
||||||
|
*/
|
||||||
|
public function icon(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(SysFileModel::class, 'id', 'icon');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图标链接
|
||||||
|
public function icon_url(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => $this->icon->preview_url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 父分类
|
* 父分类
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ return new class extends Migration
|
|||||||
if (! Schema::hasTable('customer_level')) {
|
if (! Schema::hasTable('customer_level')) {
|
||||||
Schema::create('customer_level', function (Blueprint $table) {
|
Schema::create('customer_level', function (Blueprint $table) {
|
||||||
$table->increments('id')->comment('等级ID');
|
$table->increments('id')->comment('等级ID');
|
||||||
|
$table->integer('icon')->nullable()->comment('等级图标ID');
|
||||||
$table->string('name', 50)->comment('等级名称(如:一级客户、二级客户)');
|
$table->string('name', 50)->comment('等级名称(如:一级客户、二级客户)');
|
||||||
$table->integer('sort')->default(0)->comment('排序');
|
$table->integer('sort')->default(0)->comment('排序');
|
||||||
$table->integer('status')->default(1)->comment('状态(1正常 0停用)');
|
$table->integer('status')->default(1)->comment('状态(1正常 0停用)');
|
||||||
$table->string('remark', 255)->default('')->comment('备注');
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
$table->comment('客户等级表');
|
$table->comment('客户等级表');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ return new class extends Migration
|
|||||||
Schema::create('product_category', function (Blueprint $table) {
|
Schema::create('product_category', function (Blueprint $table) {
|
||||||
$table->increments('id')->comment('分类ID');
|
$table->increments('id')->comment('分类ID');
|
||||||
$table->integer('parent_id')->default(0)->comment('父级分类ID(0为顶级)');
|
$table->integer('parent_id')->default(0)->comment('父级分类ID(0为顶级)');
|
||||||
|
$table->integer('icon')->nullable()->comment('商品图标');
|
||||||
$table->string('name', 50)->comment('分类名称');
|
$table->string('name', 50)->comment('分类名称');
|
||||||
$table->integer('sort')->default(0)->comment('排序(采购单导出按此排序)');
|
$table->integer('sort')->default(0)->comment('排序(采购单导出按此排序)');
|
||||||
$table->integer('status')->default(1)->comment('状态(1正常 0停用)');
|
$table->integer('status')->default(1)->comment('状态(1正常 0停用)');
|
||||||
@@ -34,10 +35,12 @@ return new class extends Migration
|
|||||||
$table->integer('supplier_id')->default(0)->comment('默认供应商ID');
|
$table->integer('supplier_id')->default(0)->comment('默认供应商ID');
|
||||||
$table->string('name', 100)->comment('品名');
|
$table->string('name', 100)->comment('品名');
|
||||||
$table->string('spec', 100)->default('')->comment('规格/包规');
|
$table->string('spec', 100)->default('')->comment('规格/包规');
|
||||||
$table->string('grade', 50)->default('')->comment('商品等级');
|
|
||||||
$table->string('unit', 20)->default('斤')->comment('计价单位(斤/件/箱等)');
|
$table->string('unit', 20)->default('斤')->comment('计价单位(斤/件/箱等)');
|
||||||
$table->string('image', 255)->default('')->comment('商品图片');
|
$table->string('image', 255)->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('stock')->default(0)->comment('库存');
|
||||||
$table->integer('status')->default(1)->comment('状态(1上架 0下架)');
|
$table->integer('status')->default(1)->comment('状态(1上架 0下架)');
|
||||||
$table->string('remark', 255)->default('')->comment('备注');
|
$table->string('remark', 255)->default('')->comment('备注');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
@@ -48,7 +51,7 @@ return new class extends Migration
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 商品等级价格表(A2 价格策略:同一商品对不同客户等级显示不同单价)
|
// 商品等级价格表
|
||||||
if (! Schema::hasTable('product_price')) {
|
if (! Schema::hasTable('product_price')) {
|
||||||
Schema::create('product_price', function (Blueprint $table) {
|
Schema::create('product_price', function (Blueprint $table) {
|
||||||
$table->increments('id')->comment('价格ID');
|
$table->increments('id')->comment('价格ID');
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ class SysDataSeeder extends Seeder
|
|||||||
['id' => 5, 'name' => '系统附件', 'sort' => 4, 'describe' => '系统附件分组', 'created_at' => $date, 'updated_at' => $date],
|
['id' => 5, 'name' => '系统附件', 'sort' => 4, 'describe' => '系统附件分组', 'created_at' => $date, 'updated_at' => $date],
|
||||||
['id' => 6, 'name' => '其他文件', 'sort' => 5, 'describe' => '其他文件分组', 'created_at' => $date, 'updated_at' => $date],
|
['id' => 6, 'name' => '其他文件', 'sort' => 5, 'describe' => '其他文件分组', 'created_at' => $date, 'updated_at' => $date],
|
||||||
['id' => 7, 'name' => '临时文件', 'sort' => 6, 'describe' => '临时文件分组,用于存放临时上传的文件', 'created_at' => $date, 'updated_at' => $date],
|
['id' => 7, 'name' => '临时文件', 'sort' => 6, 'describe' => '临时文件分组,用于存放临时上传的文件', 'created_at' => $date, 'updated_at' => $date],
|
||||||
|
['id' => 8, 'name' => '等级图片', 'sort' => 7, 'describe' => '存放等级图片', 'created_at' => $date, 'updated_at' => $date],
|
||||||
|
['id' => 9, 'name' => '分类图片', 'sort' => 8, 'describe' => '存放商品分类图片', 'created_at' => $date, 'updated_at' => $date],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
const ImageUploader: React.FC<ImageUploaderProps> = ({
|
const ImageUploader: React.FC<ImageUploaderProps> = ({
|
||||||
action,
|
action,
|
||||||
value,
|
value,
|
||||||
|
changeType,
|
||||||
onChange,
|
onChange,
|
||||||
mode = 'single',
|
mode = 'single',
|
||||||
disabled = false,
|
disabled = false,
|
||||||
@@ -115,9 +116,17 @@ const ImageUploader: React.FC<ImageUploaderProps> = ({
|
|||||||
.filter((file) => file.status === 'done' && file.response)
|
.filter((file) => file.status === 'done' && file.response)
|
||||||
.map((file) => file.response.data as ISysFileInfo);
|
.map((file) => file.response.data as ISysFileInfo);
|
||||||
if( mode === 'single' ) {
|
if( mode === 'single' ) {
|
||||||
|
if (changeType !== 'id') {
|
||||||
onChange?.(uploadedFiles[0]);
|
onChange?.(uploadedFiles[0]);
|
||||||
} else {
|
} else {
|
||||||
|
onChange?.(uploadedFiles[0].id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (changeType !== 'id') {
|
||||||
onChange?.(uploadedFiles);
|
onChange?.(uploadedFiles);
|
||||||
|
} else {
|
||||||
|
onChange?.(uploadedFiles.map(item => item.id!));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 上传失败的文件
|
// 上传失败的文件
|
||||||
const errorFiles = newFileList.filter((file) => file.status === 'error');
|
const errorFiles = newFileList.filter((file) => file.status === 'error');
|
||||||
|
|||||||
@@ -17,11 +17,17 @@ export interface ImageUploaderProps {
|
|||||||
*/
|
*/
|
||||||
value?: ISysFileInfo | ISysFileInfo[] | null;
|
value?: ISysFileInfo | ISysFileInfo[] | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赋值类型
|
||||||
|
* 上传完成后赋值的类型。
|
||||||
|
*/
|
||||||
|
changeType?: 'id' | 'object';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传完成回调
|
* 上传完成回调
|
||||||
* @param value ISysFileInfo | ISysFileInfo[] | null
|
* @param value ISysFileInfo | ISysFileInfo[] | null
|
||||||
*/
|
*/
|
||||||
onChange?: (value: ISysFileInfo | ISysFileInfo[] | null) => void;
|
onChange?: (value?: ISysFileInfo | ISysFileInfo[] | number | number[] | null) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传模式
|
* 上传模式
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export default interface ICustomerLevel {
|
|||||||
name?: string;
|
name?: string;
|
||||||
sort?: number;
|
sort?: number;
|
||||||
status?: number;
|
status?: number;
|
||||||
remark?: string;
|
icon?: number;
|
||||||
|
icon_url?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export default interface IProductCategory {
|
|||||||
name?: string;
|
name?: string;
|
||||||
sort?: number;
|
sort?: number;
|
||||||
status?: number;
|
status?: number;
|
||||||
|
icon?: number;
|
||||||
|
icon_url?: string;
|
||||||
children?: IProductCategory[];
|
children?: IProductCategory[];
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Tag, Typography } from 'antd';
|
import { Tag, Typography, Image } from 'antd';
|
||||||
import XinTable from '@/components/XinTable';
|
import XinTable from '@/components/XinTable';
|
||||||
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
||||||
import type ICustomerLevel from '@/domain/iCustomerLevel.ts';
|
import type ICustomerLevel from '@/domain/iCustomerLevel.ts';
|
||||||
@@ -25,6 +25,7 @@ const CustomerLevelPage: React.FC = () => {
|
|||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
valueType: 'text',
|
valueType: 'text',
|
||||||
required: true,
|
required: true,
|
||||||
|
align: 'center',
|
||||||
rules: [{ required: true, message: '请输入等级名称' }],
|
rules: [{ required: true, message: '请输入等级名称' }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -32,8 +33,34 @@ const CustomerLevelPage: React.FC = () => {
|
|||||||
dataIndex: 'sort',
|
dataIndex: 'sort',
|
||||||
valueType: 'digit',
|
valueType: 'digit',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
initialValue: 0,
|
||||||
fieldProps: { min: 0 },
|
fieldProps: { min: 0 },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '图片等级',
|
||||||
|
dataIndex: 'icon',
|
||||||
|
valueType: 'image',
|
||||||
|
fieldProps: {
|
||||||
|
action: '/customer/level/upload',
|
||||||
|
mode: 'single',
|
||||||
|
maxCount: 1,
|
||||||
|
changeType: "id"
|
||||||
|
},
|
||||||
|
render: (_, record: ICustomerLevel) => {
|
||||||
|
const url = record.icon_url
|
||||||
|
if (!url) return '-';
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={url}
|
||||||
|
width={60}
|
||||||
|
height={60}
|
||||||
|
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
align: 'center',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
@@ -51,11 +78,11 @@ const CustomerLevelPage: React.FC = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '备注',
|
title: '创建时间',
|
||||||
dataIndex: 'remark',
|
dataIndex: 'updated_at',
|
||||||
valueType: 'textarea',
|
hideInForm: true,
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
fieldProps: { rows: 2 },
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
@@ -74,6 +101,7 @@ const CustomerLevelPage: React.FC = () => {
|
|||||||
formProps: {
|
formProps: {
|
||||||
grid: true,
|
grid: true,
|
||||||
colProps: { span: 12 },
|
colProps: { span: 12 },
|
||||||
|
rowProps: { gutter: 20 },
|
||||||
layout: 'vertical',
|
layout: 'vertical',
|
||||||
},
|
},
|
||||||
modalProps: { width: 640 },
|
modalProps: { width: 640 },
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Button, Space, Tag, Typography } from 'antd';
|
import {Button, Image, Tag, Typography} from 'antd';
|
||||||
import {NodeExpandOutlined} from '@ant-design/icons';
|
import {NodeExpandOutlined} from '@ant-design/icons';
|
||||||
import XinTable from '@/components/XinTable';
|
import XinTable from '@/components/XinTable';
|
||||||
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
||||||
@@ -36,17 +36,6 @@ const ProductCategoryPage: React.FC = () => {
|
|||||||
const [allIds, setAllIds] = useState<number[]>([]);
|
const [allIds, setAllIds] = useState<number[]>([]);
|
||||||
const [categoryTree, setCategoryTree] = useState<IProductCategory[]>([]);
|
const [categoryTree, setCategoryTree] = useState<IProductCategory[]>([]);
|
||||||
|
|
||||||
const loadTree = async () => {
|
|
||||||
const res = await getCategoryTable();
|
|
||||||
const tree = res.data.data ?? [];
|
|
||||||
setCategoryTree(tree);
|
|
||||||
setAllIds(collectAllIds(tree));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadTree();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const columns: XinTableColumn<IProductCategory>[] = [
|
const columns: XinTableColumn<IProductCategory>[] = [
|
||||||
{
|
{
|
||||||
title: '分类名称',
|
title: '分类名称',
|
||||||
@@ -95,6 +84,45 @@ const ProductCategoryPage: React.FC = () => {
|
|||||||
},
|
},
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '分类图标',
|
||||||
|
dataIndex: 'icon',
|
||||||
|
valueType: 'image',
|
||||||
|
fieldProps: {
|
||||||
|
action: '/product/category/upload',
|
||||||
|
mode: 'single',
|
||||||
|
maxCount: 1,
|
||||||
|
changeType: "id"
|
||||||
|
},
|
||||||
|
render: (_, record: IProductCategory) => {
|
||||||
|
const url = record.icon_url
|
||||||
|
if (!url) return '-';
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={url}
|
||||||
|
width={60}
|
||||||
|
height={60}
|
||||||
|
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
align: 'center',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'updated_at',
|
||||||
|
hideInForm: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
hideInForm: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const tableProps: XinTableProps<IProductCategory> = {
|
const tableProps: XinTableProps<IProductCategory> = {
|
||||||
@@ -115,24 +143,8 @@ const ProductCategoryPage: React.FC = () => {
|
|||||||
expandedRowKeys: expandedKeys,
|
expandedRowKeys: expandedKeys,
|
||||||
onExpandedRowsChange: (keys) => setExpandedKeys([...keys] as number[]),
|
onExpandedRowsChange: (keys) => setExpandedKeys([...keys] as number[]),
|
||||||
},
|
},
|
||||||
formProps: {
|
actionBarRender: (dom) => [
|
||||||
grid: true,
|
dom.add,
|
||||||
colProps: { span: 12 },
|
|
||||||
layout: 'vertical',
|
|
||||||
},
|
|
||||||
modalProps: { width: 640 },
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="mb-5 flex items-start justify-between">
|
|
||||||
<div>
|
|
||||||
<Title level={3}>商品分类</Title>
|
|
||||||
<Text type="secondary">
|
|
||||||
多级分类(如蔬菜/水果/其他),采购单导出与对账筛选按分类归组;有子分类或挂载商品时不可删除。
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
<Space>
|
|
||||||
<Button
|
<Button
|
||||||
icon={<NodeExpandOutlined />}
|
icon={<NodeExpandOutlined />}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -140,8 +152,25 @@ const ProductCategoryPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{expandedKeys.length ? '全部收起' : '全部展开'}
|
{expandedKeys.length ? '全部收起' : '全部展开'}
|
||||||
</Button>
|
</Button>,
|
||||||
</Space>
|
dom.keywordSearch,
|
||||||
|
],
|
||||||
|
formProps: {
|
||||||
|
grid: true,
|
||||||
|
colProps: { span: 12 },
|
||||||
|
rowProps: { gutter: 20 },
|
||||||
|
layout: 'vertical',
|
||||||
|
},
|
||||||
|
modalProps: { width: 640 },
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={"mb-5"}>
|
||||||
|
<Title level={3}>商品分类</Title>
|
||||||
|
<Text type="secondary">
|
||||||
|
多级分类(如蔬菜/水果/其他),采购单导出与对账筛选按分类归组;有子分类或挂载商品时不可删除。
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<XinTable<IProductCategory> {...tableProps} />
|
<XinTable<IProductCategory> {...tableProps} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ const ProductGoodsPage: React.FC = () => {
|
|||||||
formProps: {
|
formProps: {
|
||||||
grid: true,
|
grid: true,
|
||||||
colProps: { span: 12 },
|
colProps: { span: 12 },
|
||||||
|
rowProps: { gutter: 20 },
|
||||||
layout: 'vertical',
|
layout: 'vertical',
|
||||||
},
|
},
|
||||||
modalProps: { width: 800 },
|
modalProps: { width: 800 },
|
||||||
@@ -363,7 +364,7 @@ const ProductGoodsPage: React.FC = () => {
|
|||||||
title="价格矩阵 · 批量调价"
|
title="价格矩阵 · 批量调价"
|
||||||
open={matrixOpen}
|
open={matrixOpen}
|
||||||
onClose={() => setMatrixOpen(false)}
|
onClose={() => setMatrixOpen(false)}
|
||||||
width={matrixLevels.length * 150 + 260}
|
size={800}
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
<Button onClick={() => loadMatrix()}>刷新</Button>
|
<Button onClick={() => loadMatrix()}>刷新</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user