提交 9aa0e924 作者: 龚洪江

Merge branch 'develop'

# Conflicts:
#	src/router/router.tsx
......@@ -14,4 +14,4 @@ patches:
images:
- name: REGISTRY/NAMESPACE/IMAGE:TAG
newName: mmc-registry.cn-shenzhen.cr.aliyuncs.com/sharefly-dev/admin
newTag: e542681506090b76695aee27259a3438c8c366d0
newTag: 046b00bc34aeb1390a76df60cbc7ff65172efa52
import { InterFunction, InterItemFunction } from '~/api/interface';
import { InterFunction, InterItemFunction, InterListFunction } from '~/api/interface';
// 分类列表
export interface categoryEntity {
id: number;
......@@ -131,3 +131,178 @@ export type updateSubCategoryType = InterFunction<
>;
//分类管理-2级分类删除(新)
export type deleteSubCategoryType = InterFunction<{ id: number }, any>;
// 一级行业列表
export type industryListPagesType = InterListFunction<
{
id?: number;
typeName?: string;
},
{
id: number;
typeName: string;
typeImg: string;
description: string;
saleState: number;
createTime: string;
inspectionDTOS: Array<{
id: number;
inspectionNo: string;
inspectionName: string;
industryTypeId: number;
inspectionImg: string;
inspectionDescription: string;
saleState: null;
caseImg: string;
caseVideo: string;
companyInspectionDTOS: null;
}>;
inspectionName: string;
inspectionImg: string;
inspectionDescription: string;
}
>;
// 新增行业
export type industryInsertType = InterFunction<
{
description?: string;
id?: number;
saleState?: number;
typeImg?: string;
typeName?: string;
},
NonNullable<unknown>
>;
// 修改行业
export type industryUpdateType = InterFunction<
{
description?: string;
id?: number;
saleState?: number;
typeImg?: string;
typeName?: string;
},
NonNullable<unknown>
>;
// 新增服务
export type inspectionInsertType = InterFunction<
{
caseImg?: string;
caseVideo?: string;
id?: number;
industryTypeId?: number;
inspectionDescription?: string;
inspectionImg?: string;
inspectionName?: string;
inspectionNo?: string;
saleState?: number;
},
NonNullable<unknown>
>;
// 修改服务
export type inspectionUpdateType = InterFunction<
{
caseImg?: string;
caseVideo?: string;
id?: number;
industryTypeId?: number;
inspectionDescription?: string;
inspectionImg?: string;
inspectionName?: string;
inspectionNo?: string;
saleState?: number;
},
NonNullable<unknown>
>;
// 删除行业
export type industryRemoveType = InterFunction<{ id: number }, NonNullable<unknown>>;
// 删除服务
export type inspectionRemoveType = InterFunction<{ id: number }, NonNullable<unknown>>;
// 根据服务id查询标签列表
export type listByInspectionIdType = InterFunction<
{ id: number },
{
id: number;
inspectionId: number;
tagName: string;
}[]
>;
// 新增服务标签
export type inspectionTagInsertType = InterFunction<
{
id?: number;
inspectionId?: number;
tagName?: string;
},
NonNullable<unknown>
>;
// 修改服务标签
export type inspectionTagUpdateType = InterFunction<
{
id?: number;
inspectionId?: number;
tagName?: string;
},
NonNullable<unknown>
>;
// 删除服务标签
export type inspectionTagDeleteType = InterFunction<
{
id: number;
},
NonNullable<unknown>
>;
// 服务详情
export type inspectionDetailType = InterFunction<
{
id: number;
},
{
caseImg: string;
caseVideo: string;
companyInspectionDTOS: Array<{
companyInfoId: number;
companyName: string;
detailPage: string;
id: number;
industryTypeDTO: {
createTime: string;
description: string;
id: number;
inspectionDTOS: null;
saleState: number;
typeImg: string;
typeName: string;
};
inspectionDTO: null;
inspectionFileDTOS: Array<{
companyInspectionId: number;
fileType: number;
fileUrl: string;
first: number;
id: number;
}>;
inspectionFirstImg: string;
inspectionId: number;
inspectionPriceUnitId: number;
inspectionTagDTO: {
id: number;
inspectionId: number;
tagName: string;
};
inspectionTagId: number;
price: number;
priceRemark: string;
remark: string;
saleState: number;
serviceArea: string;
}>;
createTime: string;
id: number;
industryTypeId: number;
inspectionDescription: string;
inspectionImg: string;
inspectionName: string;
inspectionNo: string;
saleState: number;
}
>;
......@@ -177,5 +177,4 @@ type locationType = {
level: number;
childInfo: locationType[];
};
export type getSecondDistrictInfoType = InterFunction<any, locationType[]>;
......@@ -50,6 +50,6 @@ export type likeUserInfoType = InterFunction<
>;
// 论坛-审核
export type checkDynamicType = InterFunction<
{ dynamicId: number; status: number },
{ dynamicId: number; status: boolean },
NonNullable<unknown>
>;
......@@ -93,8 +93,6 @@ export type detailGoodsType = InterFunction<
id: number;
}
>;
//商品-其它服务列表
export type otherServiceType = InterFunction<any, { id: number; saleServiceName: string }[]>;
//商品-规格单位
export type skuUnitType = InterFunction<any, { id: number; unitName: string }[]>;
//商品-批量上下架
......@@ -164,7 +162,7 @@ export type editMallGoodsType = InterFunction<goodsItemType & { id: number }, an
//商品-列表(新)
export type listPageGoodsInfoType = InterItemFunction<
{ categoryPrimaryId?: number; tradeName?: string; shelfStatus?: number },
goodsItemType & { id: number }[]
(goodsItemType & { id: number })[]
>;
//商城-详情(新)
export type mallGoodsDetailsType = InterFunction<{ id: number }, goodsItemType & { id: number }>;
......@@ -174,3 +174,232 @@ export type serviceDetailType = InterFunction<
>;
//服务-上下移动
export type exChangeServiceType = InterFunction<{ id: number; sort: number }[], any>;
// pc-单位服务列表
export type listCompanyInspectionPageType = InterListFunction<
{
companyInfoId?: number;
industryTypeId?: number;
inspectionId?: number;
keyword?: string;
},
{
companyInfoId: number;
companyName: string;
detailPage: string;
id: number;
industryTypeDTO: {
createTime: string;
description: string;
id: number;
inspectionDTOS: Array<{
caseImg: string;
caseVideo: string;
companyInspectionDTOS: null;
id: number;
industryTypeId: number;
inspectionDescription: string;
inspectionImg: string;
inspectionName: string;
inspectionNo: string;
saleState: number;
}>;
saleState: number;
typeImg: string;
typeName: string;
};
inspectionDTO: {
caseImg: string;
caseVideo: string;
companyInspectionDTOS: null;
id: number;
industryTypeId: number;
inspectionDescription: string;
inspectionImg: string;
inspectionName: string;
inspectionNo: string;
saleState: number;
};
inspectionFileDTOS: Array<{
companyInspectionId: number;
fileType: number;
fileUrl: string;
first: number;
id: number;
}>;
inspectionFirstImg: string;
inspectionId: number;
inspectionPriceUnitId: number;
inspectionTagDTO: {
id: number;
inspectionId: number;
tagName: string;
};
inspectionTagId: number;
price: number;
priceRemark: string;
remark: string;
saleState: number;
serviceArea: string;
}
>;
// 新增服务
export type companyInspectionInsertType = InterFunction<
{
companyInfoId?: number;
companyInspectionFiles?: Array<{
companyInspectionId?: number;
fileType?: number;
fileUrl?: string;
first?: number;
id?: number;
}>;
detailPage?: string;
id?: number;
inspectionId?: number;
inspectionPriceUnitId?: number;
inspectionTagId?: number;
price?: number;
priceRemark?: string;
remark?: string;
saleState?: number;
serviceArea?: string;
},
NonNullable<unknown>
>;
// 修改服务
export type companyInspectionUpdateType = InterFunction<
{
companyInfoId?: number;
companyInspectionFiles?: Array<{
companyInspectionId?: number;
fileType?: number;
fileUrl?: string;
first?: number;
id?: number;
}>;
detailPage?: string;
id?: number;
inspectionId?: number;
inspectionPriceUnitId?: number;
inspectionTagId?: number;
price?: number;
priceRemark?: string;
remark?: string;
saleState?: number;
serviceArea?: string;
},
NonNullable<unknown>
>;
// 单位服务详情
export type getCompanyInspectionByIdType = InterFunction<
{
id: number;
},
{
companyInfoId: number;
companyName: string;
detailPage: string;
id: number;
industryTypeDTO: {
createTime: string;
description: string;
id: number;
inspectionDTOS: Array<{
caseImg: string;
caseVideo: string;
companyInspectionDTOS: null;
createTime: string;
id: number;
industryTypeId: number;
inspectionDescription: string;
inspectionImg: string;
inspectionName: string;
inspectionNo: string;
saleState: number;
}>;
saleState: number;
typeImg: string;
typeName: string;
};
inspectionDTO: {
caseImg: string;
caseVideo: string;
companyInspectionDTOS: null;
createTime: string;
id: number;
industryTypeId: number;
inspectionDescription: string;
inspectionImg: string;
inspectionName: string;
inspectionNo: string;
saleState: number;
};
inspectionFileDTOS: Array<{
companyInspectionId: number;
fileType: number;
fileUrl: string;
first: number;
id: number;
}>;
inspectionFirstImg: string;
inspectionId: number;
inspectionPriceUnitId: number;
inspectionTagDTO: {
id: number;
inspectionId: number;
tagName: string;
};
inspectionTagId: number;
price: number;
priceRemark: string;
remark: string;
saleState: number;
serviceArea: string;
}
>;
// 价格单位列表
export type listInspectionPriceUnitType = InterFunction<
NonNullable<unknown>,
{
id: number;
unitName: string;
}[]
>;
// 删除单位服务
export type companyInspectionRemoveType = InterFunction<{ id: number }, NonNullable<unknown>>;
// 合作商家列表-根据合作标签id获取
export type listCompanyInfoByCoopIdType = InterListFunction<
{
coopId: number;
lat: number;
lon: number;
pageNo: number;
pageSize: number;
},
{
address: string;
brandLogo: string;
brandName: string;
companyName: string;
companyType: number;
companyUserName: string;
content: string;
creditCode: string;
fullName: string;
id: number;
lat: number;
leader: number;
licenseImg: string;
lon: number;
phoneNum: number;
remark: string;
score: string;
userAccountId: number;
backImg: string;
city: string;
distance: number;
district: string;
province: string;
backUserAccountId: number;
}
>;
......@@ -31,9 +31,166 @@ export type listBrandInfoType = InterItemFunction<
>;
//租赁-型号-新增
export type addRentModeType = InterFunction<{ modeName: string; productTypeId: number }, any>;
export type addRentModeType = InterFunction<
{ modeName: string; productTypeId: number; brandInfoId: number; tag?: string },
any
>;
//租赁-型号-编辑
export type editRentModeType = InterFunction<
{ modeName: string; productTypeId: number; brandInfoId: number; tag?: string; id?: number },
any
>;
//租赁-型号-删除
export type deleteModeType = InterFunction<{ id: number }, any>;
//租赁-型号-列表
export type rentModeListType = InterItemFunction<
{ brandInfoId?: number; productTypeId?: number },
{ modeName: string; id: number; brandInfoId: number; productTypeId: number; tag: string }[]
>;
//租赁-商品-新增
export type addRentGoodsType = InterFunction<
{
brandInfoId: number;
deviceModeId: number;
leasePartsList: { name: string; number: number; price: number }[];
level: number;
logisticsCompany: string;
maxLeaseTerm: number;
minLeaseTerm: number;
modeOfDelivery: number;
priceStock: {
cashPledge: number;
maxDaysRental?: number;
ninetyDaysRental?: number;
productSpec: string;
sevenDaysRental?: number;
showPrice?: number;
skuImage?: string;
stock?: number;
stockOut: number;
thirtyDaysRental?: number;
threeDaysRental?: number;
}[];
productDetails: string;
productParam: string;
productTypeId: number;
resourcesList: { type: number; url: string }[];
returnAddress: number;
sellingPoint: string;
shelfStatus: number;
shipAddress: number;
tradeName: string;
specAttrList: { specName: string; specValuesList: { specName: string }[] }[];
},
any
>;
//租赁商品-列表
type rentGoodsType = {
brandInfoId: number;
brandName: string;
deviceModeId: number;
deviceModeName: string;
leasePartsList: { id: number; name: string; number: number; price: number }[];
level: number;
logisticsCompany: string;
maxLeaseTerm: number;
minLeaseTerm: number;
modeOfDelivery: number;
modeOfDeliveryInfo: string;
priceStock: {
cashPledge: number;
maxDaysRental?: number;
ninetyDaysRental?: number;
productSpec: string;
sevenDaysRental?: number;
showPrice?: number;
skuImage?: string;
stock?: number;
stockOut: number;
thirtyDaysRental?: number;
threeDaysRental?: number;
id: number;
}[];
productDetails: string;
productParam: string;
productTypeId: number;
productTypeName: string;
resourcesList: { id: number; type: number; url: string }[];
returnAddress: number;
sellingPoint: string;
shelfStatus: number;
shipAddress: number;
tradeName: string;
specAttrList: {
id: number;
specName: string;
specValuesList: { specName: string; id: number }[];
}[];
id: number;
createTime: string;
};
export type leaseGoodsListType = InterItemFunction<
{
brandInfoId?: number;
districtCode?: number;
productTypeId?: number;
shelfStatus?: number;
tradeName?: string;
},
rentGoodsType[]
>;
//租赁-商品-编辑
export type editLeaseGoodsType = InterFunction<
{
brandInfoId: number;
deviceModeId: number;
leasePartsList: { id?: number; name: string; number: number; price: number }[];
level: number;
logisticsCompany: string;
maxLeaseTerm: number;
minLeaseTerm: number;
modeOfDelivery: number;
priceStock: {
cashPledge: number;
maxDaysRental?: number;
ninetyDaysRental?: number;
productSpec: string;
sevenDaysRental?: number;
showPrice?: number;
skuImage?: string;
stock?: number;
stockOut: number;
thirtyDaysRental?: number;
threeDaysRental?: number;
id?: number;
}[];
productDetails: string;
productParam: string;
productTypeId: number;
resourcesList: { type: number; url: string }[];
returnAddress: number;
sellingPoint: string;
shelfStatus: number;
shipAddress: number;
tradeName: string;
specAttrList: {
id: number;
specName: string;
specValuesList: { id: number; specName: string }[];
}[];
id: number;
},
any
>;
//租赁-商品-详情
export type leaseGoodsDetailsType = InterFunction<{ id: number }, rentGoodsType>;
//租赁-商品-租期信息
export type leaseTermInfoType = InterFunction<any, { id: number; leaseDate: string }[]>;
//租赁-商品-物流-配送方式
export type otherServiceType = InterFunction<any, { id: number; saleServiceName: string }[]>;
//租赁-商品-上下架-批量
export type batchOnShelfOrTakeDownType = InterFunction<{ goodsIds: number[]; status: number }, any>;
//租赁-商品-删除-批量
export type batchRemoveWareInfoType = InterFunction<number[], any>;
......@@ -299,3 +299,44 @@ export type listMenuInfoType = InterFunction<any, menType>;
export type listRoleMenuInfoType = InterFunction<{ roleId: number }, any>;
//账号权限-修改角色菜单权限
export type updateRoleMenuInfoType = InterFunction<{ menuInfoIds: number[]; roleId: number }, any>;
//地址管理-列表
export type addressListType = InterFunction<
any,
{
takeAddress: string;
takeName: string;
takePhone: string;
takeRegion: string;
type: number;
id: number;
districtCode: string;
}[]
>;
//地址管理-添加地址
export type addressInsetType = InterFunction<
{
takeAddress: string;
takeName: string;
takePhone: string;
takeRegion: string;
type: number;
districtCode: string;
},
any
>;
//地址管理-编辑地址
export type editAddressType = InterFunction<
{
id?: number;
takeAddress: string;
takeName: string;
takePhone: string;
takeRegion: string;
type: number;
districtCode: string;
},
any
>;
//地址管理-删除地址
export type deleteAddressType = InterFunction<{ id: number }, any>;
......@@ -11,6 +11,18 @@ import {
directoryListType,
directoryPageListType,
exchangeType,
industryInsertType,
industryListPagesType,
industryRemoveType,
industryUpdateType,
inspectionDetailType,
inspectionInsertType,
inspectionRemoveType,
inspectionTagDeleteType,
inspectionTagInsertType,
inspectionTagUpdateType,
inspectionUpdateType,
listByInspectionIdType,
updatePrimaryCategoryType,
updateSubCategoryType,
} from '~/api/interface/categoryManage';
......@@ -129,4 +141,40 @@ export class CategoryManageAPI {
// 分类管理-2级分类删除(新)
static deleteSubCategory: deleteSubCategoryType = (params) =>
axios.get('/pms/category/deleteSubCategory', { params });
// 一级行业列表
static industryListPages: industryListPagesType = (params) =>
axios.post('/pms/industry/listPages', params);
// 新增行业
static industryInsert: industryInsertType = (params) =>
axios.post('/pms/industry/insert', params);
// 修改行业
static industryUpdate: industryUpdateType = (params) =>
axios.post('/pms/industry/update', params);
// 新增服务
static inspectionInsert: inspectionInsertType = (params) =>
axios.post('/pms/inspection/insert', params);
// 修改服务
static inspectionUpdate: inspectionUpdateType = (params) =>
axios.post('/pms/inspection/update', params);
// 删除行业
static industryRemove: industryRemoveType = (params) =>
axios.get('/pms/industry/remove', { params });
// 删除服务
static inspectionRemove: inspectionRemoveType = (params) =>
axios.get('/pms/inspection/remove', { params });
// 根据服务id查询标签列表
static listByInspectionId: listByInspectionIdType = (params) =>
axios.get('/pms/inspection-tag/listByInspectionId', { params });
// 新增服务标签
static inspectionTagInsert: inspectionTagInsertType = (params) =>
axios.post('/pms/inspection-tag/insert', params);
// 修改服务标签
static inspectionTagUpdate: inspectionTagUpdateType = (params) =>
axios.post('/pms/inspection-tag/update', params);
// 删除服务标签
static inspectionTagDelete: inspectionTagDeleteType = (params) =>
axios.get('/pms/inspection-tag/delete', { params });
// 服务详情
static inspectionDetail: inspectionDetailType = (params) =>
axios.get('/pms/inspection/detail', { params });
}
......@@ -10,7 +10,6 @@ import {
listGoodsType,
listPageGoodsInfoType,
mallGoodsDetailsType,
otherServiceType,
removeMallGoodsType,
skuUnitType,
upOrDownShelfType,
......@@ -38,10 +37,6 @@ class GoodsAPI {
static getSkuUnit: skuUnitType = () => {
return axios.get('/pms/mall/goods/getSkuUnit');
};
// 商品-其它服务列表
static getOtherServiceList: otherServiceType = () => {
return axios.get('/pms/goods/listOtherService');
};
// 商品-批量上下架
static batchOnShelfOrTakeDown: batchOnShelfOrTakeDownType = (data) => {
return axios.post('/pms/goods/batchOnShelfOrTakeDown', data);
......
......@@ -13,6 +13,13 @@ import {
deleteServiceType,
serviceDetailType,
exChangeServiceType,
listCompanyInspectionPageType,
companyInspectionInsertType,
companyInspectionUpdateType,
getCompanyInspectionByIdType,
listInspectionPriceUnitType,
companyInspectionRemoveType,
listCompanyInfoByCoopIdType,
} from '~/api/interface/mallManageType';
export class MallManageAPI {
......@@ -56,4 +63,26 @@ export class MallManageAPI {
// 服务-上下移
static exChangeService: exChangeServiceType = (data) =>
axios.post('/pms/backstage/work/exchange', data);
// pc-单位服务列表
static listCompanyInspectionPage: listCompanyInspectionPageType = (data) =>
axios.post('/pms/company-inspection/listCompanyInspectionPage', data);
// 新增服务
static companyInspectionInsert: companyInspectionInsertType = (data) =>
axios.post('/pms/company-inspection/insert', data);
// 修改服务
static companyInspectionUpdate: companyInspectionUpdateType = (data) =>
axios.post('/pms/company-inspection/update', data);
// 单位服务详情
static getCompanyInspectionById: getCompanyInspectionByIdType = (params) =>
axios.get('/pms/company-inspection/getCompanyInspectionById', { params });
// 价格单位列表
static listInspectionPriceUnit: listInspectionPriceUnitType = (params) =>
axios.get('/pms/company-inspection/listInspectionPriceUnit', { params });
// 删除单位服务
static companyInspectionRemove: companyInspectionRemoveType = (params) =>
axios.get('/pms/company-inspection/remove', { params });
// 合作商家列表-根据合作标签id获取
static listCompanyInfoByCoopId: listCompanyInfoByCoopIdType = (params) =>
axios.get('/userapp/cooperation/listCompanyInfoByCoopId', { params });
}
import {
addRentGoodsType,
addRentModeType,
addType,
batchRemoveWareInfoType,
deleteModeType,
editBrandInfoType,
editLeaseGoodsType,
editRentModeType,
getTypeListType,
leaseGoodsDetailsType,
leaseGoodsListType,
leaseTermInfoType,
listBrandInfoType,
otherServiceType,
rentMakeAddType,
rentModeListType,
rentTypeEditType,
rentTypeRemoveType,
} from '~/api/interface/rentManageType';
import axios from '../request';
import { batchOnShelfOrTakeDownType } from '~/api/interface/goodsType';
export class RentManageAPI {
//租赁-类型-新增
......@@ -35,6 +45,36 @@ export class RentManageAPI {
// 租赁-型号-新增
static addRentMode: addRentModeType = (data) => axios.post('/pms/brand/addMode', data);
//租赁-型号-编辑
static editRentMode: editRentModeType = (data) => axios.post('/pms/brand/editMode', data);
//租赁-型号-删除
static deleteMode: deleteModeType = (params) => axios.get('/pms/brand/deleteMode', { params });
// 租赁-型号-列表
static getRentModeList: rentModeListType = (data) => axios.post('/pms/brand/modeList', data);
// 租赁-商品-新增
static addRentGoods: addRentGoodsType = (data) =>
axios.post('/pms/lease/goods/addLeaseGoods', data);
//租赁-商品-列表
static getLeaseGoodsList: leaseGoodsListType = (data) =>
axios.post('/pms/lease/goods/leaseGoodsList', data);
//租赁-商品-编辑
static editLeaseGoods: editLeaseGoodsType = (data) =>
axios.post('/pms/lease/goods/editLeaseGoods', data);
//租赁-商品-详情
static getLeaseGoodsDetails: leaseGoodsDetailsType = (params) =>
axios.get('/pms/lease/goods/leaseGoodsDetails', { params });
// 租赁-商品-租期信息
static getLeaseTermInfo: leaseTermInfoType = () =>
axios.post('/pms/lease/goods/getLeaseTermInfo');
//租赁-商品-上下架-批量
static batchOnShelfOrTakeDown: batchOnShelfOrTakeDownType = (data) =>
axios.post('/pms/lease/goods/batchOnShelfOrTakeDown', data);
////租赁-商品-删除-批量
static batchRemoveWareInfo: batchRemoveWareInfoType = (data) =>
axios.post('/pms/lease/goods/batchRemoveWareInfo', data);
// 商品-其它服务列表
static getOtherServiceList: otherServiceType = () => {
return axios.get('/pms/goods/listOtherService');
};
}
import {
addressInsetType,
addressListType,
bindingCompanyMemberType,
deleteAddressType,
deleteRoleInfoType,
editAddressType,
getCompanyInfoByIdType,
getSecondDistrictInfo,
insertBAccountType,
......@@ -104,4 +108,15 @@ export class SystemManageAPI {
//账号权限-修改角色菜单权限
static updateRoleMenuInfo: updateRoleMenuInfoType = (data) =>
axios.post('/userapp/role/updateRoleMenuInfo', data);
// 地址管理-新增
static addressInset: addressInsetType = (data) => axios.post('/oms/user-address/insert', data);
// 地址管理-列表
static getAddressList: addressListType = (data) =>
axios.post('/oms/user-address/selectList', data);
// 地址管理-编辑
static editAddress: editAddressType = (data) => axios.post('/oms/user-address/update', data);
// 地址管理-删除
static deleteAddress: deleteAddressType = (params) =>
axios.get('/oms/user-address/deleteById', { params });
}
import { Form, InputNumber, Input, Select, Radio } from 'antd';
import { Form, InputNumber, Input, Select, Radio, Switch } from 'antd';
import React from 'react';
import { Uploader } from '~/components/uploader';
import { UploadOutlined } from '@ant-design/icons';
......@@ -8,7 +8,7 @@ export interface EditableCellProps extends React.HTMLAttributes<HTMLElement> {
editing: boolean;
dataIndex: string;
title: any;
inputType: 'number' | 'text' | 'select' | 'uploader' | 'radio' | 'textArea';
inputType: 'number' | 'text' | 'select' | 'uploader' | 'radio' | 'textArea' | 'switch';
record: any;
index: number;
children: React.ReactNode;
......@@ -41,7 +41,13 @@ const EditableCell: React.FC<
const inputNode = () => {
switch (inputType) {
case 'number':
return <InputNumber placeholder={`请输入${placeholder || title}`} maxLength={maxLength} />;
return (
<InputNumber
placeholder={`请输入${placeholder || title}`}
style={{ width: '100%' }}
maxLength={maxLength}
/>
);
case 'select':
return (
<Select placeholder={`请选择${placeholder || title}`} style={{ textAlign: 'start' }}>
......@@ -82,14 +88,28 @@ const EditableCell: React.FC<
showCount
/>
);
case 'switch':
return <Switch />;
default:
return <Input placeholder={`请输入${placeholder || title}`} maxLength={maxLength} />;
return (
<Input
placeholder={`请输入${placeholder || title}`}
maxLength={maxLength}
style={{ width: '100%' }}
/>
);
}
};
return (
<td {...restProps}>
{editing ? (
<Form.Item name={dataIndex + record.id} style={{ margin: 0 }} rules={rules}>
<Form.Item
name={dataIndex + record.id}
style={{ margin: 0 }}
rules={rules}
//valuePropName:当 form setFieldsValue 设置switch不生效时
valuePropName={inputType === 'switch' ? 'checked' : undefined}
>
{inputNode()}
</Form.Item>
) : (
......
import React, { useState, useEffect } from 'react';
import { Modal, Form, Input, Select, Radio, Button, Table, message, ModalProps } from 'antd';
import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
import type { RadioChangeEvent } from 'antd';
import EditableCell from '~/components/EditableCell';
import { InterDataType } from '~/api/interface';
import { ProduceManageAPI } from '~/api';
import {
cooperationTagType,
productListType,
ProductSpecListType,
} from '~/api/interface/produceManageType';
import { customizeEntity, skuUnitType, specEntity } from '~/api/interface/goodsType';
import { Uploader } from '~/components/uploader';
import ConfigurePriceModal from '../configurePriceModal';
//产品返回类型
type productType = InterDataType<productListType>['list'];
//产品-规格返回类型
type productSpecType = InterDataType<ProductSpecListType>['list'];
//产品-规格单位返回类型
type unitType = InterDataType<skuUnitType>;
//加盟标签返回类型
type cooperationTagResponseType = InterDataType<cooperationTagType>;
interface selfProps {
handleCancel: () => void;
handleOk: (data: specEntity) => void;
currentDesc: number;
curtRowData: Partial<specEntity>;
skuUnitList: unitType;
goodsType: number;
}
// interface tableRowEntity {
// goodsTypeId: number;
// prodOrScheme: any;
// selectSource: any;
// skuUnit: any;
// }
const AddOrEditSkuModal: React.FC<ModalProps & selfProps> = ({
open,
handleCancel,
handleOk,
currentDesc,
curtRowData,
skuUnitList,
goodsType,
}) => {
// 弹窗标题
const [dialogTitle, setDialogTitle] = useState<string>('添加');
// 规格表单
const [skuForm] = Form.useForm<any>();
// 自定义产品表单
const [selfProduct] = Form.useForm<any>();
// 是否自定义产品类型
const [isCustomProd, setIsCustomProd] = useState<boolean>(false);
//产品列表
const [productList, setProductList] = useState<productType>([]);
//产品规格列表
const [productSpecList, setProductSpecList] = useState<productSpecType>([]);
// 配置价格弹窗
const [configurePriceModalShow, setConfigurePriceModalShow] = useState<boolean>(false);
const [customRowData, setCustomRowData] = useState<Partial<customizeEntity>>({
id: -1,
productSpecCPQVO: {},
});
const [selfProductData, setSelfProductData] = useState<customizeEntity[]>([
{
id: 1,
specName: '',
partNo: '',
specImage: '',
fileList: [],
productSpecCPQVO: {},
},
]);
// 等级标签列表
const [tagInfoList, setTagInfoList] = useState<cooperationTagResponseType>([]);
// 自定义id数组
const [selfIds, setSelfIds] = useState<number[]>([]);
// 自定义选项表格
const selfSelectColumns = [
{
title: '选项名称',
align: 'center',
dataIndex: 'specName',
editable: true,
rules: [{ required: true, message: '请输入选项名称' }],
},
{
title: '料号',
align: 'center',
dataIndex: 'partNo',
editable: true,
rules: [{ required: true, message: '请输入料号' }],
},
{
title: '操作',
align: 'center',
width: '220px',
ellipsis: true,
render: (_text: string, record: any, i: number) => {
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Uploader
fileUpload
listType='picture-card'
onChange={(fileList) => selfUploadSuccess(fileList, record, i)}
defaultFileList={record.fileList}
>
<UploadOutlined />
</Uploader>
<Button size='small' type='link' onClick={() => configurePriceEvent(record)}>
配置价格
</Button>
{i != 0 ? (
<Button
className='delete'
size='small'
type='link'
onClick={() => deleteSelfProduct(record)}
>
删除
</Button>
) : (
''
)}
</div>
);
},
},
];
const mergedColumns = selfSelectColumns.map((col: any) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: any) => ({
record,
inputType: col.dataIndex === 'age' ? 'number' : 'text',
dataIndex: col.dataIndex,
title: col.title,
editing: true,
rules: col.rules,
}),
};
});
//
// // 根据所属行业获取方案列表
// const handleIndeTypeSelect = async (id: number) => {
// getIndustrySku(id);
// skuForm.setFieldsValue({
// skuId: undefined,
// specIds: undefined,
// });
// };
//
// 根据选择产品或行业获取选项来源列表
const handleProdOrInduSelected = (id: number) => {
getProductSpecList(id);
};
// // 选项来源选中
const handleSourceSelect = (id: number | string) => {
const values: any[] = skuForm.getFieldValue('specIds');
const obj = productSpecList.find((v) => v.id === id);
if (!obj?.priceList) {
message.warning('所选规格来源未配置好价格,请配置好SKU价格后重新选择!');
const index: number = values.findIndex((i: number) => i === id);
values.splice(index, 1);
}
skuForm.setFieldValue('specIds', values);
};
// // 规格来源切换
const skuSourceRadioChange = (e: RadioChangeEvent) => {
setIsCustomProd(e.target.value === 1);
};
// // 清除表单
const clearEvent = () => {
skuForm.resetFields();
selfProduct.resetFields();
setSelfProductData([
{
id: 1,
specName: '',
partNo: '',
specImage: '',
fileList: [],
productSpecCPQVO: {},
},
]);
setCustomRowData({
id: -1,
productSpecCPQVO: {},
});
setIsCustomProd(false);
};
const onCancel = () => {
// 关闭前恢复初始值
clearEvent();
handleCancel();
};
//获取产品列表
const getProductList = () => {
ProduceManageAPI.listPageProductSku({ pageNo: 1, pageSize: 9999, type: goodsType }).then(
({ result }) => {
setProductList(result.list || []);
},
);
};
//产品-规格
const getProductSpecList = (productSkuId: number, isDetail?: boolean) => {
ProduceManageAPI.listPageProductSpec({ pageNo: 1, pageSize: 9999, productSkuId }).then(
({ result }) => {
setProductSpecList(result.list || []);
if (result.list && !isDetail) {
const ids: number[] = result.list.reduce((pre: number[], cur) => {
return cur.priceList || goodsType === 1 ? [...pre, cur.id] : pre;
}, []);
skuForm.setFieldValue('specIds', ids.length ? ids : undefined);
}
},
);
};
// 添加自定义产品项
const addSelfSelectItem = () => {
setSelfProductData([
...selfProductData,
{
id: selfProductData.length + 1,
specName: '',
partNo: '',
specImage: '',
fileList: [],
productSpecCPQVO: {},
},
]);
};
// 删除自定义产品
const deleteSelfProduct = (record: customizeEntity) => {
const cusObj: customizeEntity | undefined =
curtRowData.customizeInfo &&
curtRowData.customizeInfo.find((i: customizeEntity) => i.id === record.id);
if (cusObj) {
setSelfIds([...selfIds, cusObj.id]);
}
const index: number = selfProductData.findIndex((item: any) => item.id === record.id);
selfProductData.splice(index, 1);
setSelfProductData([...selfProductData]);
};
//自定义商品上传成功
const selfUploadSuccess = (
fileList: {
id: number;
name: string;
uid: number;
url: string;
}[],
record: any,
index: number,
) => {
record.fileList = [
{
uid: `${Math.random()}`,
status: 'done',
url: fileList[0].url,
name: fileList[0].name,
},
];
record.specImage = fileList[0].url;
selfProductData.splice(index, 1, record);
setSelfProductData([...selfProductData]);
};
// 配置价格操作
const configurePriceEvent = (record: customizeEntity) => {
getTagNameList();
setConfigurePriceModalShow(true);
setCustomRowData({ ...record });
};
const configurePriceHandleOk = (specPrice: any, leaseTerm?: number) => {
setConfigurePriceModalShow(false);
const index: number = selfProductData.findIndex(
(i: customizeEntity) => i.id === customRowData.id,
);
selfProductData[index].productSpecCPQVO = {
productSpecId: selfProductData[index].productSpecCPQVO.productSpecId || undefined,
specPrice,
type: goodsType,
leaseTerm,
};
selfProductData.splice(index, 1, selfProductData[index]);
setSelfProductData([...selfProductData]);
};
const configurePriceHandleCancel = () => {
setConfigurePriceModalShow(false);
};
// 获取等级标签
const getTagNameList = () => {
ProduceManageAPI.getCooperationListTag().then(({ result }) => {
setTagInfoList(result || []);
});
};
// 点击确定,触发表单验证
const submitSku = () => {
skuForm.validateFields().then(async (values) => {
if (!isCustomProd) {
let productObj: productType[0] | undefined = undefined;
switch (currentDesc) {
case 2:
// const schemeObj = schemeList.find((i) => i.id === values.skuId);
// values.skuName = schemeObj?.solutionName;
break;
default:
productObj = productList.find((i) => i.id === values.skuId);
values.skuName = productObj?.productName;
}
values.specIds = values.specIds.reduce((pre: any, cur: number | string) => {
const sourceObj = productSpecList.find((i) => i.id === cur);
const specObj: any =
curtRowData?.specIds && curtRowData?.specIds.find((i: any) => i.mallSpecId === cur);
pre.push({
mallSpecId: cur,
specName: sourceObj?.specName,
partNo: sourceObj?.partNo,
id: specObj ? specObj.id : undefined,
});
return pre;
}, []);
handleOk({ ...values, id: curtRowData?.id || Math.random() });
} else {
const res: any = await Promise.all([validateCustomForm()]);
handleOk({
...values,
customizeInfo: res[0],
id: curtRowData?.id || Math.random(),
skuId: curtRowData?.skuId,
skuName: values.productName,
delProductSpecId: selfIds,
});
}
clearEvent();
});
// 验证自定义表单
const validateCustomForm: any = () => {
return new Promise((resolve, reject) => {
selfProduct
.validateFields()
.then(async (values) => {
for (let i = 0; i < selfProductData.length; i++) {
const v = selfProductData[i];
if (!v.specImage) {
return message.warning(`自定义选项第${i + 1}行未上传图片`);
}
}
for (let i = 0; i < selfProductData.length; i++) {
const v = selfProductData[i];
if (!(Object.keys(v.productSpecCPQVO).length != 0 && v.productSpecCPQVO.specPrice)) {
return message.warning(`自定义选项第${i + 1}行未配置价格`);
}
}
const arr: customizeEntity[] = selfProductData.map((i: customizeEntity) => {
return {
specName: values[`specName${i.id}`],
partNo: values[`partNo${i.id}`],
specImage: i.specImage,
productSpecCPQVO: i.productSpecCPQVO,
fileList: i.fileList,
id: i.id,
productSkuId: curtRowData && curtRowData.skuId,
};
});
resolve([...arr]);
})
.catch((err) => {
message.warning(err.errorFields[0].errors[0]).then();
reject([]);
});
});
};
};
useEffect(() => {
if (Object.keys(curtRowData).length !== 0) {
setDialogTitle('编辑');
if (!curtRowData.flag) {
getProductSpecList(curtRowData.skuId as number, true);
}
const specIds: number[] =
curtRowData.specIds && curtRowData.specIds.map((i: any) => i.mallSpecId);
skuForm.setFieldsValue({
goodsSpecName: curtRowData.goodsSpecName,
chooseType: curtRowData.chooseType,
skuUnitId: curtRowData.skuUnitId,
categoryId: curtRowData.categoryId, // 产品类型或所属行业ID
skuId: curtRowData.skuId, // 产品或方案ID
must: curtRowData.must,
specIds, // 选项来源ID数组
productName: curtRowData.flag === 1 ? curtRowData.skuName : undefined,
flag: curtRowData.flag,
});
setIsCustomProd(curtRowData.flag === 1);
if (curtRowData.flag === 1) {
setSelfProductData([...(curtRowData.customizeInfo as customizeEntity[])]);
const objForm: any = curtRowData.customizeInfo?.reduce((pre: any, cur: customizeEntity) => {
pre[`partNo${cur.id}`] = cur.partNo;
pre[`specName${cur.id}`] = cur.specName;
return pre;
}, {});
selfProduct.setFieldsValue(objForm);
}
} else {
setDialogTitle('添加');
}
}, [curtRowData]);
useEffect(() => {
getProductList();
}, []);
return (
<div>
<Modal
open={open}
title={`${dialogTitle}规格`}
onOk={() => submitSku()}
onCancel={onCancel}
width={820}
destroyOnClose
>
<Form
preserve={false}
labelCol={{ span: 5 }}
wrapperCol={{ span: 16 }}
form={skuForm}
initialValues={{ chooseType: 0, skuUnitId: 1, must: 0, flag: 0 }}
>
<Form.Item
label='规格名称'
name='goodsSpecName'
rules={[{ required: true, message: '请输入规格名称' }]}
>
<Input placeholder='请输入规格名称' maxLength={30} />
</Form.Item>
{currentDesc != 2 && (
<Form.Item label='规格来源' name='flag'>
<Radio.Group onChange={skuSourceRadioChange}>
<Radio value={0}>获取</Radio>
<Radio value={1} disabled={goodsType === 1}>
自定义
</Radio>
</Radio.Group>
</Form.Item>
)}
{currentDesc != 2 ? (
<>
{/*<Form.Item*/}
{/* name='categoryId'*/}
{/* label='产品类型'*/}
{/* rules={[{ required: true, message: '请选择产品类型' }]}*/}
{/*>*/}
{/* <Select*/}
{/* placeholder='请选择产品类型'*/}
{/* onSelect={handleProdTypeSelect}*/}
{/* showSearch*/}
{/* filterOption={(input, option: any) =>*/}
{/* (option?.label ?? '').toLowerCase().includes(input.toLowerCase())*/}
{/* }*/}
{/* >*/}
{/* {categoryList.map((item) => (*/}
{/* <Select.Option value={item.id} key={item.id}>*/}
{/* {item.classifyName}*/}
{/* </Select.Option>*/}
{/* ))}*/}
{/* </Select>*/}
{/*</Form.Item>*/}
{!isCustomProd ? (
<Form.Item
name='skuId'
label='产品'
rules={[{ required: true, message: '请选择产品' }]}
>
<Select
placeholder='请选择产品'
onSelect={handleProdOrInduSelected}
filterOption={(input, option) =>
(option!.children as unknown as string)
.toLowerCase()
.includes(input.toLowerCase())
}
showSearch
>
{productList.map((item: any) => (
<Select.Option value={item.id} key={item.id}>
{item.productName}
</Select.Option>
))}
</Select>
</Form.Item>
) : (
<Form.Item
name='productName'
label='产品'
rules={[{ required: true, message: '请选择产品' }]}
>
<Input placeholder='请输入产品名称' maxLength={50} />
</Form.Item>
)}
</>
) : (
<>
<Form.Item
label='所属行业'
name='goodsTypeId'
rules={[{ required: true, message: '请选择所属行业' }]}
>
<Select
placeholder='请选择所属行业'
// onSelect={handleIndeTypeSelect}
filterOption={(input, option) =>
(option!.children as unknown as string)
.toLowerCase()
.includes(input.toLowerCase())
}
showSearch
>
{/*{categoryList.map((item: categoryEntity) => (*/}
{/* <Select.Option value={item.goodsMasterTypeId} key={item.goodsMasterTypeId}>*/}
{/* {item.goodsMasterType}*/}
{/* </Select.Option>*/}
{/*))}*/}
</Select>
</Form.Item>
<Form.Item
label='方案'
name='skuId'
rules={[{ required: true, message: '请选择方案' }]}
>
<Select
showSearch
placeholder='请选择方案'
// onSelect={handleProdOrInduSelected}
filterOption={(input, option) =>
(option!.children as unknown as string)
.toLowerCase()
.includes(input.toLowerCase())
}
>
{/*{schemeList.map((item: any) => (*/}
{/* <Select.Option value={item.id} key={item.id}>*/}
{/* {item.solutionName}*/}
{/* </Select.Option>*/}
{/*))}*/}
</Select>
</Form.Item>
</>
)}
{isCustomProd ? (
<Form.Item label='自定义选项'>
<div style={{ marginBottom: '10px' }}>
<Button icon={<PlusOutlined />} type='primary' onClick={addSelfSelectItem} />
</div>
<Form form={selfProduct} component={false}>
<Table
size='small'
rowKey='id'
bordered
columns={mergedColumns}
dataSource={selfProductData}
pagination={false}
components={{
body: {
cell: EditableCell,
},
}}
/>
</Form>
</Form.Item>
) : (
<Form.Item
name='specIds'
label='选项来源'
rules={[{ required: true, message: '请选择选项来源' }]}
>
<Select
placeholder='请选择选项来源'
allowClear
mode='multiple'
onSelect={handleSourceSelect}
>
{productSpecList.map((item: any) => (
<Select.Option value={item.id} key={item.id} disabled={item.disabled}>
{item.specName}&nbsp;{item.partNo && `(${item.partNo})`}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item label='选择方式' name='chooseType'>
<Radio.Group>
<Radio value={0}>单选</Radio>
<Radio value={1} disabled={goodsType === 1}>
多选
</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label='是否必选' name='must'>
<Radio.Group>
<Radio value={0}>非必选</Radio>
<Radio value={1}>必选</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label='规格单位'
name='skuUnitId'
rules={[{ required: true, message: '请选择规格单位' }]}
>
<Select placeholder='请选择规格单位'>
{skuUnitList.map((item: any) => {
return (
<Select.Option value={item.id} key={item.id}>
{item.unitName}
</Select.Option>
);
})}
</Select>
</Form.Item>
</Form>
</Modal>
{/*配置价格*/}
<ConfigurePriceModal
open={configurePriceModalShow}
handleOk={configurePriceHandleOk}
handleCancel={configurePriceHandleCancel}
customRowData={customRowData}
tagInfoList={tagInfoList}
goodsType={goodsType}
/>
</div>
);
};
export default AddOrEditSkuModal;
import React, { forwardRef, useEffect, useState, useImperativeHandle } from 'react';
import { Button, Form, Input, Radio, Select, Cascader } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { Uploader } from '~/components/uploader';
import './index.scss';
import { BaseInfoType, detailGoodsType } from '~/api/interface/goodsType';
import { CategoryManageAPI } from '~/api';
import { InterDataType } from '~/api/interface';
import { categoryListType, directoryPageListType } from '~/api/interface/categoryManage';
import deletePng from '~/assets/image/delete.png';
//目录返回类型
type directoryType = InterDataType<directoryPageListType>['list'];
//分类返回类型
type categoryType = InterDataType<categoryListType>['list'];
//商品返回类型
type goodsDetailType = InterDataType<detailGoodsType>;
interface selfProps {
getCategoryList: (id: number) => void;
categoryList: categoryType;
ref: any;
goodsDetail: goodsDetailType | undefined;
isDetail: boolean; //是否详情
goodsType: number; //0:销售,1:租赁
}
const BaseInfo: React.FC<selfProps> = forwardRef(
({ getCategoryList, categoryList, goodsDetail, isDetail, goodsType }: selfProps, ref) => {
const [form] = Form.useForm<
BaseInfoType & {
mainImg: {
id: number;
name: string;
uid: number;
url: string;
}[];
subImg: {
id: number;
name: string;
uid: number;
url: string;
}[];
masterTypeId: number[];
}
>();
//目录
const [directoryList, setDirectoryList] = useState<directoryType>([]);
//是否添加标签
const [isAddTag, setIsAddTag] = useState<boolean>(false);
//主图文件列表
const [mainImgList, setMainImgList] = useState<
{
id: number;
name: string;
uid: number;
url: string;
}[]
>([]);
//副图文件列表
const [subImgList, setSubImgList] = useState<
{
id: number;
name: string;
uid: number;
url: string;
}[]
>([]);
//视频文件列表
const [videoList, setVideoList] = useState<
{
id: number;
name: string;
uid: number;
url: string;
}[]
>([]);
useImperativeHandle(ref, () => ({
baseInform: form,
}));
//获取目录列表
const getDirectoryList = () => {
CategoryManageAPI.getDirectoryListClone({ type: goodsType ? 2 : 4 }).then(({ result }) => {
setDirectoryList(result || []);
});
};
const directorySelectChange = (value: number) => {
form.setFieldsValue({
masterTypeId: undefined,
});
getCategoryList(value);
};
//商品标签
const radioChangeEvent = (e: any) => {
setIsAddTag(e.target.value);
};
//主图上传
const mainImgUploadSuccess = (
fileList: {
id: number;
name: string;
uid: number;
url: string;
}[],
) => {
setMainImgList(fileList);
form.setFieldsValue({
mainImg: fileList,
});
};
//副图上传
const subImgUploadSuccess = (
fileList: {
id: number;
name: string;
uid: number;
url: string;
}[],
) => {
setSubImgList(fileList);
form.setFieldsValue({
subImg: fileList,
});
};
//视频上传
const videoUploadSuccess = (
fileList: {
id: number;
name: string;
uid: number;
url: string;
}[],
) => {
setVideoList(fileList);
form.setFieldsValue({
goodsVideo: fileList[0].url,
});
};
//视频移除
const deleteVideo = () => {
setVideoList([]);
form.setFieldsValue({
goodsVideo: undefined,
});
};
useEffect(() => {
getDirectoryList();
}, []);
useEffect(() => {
if (goodsDetail) {
const goodsMainImg = goodsDetail.images
.filter((v) => v.imgType === 0)
.map((v) => ({
id: v.id,
name: 'mainImg',
uid: v.id,
url: v.imgUrl,
}));
setMainImgList(goodsMainImg);
const goodsSubImg = goodsDetail.images
.filter((v) => v.imgType === 1)
.map((v) => ({
id: v.id,
name: 'sunImg',
uid: v.id,
url: v.imgUrl,
}));
setSubImgList(goodsSubImg);
if (goodsDetail.goodsVideo) {
setVideoList([
{
id: goodsDetail.goodsVideoId || Math.random(),
name: 'video',
uid: goodsDetail.goodsVideoId || Math.random(),
url: goodsDetail.goodsVideo,
},
]);
}
form.setFieldsValue({
mainImg: goodsMainImg,
subImg: goodsSubImg.length ? goodsSubImg : undefined,
goodsVideo: goodsDetail.goodsVideo || undefined,
goodsName: goodsDetail.goodsName,
goodsDesc: goodsDetail.goodsDetail.goodsDesc,
masterTypeId: [goodsDetail.categoryByOne, goodsDetail.categoryByTwo],
directoryId: goodsDetail.directoryId,
shelfStatus: goodsDetail.shelfStatus,
tag: goodsDetail.tag || undefined,
});
setIsAddTag(!!goodsDetail.tag);
}
}, [goodsDetail]);
return (
<div className='base-info'>
<div className='base-info-title'>基本信息</div>
<div className='base-info-form'>
<Form
labelCol={{ span: 2 }}
wrapperCol={{ span: 16 }}
initialValues={{ shelfStatus: 1 }}
form={form}
disabled={isDetail}
>
<Form.Item
name='mainImg'
label='商品主图'
rules={[{ required: true, message: '请上传商品主图' }]}
>
<Uploader
listType='picture-card'
fileUpload
onChange={mainImgUploadSuccess}
defaultFileList={mainImgList}
>
<UploadOutlined />
</Uploader>
</Form.Item>
<Form.Item label='商品副图' name='subImg'>
<Uploader
listType={isDetail && subImgList.length === 0 ? 'text' : 'picture-card'}
fileUpload
onChange={subImgUploadSuccess}
fileLength={3}
defaultFileList={subImgList}
>
{isDetail ? subImgList.length ? '' : '暂无' : <UploadOutlined />}
</Uploader>
</Form.Item>
<Form.Item label='商品视频' name='goodsVideo'>
{videoList.length ? (
<div className='goods-video-wrap'>
<video
src={videoList[0].url}
style={{ width: '200px', height: '200px' }}
controls
/>
{!isDetail && <img src={deletePng} alt='删除' onClick={deleteVideo} />}
</div>
) : (
<Uploader
listType='text'
fileUpload
onChange={videoUploadSuccess}
defaultFileList={videoList}
fileType={['video/mp4', 'video/avi', 'video/wmv', 'video/rmvb']}
fileSize={50}
>
{isDetail ? (
videoList.length ? (
''
) : (
'暂无'
)
) : (
<Button icon={<UploadOutlined />}>上传视频</Button>
)}
</Uploader>
)}
</Form.Item>
<Form.Item
name='goodsName'
label='商品名称'
rules={[{ required: true, message: '请输入商品名称' }]}
>
<Input placeholder='请输入商品名称' maxLength={50} style={{ width: '400px' }} />
</Form.Item>
<Form.Item
name='goodsDesc'
label='商品描述'
rules={[{ required: true, message: '请输入商品描述' }]}
>
<Input.TextArea
placeholder='请输入商品描述'
maxLength={70}
style={{ width: '400px' }}
rows={4}
showCount
/>
</Form.Item>
<Form.Item
name='directoryId'
label='所属目录'
rules={[{ required: true, message: '请选择所属目录' }]}
>
<Select
placeholder='请选择所属目录'
style={{ width: '400px' }}
onChange={directorySelectChange}
>
{directoryList.map((v) => (
<Select.Option value={v.id} key={v.id}>
{v.directoryName}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name='masterTypeId'
label='商品分类'
rules={[{ required: true, message: '请选择商品分类' }]}
>
<Cascader
style={{ width: '400px' }}
options={categoryList}
fieldNames={{
label: 'classifyName',
value: 'id',
children: 'children',
}}
placeholder='请选择商品分类'
allowClear
/>
</Form.Item>
<Form.Item label='商品标签'>
<Radio.Group onChange={radioChangeEvent} value={isAddTag}>
<Radio value={false}>不加</Radio>
<Radio value={true}></Radio>
</Radio.Group>
</Form.Item>
{isAddTag && (
<Form.Item
name='tag'
label='标签名称'
rules={[{ required: true, message: '请输入标签名称' }]}
>
<Input placeholder='请输入标签名称' style={{ width: '400px' }} maxLength={5} />
</Form.Item>
)}
<Form.Item
label='商品状态'
name='shelfStatus'
rules={[{ required: true, message: '请选择商品状态' }]}
>
<Select placeholder='请选择商品状态' style={{ width: '400px' }}>
<Select.Option value={1}>上架</Select.Option>
<Select.Option value={0}>下架</Select.Option>
</Select>
</Form.Item>
</Form>
</div>
</div>
);
},
);
export default BaseInfo;
import React, { useEffect, useState } from 'react';
import { Modal, Form, Select, Input, message, Button, ModalProps } from 'antd';
import { InterDataType } from '~/api/interface';
import { cooperationTagType } from '~/api/interface/produceManageType';
import { customizeEntity } from '~/api/interface/goodsType';
import { filterObjAttr } from '~/utils';
//加盟标签返回类型
type cooperationTagResponseType = InterDataType<cooperationTagType>;
interface selfProps {
handleOk: (specPrice: any, leaseTerm: number) => void;
handleCancel: () => void;
customRowData: Partial<customizeEntity>;
tagInfoList: cooperationTagResponseType;
goodsType: number;
}
const ConfigurePriceModal: React.FC<ModalProps & selfProps> = ({
open,
handleOk,
handleCancel,
customRowData,
tagInfoList,
goodsType,
}) => {
// 选择的列表
const [selectList, setSelectList] = useState<number[]>([]);
// 配置价格Form
const [cfgPriceForm] = Form.useForm<any>();
const deselectEvent = (id: number) => {
const obj: any = {};
obj[id] = undefined;
cfgPriceForm.setFieldsValue(obj);
const numArr: number[] = selectList.filter((i: number) => i !== id);
setSelectList([...numArr]);
};
const levelSelectEvent = (id: number) => {
selectList.push(id);
setSelectList([...selectList]);
};
// 将val转换为label
const transValtoLabel = (id: number) => {
const item = tagInfoList.find((i) => i.id === id);
return item ? item.tagName : id;
};
// 表单验证
const handleSubmit = async () => {
cfgPriceForm
.validateFields()
.then(async (values) => {
const specPrice = Object.keys(filterObjAttr(values, ['leaseTerm'])).reduce(
(pre: any, cur: string) => {
if (Object.keys(customRowData.productSpecCPQVO).length != 0) {
const priceItem: any = customRowData.productSpecCPQVO.specPrice.find(
(i: any) => i.cooperationTag === Number(cur),
);
pre.push({
id: priceItem?.id,
price: values[cur],
cooperationTag: cur,
});
} else {
pre.push({ price: values[cur], cooperationTag: cur });
}
return pre;
},
[],
);
handleOk([...specPrice], values.leaseTerm);
})
.catch((err) => {
message.warning(err.errorFields[0].errors[0]).then();
});
};
const handleCancelEvent = () => {
cfgPriceForm.resetFields();
handleCancel();
};
// 价格正则
const priceValidator = (_rule: any, value: any) => {
const regExp = /^[1-9]\d{0,7}(\.\d{1,2})?$|^0(\.\d{1,2})?$/;
const bol: boolean = regExp.test(value);
if (!value) {
return Promise.reject(new Error('请输入定价金额'));
}
if (!bol) {
return Promise.reject(
new Error('金额应为数字,小数最多两位,整数最多八位,不能输入0开头的整数'),
);
}
return Promise.resolve();
};
useEffect(() => {
// 新增规格则清空表单数据
if (Object.keys(customRowData.productSpecCPQVO).length === 0) {
cfgPriceForm.resetFields();
setSelectList([]);
} else {
cfgPriceForm.setFieldValue('leaseTerm', customRowData.productSpecCPQVO.leaseTerm);
const ids: number[] = [];
customRowData.productSpecCPQVO.specPrice.map((item: any) => {
cfgPriceForm.setFieldValue(Number(item.cooperationTag), item.price);
if (item.cooperationTag != '0') {
ids.push(Number(item.cooperationTag));
}
setSelectList(ids);
});
}
}, [customRowData]);
return (
<Modal
title='配置价格'
open={open}
onCancel={handleCancelEvent}
zIndex={1009}
footer={[
<Button key={1} type='default' onClick={handleCancelEvent}>
取消
</Button>,
<Button key={2} type='primary' onClick={handleSubmit}>
确认
</Button>,
]}
>
<Form
labelCol={{ span: 7 }}
wrapperCol={{ span: 14 }}
form={cfgPriceForm}
initialValues={{ leaseTerm: 0 }}
>
{goodsType === 1 && (
<Form.Item
label='租期'
name='leaseTerm'
rules={[{ required: true, message: '请选择租期' }]}
>
<Select>
<Select.Option value={0}>1-7天</Select.Option>
<Select.Option value={1}>8-15天</Select.Option>
<Select.Option value={2}>16-30天</Select.Option>
<Select.Option value={3}>31天以上</Select.Option>
</Select>
</Form.Item>
)}
<Form.Item label='渠道等级'>
<Select
placeholder='请选择渠道等级'
mode='multiple'
filterOption={(input, option) =>
(option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
}
onDeselect={deselectEvent}
onSelect={levelSelectEvent}
value={selectList}
>
{tagInfoList.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.tagName}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label='市场单价'
name={0}
rules={[{ required: true, validator: priceValidator }]}
>
<Input placeholder='请输入市场单价' maxLength={11} />
</Form.Item>
{selectList.map((item: number) => (
<Form.Item
label={transValtoLabel(item)}
key={item}
name={item}
rules={[{ required: true, validator: priceValidator }]}
>
<Input placeholder='请输入定价金额' maxLength={11} />
</Form.Item>
))}
</Form>
</Modal>
);
};
export default ConfigurePriceModal;
.goods-introduce {
&-title {
font-size: 15px;
font-weight: bold;
margin: 20px 0;
}
&-content {
margin-left: 50px;
}
}
.ql-container {
height: 400px;
}
import './index.scss';
import RichText from '~/components/richText';
import { FC } from 'react';
import { InterDataType } from '~/api/interface';
import { detailGoodsType } from '~/api/interface/goodsType';
//商品返回类型
type goodsDetailType = InterDataType<detailGoodsType>;
interface selfProps {
getRichText: (html?: string) => void;
goodsDetail: goodsDetailType | undefined;
isDetail: boolean;
}
const GoodsIntroduce: FC<selfProps> = ({ getRichText, goodsDetail, isDetail }) => {
const richTextChange = (html?: string) => {
getRichText(html);
};
return (
<div className='goods-introduce'>
<div className='goods-introduce-title'>产品介绍图</div>
<div className='goods-introduce-content'>
<RichText
richTextContent={goodsDetail?.goodsDetail.content}
onChange={richTextChange}
isDetail={isDetail}
/>
</div>
</div>
);
};
export default GoodsIntroduce;
.other-info {
&-title {
font-size: 15px;
font-weight: bold;
margin: 20px 0;
}
&-form {
margin-left: 50px;
}
}
import React, { useEffect, useState } from 'react';
import { Checkbox, Form } from 'antd';
import './index.scss';
import GoodsAPI from '~/api/modules/goodsAPI';
import { detailGoodsType, otherServiceType } from '~/api/interface/goodsType';
import { InterDataType } from '~/api/interface';
//商品返回类型
type goodsDetailType = InterDataType<detailGoodsType>;
interface selfProps {
otherServiceSelect: (id: number[]) => void;
goodsDetail: goodsDetailType | undefined;
isDetail: boolean;
}
//其它服务返回类型
type otherServiceListType = InterDataType<otherServiceType>;
const OtherInfo: React.FC<selfProps> = ({ otherServiceSelect, goodsDetail, isDetail }) => {
const [otherInfoForm] = Form.useForm();
//其它服务
const [otherServiceList, setOtherServiceList] = useState<otherServiceListType>([]);
const otherServiceRadioChange = (e: any) => {
otherServiceSelect(e);
};
//获取其它服务
const getOtherServiceList = () => {
GoodsAPI.getOtherServiceList().then(({ result }) => {
setOtherServiceList(result);
});
};
useEffect(() => {
getOtherServiceList();
}, []);
useEffect(() => {
if (goodsDetail) {
otherInfoForm.setFieldsValue({
otherService: goodsDetail.otherService.map((v) => v.saleServiceId),
});
}
}, [goodsDetail]);
return (
<div className='other-info'>
<div className='other-info-title'>其它信息</div>
<div className='other-info-form'>
<Form form={otherInfoForm} disabled={isDetail}>
<Form.Item label='搭配服务' name='otherService'>
<Checkbox.Group onChange={otherServiceRadioChange}>
{otherServiceList.map((item: any, index: number) => (
<Checkbox value={item.id} key={index}>
{item.saleServiceName}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Form>
</div>
</div>
);
};
export default OtherInfo;
.stock-sku {
&-title {
font-size: 15px;
font-weight: bold;
margin: 20px 0;
}
&-content {
margin-left: 50px;
}
&-operate {
margin-bottom: 10px;
}
}
import React from 'react';
import { Table, Button } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import './index.scss';
import { ColumnsType } from 'antd/es/table';
import { customizeEntity, skuUnitType, specEntity } from '~/api/interface/goodsType';
import { InterDataType } from '~/api/interface';
//产品-规格单位返回类型
type unitType = InterDataType<skuUnitType>;
interface selfProps {
addOrEditSku: (record?: specEntity) => void;
specData: specEntity[];
skuUnitList: unitType;
deleteSku: (record: specEntity) => void;
isDetail: boolean;
}
//机构列表
const StockSku: React.FC<selfProps> = ({
addOrEditSku,
specData,
skuUnitList,
deleteSku,
isDetail,
}) => {
const columns: ColumnsType<specEntity> = [
{
title: '序号',
align: 'center',
render: (_text: string, _record: specEntity, index: number) => index + 1,
},
{
title: '规格名称',
align: 'center',
dataIndex: 'goodsSpecName',
},
{
title: `方案`,
align: 'center',
dataIndex: 'skuName',
},
{
title: '选项来源',
align: 'center',
dataIndex: 'specIds',
render: (_text: string, record) => (
<div>
{record.flag !== 1
? record.specIds.map((i: any, j: number) => (
<div key={j}>
{i.specName}
{i.partNo && `(${i.partNo})`}
</div>
))
: record.customizeInfo &&
record.customizeInfo.map((i, index: number) => (
<div key={index}>{getSelfSpecName(i)}</div>
))}
</div>
),
},
{
title: '选择方式',
align: 'center',
dataIndex: 'chooseType',
render: (text: number) => <div>{text === 0 ? '单选' : '多选'}</div>,
},
{
title: '是否必选',
align: 'center',
dataIndex: 'must',
render: (text: number) => <div>{text ? '必选' : '非必选'}</div>,
},
{
title: '规格单位',
align: 'center',
dataIndex: 'skuUnitId',
render: (text: number) => {
return <div>{getSkuUnitName(text)}</div>;
},
},
{
title: '操作',
align: 'center',
width: '20%',
render: (_text: any, record) => {
return (
<div>
<Button
type='link'
style={{ marginRight: '10px' }}
onClick={() => addOrEditSkuClick(record)}
>
编辑
</Button>
<Button type='link' onClick={() => deleteSkuClick(record)}>
删除
</Button>
</div>
);
},
},
];
//添加、编辑规格操作
const addOrEditSkuClick = (record?: specEntity) => {
addOrEditSku(record);
};
//删除规格操作
const deleteSkuClick = (record: specEntity) => {
deleteSku(record);
};
// 自定义选项来源名称
const getSelfSpecName = (obj: customizeEntity) => {
return `${obj.specName}(${obj.partNo || ''})`;
};
// 单位名称
const getSkuUnitName = (id: number) => {
const unitObj = skuUnitList.find((i) => i.id === id);
return unitObj?.unitName;
};
return (
<div className='stock-sku'>
<div className='stock-sku-title'>库存规格</div>
<div className='stock-sku-content'>
{!isDetail && (
<div className='stock-sku-operate'>
<Button icon={<PlusOutlined />} type='primary' onClick={() => addOrEditSkuClick()}>
添加规格
</Button>
</div>
)}
<Table
size='small'
bordered
dataSource={specData}
rowKey='id'
pagination={false}
columns={isDetail ? columns.filter((v) => v.title !== '操作') : columns}
/>
</div>
</div>
);
};
export default StockSku;
import { Button, Col, Form, Input, message, Row, Select, Table, Tag } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { forwardRef, useImperativeHandle, useState } from 'react';
import { filterObjAttr } from '~/utils';
import EditableCell from '~/components/EditableCell';
type EditableTableProps = Parameters<typeof Table>[0];
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
interface selfProps {
tableData: any;
updateTableData: (tableData: any) => void;
defaultColumns: (ColumnTypes[number] & {
editable?: boolean;
dataIndex?: string;
inputType?: string;
radioOption?: { name: string; id: number }[];
rules?: any;
maxLength?: number;
children?: any;
placeholder?: string;
})[];
updateDefaultColumns: (
columns: (ColumnTypes[number] & {
editable?: boolean;
dataIndex?: string;
inputType?: string;
radioOption?: { name: string; id: number }[];
rules?: any;
maxLength?: number;
children?: any;
placeholder?: string;
})[],
) => void;
uploadSuccess?: (record: any, fileList: any) => void; //可编辑表单存在上传图片的时候
ref: any;
}
//规格表单数据类型
type specificationFormListType = {
optionList: { label: string; value: string }[];
id: number;
name: string;
addSpecificationValueShow: boolean;
specificationValueList: { name: string; id: number; specificationName: string }[];
};
const CommonSkuInfo = forwardRef<any, selfProps>(
({ tableData, updateTableData, defaultColumns, updateDefaultColumns, uploadSuccess }, ref) => {
//规格项表单
const [form] = Form.useForm<{ [x: string]: string }>();
//可编辑表格表单
const [skuValueForm] = Form.useForm<{ [x: string]: string }>();
//规格表单数组
const [specificationFormList, setSpecificationFormList] = useState<specificationFormListType[]>(
[
{
id: Math.random(),
name: `specName`,
optionList: [],
specificationValueList: [],
addSpecificationValueShow: false,
},
],
);
useImperativeHandle(ref, () => ({
getSpecificationForm: () => form,
getSpecificationValueForm: () => skuValueForm,
getSpecificationFormList: () => specificationFormList,
updateSpecificationFormList: (value: specificationFormListType[]) =>
setSpecificationFormList(value),
}));
//新增规格项目
const addSpecificationClick = () => {
setSpecificationFormList([
...specificationFormList,
{
id: Math.random(),
name: `specName${specificationFormList.length + 1}`,
optionList: [],
specificationValueList: [],
addSpecificationValueShow: false,
},
]);
};
// 删除规格项目
const deleteSpecificationClick = (index: number) => {
form.setFieldValue(specificationFormList[index].name, undefined);
specificationFormList.splice(index, 1);
combineSpecificationValue();
setSpecificationFormList([...specificationFormList]);
};
//规格项名称输入完成
const specificationPressEnter = (e: any, index: number) => {
specificationFormList[index].optionList = e.target.value
? [{ label: e.target.value, value: e.target.value }]
: [];
form.setFieldValue(specificationFormList[index].name, e.target.value);
setSpecificationFormList([...specificationFormList]);
};
//规格值添加
const addSpecificationValueClick = (index: number) => {
specificationFormList[index].addSpecificationValueShow = true;
setSpecificationFormList([...specificationFormList]);
};
//存在
const specificationValuePressEnter = (e: any, index: number) => {
const isExist = specificationFormList[index].specificationValueList.some(
(v) => v.name === e.target.value,
);
if (isExist) {
return message.warning('该规格值已存在');
}
specificationFormList[index].specificationValueList.push({
id: Math.random(),
name: e.target.value,
specificationName: specificationFormList[index].optionList[0].value,
});
combineSpecificationValue();
setSpecificationFormList(specificationFormList);
specificationValueCancel(index);
};
//规格值添加-取消
const specificationValueCancel = (index: number) => {
specificationFormList[index].addSpecificationValueShow = false;
setSpecificationFormList([...specificationFormList]);
};
//规格值-删除
const specificationValueDelete = (i: number, j: number) => {
specificationFormList[i].specificationValueList.splice(j, 1);
combineSpecificationValue();
setSpecificationFormList([...specificationFormList]);
};
//组合数据
const combineSpecificationValue = () => {
let combineSpecificationList: any = [];
let tableDataList: any = [];
//过滤规格值为空的
const filterSpecificationFormList = specificationFormList.filter(
(v) => v.specificationValueList.length,
);
if (filterSpecificationFormList.length > 1) {
const combineList = filterSpecificationFormList.reduce((pre: any, cur, currentIndex) => {
// 首次组合两个数据
if (currentIndex === 0 && filterSpecificationFormList.length > 1) {
combineSpecificationList = combineEvent(
cur.specificationValueList,
filterSpecificationFormList[currentIndex + 1].specificationValueList,
);
//二维数组拆分为对象
combineSpecificationList = getCombineObj(combineSpecificationList);
// 两个数据以上的组合
} else if (
currentIndex < filterSpecificationFormList.length - 1 &&
filterSpecificationFormList[currentIndex + 1].specificationValueList.length
) {
// 上一次的组合作为下一次组合的参数
combineSpecificationList = combineEvent(
combineSpecificationList,
filterSpecificationFormList[currentIndex + 1].specificationValueList,
);
//二维数组拆分为对象
combineSpecificationList = getCombineObj(combineSpecificationList);
}
pre = combineSpecificationList;
return pre;
}, []);
tableDataList = combineList.reduce((pre: any, cur: any) => {
const tabCovertObj = cur.reduce((a: any, b: any, currentIndex: number) => {
a['name' + (currentIndex + 1)] = b.name;
a['specificationName' + (currentIndex + 1)] = b.specificationName;
return a;
}, {});
//判断表格中是否已存在该条(缓存)
const tableItemObj = tableData.find((i: any) =>
Object.getOwnPropertyNames(tabCovertObj).every((key) => i[key] === tabCovertObj[key]),
);
pre.push({
...tabCovertObj,
id: tableItemObj ? tableItemObj.id : Math.random(), //存在则用之前的id,不存在新建id
fileList: tableItemObj ? tableItemObj.fileList : [],
});
return pre;
}, []);
} else if (filterSpecificationFormList.length === 1) {
//当存在一个规格项时
tableDataList = filterSpecificationFormList[0].specificationValueList.map((v) => {
const obj = Object.create(null);
obj['name1'] = v.name;
obj['specificationName1'] = v.specificationName;
//判断表格中是否已存在该条(缓存)
const tableItemObj = tableData.find((i: any) =>
Object.getOwnPropertyNames(obj).every((key) => i[key] === obj[key]),
);
obj['id'] = Math.random();
return tableItemObj ? tableItemObj : obj; //存在则用之前的一条,不存在新建一条
});
}
if (tableDataList.length) {
setTableFormDefault(tableDataList);
mergeTableRow(filterSpecificationFormList);
}
updateTableData([...tableDataList]);
};
//组合数据拆分为对象
const getCombineObj = (combineSpecificationList: any) => {
return combineSpecificationList.reduce((pre: any, cur: any) => {
pre.push(
cur.reduce((a: any, b: any) => {
if (Array.isArray(b)) {
a.push(...b);
} else {
a.push({ ...b });
}
return a;
}, []),
);
return pre;
}, []);
};
//两数组排列组合(通用)
const combineEvent = (list1: any, list2: any) => {
return list1.reduce((pre: any, cur: any) => pre.concat(list2.map((v: any) => [cur, v])), []);
};
//表头拆分及合并列
const mergeTableRow = (filterSpecificationFormList: specificationFormListType[]) => {
const columns = filterSpecificationFormList.map((v, index) => ({
title: v.optionList[0].value,
dataIndex: 'name' + (index + 1),
align: 'center',
onCell: (_: any, i: number) => {
//合并列
if (index < filterSpecificationFormList.length - 1) {
const count: number = filterSpecificationFormList
.slice(index + 1, filterSpecificationFormList.length)
.reduce((pre: number, cur) => {
return pre * cur.specificationValueList.length;
}, 1);
return {
rowSpan: count !== 1 ? ((i + 1) % count === 1 ? count : 0) : 1,
};
} else {
return {
rowSpan: 1,
};
}
},
}));
defaultColumns[0].children = columns;
updateDefaultColumns([...defaultColumns]);
};
//排列组合规格值表单默认数据
const setTableFormDefault = (tableDataList: any) => {
const tableFormDefault = tableDataList.reduce((pre: any, cur: any) => {
return {
...pre,
...Object.getOwnPropertyNames(filterObjAttr(cur, ['id'])).reduce((a: any, b) => {
a[b + cur.id] = cur[b];
return a;
}, {}),
};
}, {});
skuValueForm.setFieldsValue(tableFormDefault);
};
const covertColumns = () => {
return defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: any) => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: col.editable,
radioOption: col.radioOption,
inputType: col.inputType,
uploadSuccess: col.inputType === 'uploader' ? uploadSuccess : undefined,
rules: col.rules,
placeholder: col.placeholder,
maxLength: col.maxLength,
}),
};
});
};
return (
<div className='common-sku-info'>
<Form wrapperCol={{ span: 5 }} labelCol={{ span: 1 }} form={form}>
{specificationFormList.map((v, index) => (
<div key={v.id}>
{/* 规格项*/}
<Row>
<Col span={7}>
<Form.Item
label={'规格项' + (index + 1)}
wrapperCol={{ span: 18 }}
labelCol={{ span: 5 }}
name={v.name}
rules={[{ required: true, message: `请输入规格项${index + 1}` }]}
>
<Select
placeholder='请输入规格项,按回车键完成'
dropdownRender={(menu) => (
<>
{v.optionList.length ? menu : ''}
<Input
onPressEnter={(e) => specificationPressEnter(e, index)}
maxLength={30}
/>
</>
)}
options={v.optionList}
/>
</Form.Item>
</Col>
{index ? (
<Col span={2}>
<Button
danger
icon={<DeleteOutlined />}
onClick={() => deleteSpecificationClick(index)}
>
删除
</Button>
</Col>
) : (
''
)}
</Row>
{/*规格值显示*/}
{v.specificationValueList.length ? (
<Row style={{ marginBottom: '10px' }}>
<Col span={2}></Col>
<Col span={4}>
<div>
{v.specificationValueList.map((v, i) => (
<Tag key={v.id} closable onClose={() => specificationValueDelete(index, i)}>
{v.name}
</Tag>
))}
</div>
</Col>
</Row>
) : (
''
)}
{/*规格值操作*/}
{v.optionList.length ? (
<Row>
<Col span={2}></Col>
<Col span={4} style={{ marginBottom: '10px' }}>
{v.addSpecificationValueShow ? (
<Input
placeholder='请输入规格值,按回车键完成'
onPressEnter={(e) => specificationValuePressEnter(e, index)}
maxLength={30}
/>
) : (
<Button
type='link'
danger
icon={<PlusOutlined />}
onClick={() => addSpecificationValueClick(index)}
>
添加规格值
</Button>
)}
</Col>
{v.addSpecificationValueShow ? (
<Col>
<Button
type='primary'
style={{ marginLeft: '10px' }}
onClick={() => specificationValueCancel(index)}
>
取消
</Button>
</Col>
) : (
''
)}
</Row>
) : (
''
)}
</div>
))}
<Row>
<Col span={2}></Col>
<Col>
<Button type='primary' icon={<PlusOutlined />} onClick={addSpecificationClick}>
添加规格
</Button>
</Col>
</Row>
</Form>
{tableData.length ? (
<Form form={skuValueForm}>
<Table
style={{ marginTop: '10px' }}
rowKey='id'
columns={covertColumns() as ColumnTypes}
components={{
body: {
cell: EditableCell,
},
}}
bordered
dataSource={tableData}
pagination={false}
size='small'
/>
</Form>
) : (
''
)}
</div>
);
},
);
export default CommonSkuInfo;
import React, { useState } from 'react';
import { useState } from 'react';
import { Header } from 'antd/es/layout/layout';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { Button, Dropdown, Image, MenuProps, Modal } from 'antd';
......
......@@ -19,6 +19,7 @@ export interface searchColumns {
name?: string;
value?: string | number | boolean | null;
label?: string;
children?: any;
}[];
onSelect?: (value: any) => void;
disable?: boolean;
......@@ -87,6 +88,7 @@ const Index: React.FC<propsType> = (props) => {
// 过滤为空项
Object.entries(data).filter((i) => i[1] !== '' && i[1] !== undefined && i[1] !== null),
);
// console.log('此处 --->', obj);
props.searchData(obj);
};
useImperativeHandle(props.baseRef, () => ({
......
......@@ -54,7 +54,7 @@ export const Uploader: React.FC<PropsType> = (props) => {
listType,
fileList,
beforeUpload: (res) => {
console.log('文件类型-->', res);
// console.log('文件类型-->', res);
const isType = fileType?.includes(res.type);
const isSize = res.size / 1024 / 1024 < (fileSize || 2);
if (!isType) {
......
.video-preview {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
.preview-mask{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.35);
}
.button{
position: absolute;
top: 0;
right: 0;
width: 40px;
height: 40px;
background: rgba(0,0,0,0.35);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 20px;
cursor: pointer;
}
.video{
height: 80%;
z-index: 200;
}
}
import React from 'react';
import { Image } from 'antd';
import './index.scss';
// 参数类型
interface propsType {
src: string;
width?: number;
height?: number;
autoPlay?: boolean;
loop?: boolean;
controls?: boolean;
}
const Video: React.FC<propsType> = (props) => {
// 组件的默认值
Video.defaultProps = {
src: 'http://pad-video-x.oss-cn-shenzhen.aliyuncs.com/file/3505c402-cbf9-41a5-9d6f-bdb350625bea.jpg',
width: 40,
height: 40,
};
// 视频是否预览
const [isPreview, setIsPreview] = React.useState<boolean>(false);
return (
<>
<Image
src={`${props?.src}?x-oss-process=video/snapshot,t_3618&x-oss-process=image/quality,q_25`}
width={props?.width}
height={props?.height}
preview={false}
onClick={() => {
setIsPreview(true);
}}
/>
{isPreview && (
<div className='video-preview'>
<view className='preview-mask' onClick={() => setIsPreview(false)}></view>
{/*<Button className='button' shape='circle' icon={<CloseOutlined />} />*/}
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
<video className='video' src={props?.src} autoPlay controls></video>
</div>
)}
</>
);
};
export default Video;
import React, { useEffect } from 'react';
import { Form, Input, message, Modal, ModalProps } from 'antd';
import { InterListType, InterReqType } from '~/api/interface';
import { industryListPagesType, inspectionInsertType } from '~/api/interface/categoryManage';
import { PlusOutlined } from '@ant-design/icons';
import { Uploader } from '~/components/uploader';
import { CategoryManageAPI } from '~/api';
// 列表的类型
type TableType = InterListType<industryListPagesType>;
// 表单参数
type ReqType = InterReqType<inspectionInsertType>;
// 参数类型
interface selfProps {
id: number;
data?: TableType[0]['inspectionDTOS'][0];
onCancel: () => void;
}
const AddEditChildrenView: React.FC<ModalProps & selfProps> = ({
id,
open,
title,
onCancel,
data,
}) => {
// 表格数据类型
const [form] = Form.useForm<ReqType>();
// 关闭事件
const handleCancel = () => {
form.resetFields();
onCancel();
};
// 确定事件
const handleOk = () => {
form
.validateFields()
.then(async (values) => {
// console.log('表单验证 --->', values);
await handleSubmit(values);
})
.catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
};
// 提交数据
const handleSubmit = async (values: ReqType) => {
const res = await CategoryManageAPI[data?.id ? 'inspectionUpdate' : 'inspectionInsert']({
...values,
id: data?.id || undefined,
industryTypeId: data?.id ? data?.industryTypeId : id,
caseImg: values?.caseImg
? JSON.stringify((values?.caseImg as any).map((i: { url: string }) => ({ fileUrl: i.url })))
: undefined,
saleState: 1,
});
if (res && res.code === '200') {
message.success('操作成功').then();
handleCancel();
}
};
// 获取图片
const getImgParse = (text: string) => {
try {
return JSON.parse(text)?.map((i: { fileUrl: string }) => ({ url: i.fileUrl }));
} catch (e) {
return [{ url: text }];
}
};
// componentDidMount
useEffect(() => {
if (!open) return;
if (!data) return;
form.setFieldsValue({
...data,
caseImg: getImgParse(data?.caseImg),
});
}, [open]);
return (
<Modal title={title} open={open} onCancel={handleCancel} onOk={handleOk} destroyOnClose>
<Form form={form} labelAlign='right' labelCol={{ span: 5 }} wrapperCol={{ span: 10 }}>
<Form.Item
label='分类名称'
name='inspectionName'
rules={[{ required: true, message: '请输入分类名称' }]}
>
<Input placeholder='请输入分类名称' maxLength={6} allowClear />
</Form.Item>
<Form.Item
label='分类图标'
name='inspectionImg'
rules={[{ required: true, message: '请上传分类图标' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={1}
fileSize={10}
fileType={['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp']}
onChange={(e) =>
form.setFieldValue('inspectionImg', e.at(0) ? e.at(0)?.url : undefined)
}
defaultFileList={data?.inspectionImg ? [{ url: data?.inspectionImg }] : []}
>
<PlusOutlined />
</Uploader>
</Form.Item>
<Form.Item
label='分类描述'
name='inspectionDescription'
rules={[{ required: true, message: '请输入分类描述' }]}
>
<Input placeholder='请输入分类描述' maxLength={25} allowClear />
</Form.Item>
<Form.Item
label='服务编号'
name='inspectionNo'
rules={[{ required: true, message: '请输入服务编号' }]}
>
<Input placeholder='请输入服务编号' maxLength={15} allowClear />
</Form.Item>
<Form.Item
label='业务案例图片'
name='caseImg'
rules={[{ required: true, message: '请上传业务案例图片' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={4}
fileSize={10}
fileType={['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp']}
onChange={(e) => form.setFieldValue('caseImg', e?.length ? e : undefined)}
defaultFileList={data?.caseImg ? getImgParse(data?.caseImg as any) : []}
>
<PlusOutlined />
</Uploader>
</Form.Item>
<Form.Item
label='业务案例视频'
name='caseVideo'
rules={[{ required: false, message: '请上传业务案例视频' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={1}
fileSize={10}
fileType={['video/mp4', 'video/wmv', 'video/avi', 'video/mov', 'video/flv']}
onChange={(e) => form.setFieldValue('caseVideo', e.at(0) ? e.at(0)?.url : undefined)}
defaultFileList={data?.caseVideo ? [{ url: data?.caseVideo }] : []}
>
<PlusOutlined />
</Uploader>
</Form.Item>
</Form>
</Modal>
);
};
export default AddEditChildrenView;
import React, { useEffect } from 'react';
import { Form, Input, message, Modal, ModalProps } from 'antd';
import { InterListType, InterReqType } from '~/api/interface';
import { industryListPagesType, industryUpdateType } from '~/api/interface/categoryManage';
import { PlusOutlined } from '@ant-design/icons';
import { Uploader } from '~/components/uploader';
import { CategoryManageAPI } from '~/api';
// 列表的类型
type TableType = InterListType<industryListPagesType>;
// 表单参数
type ReqType = InterReqType<industryUpdateType>;
// 参数类型
interface selfProps {
data?: TableType[0];
onCancel: () => void;
}
const AddEditModalView: React.FC<ModalProps & selfProps> = ({ open, title, onCancel, data }) => {
// 表格数据类型
const [form] = Form.useForm<ReqType>();
// 关闭事件
const handleCancel = () => {
form.resetFields();
onCancel();
};
// 确定事件
const handleOk = () => {
form
.validateFields()
.then(async (values) => {
await handleSubmit(values);
})
.catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
};
// 提交数据
const handleSubmit = async (values: ReqType) => {
const res = await CategoryManageAPI[data?.id ? 'industryUpdate' : 'industryInsert']({
...values,
id: data?.id || undefined,
saleState: 1,
});
if (res && res.code === '200') {
message.success('操作成功').then();
handleCancel();
}
};
// componentDidMount
useEffect(() => {
if (!open) return;
if (!data) return;
form.setFieldsValue(data);
// console.log('data --->', data);
}, [open]);
return (
<Modal title={title} open={open} onCancel={handleCancel} onOk={handleOk} destroyOnClose>
<Form form={form} labelAlign='right' labelCol={{ span: 4 }} wrapperCol={{ span: 10 }}>
<Form.Item
label='分类名称'
name='typeName'
rules={[{ required: true, message: '请输入分类名称' }]}
>
<Input placeholder='请输入分类名称' maxLength={15} allowClear />
</Form.Item>
<Form.Item
label='分类图标'
name='typeImg'
rules={[{ required: true, message: '请上传分类图标' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={1}
fileSize={10}
fileType={['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp']}
onChange={(e) => form.setFieldValue('typeImg', e.at(0) ? e.at(0)?.url : undefined)}
defaultFileList={data?.typeImg ? [{ url: data?.typeImg }] : []}
>
<PlusOutlined />
</Uploader>
</Form.Item>
<Form.Item
label='分类描述'
name='description'
rules={[{ required: true, message: '请输入分类描述' }]}
>
<Input placeholder='请输入分类名称' maxLength={25} allowClear />
</Form.Item>
</Form>
</Modal>
);
};
export default AddEditModalView;
import React, { useEffect } from 'react';
import { Form, Input, message, Modal, ModalProps } from 'antd';
import { InterItemType, InterReqType } from '~/api/interface';
import { listByInspectionIdType, industryUpdateType } from '~/api/interface/categoryManage';
import { CategoryManageAPI } from '~/api';
// 表格类型
type TableType = InterItemType<listByInspectionIdType>;
// 表单参数
type ReqType = InterReqType<industryUpdateType>;
// 参数类型
interface selfProps {
id: number;
data?: TableType[0];
onCancel: () => void;
}
const AddEditTagModalView: React.FC<ModalProps & selfProps> = ({
id,
open,
title,
onCancel,
data,
}) => {
// 表格数据类型
const [form] = Form.useForm<ReqType>();
// 关闭事件
const handleCancel = () => {
form.resetFields();
onCancel();
};
// 确定事件
const handleOk = () => {
form
.validateFields()
.then(async (values) => {
await handleSubmit(values);
})
.catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
};
// 提交数据
const handleSubmit = async (values: ReqType) => {
const res = await CategoryManageAPI[data?.id ? 'inspectionTagUpdate' : 'inspectionTagInsert']({
...values,
inspectionId: id,
id: data?.id || undefined,
});
if (res && res.code === '200') {
message.success('操作成功').then();
handleCancel();
}
};
// componentDidMount
useEffect(() => {
if (!open) return;
if (!data) return;
form.setFieldsValue(data);
// console.log('data --->', data);
}, [open]);
return (
<Modal title={title} open={open} onCancel={handleCancel} onOk={handleOk} destroyOnClose>
<Form form={form} labelAlign='right' labelCol={{ span: 4 }} wrapperCol={{ span: 10 }}>
<Form.Item
label='标签名称'
name='tagName'
rules={[{ required: true, message: '请输入标签名称' }]}
>
<Input placeholder='请输入标签名称' maxLength={15} allowClear />
</Form.Item>
</Form>
</Modal>
);
};
export default AddEditTagModalView;
import { FC } from 'react';
import { Form, Input, Modal, ModalProps } from 'antd';
interface selfProps {
onCancel: () => void;
onOk: () => void;
}
const AddOrEditModal: FC<ModalProps & selfProps> = ({ open, onCancel, onOk }) => {
const handleCancel = () => {
onCancel();
};
const handleOk = () => {
onOk();
};
return (
<Modal title='新增分类' open={open} onCancel={handleCancel} onOk={handleOk}>
<Form>
<Form.Item label='分类名称'>
<Input placeholder='请输入分类名称' />
</Form.Item>
<Form.Item label='分类图标'></Form.Item>
<Form.Item label='分类描述'></Form.Item>
</Form>
</Modal>
);
};
export default AddOrEditModal;
import React, { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { Button, Descriptions, Image, message, Modal, Table } from 'antd';
import { ArrowLeftOutlined, PlusOutlined } from '@ant-design/icons';
import SearchBox from '~/components/search-box';
import qs from 'query-string';
import { inspectionDetailType, listByInspectionIdType } from '~/api/interface/categoryManage';
import { InterDataType, InterItemType } from '~/api/interface';
import { CategoryManageAPI } from '~/api';
import { ColumnsType } from 'antd/es/table';
import AddEditTagModalView from '~/pages/categoryManage/serviceCategoryList/comp/addEditTagModal';
import Video from '~/components/video';
// 表格类型
type TableType = InterItemType<listByInspectionIdType>;
const ServiceCategoryDetail: React.FC = () => {
// 路由钩子
const location = useLocation();
// 导航钩子
const navigate = useNavigate();
// 返回上一页
const handleBack = () => {
navigate(-1);
};
// 新增编辑标签是否显示
const [addEditVisible, setAddEditVisible] = useState(false);
// 编辑的数据
const [recordData, setRecordData] = useState<TableType[0]>();
// 表格数据
const [tableData, setTableData] = useState<TableType>([]);
// 加载列表
const getTableList = async () => {
// 只需要修改这个地方的接口即可
const res = await CategoryManageAPI.listByInspectionId({
id: Number(qs.parse(location.search).id),
});
if (res && res.code === '200') {
const { result } = res; // 解构
setTableData(result || []);
}
};
// 删除数据
const handleDelete = (record: TableType[0]) => {
Modal.confirm({
title: '提示',
content: '是否删除该场景标签?',
onOk: async () => {
const res = await CategoryManageAPI.inspectionTagDelete({
id: record.id,
});
if (res && res.code === '200') {
message.success('删除成功');
await getTableList();
}
},
});
};
// 服务详情
const [inspectionDetail, setInspectionDetail] = useState<InterDataType<inspectionDetailType>>();
// 获取服务详情
const getInspectionDetail = async () => {
const res = await CategoryManageAPI.inspectionDetail({
id: Number(qs.parse(location.search).id),
});
if (res && res.code === '200') {
setInspectionDetail(res.result);
}
};
// 获取图片
const getImgParse = (text: string) => {
try {
return JSON.parse(text);
} catch (e) {
return [{ fileUrl: text }];
}
};
// 组件挂载
useEffect(() => {
getInspectionDetail().then();
getTableList().then();
}, []);
// 表格结构
const columns: ColumnsType<TableType[0]> = [
{
title: '序号',
dataIndex: 'tagName',
align: 'center',
render: (_text, _record, index) => `${index + 1}`,
},
{
title: '场景标签',
dataIndex: 'tagName',
align: 'center',
},
{
title: '操作',
dataIndex: 'id',
align: 'center',
width: 200,
render: (_text, record) => (
<>
<Button
type='link'
size='small'
onClick={() => {
setRecordData(record);
setAddEditVisible(true);
}}
>
编辑
</Button>
<Button type='link' size='small' onClick={() => handleDelete(record)} danger>
删除
</Button>
</>
),
},
];
return (
<>
<SearchBox
child={
<Button type={'primary'} icon={<ArrowLeftOutlined />} onClick={() => handleBack()}>
返回
</Button>
}
/>
<Descriptions
title='子分类详情'
bordered
style={{ marginBottom: '20px', width: '60%' }}
column={2}
labelStyle={{ width: '15%' }}
contentStyle={{ width: '30%' }}
>
<Descriptions.Item label='分类名称'>{inspectionDetail?.inspectionName}</Descriptions.Item>
<Descriptions.Item label='分类图标'>
<Image src={inspectionDetail?.inspectionImg} width={40} height={40} />
</Descriptions.Item>
<Descriptions.Item label='分类描述'>
{inspectionDetail?.inspectionDescription}
</Descriptions.Item>
<Descriptions.Item label='服务编号'>{inspectionDetail?.inspectionNo}</Descriptions.Item>
<Descriptions.Item label='业务案例图片'>
{getImgParse(inspectionDetail?.caseImg as string).map(
(i: { fileUrl: string }, j: React.Key) => (
<Image src={i.fileUrl} width={40} height={40} key={j} />
),
)}
</Descriptions.Item>
<Descriptions.Item label='业务案例视频'>
{inspectionDetail?.caseVideo && (
<Video src={inspectionDetail?.caseVideo} width={40} height={40} />
)}
</Descriptions.Item>
</Descriptions>
<SearchBox
sufFixBtn={
<Button
type={'primary'}
icon={<PlusOutlined />}
onClick={() => {
setRecordData(undefined);
setAddEditVisible(true);
}}
>
新增场景标签
</Button>
}
/>
<Table
style={{ width: '60%' }}
size='small'
dataSource={tableData}
columns={columns}
rowKey='id'
// scroll={{ x: 1500 }}
bordered
pagination={{
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
/>
<AddEditTagModalView
title={recordData ? '编辑场景标签' : '新增场景标签'}
open={addEditVisible}
data={recordData}
id={Number(qs.parse(location.search).id)}
onCancel={() => {
setAddEditVisible(false);
setRecordData(undefined);
getTableList().then();
}}
/>
</>
);
};
export default ServiceCategoryDetail;
import { Button, Table } from 'antd';
import { ArrowDownOutlined, ArrowUpOutlined, PlusOutlined } from '@ant-design/icons';
import { useEffect, useState } from 'react';
import SearchView from '~/components/search-box';
import { Button, message, Table, Image, Modal } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { industryListPagesType } from '~/api/interface/categoryManage';
import { InterListType } from '~/api/interface';
import { ColumnsType } from 'antd/es/table';
import AddOrEditModal from './components/addOrEditModal';
import { useState } from 'react';
import { CategoryManageAPI } from '~/api';
import Video from '~/components/video';
import AddEditModalView from '~/pages/categoryManage/serviceCategoryList/comp/addEditModal';
import AddEditChildrenView from '~/pages/categoryManage/serviceCategoryList/comp/addEditChildren';
import { useNavigate } from 'react-router-dom';
import qs from 'query-string';
const ServiceCategoryList = () => {
const tableColumns: ColumnsType<any> = [
// 列表的类型
type TableType = InterListType<industryListPagesType>;
function ServiceCategoryList() {
// 路由钩子
const navigate = useNavigate();
// 新增编辑弹窗是否显示
const [addEditVisible, setAddEditVisible] = useState(false);
// 新增编辑子分类弹窗是否显示
const [addEditChildVisible, setAddEditChildVisible] = useState(false);
// 编辑的数据
const [recordData, setRecordData] = useState<TableType[0]>();
// 编辑子分类的数据
const [recordChildData, setRecordChildData] = useState<TableType[0]['inspectionDTOS'][0]>();
// 表格数据
const [tableData, setTableData] = useState<TableType>([]);
// 表格分页配置
const [pagination, setPagination] = useState({
total: 0,
pageSize: 10,
current: 1,
totalPage: 0,
});
// 加载列表
const getTableList = async (value = {}) => {
// 只需要修改这个地方的接口即可
const res = await CategoryManageAPI.industryListPages({
pageNo: pagination.current,
pageSize: pagination.pageSize,
...value,
});
if (res && res.code === '200') {
const { list, pageNo, totalCount, pageSize, totalPage } = res.result; // 解构
setPagination({
total: totalCount,
current: pageNo,
pageSize,
totalPage,
});
setTableData(list || []);
// setPointData(res?.result?.list);
} else {
message.warning(res.message);
}
};
// 翻页
const paginationChange = (pageNo: number, pageSize: number) => {
getTableList({ pageNo, pageSize }).then();
};
// 排序
// const handleSort = (type: string) => {
// console.log(type);
// };
// 删除数据
const handleDelete = (record: TableType[0]) => {
Modal.confirm({
title: '提示',
content: '是否删除该分类?',
onOk: async () => {
const res = await CategoryManageAPI[
record.typeName ? 'industryRemove' : 'inspectionRemove'
]({
id: record.id,
});
if (res && res.code === '200') {
message.success('删除成功');
paginationChange(
tableData.length === 1 ? pagination.current - 1 : pagination.current,
pagination.pageSize,
);
}
},
});
};
// 跳转详情
const handleNavigation = (record: TableType[0]) => {
const search = {
id: record.id,
};
navigate(`/categoryManage/mallCategoryList/detail?${qs.stringify(search)}`);
};
// 获取图片
const getImgParse = (text: string) => {
try {
return JSON.parse(text)?.[0]?.fileUrl;
} catch (e) {
return text;
}
};
// 表格列
const columns: ColumnsType<TableType[0]> = [
{
title: '分类名称',
dataIndex: 'typeName',
align: 'center',
render: (text, record) => text || record.inspectionName,
},
{
title: '图片',
dataIndex: 'typeImg',
align: 'center',
render: (text, record) => (
<>
<Image src={text || record.inspectionImg} width={40} height={40} />
</>
),
},
{
title: '描述',
dataIndex: 'description',
align: 'center',
render: (text, record) => text || record.inspectionDescription,
},
{
title: '业务案例图片',
dataIndex: 'caseImg',
align: 'center',
render: (text) => <>{text && <Image src={getImgParse(text)} width={40} height={40} />}</>,
},
{
title: '业务案例视频',
dataIndex: 'caseVideo',
align: 'center',
render: (text) => <>{text && <Video src={text} width={40} height={40} />}</>,
},
{
title: '创建时间',
dataIndex: 'createTime',
align: 'center',
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
render: () => (
fixed: 'right',
width: 180,
render: (_text, record) => (
<>
<Button type='link'>新增子分类</Button>
<Button type='link'>编辑</Button>
<Button type='link'>删除</Button>
{!record.inspectionName && (
<Button
type='link'
onClick={() => {
setRecordData(record);
setRecordChildData(undefined);
setAddEditChildVisible(true);
}}
>
新增子分类
</Button>
)}
{record.inspectionName && (
<Button
type='link'
onClick={() => {
handleNavigation(record);
}}
>
详情
</Button>
)}
{!record.inspectionName && (
<Button
type='link'
onClick={() => {
setRecordData(record);
setAddEditVisible(true);
}}
>
编辑
</Button>
)}
{record.inspectionName && (
<Button
type='link'
onClick={() => {
setRecordData(record);
setRecordChildData(record as any);
setAddEditChildVisible(true);
}}
>
编辑
</Button>
)}
<Button
type='link'
onClick={() => {
handleDelete(record);
}}
danger
>
删除
</Button>
</>
),
},
];
const [tableData, setTableData] = useState<any>([{ id: 1 }]);
const [addOrEditModalShow, setAddOrEditModalShow] = useState<boolean>(false);
//新增分类弹窗
const addOrEditModalClick = () => {
setAddOrEditModalShow(true);
};
const addOrEditModalOk = () => {
setAddOrEditModalShow(false);
};
const addOrEditModalCancel = () => {
setAddOrEditModalShow(false);
};
// 组件挂载
useEffect(() => {
(async () => {
await getTableList();
})();
}, []);
return (
<div className='service-category-list'>
<div className='list-operate' style={{ marginBottom: '10px' }}>
<Button
type='primary'
icon={<PlusOutlined />}
style={{ marginRight: '10px' }}
onClick={addOrEditModalClick}
>
新增分类
</Button>
<Button icon={<ArrowUpOutlined />} style={{ marginRight: '10px' }} type='primary'></Button>
<Button icon={<ArrowDownOutlined />} type='primary'></Button>
</div>
<Table columns={tableColumns} bordered rowKey='id' />
<AddOrEditModal
open={addOrEditModalShow}
onOk={addOrEditModalOk}
onCancel={addOrEditModalCancel}
<>
<SearchView
sufFixBtn={
<>
<Button
type='primary'
onClick={() => {
setAddEditVisible(true);
}}
icon={<PlusOutlined />}
>
新增分类
</Button>
{/*<Button*/}
{/* icon={<ArrowUpOutlined />}*/}
{/* type='primary'*/}
{/* onClick={() => handleSort('up')}*/}
{/*></Button>*/}
{/*<Button*/}
{/* icon={<ArrowDownOutlined />}*/}
{/* type='primary'*/}
{/* onClick={() => handleSort('down')}*/}
{/*></Button>*/}
</>
}
/>
</div>
<Table
size={'small'}
columns={columns}
rowKey={'id'}
// scroll={{ x: 1000 }}
// rowSelection={{ ...rowSelection, checkStrictly }}
dataSource={tableData}
pagination={{
total: pagination.total,
pageSize: pagination.pageSize,
current: pagination.current,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
expandable={{
childrenColumnName: 'inspectionDTOS',
fixed: true,
// expandIcon: ({ expanded, onExpand, record }) =>
// record?.inspectionDTOS?.length ? (
// expanded ? (
// <CaretDownOutlined
// onClick={(e) => onExpand(record, e)}
// style={{ marginRight: '10px', marginLeft: '-20px', color: '#1668dc' }}
// />
// ) : (
// <CaretRightOutlined
// onClick={(e) => onExpand(record, e)}
// style={{ marginRight: '10px', color: '#1668dc' }}
// />
// )
// ) : (
// <span style={{ marginRight: '0px', marginLeft: '-20px' }}>&nbsp;</span>
// ),
}}
/>
<AddEditModalView
title={recordData ? '编辑分类' : '新增分类'}
open={addEditVisible}
data={recordData}
onCancel={() => {
setAddEditVisible(false);
setRecordData(undefined);
paginationChange(pagination.current, pagination.pageSize);
}}
/>
<AddEditChildrenView
title={recordChildData ? '编辑子分类' : '新增子分类'}
open={addEditChildVisible}
data={recordChildData}
id={Number(recordData?.id)}
onCancel={() => {
setAddEditChildVisible(false);
setRecordData(undefined);
setRecordChildData(undefined);
paginationChange(pagination.current, pagination.pageSize);
}}
/>
</>
);
};
}
export default ServiceCategoryList;
......@@ -199,14 +199,14 @@ const DynamicList = () => {
okText: '审核通过',
cancelText: '审核不通过',
onOk: async () => {
const res = await ForumManageAPI.checkDynamic({ dynamicId: record.id, status: 1 });
const res = await ForumManageAPI.checkDynamic({ dynamicId: record.id, status: true });
if (res && res.code === '200') {
message.success('操作成功');
paginationChange(pagination.pageNo, pagination.pageSize);
}
},
onCancel: async () => {
const res = await ForumManageAPI.checkDynamic({ dynamicId: record.id, status: 2 });
const res = await ForumManageAPI.checkDynamic({ dynamicId: record.id, status: false });
if (res && res.code === '200') {
message.success('操作成功');
paginationChange(pagination.pageNo, pagination.pageSize);
......
import { Button, Col, Form, Input, message, Row, Select, Table, Tag } from 'antd';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { Table } from 'antd';
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { isEmptyBol, regPriceNumber } from '~/utils/validateUtils';
import EditableCell from '~/components/EditableCell';
import { InterDataType, InterReqType } from '~/api/interface';
import { addMallGoodsType, mallGoodsDetailsType } from '~/api/interface/goodsType';
import { filterObjAttr } from '~/utils';
import CommonSkuInfo from '~/components/goods/commonSkuInfo';
type EditableTableProps = Parameters<typeof Table>[0];
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
......@@ -36,10 +35,7 @@ interface selfProps {
}
const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
//规格项表单
const [form] = Form.useForm<{ [x: string]: string }>();
//可编辑表格表单
const [skuValueForm] = Form.useForm<{ [x: string]: string }>();
const commonSkuInfoRef = useRef<any>();
//表格数据
const [tableData, setTableData] = useState<(skuTableType & { [key: string]: string })[]>([]);
......@@ -153,200 +149,12 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
inputType: 'number',
},
]);
const covertColumns = () => {
return defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: any) => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: col.editable,
radioOption: col.radioOption,
inputType: col.inputType,
uploadSuccess: col.inputType === 'uploader' ? uploadSuccess : undefined,
rules: col.rules,
placeholder: col.placeholder,
}),
};
});
};
//规格表单数组
const [specificationFormList, setSpecificationFormList] = useState<specificationFormListType[]>([
{
id: Math.random(),
name: `specName`,
optionList: [],
specificationValueList: [],
addSpecificationValueShow: false,
},
]);
useImperativeHandle(ref, () => ({
submitSku,
getForm: () => skuValueForm,
getForm: () => commonSkuInfoRef.current.getSpecificationValueForm(),
}));
//新增规格项目
const addSpecificationClick = () => {
setSpecificationFormList([
...specificationFormList,
{
id: Math.random(),
name: `specName${specificationFormList.length + 1}`,
optionList: [],
specificationValueList: [],
addSpecificationValueShow: false,
},
]);
};
// 删除规格项目
const deleteSpecificationClick = (index: number) => {
console.log('数据-->', specificationFormList[index]);
form.setFieldValue(specificationFormList[index].name, undefined);
specificationFormList.splice(index, 1);
combineSpecificationValue();
setSpecificationFormList([...specificationFormList]);
};
//规格项名称输入完成
const specificationPressEnter = (e: any, index: number) => {
specificationFormList[index].optionList = e.target.value
? [{ label: e.target.value, value: e.target.value }]
: [];
form.setFieldValue(specificationFormList[index].name, e.target.value);
setSpecificationFormList([...specificationFormList]);
};
//规格值添加
const addSpecificationValueClick = (index: number) => {
specificationFormList[index].addSpecificationValueShow = true;
setSpecificationFormList([...specificationFormList]);
};
//存在
const specificationValuePressEnter = (e: any, index: number) => {
const isExist = specificationFormList[index].specificationValueList.some(
(v) => v.name === e.target.value,
);
if (isExist) {
return message.warning('该规格值已存在');
}
specificationFormList[index].specificationValueList.push({
id: Math.random(),
name: e.target.value,
specificationName: specificationFormList[index].optionList[0].value,
});
combineSpecificationValue();
setSpecificationFormList(specificationFormList);
specificationValueCancel(index);
};
//规格值添加-取消
const specificationValueCancel = (index: number) => {
specificationFormList[index].addSpecificationValueShow = false;
setSpecificationFormList([...specificationFormList]);
};
//规格值-删除
const specificationValueDelete = (i: number, j: number) => {
specificationFormList[i].specificationValueList.splice(j, 1);
combineSpecificationValue();
setSpecificationFormList([...specificationFormList]);
};
//组合数据
const combineSpecificationValue = () => {
let combineSpecificationList: any = [];
let tableDataList: any = [];
//过滤规格值为空的
const filterSpecificationFormList = specificationFormList.filter(
(v) => v.specificationValueList.length,
);
if (filterSpecificationFormList.length > 1) {
const combineList = filterSpecificationFormList.reduce((pre: any, cur, currentIndex) => {
// 首次组合两个数据
if (currentIndex === 0 && filterSpecificationFormList.length > 1) {
combineSpecificationList = combineEvent(
cur.specificationValueList,
filterSpecificationFormList[currentIndex + 1].specificationValueList,
);
//二维数组拆分为对象
combineSpecificationList = getCombineObj(combineSpecificationList);
// 两个数据以上的组合
} else if (
currentIndex < filterSpecificationFormList.length - 1 &&
filterSpecificationFormList[currentIndex + 1].specificationValueList.length
) {
// 上一次的组合作为下一次组合的参数
combineSpecificationList = combineEvent(
combineSpecificationList,
filterSpecificationFormList[currentIndex + 1].specificationValueList,
);
//二维数组拆分为对象
combineSpecificationList = getCombineObj(combineSpecificationList);
}
pre = combineSpecificationList;
return pre;
}, []);
tableDataList = combineList.reduce((pre: any, cur: any) => {
const tabCovertObj = cur.reduce((a: any, b: any, currentIndex: number) => {
a['name' + (currentIndex + 1)] = b.name;
a['specificationName' + (currentIndex + 1)] = b.specificationName;
return a;
}, {});
//判断表格中是否已存在该条(缓存)
const tableItemObj = tableData.find((i) =>
Object.getOwnPropertyNames(tabCovertObj).every((key) => i[key] === tabCovertObj[key]),
);
pre.push({
...tabCovertObj,
id: tableItemObj ? tableItemObj.id : Math.random(), //存在则用之前的id,不存在新建id
fileList: tableItemObj ? tableItemObj.fileList : [],
});
return pre;
}, []);
} else if (filterSpecificationFormList.length === 1) {
//当存在一个规格项时
tableDataList = filterSpecificationFormList[0].specificationValueList.map((v) => {
const obj = Object.create(null);
obj['name1'] = v.name;
obj['specificationName1'] = v.specificationName;
//判断表格中是否已存在该条(缓存)
const tableItemObj = tableData.find((i) =>
Object.getOwnPropertyNames(obj).every((key) => i[key] === obj[key]),
);
obj['id'] = Math.random();
return tableItemObj ? tableItemObj : obj; //存在则用之前的一条,不存在新建一条
});
}
if (tableDataList.length) {
setTableFormDefault(tableDataList);
mergeTableRow(filterSpecificationFormList);
}
setTableData([...tableDataList]);
};
//组合数据拆分为对象
const getCombineObj = (combineSpecificationList: any) => {
return combineSpecificationList.reduce((pre: any, cur: any) => {
pre.push(
cur.reduce((a: any, b: any) => {
if (Array.isArray(b)) {
a.push(...b);
} else {
a.push({ ...b });
}
return a;
}, []),
);
return pre;
}, []);
};
//两数组排列组合(通用)
const combineEvent = (list1: any, list2: any) => {
return list1.reduce((pre: any, cur: any) => pre.concat(list2.map((v: any) => [cur, v])), []);
};
//表头拆分及合并列
const mergeTableRow = (filterSpecificationFormList: specificationFormListType[]) => {
const columns = filterSpecificationFormList.map((v, index) => ({
......@@ -390,7 +198,7 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
tableData[tableIndex].fileList = fileList;
const obj = Object.create(null);
obj['skuImage' + record.id] = fileList.length ? fileList[0].url : undefined;
skuValueForm.setFieldsValue(obj);
commonSkuInfoRef.current.getSpecificationValueForm().setFieldsValue(obj);
setTableData([...tableData]);
}
};
......@@ -398,9 +206,12 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
//提交验证
const submitSku = () => {
return new Promise((resolve, reject) => {
form
commonSkuInfoRef.current
.getSpecificationForm()
.validateFields()
.then(() => {
const specificationFormList: specificationFormListType[] =
commonSkuInfoRef.current.getSpecificationFormList();
const specificationFormItem = specificationFormList.find(
(v) => !v.specificationValueList.length,
);
......@@ -422,9 +233,10 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
: undefined,
})),
}));
skuValueForm
commonSkuInfoRef.current
.getSpecificationValueForm()
.validateFields()
.then((value) => {
.then((value: any) => {
//规格值数据转化
const priceStock = tableData.reduce((pre: any, cur: any) => {
//规格名,规格值组合类型
......@@ -448,12 +260,12 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
}, []);
resolve({ priceStock, specAttrList });
})
.catch((err) => {
.catch((err: any) => {
reject(err);
});
}
})
.catch((err) => {
.catch((err: any) => {
reject(err);
});
});
......@@ -469,7 +281,15 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
}, {}),
};
}, {});
skuValueForm.setFieldsValue(tableFormDefault);
commonSkuInfoRef.current.getSpecificationValueForm().setFieldsValue(tableFormDefault);
};
//更新表单
const updateTableData = (tableData: any) => {
setTableData([...tableData]);
};
//更新表单列
const updateDefaultColumns = (columns: any) => {
setDefaultColumns([...columns]);
};
useEffect(() => {
......@@ -494,8 +314,8 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
},
{},
);
form.setFieldsValue(specFormDefault);
setSpecificationFormList([...covertSpecAttrList]);
commonSkuInfoRef.current.getSpecificationForm().setFieldsValue(specFormDefault);
commonSkuInfoRef.current.updateSpecificationFormList([...covertSpecAttrList]);
mergeTableRow(covertSpecAttrList);
const tableDataList: (skuTableType & { [key: string]: string })[] =
goodsDetailsInfo.priceStock.map((v) => ({
......@@ -524,136 +344,14 @@ const SkuInfo = forwardRef<any, selfProps>(({ goodsDetailsInfo }, ref) => {
return (
<div className='sku-info'>
<Form wrapperCol={{ span: 5 }} labelCol={{ span: 1 }} form={form}>
{specificationFormList.map((v, index) => (
<>
{/* 规格项*/}
<Row key={v.id}>
<Col span={7}>
<Form.Item
label={'规格项' + (index + 1)}
wrapperCol={{ span: 18 }}
labelCol={{ span: 5 }}
name={v.name}
rules={[{ required: true, message: `请输入规格项${index + 1}` }]}
>
<Select
placeholder='请输入规格项,按回车键完成'
dropdownRender={(menu) => (
<>
{v.optionList.length ? menu : ''}
<Input
onPressEnter={(e) => specificationPressEnter(e, index)}
maxLength={30}
/>
</>
)}
options={v.optionList}
/>
</Form.Item>
</Col>
{index ? (
<Col span={2}>
<Button
danger
icon={<DeleteOutlined />}
onClick={() => deleteSpecificationClick(index)}
>
删除
</Button>
</Col>
) : (
''
)}
</Row>
{/*规格值显示*/}
{v.specificationValueList.length ? (
<Row style={{ marginBottom: '10px' }}>
<Col span={2}></Col>
<Col span={4}>
<div>
{v.specificationValueList.map((v, i) => (
<Tag key={v.id} closable onClose={() => specificationValueDelete(index, i)}>
{v.name}
</Tag>
))}
</div>
</Col>
</Row>
) : (
''
)}
{/*规格值操作*/}
{v.optionList.length ? (
<Row>
<Col span={2}></Col>
<Col span={4} style={{ marginBottom: '10px' }}>
{v.addSpecificationValueShow ? (
<Input
placeholder='请输入规格值,按回车键完成'
onPressEnter={(e) => specificationValuePressEnter(e, index)}
maxLength={30}
/>
) : (
<Button
type='link'
danger
icon={<PlusOutlined />}
onClick={() => addSpecificationValueClick(index)}
>
添加规格值
</Button>
)}
</Col>
{v.addSpecificationValueShow ? (
<Col>
<Button
type='primary'
style={{ marginLeft: '10px' }}
onClick={() => specificationValueCancel(index)}
>
取消
</Button>
</Col>
) : (
''
)}
</Row>
) : (
''
)}
</>
))}
<Row>
<Col span={2}></Col>
<Col>
<Button type='primary' icon={<PlusOutlined />} onClick={addSpecificationClick}>
添加规格
</Button>
</Col>
</Row>
</Form>
{tableData.length ? (
<Form form={skuValueForm}>
<Table
style={{ marginTop: '10px' }}
rowKey='id'
columns={covertColumns() as ColumnTypes}
components={{
body: {
cell: EditableCell,
},
}}
bordered
dataSource={tableData}
pagination={false}
size='small'
/>
</Form>
) : (
''
)}
<CommonSkuInfo
tableData={tableData}
updateTableData={updateTableData}
defaultColumns={defaultColumns}
updateDefaultColumns={updateDefaultColumns}
uploadSuccess={uploadSuccess}
ref={commonSkuInfoRef}
/>
</div>
);
});
......
import { Button, Form, Image } from 'antd';
import { useNavigate, useSearchParams } from 'react-router-dom';
import './index.scss';
import { MallManageAPI } from '~/api';
import { useEffect, useState } from 'react';
import { InterDataType } from '~/api/interface';
import { serviceDetailType } from '~/api/interface/mallManageType';
import { Button, Cascader, Form, Input, message, Modal, Select } from 'antd';
import { ArrowLeftOutlined, PlusOutlined, SaveOutlined } from '@ant-design/icons';
import SearchBox from '~/components/search-box';
import { useLocation, useNavigate } from 'react-router-dom';
import { InterDataType, InterListType, InterReqType } from '~/api/interface';
import { getCompanyInspectionByIdType } from '~/api/interface/mallManageType';
import { Uploader } from '~/components/uploader';
import { commonPriceValidator } from '~/utils/validateUtils';
import { CategoryManageAPI, MallManageAPI } from '~/api';
import RichText from '~/components/richText';
import DistrictJson from '~/assets/json/district.json';
import { useSelector } from 'react-redux';
import qs from 'query-string';
import { industryListPagesType } from '~/api/interface/categoryManage';
//服务详情-返回类型
type detailType = InterDataType<serviceDetailType>;
const ServiceDetail = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
// 表单类型
type ReqType = InterReqType<getCompanyInspectionByIdType>;
const [serviceDetail, setServiceDetail] = useState<detailType>();
//服务详情
const getServiceDetail = (id: number) => {
MallManageAPI.getServiceDetail({ id }).then(({ result }) => {
setServiceDetail(result);
function ServiceDetailView() {
// 路由钩子
const location = useLocation();
// 导航钩子
const navigate = useNavigate();
// 返回上一页
const handleBack = () => {
navigate(-1);
// Modal.confirm({
// title: '提示',
// content: '请确认是否保存?未保存的数据返回后将丢失',
// okText: '返回',
// cancelText: '取消',
// onOk: () => {
// navigate(-1);
// },
// });
};
// 用户信息
const { userInfo } = useSelector((state: any) => state.UserInfo);
// 表格数据类型
const [form] = Form.useForm<ReqType>();
// 地区列表
const [districtList, setDistrictList] = useState<{ value: string; label: string }[]>();
// 价格单位列表
const [priceUnitList, setPriceUnitList] = useState<{ value: number; label: string }[]>();
// 原始数据
const [industryOriginList, setIndustryOriginList] =
useState<InterListType<industryListPagesType>>();
// 行业应用列表
const [industryList, setIndustryList] = useState<
{ label: string; value: number; children: { label: string; value: number }[] }[]
>([]);
// 获取价格单位列表
const getUnitList = async () => {
const res = await MallManageAPI.listInspectionPriceUnit();
if (res && res.code === '200') {
setPriceUnitList(res.result?.map((i) => ({ label: i.unitName, value: i.id })) || []);
}
};
// 获取地区列表
const getDistrictList = () => {
const list = DistrictJson.map((i) => ({
label: i.name,
value: String(i.id),
}));
setDistrictList(list || []);
};
// 获取行业列表
const getIndustryList = async () => {
const res = await CategoryManageAPI.industryListPages({
pageNo: 1,
pageSize: 9999,
});
if (res && res.code === '200') {
const list = res.result?.list || [];
setIndustryList(
list.map((i) => ({
label: i.typeName,
value: i.id,
children: i.inspectionDTOS?.map((j) => {
return {
label: j.inspectionName,
value: j.id,
};
}),
})),
);
setIndustryOriginList(list);
}
};
//返回
const backRoute = () => {
navigate(-1);
// 上传图片
const handleUpload = ({
fileList,
name,
type,
file,
}: {
fileList: { url: string }[];
name: string;
type: number;
file: number;
}) => {
form.setFieldValue(
name,
fileList.map((i) => ({
fileType: file,
fileUrl: i.url,
first: type,
companyInspectionId: location.search ? Number(qs.parse(location.search).id) : undefined,
})),
);
// console.log('上传图片 --->', list);
};
// 获取默认图片列表
const getDefaultFileList = ({ name }: { name: string }) => {
return (
form.getFieldValue(name)?.map((i: { fileUrl: string }) => ({
url: i.fileUrl,
})) || []
);
};
// 所属服务id
const [inspectionId, setInspectionId] = useState<number>();
// 服务标签列表
const [inspectionTagList, setInspectionTagList] = useState<{ label: string; value: number }[]>();
// 获取服务标签列表
const getInspectionTagList = async () => {
const res = await CategoryManageAPI.listByInspectionId({
id: Number(inspectionId),
});
if (res && res.code === '200') {
setInspectionTagList(res.result?.map((i) => ({ label: i.tagName, value: i.id })) || []);
// console.log(res.result);
}
};
// 表单提交
const handleValid = () => {
form
.validateFields()
.then((values) => {
Modal.confirm({
title: '提示',
content: '请确认信息是否填写完成?',
onOk: async () => {
await handleSubmit(values);
},
});
})
.catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
};
// 提交数据
const handleSubmit = async (values: any) => {
const id = Number(qs.parse(location.search).id);
const res = await MallManageAPI[id ? 'companyInspectionUpdate' : 'companyInspectionInsert']({
...values,
companyInspectionFiles: [
...(values.companyInspectionFiles1 || []),
...(values.companyInspectionFiles2 || []),
...(values.companyInspectionFiles3 || []),
],
inspectionId: values.inspectionId.at(-1),
companyInfoId: userInfo.companyInfoVO.id,
serviceArea: values.serviceArea?.join(','),
saleState: id ? serviceDetail?.saleState : 1,
id: id || undefined,
});
if (res && res.code === '200') {
message.success('操作成功');
handleBack();
}
};
// 服务详情
const [serviceDetail, setServiceDetail] = useState<InterDataType<getCompanyInspectionByIdType>>();
// 当前编辑的服务详情
const getCompanyInspectionById = async ({ id }: { id: number }) => {
const res = await MallManageAPI.getCompanyInspectionById({
id,
});
if (res && res.code === '200') {
setServiceDetail(res.result);
setInspectionId(res.result.inspectionId);
form.setFieldsValue({
...res.result,
companyInspectionFiles1: res.result.inspectionFileDTOS.filter((i) => i.first === 1),
companyInspectionFiles2: res.result.inspectionFileDTOS.filter(
(i) => i.first === 0 && i.fileType === 0,
),
companyInspectionFiles3: res.result.inspectionFileDTOS.filter((i) => i.fileType === 1),
serviceArea: res.result.serviceArea?.split(','),
});
}
};
// 获取子分类的父分类id
const getParentId = () => {
if (!location.search) return;
const list = industryOriginList?.map((i) => i.inspectionDTOS).flat() || [];
const item = list.find((i) => i?.id === serviceDetail?.inspectionId);
form.setFieldValue('inspectionId', [item?.industryTypeId, item?.id]);
};
// 组件挂载
useEffect(() => {
getServiceDetail(Number(searchParams.get('id')));
getDistrictList();
getUnitList().then();
getIndustryList().then();
if (!location.search) return;
getCompanyInspectionById({ id: Number(qs.parse(location.search)?.id) }).then();
// console.log('用户信息 --->', qs.parse(location.search));
}, []);
// 监听服务id
useEffect(() => {
if (!inspectionId) return;
console.log('执行到此处 --->', inspectionId);
getInspectionTagList().then();
}, [inspectionId]);
// 监听行业列表
useEffect(() => {
if (!industryOriginList || industryOriginList.length === 0) return;
getParentId();
}, [industryOriginList]);
return (
<div className='service-detail'>
<div className='service-detail-operate'>
<Button type='primary' onClick={backRoute}>
返回
</Button>
</div>
<div className='service-detail-form'>
<Form labelCol={{ span: 2 }} wrapperCol={{ span: 16 }}>
<Form.Item label='服务名称'>{serviceDetail?.serviceName}</Form.Item>
<Form.Item label='应用类型'>{serviceDetail?.applicationName}</Form.Item>
<Form.Item label='对应行业'>{serviceDetail?.industryName}</Form.Item>
<Form.Item label='展示状态'>
{serviceDetail?.displayState === 0 ? '上架' : '下架'}
</Form.Item>
<Form.Item label='所属单位'>{serviceDetail?.companyName}</Form.Item>
<Form.Item label='封面图'>
<Image src={serviceDetail?.coverPlan} width={100} height={100} />
</Form.Item>
<Form.Item label='分享卡片'>
{serviceDetail?.shareCard ? (
<Image src={serviceDetail.shareCard} width={100} height={100} />
) : (
'暂无'
)}
</Form.Item>
<Form.Item label='视频'>
{serviceDetail?.video ? (
<video
src={serviceDetail?.video}
controls
style={{ width: '200px', height: '200px' }}
/>
) : (
'暂无'
)}
</Form.Item>
</Form>
</div>
</div>
<>
<SearchBox
preFixBtn={
<div
style={{
width: '100%',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<div style={{ fontSize: '16px', fontWeight: 'bold' }}>新增服务</div>
<div>
<Button type={'primary'} icon={<SaveOutlined />} onClick={() => handleValid()}>
保存
</Button>
<Button type={'default'} icon={<ArrowLeftOutlined />} onClick={() => handleBack()}>
返回
</Button>
</div>
</div>
}
/>
<Form form={form} labelAlign='right' labelCol={{ span: 3 }} wrapperCol={{ span: 10 }}>
<Form.Item
label='服务主图'
name='companyInspectionFiles1'
rules={[{ required: true, message: '请上传服务主图' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={1}
fileSize={10}
fileType={['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp']}
onChange={(e) =>
handleUpload({ fileList: e, name: 'companyInspectionFiles1', type: 1, file: 0 })
}
defaultFileList={getDefaultFileList({ name: 'companyInspectionFiles1' })}
>
<PlusOutlined />
</Uploader>
</Form.Item>
<Form.Item
label='服务副图'
name='companyInspectionFiles2'
rules={[{ required: false, message: '请上传服务副图' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={5}
fileSize={10}
fileType={['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp']}
onChange={(e) =>
handleUpload({ fileList: e, name: 'companyInspectionFiles2', type: 0, file: 0 })
}
defaultFileList={getDefaultFileList({ name: 'companyInspectionFiles2' })}
>
<PlusOutlined />
</Uploader>
</Form.Item>
<Form.Item
label='服务视频'
name='companyInspectionFiles3'
rules={[{ required: false, message: '请上传服务视频' }]}
>
<Uploader
listType={'picture-card'}
fileUpload
fileLength={1}
fileSize={500}
fileType={['video/mp4', 'video/wmv', 'video/avi', 'video/mov', 'video/flv']}
onChange={(e) =>
handleUpload({ fileList: e, name: 'companyInspectionFiles3', type: 0, file: 1 })
}
defaultFileList={getDefaultFileList({ name: 'companyInspectionFiles3' })}
>
<PlusOutlined />
</Uploader>
</Form.Item>
<Form.Item
label='服务地区'
name='serviceArea'
rules={[
{ required: true, message: '请选择服务地区' },
// 最多只能选择5个地区
() => ({
validator(_, value) {
if (value && value.length <= 5) {
return Promise.resolve();
}
return Promise.reject(new Error('最多只能选择5个地区'));
},
}),
]}
>
<Select
placeholder={'请选择服务地区'}
options={districtList}
allowClear
mode={'multiple'}
/>
</Form.Item>
<Form.Item
label='所属服务'
name='inspectionId'
required
rules={[
// { required: true, message: '请选择所属服务' },
() => ({
validator(_, value) {
if (value && value.length === 2) {
return Promise.resolve();
}
return Promise.reject(new Error('请选择所属服务'));
},
}),
]}
>
<Cascader
placeholder={'请选择所属服务'}
options={industryList}
allowClear
onChange={(e) => {
form.setFieldValue('inspectionTagId', undefined);
if (e.length === 2) setInspectionId(Number(e?.at(-1)));
}}
/>
</Form.Item>
<Form.Item
label='场景标签'
name='inspectionTagId'
rules={[{ required: false, message: '请选择场景标签' }]}
>
<Select placeholder={'请选择场景标签'} options={inspectionTagList} allowClear />
</Form.Item>
<Form.Item
label='服务报价'
name='price'
rules={[{ required: true, validator: commonPriceValidator }]}
>
<Input placeholder='请输入服务报价' maxLength={15} allowClear type={'number'} />
</Form.Item>
<Form.Item
label='报价说明'
name='priceRemark'
rules={[{ required: true, message: '请输入报价说明' }]}
>
<Input.TextArea placeholder='请输入报价说明' maxLength={50} allowClear showCount />
</Form.Item>
<Form.Item
label='价格单位'
name='inspectionPriceUnitId'
rules={[{ required: true, message: '请选择价格单位' }]}
>
<Select placeholder={'请选择价格单位'} options={priceUnitList} allowClear />
</Form.Item>
<Form.Item label='备注' name='remark' rules={[{ required: false, message: '请输入备注' }]}>
<Input.TextArea placeholder='请输入备注' maxLength={50} allowClear showCount />
</Form.Item>
<Form.Item
label='详情页'
name='detailPage'
rules={[{ required: true, message: '请输入详情页' }]}
wrapperCol={{ span: 20 }}
>
<RichText
richTextContent={form.getFieldValue('detailPage')}
onChange={(e) => form.setFieldValue('detailPage', e)}
height={250}
/>
</Form.Item>
</Form>
</>
);
};
export default ServiceDetail;
}
export default ServiceDetailView;
import React, { FC, useEffect, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { searchColumns } from '~/components/search-box';
import SearchBox from '~/components/search-box';
import AddOrEditServiceModal from './components/addOrEditServiceModal';
import PreviewImageVideo from '~/components/previewImageVideo';
import { Button, Card, Image, message, Modal, Table } from 'antd';
import {
PlusOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import { ColumnsType } from 'antd/es/table';
import { useEffect, useState } from 'react';
import { Button, Image, message, Modal, Table } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import SearchView from '~/components/search-box';
import { CategoryManageAPI, MallManageAPI } from '~/api';
import { InterDataType, InterReqType, PaginationProps } from '~/api/interface';
import { categoryListType } from '~/api/interface/categoryManage';
import { serviceType } from '~/api/interface/mallManageType';
import qs from 'query-string';
import { InterListType, InterReqListType } from '~/api/interface';
import { listCompanyInspectionPageType } from '~/api/interface/mallManageType';
import { ColumnsType } from 'antd/es/table';
import { useNavigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
//分类返回类型
type categoryType = InterDataType<categoryListType>['list'];
//服务返回类型
type serviceListType = InterDataType<serviceType>['list'];
//服务列表请求类型
type serviceParametersType = InterReqType<serviceType>;
// 列表类型
type TableType = InterListType<listCompanyInspectionPageType>;
// 请求的参数
type ReqType = InterReqListType<listCompanyInspectionPageType>;
// 搜索表单的数据
let query: ReqType = {};
const ServiceList: FC<any> = () => {
const searchRef = useRef<any>();
const ServiceListView = () => {
// 路由钩子
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const tabList = [
{
key: '1',
tab: '全部',
},
{
key: '2',
tab: '上架中',
},
{
key: '3',
tab: '仓库中',
},
];
const [activeTabKey, setActiveTabKey] = useState<string>('1');
const [searchColumnsData, setSearchColumnsData] = useState<searchColumns[]>([
{
type: 'input',
placeholder: '请输入服务名称',
label: '服务名称',
name: 'serviceName',
},
const { userInfo } = useSelector((state: any) => state.UserInfo);
// 行业应用列表
const [industryList, setIndustryList] = useState<
{ label: string; value: number; children: { label: string; value: number }[] }[]
>([]);
// 获取行业列表
const getIndustryList = async () => {
const res = await CategoryManageAPI.industryListPages({
pageNo: 1,
pageSize: 9999,
});
if (res && res.code === '200') {
const list = res.result?.list || [];
setIndustryList(
list.map((i) => ({
label: i.typeName,
value: i.id,
children: i.inspectionDTOS?.map((j) => {
return {
label: j.inspectionName,
value: j.id,
};
}),
})),
);
}
};
// 表格数据
const [tableData, setTableData] = useState<TableType>([]);
// 表格分页配置
const [pagination, setPagination] = useState({
total: 0,
pageSize: 10,
current: 1,
totalPage: 0,
});
// 加载列表
const getTableList = async (value = {}) => {
// 只需要修改这个地方的接口即可
const res = await MallManageAPI.listCompanyInspectionPage({
pageNo: pagination.current,
pageSize: pagination.pageSize,
...value,
...query,
});
if (res && res.code === '200') {
const { list, pageNo, totalCount, pageSize, totalPage } = res.result; // 解构
setPagination({
total: totalCount,
current: pageNo,
pageSize,
totalPage,
});
setTableData(list || []);
// console.log(list);
} else {
message.warning(res.message);
}
};
// 翻页
const paginationChange = (pageNo: number, pageSize: number) => {
getTableList({ pageNo, pageSize }).then();
};
// 表单提交
const onFinish = (data: ReqType & { cascaderData: number[] }) => {
const obj = {
...data,
industryTypeId: data.cascaderData?.length >= 1 ? data.cascaderData[0] : undefined,
inspectionId: data.cascaderData?.length === 2 ? data.cascaderData[1] : undefined,
};
pagination.current = 1;
query = obj;
getTableList(obj).then();
};
// 新增服务
const handleAdd = () => {
navigate('/mallManage/serviceDetail');
};
// 跳转详情
const handleDetail = (record: TableType[0]) => {
navigate(`/mallManage/serviceDetail?id=${record.id}`);
};
// 删除服务
const handleDelete = (record: TableType[0]) => {
Modal.confirm({
title: '提示',
content: '是否删除该服务?',
onOk: async () => {
const res = await MallManageAPI.companyInspectionRemove({ id: record.id });
if (res && res.code === '200') {
message.success('删除成功');
paginationChange(pagination.current, pagination.pageSize);
}
},
});
};
// 上下架
const handleState = (record: TableType[0]) => {
Modal.confirm({
title: '提示',
content: `是否${record.saleState === 1 ? '下架' : '上架'}该服务?`,
onOk: async () => {
const res = await MallManageAPI.companyInspectionUpdate({
id: record.id,
saleState: record.saleState === 1 ? 0 : 1,
});
if (res && res.code === '200') {
message.success(`${record.saleState === 1 ? '下架' : '上架'}成功`);
paginationChange(pagination.current, pagination.pageSize);
}
},
});
};
// 价格单位列表
const [priceUnitList, setPriceUnitList] = useState<{ id: number; unitName: string }[]>([]);
// 获取价格单位
const getPriceUnit = async () => {
const res = await MallManageAPI.listInspectionPriceUnit({});
if (res && res.code === '200') {
setPriceUnitList(res.result || []);
}
};
// 获取商品的价格单位
const getPriceUnitStr = (i: TableType[0]) =>
priceUnitList.find((j) => j.id === i?.inspectionPriceUnitId)?.unitName || '';
// 飞手团队列表
const [companyInfoList, setCompanyInfoList] = useState<{ label: string; value: number }[]>([]);
// 获取飞手团队列表
const getListCompanyInfoByCoopId = async () => {
const res = await MallManageAPI.listCompanyInfoByCoopId({
lat: 22,
lon: 113,
pageNo: 1,
pageSize: 9999,
coopId: 5,
});
if (res && res.code === '200') {
const list = res.result?.list?.map((i) => ({ label: i.companyName, value: i.id }));
setCompanyInfoList(list || []);
}
};
// 组件挂载
useEffect(() => {
getIndustryList().then();
getPriceUnit().then();
getListCompanyInfoByCoopId().then();
getTableList().then();
}, []);
// 表格结构
const columns: ColumnsType<TableType[0]> = [
{
type: 'select',
placeholder: '请选择应用类型',
label: '应用类型',
name: 'applicationId',
options: [],
title: '序号',
dataIndex: 'id',
align: 'center',
width: '50px',
render: (_text, _record, index) => (pagination.current - 1) * pagination.pageSize + index + 1,
},
{
type: 'select',
placeholder: '请选择对应行业',
label: '对应行业',
name: 'industryId',
options: [],
title: '服务编号',
dataIndex: 'id',
align: 'center',
render: (_text, record) => record.inspectionDTO.inspectionNo,
},
]);
const tableColumns: ColumnsType<serviceListType[0]> = [
{
title: '序号',
title: '服务名称',
dataIndex: 'id',
align: 'center',
render: (_text: any, _record: any, index: number) =>
(pagination.pageNo - 1) * pagination.pageSize + index + 1,
render: (_text, record) => record.inspectionDTO.inspectionName,
},
{ title: '服务名称', align: 'center', dataIndex: 'serviceName', width: '20%' },
{ title: '应用', align: 'center', dataIndex: 'applicationName' },
{ title: '对应行业', align: 'center', dataIndex: 'industryName' },
{
title: '封面图',
title: '行业应用',
dataIndex: 'id',
align: 'center',
dataIndex: 'coverPlan',
render: (text: string) => <Image src={text} width={50} height={50} />,
render: (_text, record) => record.industryTypeDTO.typeName,
},
{
title: '分享卡片',
title: '作业团队',
dataIndex: 'companyName',
align: 'center',
dataIndex: 'shareCard',
render: (text: string) => (text ? <Image src={text} width={50} height={50} /> : '暂无'),
width: '150px',
ellipsis: true,
},
{
title: '视频',
title: '服务主图',
dataIndex: 'inspectionFirstImg',
align: 'center',
dataIndex: 'video',
render: (text: string) =>
text ? (
<Button type='link' onClick={() => previewVideo(text)}>
查看
</Button>
) : (
'暂无'
),
render: (text) => (
<>
<Image src={text} width={40} height={40} />
</>
),
},
{
title: '服务介绍',
title: '价格',
dataIndex: 'price',
align: 'center',
dataIndex: 'serviceIntroduction',
render: (_text: string, record) => (
<Button type='link' onClick={() => toServiceIntroduce(record)}>
编辑
</Button>
render: (text, record) => (
<>
{text.toLocaleString()}/{getPriceUnitStr(record)}
</>
),
},
{
title: '状态',
dataIndex: 'saleState',
align: 'center',
render: (text) => (text === 1 ? '上架' : '下架'),
},
{
title: '备注',
dataIndex: 'remark',
align: 'center',
dataIndex: 'displayState',
render: (text: number) => (text === 0 ? '上架' : '下架'),
width: '100px',
ellipsis: true,
},
{
title: '操作',
dataIndex: 'id',
align: 'center',
render: (_text: any, record) => (
width: '150px',
fixed: 'right',
render: (_text, record) => (
<>
<Button type='link' onClick={() => addOrEditServiceModalShow(record)}>
编辑
</Button>
<Button type='link' onClick={() => toServiceDetail(record)}>
详情
</Button>
</>
),
},
];
const [loading, setLoading] = useState<boolean>(false);
const [tableData, setTableData] = useState<serviceListType>([]); //表格数据
const [allServiceData, setAllServiceData] = useState<serviceListType>([]);
const [currentServiceData, setCurrentServiceData] = useState<serviceListType[0]>();
// 表格多选
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [pagination, setPagination] = useState<
PaginationProps & { totalCount: number; totalPage: number }
>({
pageNo: 1,
pageSize: 10,
totalCount: 0,
totalPage: 1,
});
//筛选
const [query, setQuery] = useState<serviceParametersType>({
displayState: undefined,
});
//行业分类列表
const [industryCategoryList, setIndustryCategoryList] = useState<categoryType>([]);
//应用分类列表
const [applicationCategoryList, setApplicationCategoryList] = useState<categoryType>([]);
//新增、编辑服务弹窗
const [addOrEditServiceModalOpen, setAddOrEditServiceModalOpen] = useState<boolean>(false);
const [addOrEditServiceModalTitle, setAddOrEditServiceModalTitle] = useState<string>('新增服务');
//预览视频、图片
const [previewShow, setPreviewShow] = useState<boolean>(false);
const [previewUrl, setPreviewUrl] = useState<string>('');
const onTabChange = (key: string) => {
pagination.pageNo = 1;
pagination.pageSize = 10;
setQuery({ ...query, displayState: key === '1' ? undefined : key === '2' ? 0 : 1 });
setSearchParams(
qs.stringify({ ...query, displayState: key === '1' ? 'all' : key === '2' ? 0 : 1 }),
);
setActiveTabKey(key);
getServiceList({ ...query, displayState: key === '1' ? undefined : key === '2' ? 0 : 1 });
getServiceList(
{
...query,
displayState: key === '1' ? undefined : key === '2' ? 0 : 1,
pageNo: 1,
pageSize: 9999,
},
true,
);
};
//新增服务
const addOrEditServiceModalShow = (record?: serviceListType[0]) => {
setAddOrEditServiceModalTitle(record ? '编辑服务' : '新增服务');
setAddOrEditServiceModalOpen(true);
setCurrentServiceData(record && { ...record });
};
const addOrEditServiceModalCancel = () => {
setAddOrEditServiceModalOpen(false);
};
const addOrEditServiceModalOk = () => {
setAddOrEditServiceModalOpen(false);
getServiceList(query);
};
//服务-列表
const getServiceList = (query?: serviceParametersType, isAll?: boolean) => {
setLoading(true);
MallManageAPI.getServiceList({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
...query,
}).then(({ result }) => {
setLoading(false);
if (isAll) {
setAllServiceData(result.list || []);
} else {
setTableData(result.list || []);
pagination.totalCount = result.totalCount;
pagination.totalPage = result.totalPage;
setPagination(pagination);
}
});
};
//行业分类列表
const getIndustryCategoryList = () => {
CategoryManageAPI.getCategoryList({ directoryId: 2, pageSize: 99999, pageNo: 1 }).then(
({ result }) => {
setIndustryCategoryList(result.list || []);
searchColumnsData[2].options =
result.list?.map((v) => ({
id: v.id,
name: v.classifyName,
})) || [];
setSearchColumnsData(searchColumnsData);
},
);
};
//应用分类列表
const getApplicationCategoryList = () => {
CategoryManageAPI.getCategoryList({ directoryId: 3, pageSize: 99999, pageNo: 1 }).then(
({ result }) => {
setApplicationCategoryList(result.list || []);
searchColumnsData[1].options =
result.list?.map((v) => ({
id: v.id,
name: v.classifyName,
})) || [];
setSearchColumnsData(searchColumnsData);
},
);
};
//服务详情
const toServiceDetail = (record: serviceListType[0]) => {
navigate({ pathname: '/mallManage/serviceDetail', search: `id=${record.id}` });
};
//服务介绍
const toServiceIntroduce = (record: serviceListType[0]) => {
navigate({ pathname: '/mallManage/serviceIntroduce', search: `id=${record.id}` });
};
//上下架
const groundingOrOffService = (status: number) => {
if (selectedRowKeys.length === 0) {
message.warning('请先选择服务');
return;
}
MallManageAPI.batchUpAndDownWorkService({
displayState: status,
ids: selectedRowKeys as number[],
}).then(({ code }) => {
if (code === '200') {
message.success(status ? '下架成功' : '上架成功');
getServiceList(query);
setSelectedRowKeys([]);
}
});
};
//服务删除
const deleteService = () => {
if (selectedRowKeys.length === 0) {
message.warning('请先选择服务');
return;
}
Modal.confirm({
title: '提示',
content: '删除后数据将会丢失,确定删除吗?',
onOk() {
MallManageAPI.deleteService(selectedRowKeys as number[]).then(({ code }) => {
if (code === '200') {
if (pagination.pageNo !== 1 && tableData.length === 1) {
pagination.pageNo -= 1;
}
message.success('删除成功');
getServiceList(query);
}
});
},
});
};
//分页事件
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageNo = pageNo;
pagination.pageSize = pageSize;
setSearchParams(
qs.stringify({
...query,
pageNo,
pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
getServiceList(query);
getServiceList({ ...query, pageNo: 1, pageSize: 9999 }, true);
};
// 表格多选事件
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
};
//筛选
const searchSuccess = (data: any) => {
pagination.pageNo = 1;
pagination.pageSize = 10;
setSearchParams(
qs.stringify({
...data,
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
setQuery(data);
getServiceList(data);
getServiceList({ ...data, pageNo: 1, pageSize: 9999 }, true);
};
//预览视频
const previewVideo = (url: string) => {
setPreviewShow(true);
setPreviewUrl(url);
};
const previewCancel = () => {
setPreviewShow(false);
};
//上移
const upServiceClick = () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择服务');
} else if (selectedRowKeys.length > 1) {
message.warning('最多选择一个服务');
} else {
const index = tableData.findIndex((v) => v.id === selectedRowKeys[0]);
const allIndex = allServiceData.findIndex((v) => v.id === selectedRowKeys[0]);
if (index === 0 && pagination.pageNo === 1) {
message.warning('位置已到最前列,无法上移');
} else {
const exReqData =
index === 0
? allServiceData
.filter((_v, index) => index === allIndex - 1 || index === allIndex)
.map((v) => ({ id: v.id, sort: v.sort }))
: tableData
.filter((_v, i) => index - 1 === i || index === i)
.map((v) => ({ id: v.id, sort: v.sort }));
MallManageAPI.exChangeService(exReqData).then(({ code }) => {
if (code === '200') {
message.success('上移成功');
if (index === 0 && pagination.pageNo !== 1) {
pagination.pageNo -= 1;
setSearchParams(
qs.stringify({
...query,
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
}
getServiceList(query);
getServiceList({ ...query, pageNo: 1, pageSize: 9999 }, true);
}
});
}
}
};
//下移
const downServiceClick = () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择服务');
} else if (selectedRowKeys.length > 1) {
message.warning('最多选择一个服务');
} else {
const index = tableData.findIndex((v) => v.id === selectedRowKeys[0]);
const allIndex = allServiceData.findIndex((v) => v.id === selectedRowKeys[0]);
if (index === tableData.length - 1 && pagination.pageNo === pagination.totalPage) {
message.warning('位置已到最后,无法下移');
} else {
const exReqData =
index === tableData.length - 1
? allServiceData
.filter((_v, index) => index === allIndex + 1 || index === allIndex)
.map((v) => ({ id: v.id, sort: v.sort }))
: tableData
.filter((_v, i) => index + 1 === i || index === i)
.map((v) => ({ id: v.id, sort: v.sort }));
MallManageAPI.exChangeService(exReqData).then(({ code }) => {
if (code === '200') {
message.success('下移成功');
if (index === tableData.length - 1 && pagination.pageNo !== pagination.totalPage) {
pagination.pageNo += 1;
setSearchParams(
qs.stringify({
...query,
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
}
getServiceList(query);
getServiceList({ ...query, pageNo: 1, pageSize: 9999 }, true);
}
});
}
}
};
useEffect(() => {
pagination.pageNo = Number(searchParams.get('pageNo') || 1);
pagination.pageSize = Number(searchParams.get('pageSize') || 10);
searchRef.current.getForm().setFieldsValue({
serviceName: searchParams.get('serviceName') || undefined,
applicationId: searchParams.get('applicationId')
? Number(searchParams.get('applicationId'))
: undefined,
industryId: searchParams.get('industryId')
? Number(searchParams.get('industryId'))
: undefined,
});
const queryObj = {
serviceName: searchParams.get('serviceName') || undefined,
applicationId: searchParams.get('applicationId')
? Number(searchParams.get('applicationId'))
: undefined,
industryId: searchParams.get('industryId')
? Number(searchParams.get('industryId'))
: undefined,
displayState:
searchParams.get('displayState') === 'all' || searchParams.get('displayState') === null
? undefined
: Number(searchParams.get('displayState')),
};
setQuery(queryObj);
setActiveTabKey(
searchParams.get('displayState') === 'all' || searchParams.get('displayState') === null
? '1'
: Number(searchParams.get('displayState')) === 0
? '2'
: '3',
);
getServiceList(queryObj);
getServiceList({ ...queryObj, pageNo: 1, pageSize: 9999 }, true);
getIndustryCategoryList();
getApplicationCategoryList();
}, []);
return (
<div className='service-list'>
<SearchBox
search={searchColumnsData}
child={
<Button
type='primary'
icon={<PlusOutlined />}
onClick={() => addOrEditServiceModalShow()}
type={'link'}
onClick={() => {
handleDetail(record);
}}
>
新增服务
详情
</Button>
}
searchData={searchSuccess}
baseRef={searchRef}
/>
<Card tabList={tabList} activeTabKey={activeTabKey} onTabChange={onTabChange}>
<div className='header-operate' style={{ marginBottom: '10px' }}>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowUpOutlined />}
onClick={upServiceClick}
type={'link'}
danger
onClick={() => {
handleDelete(record);
}}
>
上移
删除
</Button>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowDownOutlined />}
onClick={downServiceClick}
type={'link'}
onClick={() => {
handleState(record);
}}
>
下移
{record.saleState === 1 ? '下架' : '上架'}
</Button>
{activeTabKey !== '2' ? (
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowUpOutlined />}
onClick={() => groundingOrOffService(0)}
>
上架
</Button>
) : (
''
)}
{activeTabKey !== '3' ? (
</>
),
},
];
return (
<>
<SearchView
child={
<>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowDownOutlined />}
onClick={() => groundingOrOffService(1)}
onClick={() => handleAdd()}
icon={<PlusOutlined />}
disabled={!!userInfo.roleInfo.superAdmin}
>
下架
新增服务
</Button>
) : (
''
)}
<Button danger icon={<DeleteOutlined />} onClick={deleteService}>
删除
</Button>
</div>
<Table
columns={tableColumns}
loading={loading}
bordered
rowKey='id'
dataSource={tableData}
pagination={{
total: pagination.totalCount,
pageSize: pagination.pageSize,
current: pagination.pageNo,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
rowSelection={{
selectedRowKeys,
onChange: onSelectChange,
}}
/>
</Card>
{/*新增/编辑服务弹窗*/}
<AddOrEditServiceModal
open={addOrEditServiceModalOpen}
title={addOrEditServiceModalTitle}
handleCancel={addOrEditServiceModalCancel}
industryCategoryList={industryCategoryList}
applicationCategoryList={applicationCategoryList}
handleOk={addOrEditServiceModalOk}
currentServiceData={currentServiceData}
</>
}
search={[
{
label: '服务名称',
placeholder: '请输入服务编号、名称',
type: 'input',
name: 'keyword',
},
{
label: '行业应用',
placeholder: '请选择行业应用',
type: 'Cascader',
name: 'cascaderData',
options: industryList,
},
{
label: '作业团队',
placeholder: '请选择作业团队',
type: 'Select',
name: 'companyInfoId',
options: companyInfoList,
},
]}
searchData={onFinish}
/>
{/* 预览*/}
<PreviewImageVideo
open={previewShow}
onCancel={previewCancel}
type='video'
url={previewUrl}
<Table
size='small'
dataSource={tableData}
columns={columns}
rowKey='id'
scroll={{ x: 1000 }}
bordered
pagination={{
total: pagination.total,
pageSize: pagination.pageSize,
current: pagination.current,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
/>
</div>
</>
);
};
export default ServiceList;
export default ServiceListView;
import { Button, Form, Image } from 'antd';
import { useNavigate, useSearchParams } from 'react-router-dom';
import './index.scss';
import { MallManageAPI } from '~/api';
import { useEffect, useState } from 'react';
import { InterDataType } from '~/api/interface';
import { serviceDetailType } from '~/api/interface/mallManageType';
//服务详情-返回类型
type detailType = InterDataType<serviceDetailType>;
const ServiceDetail = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [serviceDetail, setServiceDetail] = useState<detailType>();
//服务详情
const getServiceDetail = (id: number) => {
MallManageAPI.getServiceDetail({ id }).then(({ result }) => {
setServiceDetail(result);
});
};
//返回
const backRoute = () => {
navigate(-1);
};
useEffect(() => {
getServiceDetail(Number(searchParams.get('id')));
}, []);
return (
<div className='service-detail'>
<div className='service-detail-operate'>
<Button type='primary' onClick={backRoute}>
返回
</Button>
</div>
<div className='service-detail-form'>
<Form labelCol={{ span: 2 }} wrapperCol={{ span: 16 }}>
<Form.Item label='服务名称'>{serviceDetail?.serviceName}</Form.Item>
<Form.Item label='应用类型'>{serviceDetail?.applicationName}</Form.Item>
<Form.Item label='对应行业'>{serviceDetail?.industryName}</Form.Item>
<Form.Item label='展示状态'>
{serviceDetail?.displayState === 0 ? '上架' : '下架'}
</Form.Item>
<Form.Item label='所属单位'>{serviceDetail?.companyName}</Form.Item>
<Form.Item label='封面图'>
<Image src={serviceDetail?.coverPlan} width={100} height={100} />
</Form.Item>
<Form.Item label='分享卡片'>
{serviceDetail?.shareCard ? (
<Image src={serviceDetail.shareCard} width={100} height={100} />
) : (
'暂无'
)}
</Form.Item>
<Form.Item label='视频'>
{serviceDetail?.video ? (
<video
src={serviceDetail?.video}
controls
style={{ width: '200px', height: '200px' }}
/>
) : (
'暂无'
)}
</Form.Item>
</Form>
</div>
</div>
);
};
export default ServiceDetail;
import React, { FC, useEffect, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { searchColumns } from '~/components/search-box';
import SearchBox from '~/components/search-box';
import AddOrEditServiceModal from './components/addOrEditServiceModal';
import PreviewImageVideo from '~/components/previewImageVideo';
import { Button, Card, Image, message, Modal, Table } from 'antd';
import {
PlusOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import { ColumnsType } from 'antd/es/table';
import { CategoryManageAPI, MallManageAPI } from '~/api';
import { InterDataType, InterReqType, PaginationProps } from '~/api/interface';
import { categoryListType } from '~/api/interface/categoryManage';
import { serviceType } from '~/api/interface/mallManageType';
import qs from 'query-string';
//分类返回类型
type categoryType = InterDataType<categoryListType>['list'];
//服务返回类型
type serviceListType = InterDataType<serviceType>['list'];
//服务列表请求类型
type serviceParametersType = InterReqType<serviceType>;
const ServiceList: FC<any> = () => {
const searchRef = useRef<any>();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const tabList = [
{
key: '1',
tab: '全部',
},
{
key: '2',
tab: '上架中',
},
{
key: '3',
tab: '仓库中',
},
];
const [activeTabKey, setActiveTabKey] = useState<string>('1');
const [searchColumnsData, setSearchColumnsData] = useState<searchColumns[]>([
{
type: 'input',
placeholder: '请输入服务名称',
label: '服务名称',
name: 'serviceName',
},
{
type: 'select',
placeholder: '请选择应用类型',
label: '应用类型',
name: 'applicationId',
options: [],
},
{
type: 'select',
placeholder: '请选择对应行业',
label: '对应行业',
name: 'industryId',
options: [],
},
]);
const tableColumns: ColumnsType<serviceListType[0]> = [
{
title: '序号',
align: 'center',
render: (_text: any, _record: any, index: number) =>
(pagination.pageNo - 1) * pagination.pageSize + index + 1,
},
{ title: '服务名称', align: 'center', dataIndex: 'serviceName', width: '20%' },
{ title: '应用', align: 'center', dataIndex: 'applicationName' },
{ title: '对应行业', align: 'center', dataIndex: 'industryName' },
{
title: '封面图',
align: 'center',
dataIndex: 'coverPlan',
render: (text: string) => <Image src={text} width={50} height={50} />,
},
{
title: '分享卡片',
align: 'center',
dataIndex: 'shareCard',
render: (text: string) => (text ? <Image src={text} width={50} height={50} /> : '暂无'),
},
{
title: '视频',
align: 'center',
dataIndex: 'video',
render: (text: string) =>
text ? (
<Button type='link' onClick={() => previewVideo(text)}>
查看
</Button>
) : (
'暂无'
),
},
{
title: '服务介绍',
align: 'center',
dataIndex: 'serviceIntroduction',
render: (_text: string, record) => (
<Button type='link' onClick={() => toServiceIntroduce(record)}>
编辑
</Button>
),
},
{
title: '状态',
align: 'center',
dataIndex: 'displayState',
render: (text: number) => (text === 0 ? '上架' : '下架'),
},
{
title: '操作',
align: 'center',
render: (_text: any, record) => (
<>
<Button type='link' onClick={() => addOrEditServiceModalShow(record)}>
编辑
</Button>
<Button type='link' onClick={() => toServiceDetail(record)}>
详情
</Button>
</>
),
},
];
const [loading, setLoading] = useState<boolean>(false);
const [tableData, setTableData] = useState<serviceListType>([]); //表格数据
const [allServiceData, setAllServiceData] = useState<serviceListType>([]);
const [currentServiceData, setCurrentServiceData] = useState<serviceListType[0]>();
// 表格多选
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [pagination, setPagination] = useState<
PaginationProps & { totalCount: number; totalPage: number }
>({
pageNo: 1,
pageSize: 10,
totalCount: 0,
totalPage: 1,
});
//筛选
const [query, setQuery] = useState<serviceParametersType>({
displayState: undefined,
});
//行业分类列表
const [industryCategoryList, setIndustryCategoryList] = useState<categoryType>([]);
//应用分类列表
const [applicationCategoryList, setApplicationCategoryList] = useState<categoryType>([]);
//新增、编辑服务弹窗
const [addOrEditServiceModalOpen, setAddOrEditServiceModalOpen] = useState<boolean>(false);
const [addOrEditServiceModalTitle, setAddOrEditServiceModalTitle] = useState<string>('新增服务');
//预览视频、图片
const [previewShow, setPreviewShow] = useState<boolean>(false);
const [previewUrl, setPreviewUrl] = useState<string>('');
const onTabChange = (key: string) => {
pagination.pageNo = 1;
pagination.pageSize = 10;
setQuery({ ...query, displayState: key === '1' ? undefined : key === '2' ? 0 : 1 });
setSearchParams(
qs.stringify({ ...query, displayState: key === '1' ? 'all' : key === '2' ? 0 : 1 }),
);
setActiveTabKey(key);
getServiceList({ ...query, displayState: key === '1' ? undefined : key === '2' ? 0 : 1 });
getServiceList(
{
...query,
displayState: key === '1' ? undefined : key === '2' ? 0 : 1,
pageNo: 1,
pageSize: 9999,
},
true,
);
};
//新增服务
const addOrEditServiceModalShow = (record?: serviceListType[0]) => {
setAddOrEditServiceModalTitle(record ? '编辑服务' : '新增服务');
setAddOrEditServiceModalOpen(true);
setCurrentServiceData(record && { ...record });
};
const addOrEditServiceModalCancel = () => {
setAddOrEditServiceModalOpen(false);
};
const addOrEditServiceModalOk = () => {
setAddOrEditServiceModalOpen(false);
getServiceList(query);
};
//服务-列表
const getServiceList = (query?: serviceParametersType, isAll?: boolean) => {
setLoading(true);
MallManageAPI.getServiceList({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
...query,
}).then(({ result }) => {
setLoading(false);
if (isAll) {
setAllServiceData(result.list || []);
} else {
setTableData(result.list || []);
pagination.totalCount = result.totalCount;
pagination.totalPage = result.totalPage;
setPagination(pagination);
}
});
};
//行业分类列表
const getIndustryCategoryList = () => {
CategoryManageAPI.getCategoryList({ directoryId: 2, pageSize: 99999, pageNo: 1 }).then(
({ result }) => {
setIndustryCategoryList(result.list || []);
searchColumnsData[2].options =
result.list?.map((v) => ({
id: v.id,
name: v.classifyName,
})) || [];
setSearchColumnsData(searchColumnsData);
},
);
};
//应用分类列表
const getApplicationCategoryList = () => {
CategoryManageAPI.getCategoryList({ directoryId: 3, pageSize: 99999, pageNo: 1 }).then(
({ result }) => {
setApplicationCategoryList(result.list || []);
searchColumnsData[1].options =
result.list?.map((v) => ({
id: v.id,
name: v.classifyName,
})) || [];
setSearchColumnsData(searchColumnsData);
},
);
};
//服务详情
const toServiceDetail = (record: serviceListType[0]) => {
navigate({ pathname: '/mallManage/serviceDetail', search: `id=${record.id}` });
};
//服务介绍
const toServiceIntroduce = (record: serviceListType[0]) => {
navigate({ pathname: '/mallManage/serviceIntroduce', search: `id=${record.id}` });
};
//上下架
const groundingOrOffService = (status: number) => {
if (selectedRowKeys.length === 0) {
message.warning('请先选择服务');
return;
}
MallManageAPI.batchUpAndDownWorkService({
displayState: status,
ids: selectedRowKeys as number[],
}).then(({ code }) => {
if (code === '200') {
message.success(status ? '下架成功' : '上架成功');
getServiceList(query);
setSelectedRowKeys([]);
}
});
};
//服务删除
const deleteService = () => {
if (selectedRowKeys.length === 0) {
message.warning('请先选择服务');
return;
}
Modal.confirm({
title: '提示',
content: '删除后数据将会丢失,确定删除吗?',
onOk() {
MallManageAPI.deleteService(selectedRowKeys as number[]).then(({ code }) => {
if (code === '200') {
if (pagination.pageNo !== 1 && tableData.length === 1) {
pagination.pageNo -= 1;
}
message.success('删除成功');
getServiceList(query);
}
});
},
});
};
//分页事件
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageNo = pageNo;
pagination.pageSize = pageSize;
setSearchParams(
qs.stringify({
...query,
pageNo,
pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
getServiceList(query);
getServiceList({ ...query, pageNo: 1, pageSize: 9999 }, true);
};
// 表格多选事件
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
};
//筛选
const searchSuccess = (data: any) => {
pagination.pageNo = 1;
pagination.pageSize = 10;
setSearchParams(
qs.stringify({
...data,
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
setQuery(data);
getServiceList(data);
getServiceList({ ...data, pageNo: 1, pageSize: 9999 }, true);
};
//预览视频
const previewVideo = (url: string) => {
setPreviewShow(true);
setPreviewUrl(url);
};
const previewCancel = () => {
setPreviewShow(false);
};
//上移
const upServiceClick = () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择服务');
} else if (selectedRowKeys.length > 1) {
message.warning('最多选择一个服务');
} else {
const index = tableData.findIndex((v) => v.id === selectedRowKeys[0]);
const allIndex = allServiceData.findIndex((v) => v.id === selectedRowKeys[0]);
if (index === 0 && pagination.pageNo === 1) {
message.warning('位置已到最前列,无法上移');
} else {
const exReqData =
index === 0
? allServiceData
.filter((_v, index) => index === allIndex - 1 || index === allIndex)
.map((v) => ({ id: v.id, sort: v.sort }))
: tableData
.filter((_v, i) => index - 1 === i || index === i)
.map((v) => ({ id: v.id, sort: v.sort }));
MallManageAPI.exChangeService(exReqData).then(({ code }) => {
if (code === '200') {
message.success('上移成功');
if (index === 0 && pagination.pageNo !== 1) {
pagination.pageNo -= 1;
setSearchParams(
qs.stringify({
...query,
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
}
getServiceList(query);
getServiceList({ ...query, pageNo: 1, pageSize: 9999 }, true);
}
});
}
}
};
//下移
const downServiceClick = () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择服务');
} else if (selectedRowKeys.length > 1) {
message.warning('最多选择一个服务');
} else {
const index = tableData.findIndex((v) => v.id === selectedRowKeys[0]);
const allIndex = allServiceData.findIndex((v) => v.id === selectedRowKeys[0]);
if (index === tableData.length - 1 && pagination.pageNo === pagination.totalPage) {
message.warning('位置已到最后,无法下移');
} else {
const exReqData =
index === tableData.length - 1
? allServiceData
.filter((_v, index) => index === allIndex + 1 || index === allIndex)
.map((v) => ({ id: v.id, sort: v.sort }))
: tableData
.filter((_v, i) => index + 1 === i || index === i)
.map((v) => ({ id: v.id, sort: v.sort }));
MallManageAPI.exChangeService(exReqData).then(({ code }) => {
if (code === '200') {
message.success('下移成功');
if (index === tableData.length - 1 && pagination.pageNo !== pagination.totalPage) {
pagination.pageNo += 1;
setSearchParams(
qs.stringify({
...query,
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
displayState: query.displayState === undefined ? 'all' : query.displayState,
}),
);
}
getServiceList(query);
getServiceList({ ...query, pageNo: 1, pageSize: 9999 }, true);
}
});
}
}
};
useEffect(() => {
pagination.pageNo = Number(searchParams.get('pageNo') || 1);
pagination.pageSize = Number(searchParams.get('pageSize') || 10);
searchRef.current.getForm().setFieldsValue({
serviceName: searchParams.get('serviceName') || undefined,
applicationId: searchParams.get('applicationId')
? Number(searchParams.get('applicationId'))
: undefined,
industryId: searchParams.get('industryId')
? Number(searchParams.get('industryId'))
: undefined,
});
const queryObj = {
serviceName: searchParams.get('serviceName') || undefined,
applicationId: searchParams.get('applicationId')
? Number(searchParams.get('applicationId'))
: undefined,
industryId: searchParams.get('industryId')
? Number(searchParams.get('industryId'))
: undefined,
displayState:
searchParams.get('displayState') === 'all' || searchParams.get('displayState') === null
? undefined
: Number(searchParams.get('displayState')),
};
setQuery(queryObj);
setActiveTabKey(
searchParams.get('displayState') === 'all' || searchParams.get('displayState') === null
? '1'
: Number(searchParams.get('displayState')) === 0
? '2'
: '3',
);
getServiceList(queryObj);
getServiceList({ ...queryObj, pageNo: 1, pageSize: 9999 }, true);
getIndustryCategoryList();
getApplicationCategoryList();
}, []);
return (
<div className='service-list'>
<SearchBox
search={searchColumnsData}
child={
<Button
type='primary'
icon={<PlusOutlined />}
onClick={() => addOrEditServiceModalShow()}
>
新增服务
</Button>
}
searchData={searchSuccess}
baseRef={searchRef}
/>
<Card tabList={tabList} activeTabKey={activeTabKey} onTabChange={onTabChange}>
<div className='header-operate' style={{ marginBottom: '10px' }}>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowUpOutlined />}
onClick={upServiceClick}
>
上移
</Button>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowDownOutlined />}
onClick={downServiceClick}
>
下移
</Button>
{activeTabKey !== '2' ? (
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowUpOutlined />}
onClick={() => groundingOrOffService(0)}
>
上架
</Button>
) : (
''
)}
{activeTabKey !== '3' ? (
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowDownOutlined />}
onClick={() => groundingOrOffService(1)}
>
下架
</Button>
) : (
''
)}
<Button danger icon={<DeleteOutlined />} onClick={deleteService}>
删除
</Button>
</div>
<Table
columns={tableColumns}
loading={loading}
bordered
rowKey='id'
dataSource={tableData}
pagination={{
total: pagination.totalCount,
pageSize: pagination.pageSize,
current: pagination.pageNo,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
rowSelection={{
selectedRowKeys,
onChange: onSelectChange,
}}
/>
</Card>
{/*新增/编辑服务弹窗*/}
<AddOrEditServiceModal
open={addOrEditServiceModalOpen}
title={addOrEditServiceModalTitle}
handleCancel={addOrEditServiceModalCancel}
industryCategoryList={industryCategoryList}
applicationCategoryList={applicationCategoryList}
handleOk={addOrEditServiceModalOk}
currentServiceData={currentServiceData}
/>
{/* 预览*/}
<PreviewImageVideo
open={previewShow}
onCancel={previewCancel}
type='video'
url={previewUrl}
/>
</div>
);
};
export default ServiceList;
.accessory-list{
&-title{
font-size: 15px;
font-weight: bold;
line-height: 40px;
}
}
import './index.scss';
import { Button, Col, Form, Row, Table, Tooltip } from 'antd';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { InterDataType, InterReqType } from '~/api/interface';
import { addRentGoodsType, leaseGoodsDetailsType } from '~/api/interface/rentManageType';
import EditableCell from '~/components/EditableCell';
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
import { isEmptyBol, regPriceNumber } from '~/utils/validateUtils';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
//租赁-清单类型
type leasePartsListType = Exclude<
InterReqType<addRentGoodsType>,
undefined
>['leasePartsList'][0] & { id?: number };
type EditableTableProps = Parameters<typeof Table>[0];
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
interface selfProps {
ref: any;
rentGoodsDetails: rentGoodsDetailType | undefined;
}
const AccessoryList = forwardRef<any, selfProps>(({ rentGoodsDetails }, ref) => {
const [accessoryTableForm] = Form.useForm<{ [x: string]: string | number }>();
//库存正则校验
const stockValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (/^[+]{0,1}(\d+)$/.test(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('库存最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('请输入正整数'));
}
} else {
return Promise.resolve();
}
};
//价格正则校验
const priceValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (regPriceNumber(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('价格最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('为整数且最多保留两位小数'));
}
} else {
return Promise.resolve();
}
};
const accessoryTableDefaultColumns: (ColumnTypes[number] & {
editable?: boolean;
dataIndex?: string;
inputType?: string;
rules?: any;
maxLength?: number;
})[] = [
{
title: '序号',
align: 'center',
render: (_: any, _record: any, index: number) => index + 1,
},
{
title: '名称',
editable: true,
dataIndex: 'name',
align: 'center',
width: '30%',
maxLength: 30,
},
{
title: '数量',
editable: true,
dataIndex: 'number',
align: 'center',
inputType: 'number',
width: '15%',
rules: [{ required: false, validator: stockValidator }],
},
{
title: '参考价格',
editable: true,
dataIndex: 'price',
align: 'center',
inputType: 'number',
rules: [{ required: false, validator: priceValidator }],
},
{
title: '操作',
align: 'center',
render: (_: any, _record: any, index: number) => (
<>
{index === accessoryTableData.length - 1 ? (
<Tooltip placement='top' title='添加一行'>
<Button
type='primary'
icon={<PlusOutlined />}
style={{ marginRight: '5px' }}
onClick={addAccessoryTableClick}
></Button>
</Tooltip>
) : (
''
)}
{index ? (
<Tooltip placement='top' title='删除一行'>
<Button
type='primary'
icon={<MinusOutlined />}
onClick={() => deleteAccessoryTableClick(index)}
></Button>
</Tooltip>
) : (
''
)}
</>
),
},
];
const accessoryTableColumns = accessoryTableDefaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: any) => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: col.editable,
inputType: col.inputType,
rules: col.rules,
maxLength: col.maxLength,
}),
};
});
const [accessoryTableData, setAccessoryTableData] = useState<leasePartsListType[]>([
{ id: Math.random(), name: '', number: 0, price: 0 },
]);
useImperativeHandle(ref, () => ({
accessoryTableFormSubmit,
}));
//新加一行
const addAccessoryTableClick = () => {
setAccessoryTableData([
...accessoryTableData,
{ id: Math.random(), name: '', number: 0, price: 0 },
]);
};
//删除一行
const deleteAccessoryTableClick = (index: number) => {
accessoryTableData.splice(index, 1);
setAccessoryTableData([...accessoryTableData]);
};
//配件清单表单提交
const accessoryTableFormSubmit = () => {
return new Promise((resolve, reject) => {
accessoryTableForm
.validateFields()
.then((value: any) => {
resolve(
accessoryTableData.reduce((pre: leasePartsListType[], cur) => {
if (value['name' + cur.id] || value['price' + cur.id] || value['number' + cur.id]) {
pre.push({
name: value['name' + cur.id],
price: value['price' + cur.id],
number: value['number' + cur.id],
id: rentGoodsDetails
? rentGoodsDetails.leasePartsList?.some((v) => v.id === cur.id)
? cur.id
: undefined
: undefined,
});
}
return pre;
}, []),
);
})
.catch((err) => {
reject(err);
});
});
};
useEffect(() => {
if (rentGoodsDetails) {
if (rentGoodsDetails.leasePartsList) {
setAccessoryTableData(rentGoodsDetails.leasePartsList);
const defaultFormValue: { [x: string]: string | undefined | number } =
rentGoodsDetails.leasePartsList.reduce(
(pre: { [x: string]: string | undefined | number }, cur) => {
pre['name' + cur.id] = cur.name || undefined;
pre['number' + cur.id] = cur.number || undefined;
pre['price' + cur.id] = cur.price || undefined;
return pre;
},
{},
);
accessoryTableForm.setFieldsValue(defaultFormValue);
}
}
}, [rentGoodsDetails]);
return (
<div className='accessory-list'>
<div className='accessory-list-title'>配件清单</div>
<Row>
<Col span={2}></Col>
<Col span={11}>
<Form form={accessoryTableForm}>
<Table
columns={accessoryTableColumns as ColumnTypes}
bordered
dataSource={accessoryTableData}
rowKey='id'
pagination={false}
components={{
body: {
cell: EditableCell,
},
}}
/>
</Form>
</Col>
</Row>
</div>
);
});
export default AccessoryList;
.address-info{
&-title{
font-size: 15px;
font-weight: bold;
line-height: 40px;
}
}
import './index.scss';
import { Button, Col, Form, Radio, Row, Select } from 'antd';
import { OrderManageAPI, RentManageAPI, SystemManageAPI } from '~/api';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { PlusOutlined } from '@ant-design/icons';
import AddOrEditAddressModal from '~/pages/systemManage/addressManage/components/addOrEditAddressModal';
import { InterDataType } from '~/api/interface';
import { leaseGoodsDetailsType } from '~/api/interface/rentManageType';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
interface selfProps {
ref: any;
rentGoodsDetails: rentGoodsDetailType | undefined;
}
const AddressInfo = forwardRef<any, selfProps>(({ rentGoodsDetails }, ref) => {
const [addressInfoForm] = Form.useForm<{
shipAddress: number;
returnAddress: number;
logisticsCompany: string;
modeOfDelivery: number;
}>();
const [addOrEditAddressModalShow, setAddOrEditAddressModalShow] = useState<boolean>(false);
const [addressOptionList, setAddressOptionList] = useState<
{ label: string; value: number; districtCode: string }[]
>([]);
const [expressOptionList, setExpressOptionList] = useState<{ label: string; value: string }[]>(
[],
);
//配送方式
const [shippingMethodList, setShippingMethodList] = useState<
{ id: number; saleServiceName: string }[]
>([]);
useImperativeHandle(ref, () => ({
addressInfoFormSubmit,
getForm: () => addressInfoForm,
}));
//地址列表
const getAddressList = () => {
SystemManageAPI.getAddressList({}).then(({ result }) => {
if (result) {
const optionList = result.map((v) => ({
label: v.takeName + v.takePhone + `(${v.takeRegion.split('/').join('') + v.takeAddress})`,
value: v.id,
districtCode: v.districtCode,
}));
const addressItemObj = result.find((v) => v.type === 0);
if (addressItemObj) {
addressInfoForm.setFieldsValue({
returnAddress: addressItemObj.id,
shipAddress: addressItemObj.id,
});
}
setAddressOptionList(optionList);
}
});
};
//物流公司列表
const getListExpressInfo = () => {
OrderManageAPI.listExpressInfo().then(({ result }) => {
if (result) {
const optionList = result.map((v) => ({
label: v.exName,
value: v.exCode,
}));
setExpressOptionList(optionList);
}
});
};
//物流-配送方式
const getOtherService = () => {
RentManageAPI.getOtherServiceList().then(({ result }) => {
setShippingMethodList(result || []);
});
};
//物流表单提交
const addressInfoFormSubmit = () => {
return new Promise((resolve, reject) => {
addressInfoForm
.validateFields()
.then((values) => {
resolve({
...values,
districtCode: addressOptionList.find((v) => v.value === values.shipAddress)
?.districtCode,
});
})
.catch((err) => {
reject(err);
});
});
};
//新增地址弹窗
const addAddressClick = () => [setAddOrEditAddressModalShow(true)];
const addOrEditAddressModalOk = () => {
getAddressList();
setAddOrEditAddressModalShow(false);
};
const addOrEditAddressModalCancel = () => {
setAddOrEditAddressModalShow(false);
};
useEffect(() => {
getAddressList();
getListExpressInfo();
getOtherService();
}, []);
useEffect(() => {
if (rentGoodsDetails) {
addressInfoForm.setFieldsValue({
shipAddress: rentGoodsDetails.shipAddress,
returnAddress: rentGoodsDetails.returnAddress,
logisticsCompany: rentGoodsDetails.logisticsCompany,
modeOfDelivery: rentGoodsDetails.modeOfDelivery,
});
}
}, [rentGoodsDetails]);
return (
<div className='address-info'>
<div className='address-info-title'>物流信息</div>
<Form labelCol={{ span: 2 }} wrapperCol={{ span: 10 }} form={addressInfoForm}>
<Form.Item
label='发货地址'
name='shipAddress'
rules={[{ required: true, message: '请选择发货地址' }]}
>
<Select placeholder='请选择发货地址' options={addressOptionList}></Select>
</Form.Item>
<Form.Item>
<Row>
<Col span={5}></Col>
<Col>
<Button type='link' icon={<PlusOutlined />} onClick={addAddressClick}>
新增地址
</Button>
</Col>
</Row>
</Form.Item>
<Form.Item
label='归还地址'
name='returnAddress'
rules={[{ required: true, message: '请选择归还地址' }]}
>
<Select placeholder='请选择归还地址' options={addressOptionList}></Select>
</Form.Item>
<Form.Item>
<Row>
<Col span={5}></Col>
<Col>
<Button type='link' icon={<PlusOutlined />} onClick={addAddressClick}>
新增地址
</Button>
</Col>
</Row>
</Form.Item>
<Form.Item
label='寄出物流'
name='logisticsCompany'
rules={[{ required: true, message: '请选择寄出物流' }]}
>
<Select placeholder='请选择寄出物流' options={expressOptionList}></Select>
</Form.Item>
<Form.Item
label='配送方式(寄出)'
name='modeOfDelivery'
rules={[{ required: true, message: '请选择配送方式(寄出)' }]}
>
<Radio.Group>
{shippingMethodList.map((v) => (
<Radio value={v.id} key={v.id}>
{v.saleServiceName}
</Radio>
))}
</Radio.Group>
</Form.Item>
</Form>
<AddOrEditAddressModal
open={addOrEditAddressModalShow}
onOk={addOrEditAddressModalOk}
onCancel={addOrEditAddressModalCancel}
/>
</div>
);
});
export default AddressInfo;
.rent-footer-operate{
background: #fff;
height: 70px;
position: fixed;
bottom: 10px;
left: 180px;
right: 10px;
box-shadow: 0 -5px 1px -5px rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
button{
height: 40px;
width: 80px;
&:first-child{
margin-right: 50px;
}
}
}
import './index.scss';
import { Button } from 'antd';
import { useNavigate } from 'react-router-dom';
import { FC } from 'react';
interface selfProps {
saveRentGoods: () => void;
}
const FooterOperate: FC<selfProps> = ({ saveRentGoods }) => {
const navigate = useNavigate();
//返回
const backRoute = () => {
navigate(-1);
};
return (
<div className='rent-footer-operate'>
<Button onClick={backRoute}>返回</Button>
<Button type='primary' onClick={saveRentGoods}>
保存
</Button>
</div>
);
};
export default FooterOperate;
.goods-info{
&-title{
font-size: 15px;
font-weight: bold;
line-height: 40px;
}
}
import { Form, Input, Radio } from 'antd';
import './index.scss';
import { forwardRef, useEffect, useImperativeHandle } from 'react';
import { InterDataType } from '~/api/interface';
import { leaseGoodsDetailsType } from '~/api/interface/rentManageType';
import { qualityList } from '~/utils/dictionary';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
type goodsInfoForm = {
tradeName: string;
sellingPoint: string;
level: number;
shelfStatus: number;
};
interface selfProps {
ref: any;
rentGoodsDetails: rentGoodsDetailType | undefined;
}
const GoodsInfo = forwardRef<any, selfProps>(({ rentGoodsDetails }, ref) => {
const [form] = Form.useForm<goodsInfoForm>();
useImperativeHandle(ref, () => ({
submitGoodsInfoForm,
getForm: () => form,
}));
const submitGoodsInfoForm = () => {
return new Promise((resolve, reject) => {
form
.validateFields()
.then((value) => {
resolve(value);
})
.catch((error) => {
console.log('商品信息错误--->', error);
reject(error);
});
});
};
useEffect(() => {
if (rentGoodsDetails) {
form.setFieldsValue({
tradeName: rentGoodsDetails.tradeName,
sellingPoint: rentGoodsDetails.sellingPoint,
level: rentGoodsDetails.level,
shelfStatus: rentGoodsDetails.shelfStatus,
});
}
}, [rentGoodsDetails]);
return (
<div className='goods-info'>
<div className='goods-info-title'>商品信息</div>
<Form
labelCol={{ span: 2 }}
wrapperCol={{ span: 10 }}
initialValues={{ level: 0, shelfStatus: 1 }}
form={form}
>
<Form.Item
label='商品标题'
name='tradeName'
rules={[{ required: true, message: '请输入商品标题' }]}
>
<Input placeholder='请输入商品标题' maxLength={30} />
</Form.Item>
<Form.Item
label='商品卖点'
name='sellingPoint'
rules={[{ required: true, message: '请输入商品卖点' }]}
>
<Input placeholder='请输入商品卖点' maxLength={30} />
</Form.Item>
<Form.Item label='商品成新' name='level'>
<Radio.Group>
{qualityList.map((v, index) => (
<Radio value={v.value} key={index}>
{v.label}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item label='商品状态' name='shelfStatus'>
<Radio.Group>
<Radio value={1}>上架</Radio>
<Radio value={0}>下架</Radio>
</Radio.Group>
</Form.Item>
</Form>
</div>
);
});
export default GoodsInfo;
.base-info {
&-title {
.rent-attr{
&-title{
font-size: 15px;
font-weight: bold;
margin-bottom: 20px;
&::before {
content: '*';
color: red;
}
line-height: 40px;
}
.goods-video-wrap{
.rent-goods-video-wrap{
position: relative;
width: 200px;
img{
......
import './index.scss';
import { Button, Col, Form, Row, Select, Table, Tooltip } from 'antd';
import EditableCell from '~/components/EditableCell';
import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import { MinusOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons';
import { Uploader } from '~/components/uploader';
import RichText from '~/components/richText';
import { RentManageAPI } from '../../../../../../api';
import deletePng from '~/assets/image/delete.png';
import { InterDataType } from '~/api/interface';
import { leaseGoodsDetailsType } from '~/api/interface/rentManageType';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
type EditableTableProps = Parameters<typeof Table>[0];
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
type RentAttrFormType = {
productTypeId: number;
brandInfoId: number;
deviceModeId: number;
mainImage: string;
subImage: string[];
goodsVideo: string;
productDetails: string;
};
//商品参数表格类型
type productParamType = {
id: number;
productParamName: string;
productParamValue: string;
};
interface selfProps {
ref: any;
rentGoodsDetails: rentGoodsDetailType | undefined;
}
const RentAttr = forwardRef<any, selfProps>(({ rentGoodsDetails }, ref) => {
//商品属性表单
const [rentAttrForm] = Form.useForm<RentAttrFormType>();
//商品参数表单
const [productParamForm] = Form.useForm<{ [key: string]: string }>();
const attrTableColumns: (ColumnTypes[number] & {
editable?: boolean;
dataIndex?: string;
inputType?: string;
rules?: any;
maxLength?: number;
})[] = [
{
title: '序号',
align: 'center',
render: (_: any, _record, index: number) => index + 1,
},
{
title: '参数名称',
dataIndex: 'productParamName',
editable: true,
align: 'center',
maxLength: 30,
},
{
title: '参数值',
dataIndex: 'productParamValue',
editable: true,
align: 'center',
maxLength: 30,
},
{
title: '操作',
dataIndex: '',
render: (_text: string, _record: any, index: number) => (
<>
{index === parameterTableData.length - 1 ? (
<Tooltip placement='top' title='添加一行'>
<Button
icon={<PlusOutlined />}
type='primary'
onClick={addParameterDataEvent}
style={{ marginRight: '10px' }}
></Button>
</Tooltip>
) : (
''
)}
{index ? (
<Tooltip placement='top' title='删除该行'>
<Button
type='primary'
icon={<MinusOutlined />}
onClick={() => deleteParameterDataEvent(index)}
></Button>
</Tooltip>
) : (
''
)}
</>
),
},
];
const columns = attrTableColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: any) => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: col.editable,
inputType: col.inputType,
rules: col.rules,
maxLength: col.maxLength,
}),
};
});
const [parameterTableData, setParameterTableData] = useState<productParamType[]>([
{ id: Math.random(), productParamName: '', productParamValue: '' },
]);
//类型下拉列表
const [rentTypeList, setTypeList] = useState<{ label: string; value: number }[]>([]);
//品牌下拉列表
const [brandInfoList, setBrandInfoList] = useState<{ label: string; value: number }[]>([]);
//型号下拉列表
const [modeTypeList, setModeTypeList] = useState<{ label: string; value: number }[]>([]);
//商品主图文件列表
const [mainImgFileList, setMainImgFileList] = useState<
{
id: number;
name: string;
uid: number;
url: string;
}[]
>([]);
//商品副图文件列表
const [subImgFileList, setSubImgFileList] = useState<
{
id: number;
name: string;
uid: number;
url: string;
}[]
>([]);
//商品视频文件列表
const [videoFileList, setVideoFileList] = useState<
{
id: number;
name: string;
uid: number;
url: string;
}[]
>([]);
useImperativeHandle(ref, () => ({
submitAttrForm,
getForm: () => rentAttrForm,
}));
//商品参数新增
const addParameterDataEvent = () => {
setParameterTableData([
...parameterTableData,
{ id: Math.random(), productParamName: '', productParamValue: '' },
]);
};
//商品参数删除
const deleteParameterDataEvent = (index: number) => {
parameterTableData.splice(index, 1);
setParameterTableData([...parameterTableData]);
};
//类型列表
const getRentTypeList = () => {
RentManageAPI.getTypeList({ pageNo: 1, pageSize: 99999 }).then(({ result }) => {
if (result.list) {
const optionList = result.list.map((v) => ({ label: v.name, value: v.id }));
setTypeList(optionList);
}
});
};
//品牌列表
const getRentMakeList = () => {
RentManageAPI.getListBrandInfo({ pageNo: 1, pageSize: 99999 }).then(({ result }) => {
if (result.list) {
const optionList = result.list.map((v) => ({ label: v.brandName, value: v.id }));
setBrandInfoList(optionList);
}
});
};
//型号列表
const getRentModelList = (brandInfoId?: number, productTypeId?: number) => {
RentManageAPI.getRentModeList({ pageNo: 1, pageSize: 99999, brandInfoId, productTypeId }).then(
({ result }) => {
const optionList = result.list?.map((v) => ({ label: v.modeName, value: v.id })) || [];
setModeTypeList(optionList);
},
);
};
//类型选中
const rentTypeOnSelect = (value: number) => {
rentAttrForm.setFieldValue('deviceModeId', undefined);
getRentModelList(rentAttrForm.getFieldValue('brandInfoId') || undefined, value);
};
//品牌选中
const brandInfoOnSelect = (value: number) => {
rentAttrForm.setFieldValue('deviceModeId', undefined);
getRentModelList(value, rentAttrForm.getFieldValue('productTypeId') || undefined);
};
//商品图片上传成功
const imgUploadSuccess = (
value: {
id: number;
name: string;
uid: number;
url: string;
}[],
type: string,
) => {
switch (type) {
case 'mainImage':
rentAttrForm.setFieldValue('mainImage', value[0].url);
setMainImgFileList(value);
break;
case 'subImage':
rentAttrForm.setFieldValue(
'subImage',
value.map((v) => v.url),
);
setSubImgFileList(value);
break;
default:
}
};
//商品视频上传成功
const videoUploadSuccess = (
value: {
id: number;
name: string;
uid: number;
url: string;
}[],
) => {
rentAttrForm.setFieldValue('goodsVideo', value[0].url);
setVideoFileList(value);
};
//商品视频删除
const deleteVideo = () => {
setVideoFileList([]);
};
//商品参数表单验证
const productParamFormSubmit = () => {
return new Promise((resolve, reject) => {
productParamForm
.validateFields()
.then((values) => {
const productParamList = parameterTableData.reduce(
(pre: { [key: string]: string }[], cur) => {
const Obj = Object.create(null);
if (values['productParamName' + cur.id] && values['productParamValue' + cur.id]) {
Obj[values['productParamName' + cur.id]] = values['productParamValue' + cur.id];
pre.push(Obj);
}
return pre;
},
[],
);
resolve(productParamList);
})
.catch((err) => {
reject(err);
});
});
};
const submitAttrForm = async () => {
try {
const values: any[] = await Promise.all([
rentAttrForm.validateFields(),
productParamFormSubmit(),
]);
return Promise.resolve({
...values[0],
productParam: values[1].length ? JSON.stringify(values[1]) : undefined,
});
} catch (error: any) {
return Promise.reject(error);
}
};
useEffect(() => {
getRentTypeList();
getRentMakeList();
}, []);
//编辑回显
useEffect(() => {
if (rentGoodsDetails) {
rentAttrForm.setFieldsValue({
productTypeId: rentGoodsDetails.productTypeId,
brandInfoId: rentGoodsDetails.brandInfoId,
deviceModeId: rentGoodsDetails.deviceModeId,
mainImage: rentGoodsDetails.resourcesList.filter((v) => v.type === 0)[0].url,
subImage: rentGoodsDetails.resourcesList.filter((v) => v.type === 1).length
? rentGoodsDetails.resourcesList.filter((v) => v.type === 1).map((v) => v.url)
: undefined,
goodsVideo: rentGoodsDetails.resourcesList.filter((v) => v.type === 2).length
? rentGoodsDetails.resourcesList.filter((v) => v.type === 2)[0].url
: undefined,
});
getRentModelList(rentGoodsDetails.brandInfoId, rentGoodsDetails.productTypeId);
if (rentGoodsDetails.productParam) {
const tableData: productParamType[] = JSON.parse(rentGoodsDetails.productParam).map(
(v: { [x: string]: string }) => ({
productParamName: Object.getOwnPropertyNames(v)[0],
productParamValue: v[Object.getOwnPropertyNames(v)[0]],
id: Math.random(),
}),
);
setParameterTableData(tableData);
const defaultFormValue: { [x: string]: string } = tableData.reduce(
(pre: { [x: string]: string }, cur) => {
pre['productParamName' + cur.id] = cur.productParamName;
pre['productParamValue' + cur.id] = cur.productParamValue;
return pre;
},
{},
);
productParamForm.setFieldsValue(defaultFormValue);
}
setMainImgFileList(
rentGoodsDetails.resourcesList
.filter((v) => v.type === 0)
.map((v) => ({ id: Math.random(), uid: Math.random(), name: '主图', url: v.url })),
);
setSubImgFileList(
rentGoodsDetails.resourcesList
.filter((v) => v.type === 1)
.map((v) => ({ id: Math.random(), uid: Math.random(), name: '主图', url: v.url })),
);
setVideoFileList(
rentGoodsDetails.resourcesList
.filter((v) => v.type === 2)
.map((v) => ({ id: Math.random(), uid: Math.random(), name: '主图', url: v.url })),
);
}
}, [rentGoodsDetails]);
return (
<div className='rent-attr'>
<div className='rent-attr-title'>商品属性</div>
<Form labelCol={{ span: 2 }} wrapperCol={{ span: 10 }} form={rentAttrForm}>
<Form.Item
label='商品类型'
name='productTypeId'
rules={[{ required: true, message: '请选择商品类型' }]}
>
<Select
placeholder='请选择商品类型'
options={rentTypeList}
onSelect={rentTypeOnSelect}
></Select>
</Form.Item>
<Form.Item
label='商品品牌'
name='brandInfoId'
rules={[{ required: true, message: '请选择商品品牌' }]}
>
<Select
placeholder='请选择商品品牌'
options={brandInfoList}
onSelect={brandInfoOnSelect}
></Select>
</Form.Item>
<Form.Item
label='商品型号'
name='deviceModeId'
rules={[{ required: true, message: '请选择商品型号' }]}
>
<Select placeholder='请选择商品型号' options={modeTypeList}></Select>
</Form.Item>
<Form.Item
label='商品主图'
name='mainImage'
rules={[{ required: true, message: '请上传商品主图' }]}
>
<Uploader
fileUpload
listType='picture-card'
onChange={(fileList) => imgUploadSuccess(fileList, 'mainImage')}
defaultFileList={mainImgFileList}
fileSize={2}
fileLength={1}
>
<UploadOutlined />
</Uploader>
</Form.Item>
<Form.Item label='商品副图' name='subImage'>
<Uploader
fileUpload
listType='picture-card'
onChange={(fileList) => imgUploadSuccess(fileList, 'subImage')}
defaultFileList={subImgFileList}
fileSize={2}
fileLength={4}
>
<UploadOutlined />
</Uploader>
</Form.Item>
<Form.Item label='商品视频' name='goodsVideo'>
{videoFileList.length ? (
<div className='rent-goods-video-wrap'>
<video
src={videoFileList[0].url}
style={{ width: '200px', height: '200px' }}
controls
/>
<img src={deletePng} alt='删除' onClick={deleteVideo} />
</div>
) : (
<Uploader
fileUpload
listType='picture-card'
onChange={videoUploadSuccess}
defaultFileList={videoFileList}
fileSize={30}
fileLength={1}
fileType={['video/mp4', 'video/avi', 'video/wmv', 'video/rmvb']}
>
<UploadOutlined />
</Uploader>
)}
</Form.Item>
<Form.Item label='商品详情页' name='productDetails'>
<RichText richTextContent={rentGoodsDetails?.productDetails} />
</Form.Item>
</Form>
<Row>
<Col span={2} style={{ textAlign: 'right' }}>
<span>商品参数:</span>
</Col>
<Col span={10}>
<Form form={productParamForm}>
<Table
columns={columns as ColumnTypes}
components={{
body: {
cell: EditableCell,
},
}}
dataSource={parameterTableData}
bordered
rowKey='id'
pagination={false}
></Table>
</Form>
</Col>
</Row>
</div>
);
});
export default RentAttr;
.sku-info{
&-title{
font-size: 15px;
font-weight: bold;
line-height: 40px;
}
}
import './index.scss';
import { Col, Form, Row, Select, Table } from 'antd';
import CommonSkuInfo from '~/components/goods/commonSkuInfo';
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { RentManageAPI } from '~/api';
import { isEmptyBol, regPriceNumber } from '~/utils/validateUtils';
import { InterDataType, InterReqType } from '~/api/interface';
import { addRentGoodsType, leaseGoodsDetailsType } from '~/api/interface/rentManageType';
import { filterObjAttr } from '~/utils';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
type EditableTableProps = Parameters<typeof Table>[0];
type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
interface selfProps {
ref: any;
rentGoodsDetails: rentGoodsDetailType | undefined;
}
//规格表单数据类型
type specificationFormListType = {
optionList: { label: string; value: string }[];
id: number;
name: string;
addSpecificationValueShow: boolean;
specificationValueList: { name: string; id: number; specificationName: string }[];
};
//sku返回类型
type priceStockType = (Exclude<InterReqType<addRentGoodsType>, undefined>['priceStock'][0] & {
id: number;
})[];
const SkuInfo = forwardRef<any, selfProps>(({ rentGoodsDetails }, ref) => {
const [form] = Form.useForm<{ minLeaseTerm: number; maxLeaseTerm: number }>();
const commonSkuInfoRef = useRef<any>();
//库存正则校验
const stockValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (/^[+]{0,1}(\d+)$/.test(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('库存最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('请输入正整数'));
}
} else {
return Promise.resolve();
}
};
//价格正则校验
const priceValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (regPriceNumber(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('价格最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('为整数且最多保留两位小数'));
}
} else {
return Promise.reject(new Error('请输入金额'));
}
};
const [defaultColumns, setDefaultColumns] = useState<
(ColumnTypes[number] & {
editable?: boolean;
dataIndex?: string;
inputType?: string;
radioOption?: { name: string; id: number }[];
rules?: any;
maxLength?: number;
children?: any;
placeholder?: string;
})[]
>([
{
title: '商品规格',
align: 'center',
children: [],
maxLength: 30,
},
{
title: '缺货',
align: 'center',
editable: true,
inputType: 'switch',
dataIndex: 'stockOut',
},
{
title: (
<div>
<span style={{ color: 'red' }}>*</span>
<span>押金</span>
</div>
),
align: 'center',
editable: true,
dataIndex: 'cashPledge',
width: '15%',
placeholder: '押金',
rules: [{ required: false, validator: priceValidator }],
},
{
title: '库存',
align: 'center',
editable: true,
dataIndex: 'stock',
width: '10%',
rules: [{ required: false, validator: stockValidator }],
},
]);
const [tableData, setTableData] = useState<priceStockType>([]);
//全部租期下拉
const [allLeaseTermInfoList, setAllLeaseTermInfoList] = useState<
{
label: string;
value: number;
key: string;
}[]
>([]);
// 低租期下拉
const [lowLeaseTermInfoList, setLowLeaseTermInfoList] = useState<
{
label: string;
value: number;
key: string;
}[]
>([]);
//高租期下拉
const [upLeaseTermInfoList, setUpLeaseTermInfoList] = useState<
{
label: string;
value: number;
key: string;
}[]
>([]);
useImperativeHandle(ref, () => ({
getForm: () => form,
skuFormSubmit,
getSpecificationValueForm: () => commonSkuInfoRef.current.getSpecificationValueForm(),
getSpecificationForm: () => commonSkuInfoRef.current.getSpecificationForm(),
}));
const updateTableData = (tableData: any) => {
setTableData([...tableData]);
};
const updateDefaultColumns = (columns: any) => {
setDefaultColumns([...columns]);
};
//租赁-商品-租期信息
const getLeaseTermInfo = () => {
RentManageAPI.getLeaseTermInfo().then(({ result }) => {
if (result) {
const optionList = result.map((v, index) => ({
label: v.leaseDate,
value: v.id,
key: [
'threeDaysRental',
'sevenDaysRental',
'thirtyDaysRental',
'ninetyDaysRental',
'maxDaysRental',
][index],
}));
setLowLeaseTermInfoList(optionList);
setUpLeaseTermInfoList(optionList);
setAllLeaseTermInfoList(optionList);
}
});
};
//最低租期选择
const lowLeaseTermInfoOnSelect = (value: number) => {
const upLeaseTermInfoIndex = allLeaseTermInfoList.findIndex((v) => v.value === value);
if (upLeaseTermInfoIndex !== -1) {
setUpLeaseTermInfoList(
allLeaseTermInfoList.slice(upLeaseTermInfoIndex, allLeaseTermInfoList.length),
);
}
if (form.getFieldValue('maxLeaseTerm')) {
const index = allLeaseTermInfoList.findIndex(
(v) => v.value === form.getFieldValue('maxLeaseTerm'),
);
if (index !== -1) {
const filterLeaseTermInfo = allLeaseTermInfoList.slice(upLeaseTermInfoIndex, index + 1);
setDefaultColumns(
defaultColumns
.filter((v) => allLeaseTermInfoList.every((i) => i.key !== v.dataIndex))
.concat(
filterLeaseTermInfo.map((v) => ({
title: (
<div>
<span style={{ color: 'red' }}>*</span>
<span>{v.label}</span>
</div>
),
align: 'center',
editable: true,
dataIndex: v.key,
width: '10%',
placeholder: `${v.label}租金价格`,
rules: [{ required: false, validator: priceValidator }],
})),
),
);
}
}
};
// 最高租期选择
const upLeaseTermInfoOnSelect = (value: number) => {
const lowLeaseTermInfoIndex = allLeaseTermInfoList.findIndex((v) => v.value === value);
if (lowLeaseTermInfoIndex !== -1) {
setLowLeaseTermInfoList(allLeaseTermInfoList.slice(0, lowLeaseTermInfoIndex + 1));
}
if (form.getFieldValue('minLeaseTerm')) {
const index = allLeaseTermInfoList.findIndex(
(v) => v.value === form.getFieldValue('minLeaseTerm'),
);
if (index !== -1) {
const filterLeaseTermInfo = allLeaseTermInfoList.slice(index, lowLeaseTermInfoIndex + 1);
setDefaultColumns(
defaultColumns
.filter((v) => allLeaseTermInfoList.every((i) => i.key !== v.dataIndex))
.concat(
filterLeaseTermInfo.map((v) => ({
title: (
<div>
<span style={{ color: 'red' }}>*</span>
<span>{v.label}</span>
</div>
),
align: 'center',
editable: true,
dataIndex: v.key,
width: '10%',
placeholder: `${v.label}租金价格`,
rules: [{ required: false, validator: priceValidator }],
})),
),
);
}
}
};
const skuFormSubmit = async () => {
try {
const values: any[] = await Promise.all([
form.validateFields(),
commonSkuInfoRef.current.getSpecificationValueForm().validateFields(),
commonSkuInfoRef.current.getSpecificationForm().validateFields(),
]);
const specificationFormList: specificationFormListType[] =
commonSkuInfoRef.current.getSpecificationFormList();
const specificationFormItem = specificationFormList.find(
(v) => !v.specificationValueList.length,
);
if (specificationFormItem) {
return Promise.reject(`请为规格项${specificationFormItem.optionList[0].value}添加规格值`);
} else {
//规格项数据转化
const specAttrList = specificationFormList.map((v) => ({
specName: v.optionList[0].value,
id: rentGoodsDetails
? rentGoodsDetails.specAttrList.find((i) => i.id === v.id)?.id
: undefined,
specValuesList: v.specificationValueList.map((j) => ({
specName: j.name,
id: rentGoodsDetails
? rentGoodsDetails.specAttrList
.find((i) => i.id === v.id)
?.specValuesList.find((i) => i.id === j.id)?.id
: undefined,
})),
}));
//规格值数据转化
const priceStock: priceStockType = tableData.reduce((pre: priceStockType, cur: any) => {
//规格名,规格值组合类型
const productSpec = specificationFormList.reduce(
(a: { [x: string]: string }, b, currentIndex) => {
a[b.optionList[0].value] = cur['name' + (currentIndex + 1)];
return a;
},
{},
);
pre.push({
...Object.getOwnPropertyNames(values[1]).reduce((a: any, b) => {
if (b.includes(cur.id)) {
a[b.replace(cur.id, '')] = values[1][b];
}
return a;
}, {}),
productSpec: JSON.stringify(productSpec),
});
return pre;
}, []);
//获取租期内的最低价格
const rentDateMinPrice = priceStock
.reduce((pre: number[], cur: any) => {
[
'threeDaysRental',
'sevenDaysRental',
'thirtyDaysRental',
'ninetyDaysRental',
'maxDaysRental',
].map((key) => {
if (cur[key]) {
pre.push(Number(cur[key]));
}
});
return pre;
}, [])
.sort((a, b) => a - b)[0];
return Promise.resolve({
...values[0],
priceStock,
specAttrList,
showPrice: rentDateMinPrice,
});
}
} catch (error: any) {
return Promise.reject(error);
}
};
//表头拆分及合并列
const mergeTableRow = (filterSpecificationFormList: specificationFormListType[]) => {
const columns = filterSpecificationFormList.map((v, index) => ({
title: v.optionList[0].value,
dataIndex: 'name' + (index + 1),
align: 'center',
onCell: (_: any, i: number) => {
//合并列
if (index < filterSpecificationFormList.length - 1) {
const count: number = filterSpecificationFormList
.slice(index + 1, filterSpecificationFormList.length)
.reduce((pre: number, cur) => {
return pre * cur.specificationValueList.length;
}, 1);
return {
rowSpan: count !== 1 ? ((i + 1) % count === 1 ? count : 0) : 1,
};
} else {
return {
rowSpan: 1,
};
}
},
}));
defaultColumns[0].children = columns;
setDefaultColumns([...defaultColumns]);
};
//排列组合规格值表单默认数据
const setTableFormDefault = (tableDataList: any) => {
const tableFormDefault = tableDataList.reduce((pre: any, cur: any) => {
return {
...pre,
...Object.getOwnPropertyNames(filterObjAttr(cur, ['id'])).reduce((a: any, b) => {
a[b + cur.id] = cur[b];
return a;
}, {}),
};
}, {});
commonSkuInfoRef.current.getSpecificationValueForm().setFieldsValue(tableFormDefault);
};
useEffect(() => {
getLeaseTermInfo();
}, []);
useEffect(() => {
if (rentGoodsDetails) {
form.setFieldsValue({
minLeaseTerm: rentGoodsDetails.minLeaseTerm,
maxLeaseTerm: rentGoodsDetails.maxLeaseTerm,
});
//转化数据
const covertSpecAttrList = rentGoodsDetails.specAttrList.map((v, index) => ({
id: v.id,
name: 'specName' + index,
optionList: [{ label: v.specName, value: v.specName }],
specificationValueList: v.specValuesList.map((i) => ({
id: i.id,
name: i.specName,
specificationName: v.specName,
})),
addSpecificationValueShow: false,
}));
//规格项表单数据默认数据
const specFormDefault = rentGoodsDetails.specAttrList.reduce(
(pre: any, cur: any, currentIndex) => {
pre['specName' + currentIndex] = cur.specName;
return pre;
},
{},
);
commonSkuInfoRef.current.getSpecificationForm().setFieldsValue(specFormDefault);
commonSkuInfoRef.current.updateSpecificationFormList([...covertSpecAttrList]);
const upLeaseTermInfoIndex = allLeaseTermInfoList.findIndex(
(v) => v.value === rentGoodsDetails.maxLeaseTerm,
);
const lowLeaseTermInfoIndex = allLeaseTermInfoList.findIndex(
(v) => v.value === rentGoodsDetails.minLeaseTerm,
);
const filterLeaseTermInfo = allLeaseTermInfoList.slice(
lowLeaseTermInfoIndex,
upLeaseTermInfoIndex + 1,
);
const addColumnsList: any = filterLeaseTermInfo.map((v) => ({
title: (
<div>
<span style={{ color: 'red' }}>*</span>
<span>{v.label}</span>
</div>
),
align: 'center',
editable: true,
dataIndex: v.key,
width: '15%',
placeholder: `${v.label}租金价格`,
rules: [{ required: false, validator: priceValidator }],
}));
defaultColumns.push(...addColumnsList);
mergeTableRow(covertSpecAttrList);
const tableDataList: priceStockType = rentGoodsDetails.priceStock.map((v) => ({
id: v.id,
stockOut: !!v.stockOut,
stock: v.stock || undefined,
threeDaysRental: v.threeDaysRental || undefined,
sevenDaysRental: v.sevenDaysRental || undefined,
thirtyDaysRental: v.thirtyDaysRental || undefined,
ninetyDaysRental: v.ninetyDaysRental || undefined,
maxDaysRental: v.maxDaysRental || undefined,
cashPledge: v.cashPledge,
...Object.getOwnPropertyNames(JSON.parse(v.productSpec)).reduce(
(pre: any, cur, currentIndex) => {
pre['name' + (currentIndex + 1)] = JSON.parse(v.productSpec)[cur];
pre['specificationName' + (currentIndex + 1)] = cur;
return pre;
},
{},
),
}));
setTableFormDefault(tableDataList);
setTableData(tableDataList);
}
}, [rentGoodsDetails]);
return (
<div className='sku-info'>
<div className='sku-info-title'>价格库存信息</div>
<Row>
<Col span={1}></Col>
<Col span={7}>
<Form wrapperCol={{ span: 18 }} labelCol={{ span: 5 }} form={form}>
<Form.Item
label='最低租期'
name='minLeaseTerm'
rules={[{ required: true, message: '请选择最低租期' }]}
>
<Select
placeholder='请选择最低租期'
options={lowLeaseTermInfoList}
onSelect={lowLeaseTermInfoOnSelect}
></Select>
</Form.Item>
<Form.Item
label='最高租期'
name='maxLeaseTerm'
rules={[{ required: true, message: '请选择最高租期' }]}
>
<Select
placeholder='请选择最高租期'
options={upLeaseTermInfoList}
onSelect={upLeaseTermInfoOnSelect}
></Select>
</Form.Item>
</Form>
</Col>
</Row>
<Row>
<Col span={1}></Col>
<Col span={22}>
<CommonSkuInfo
tableData={tableData}
updateTableData={updateTableData}
defaultColumns={defaultColumns}
updateDefaultColumns={updateDefaultColumns}
ref={commonSkuInfoRef}
/>
</Col>
</Row>
</div>
);
});
export default SkuInfo;
.rent-create-edit{
padding-bottom: 80px;
}
import GoodsInfo from './components/goodsInfo';
import RentAttr from './components/rentAttr';
import SkuInfo from './components/skuInfo';
import AddressInfo from './components/addressInfo';
import FooterOperate from './components/footerOperate';
import AccessoryList from './components/accessoryList';
import './index.scss';
import { useEffect, useRef, useState } from 'react';
import { RentManageAPI } from '~/api';
import { filterObjAttr } from '~/utils';
import { message } from 'antd';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { InterDataType } from '~/api/interface';
import { leaseGoodsDetailsType } from '~/api/interface/rentManageType';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
const RentAddOrEdit = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const goodsInfoRef = useRef<any>();
const rentAttrRef = useRef<any>();
const skuInfoRef = useRef<any>();
const accessoryListRef = useRef<any>();
const addressInfoRef = useRef<any>();
//租赁商品-编辑-id
const [rentGoodsId, setRentGoodsId] = useState<number>(0);
//租赁-编辑-商品详情
const [rentGoodsDetails, setRentGoodsDetails] = useState<rentGoodsDetailType>();
// 保存
const saveRentGoods = () => {
Promise.all([
goodsInfoRef.current.submitGoodsInfoForm(),
rentAttrRef.current.submitAttrForm(),
skuInfoRef.current.skuFormSubmit(),
accessoryListRef.current.accessoryTableFormSubmit(),
addressInfoRef.current.addressInfoFormSubmit(),
])
.then((values) => {
const resourcesList = [
{ type: 0, url: values[1].mainImage },
...(values[1].subImage?.map((v: string) => ({ type: 1, url: v })) || []),
...(values[1].goodsVideo ? [{ type: 2, url: values[1].goodsVideo }] : []),
];
values[2].priceStock = values[2].priceStock.map((v: any) => ({
...v,
stockOut: v.stockOut ? 1 : 0,
}));
RentManageAPI[rentGoodsId ? 'editLeaseGoods' : 'addRentGoods']({
...values[0],
...filterObjAttr(values[1], ['goodsImage', 'goodsVideo']),
...values[2],
leasePartsList: values[3],
...values[4],
resourcesList,
id: rentGoodsId ? rentGoodsId : undefined,
}).then(({ code }) => {
if (code === '200') {
message.success(rentGoodsId ? '编辑成功' : '新增成功');
navigate(-1);
}
});
})
.catch((error: any) => {
message.warning(error.errorFields[0].errors[0] || error);
if (error?.errorFields) {
goodsInfoRef.current
.getForm()
.scrollToField(error.errorFields[0].name[0], { behavior: 'smooth' });
rentAttrRef.current
.getForm()
.scrollToField(error.errorFields[0].name[0], { behavior: 'smooth' });
skuInfoRef.current
.getForm()
.scrollToField(error.errorFields[0].name[0], { behavior: 'smooth' });
skuInfoRef.current
.getSpecificationForm()
.scrollToField(error.errorFields[0].name[0], { behavior: 'smooth' });
skuInfoRef.current
.getSpecificationValueForm()
.scrollToField(error.errorFields[0].name[0], { behavior: 'smooth' });
addressInfoRef.current
.getForm()
.scrollToField(error.errorFields[0].name[0], { behavior: 'smooth' });
}
});
};
//获取租赁商品详情
const getRentGoodsDetail = (id: number) => {
RentManageAPI.getLeaseGoodsDetails({ id }).then(({ result }) => {
if (result) {
setRentGoodsDetails({ ...result });
}
});
};
useEffect(() => {
if (searchParams.get('id')) {
getRentGoodsDetail(Number(searchParams.get('id')));
setRentGoodsId(Number(searchParams.get('id')));
}
}, []);
return (
<div className='rent-create-edit'>
{/*商品信息*/}
<GoodsInfo ref={goodsInfoRef} rentGoodsDetails={rentGoodsDetails} />
{/*商品属性*/}
<RentAttr ref={rentAttrRef} rentGoodsDetails={rentGoodsDetails} />
{/*价格库存信息*/}
<SkuInfo ref={skuInfoRef} rentGoodsDetails={rentGoodsDetails} />
{/*配件清单*/}
<AccessoryList ref={accessoryListRef} rentGoodsDetails={rentGoodsDetails} />
{/*物流信息*/}
<AddressInfo ref={addressInfoRef} rentGoodsDetails={rentGoodsDetails} />
{/*底部操作栏*/}
<FooterOperate saveRentGoods={saveRentGoods} />
</div>
);
};
export default RentAddOrEdit;
.goods-info {
&-operate {
margin-top: 50px;
display: flex;
justify-content: center;
button {
width: 100px;
height: 40px;
&:first-child {
margin-right: 50px;
}
}
}
}
import BaseInfo from '~/components/goods/commonAddOrEdit/baseInfo';
import StockSku from '~/components/goods/commonAddOrEdit/stockSku';
import OtherInfo from '~/components/goods/commonAddOrEdit/otherInfo';
import GoodsIntroduce from '~/components/goods/commonAddOrEdit/goodsIntroduce';
import AddOrEditSkuModal from '~/components/goods/commonAddOrEdit/addOrEditSkuModal';
import { Button, message } from 'antd';
import { useNavigate, useSearchParams } from 'react-router-dom';
import './index.scss';
import { useEffect, useRef, useState } from 'react';
import { CategoryManageAPI } from '~/api';
import { InterDataType } from '~/api/interface';
import { categoryListType } from '~/api/interface/categoryManage';
import {
customizeEntity,
detailGoodsType,
skuUnitType,
specEntity,
} from '~/api/interface/goodsType';
import goodsAPI from '~/api/modules/goodsAPI';
import { filterObjAttr } from '~/utils';
import GoodsAPI from '~/api/modules/goodsAPI';
import { UploadFile } from 'antd/es/upload/interface';
//分类返回类型
type categoryType = InterDataType<categoryListType>['list'];
//产品-规格单位返回类型
type unitType = InterDataType<skuUnitType>;
//商品返回类型
type goodsDetailType = InterDataType<detailGoodsType>;
const GoodsAddOrEditOrDetail = () => {
const [searchParams] = useSearchParams();
//基本信息ref
const baseInfoRef = useRef<any>();
const navigate = useNavigate();
//当前目录
const [currentDesc, setCurrentDesc] = useState<number>(-1);
//分类
const [categoryList, setCategoryList] = useState<categoryType>([]);
//添加、编辑库存规格弹窗
const [addOrEditSkuModalShow, setAddOrEditSkuModalShow] = useState(false);
//库存规格数据
const [specData, setSpecData] = useState<specEntity[]>([]);
const [goodsSpecCopy, setGoodsSpecCopy] = useState<specEntity[]>([]);
//产品规格-单位
const [skuUnitList, setSkuUnitList] = useState<unitType>([]);
//其它服务
const [otherService, setOtherService] = useState<number[]>([]);
// 当前操作行数据
const [curtRowData, setCurtRowData] = useState<Partial<specEntity>>({});
//商品详情
const [goodsDetail, setGoodsDetail] = useState<goodsDetailType>();
//产品介绍
const [productIntroduce, setProductIntroduce] = useState<string>('');
//是否商品详情
const [isDetail, setIsDetail] = useState<boolean>(false);
//添加、编辑规格
const addOrEditSkuShowEvent = (record?: specEntity) => {
const baseInfoForm = baseInfoRef.current.baseInform;
setCurrentDesc(baseInfoForm.getFieldValue('directoryId') || -1);
if (!baseInfoForm.getFieldValue('directoryId')) {
return message.warning('请先选择目录');
}
if (record) {
setCurtRowData({ ...record });
}
setAddOrEditSkuModalShow(true);
};
//删除规格
const deleteSkuEvent = (record: specEntity) => {
const index = specData.findIndex((v) => v.id === record.id);
specData.splice(index, 1);
setSpecData([...specData]);
};
const addOrEditSkuModalCancel = () => {
setAddOrEditSkuModalShow(false);
setCurtRowData({});
};
const addOrEditSkuModalOk = (data: specEntity) => {
if (Object.keys(curtRowData).length != 0) {
const index: number = specData.findIndex((i) => i.id === data.id);
specData.splice(index, 1, data);
setSpecData([...specData]);
} else {
setSpecData([...specData, data]);
}
addOrEditSkuModalCancel();
};
//根据目录获取分类列表
const getCategoryList = (directoryId: number) => {
CategoryManageAPI.getCategoryList({ directoryId, type: 2, pageSize: 9999, pageNo: 1 }).then(
({ result }) => {
setCategoryList(result.list || []);
},
);
};
//产品-单位
const getSkuUnit = () => {
goodsAPI.getSkuUnit().then(({ result }) => {
setSkuUnitList(result || []);
});
};
//其它服务选择
const otherServiceSelect = (ids: number[]) => {
setOtherService(ids);
};
//获取产品详情
const getRichText = (html?: string) => {
setProductIntroduce(html || '');
};
//商品详情
const getGoodsDetail = (goodsInfoId: number) => {
GoodsAPI.getGoodsDetail({ goodsInfoId, type: 1, leaseTerm: 0 }).then(({ result }) => {
setGoodsDetail(result);
getCategoryList(result.directoryId);
const specList: specEntity[] = result.goodsSpec.reduce((pre: any, cur: specEntity) => {
// 自定义
if (cur.flag === 1) {
const cusList: customizeEntity[] =
cur.productSpecList &&
cur.productSpecList.reduce((preProd: any, curProd: customizeEntity, index: number) => {
const obj: UploadFile = {
uid: `img${index}`,
status: 'done',
url: curProd.specImage,
name: 'image',
};
preProd.push({ ...curProd, fileList: [obj] });
return preProd;
}, []);
cur.customizeInfo = cusList;
} else {
const specId: number[] =
result.directoryId === 2
? cur.industrySpecList &&
cur.industrySpecList.map((curIndu: any) => {
return {
mallSpecId: curIndu.industrySpecId,
specName: curIndu.specName,
partNo: curIndu.partNo,
id: curIndu.id,
};
})
: cur.productSpecList &&
cur.productSpecList.map((item: any) => {
return {
mallSpecId: item.productSpec,
specName: item.specName,
partNo: item.partNo,
id: item.id,
};
});
cur.specIds = specId;
}
pre.push({ ...cur, productName: cur.skuName });
return pre;
}, []);
setGoodsSpecCopy(result.goodsSpec);
setOtherService(result.otherService.map((v) => v.saleServiceId));
setSpecData(specList);
});
};
//保存
const saveSubmit = () => {
const baseInfoForm = baseInfoRef.current.baseInform;
baseInfoForm.validateFields().then((values: any) => {
if (specData.length === 0) {
return message.warning('清添加库存规格');
}
//主图
values.images = [
{
imgType: 0,
imgUrl: values.mainImg[0].url,
id: goodsDetail
? goodsDetail.images.some((i) => i.id === values.mainImg[0].id)
? values.mainImg[0].id
: undefined
: undefined,
},
];
//副图
if (values.subImg) {
values.images.push(
...values.subImg.map((v: any) => ({
imgType: 1,
imgUrl: v.url,
id: goodsDetail
? goodsDetail.images.some((i) => i.id === v.id)
? v.id
: undefined
: undefined,
})),
);
}
//分类
values.categoryByOne = values.masterTypeId[0];
values.categoryByTwo = values.masterTypeId[1] || undefined;
// 过滤对象属性
const goodsSpecVO: specEntity[] = specData.reduce((pre: any, cur: specEntity) => {
cur.customizeInfo = cur.customizeInfo?.reduce((cusPre: any, cusCur: customizeEntity) => {
const bol: boolean = goodsSpecCopy.some((i: specEntity) => {
return i.customizeInfo?.some((i: customizeEntity) => i.id === cusCur.id);
});
cusPre = [
...cusPre,
bol ? filterObjAttr(cusCur, ['fileList']) : filterObjAttr(cusCur, ['id', 'fileList']),
];
return cusPre;
}, []);
// 存在对象属性改变!
cur.specIds = cur.specIds?.reduce((preSpec: any, curSpec: any) => {
preSpec = [...preSpec, filterObjAttr(curSpec, ['specName', 'partNo'])];
return preSpec;
}, []);
// 是否新增
const isAdd: boolean = goodsSpecCopy.every((i: specEntity) => i.id != cur.id);
// 是否修改了某一条
const isEdit: boolean = goodsSpecCopy.every(
(i: specEntity) => i.categoryId != cur.categoryId,
);
pre = [
...pre,
!isAdd
? isEdit
? filterObjAttr(cur, ['industrySpecList', 'productSpecList', 'skuName', 'id'])
: filterObjAttr(cur, ['industrySpecList', 'productSpecList', 'skuName'])
: filterObjAttr(cur, ['id', 'skuName']),
];
return pre;
}, []);
goodsAPI[goodsDetail ? 'editGoods' : 'addGoods']({
...filterObjAttr(values, ['mainImg', 'subImg', 'video', 'masterTypeId', 'id', 'goodsDesc']),
productSpec: goodsSpecVO,
goodsType: 1,
goodsDetailVO: { goodsDesc: values.goodsDesc, productDesc: productIntroduce },
otherService: otherService,
id: goodsDetail ? goodsDetail.id : undefined,
}).then(({ code }) => {
if (code === '200') {
message.success(goodsDetail ? '编辑成功' : '新增成功');
navigate(-1);
}
});
});
};
//返回
const backRoute = () => {
navigate(-1);
};
useEffect(() => {
if (searchParams.get('id')) {
getGoodsDetail(Number(searchParams.get('id')));
}
setIsDetail(!!searchParams.get('isDetail'));
getSkuUnit();
}, []);
return (
<div className='goods-info'>
{/* 基本信息*/}
<BaseInfo
ref={baseInfoRef}
categoryList={categoryList}
getCategoryList={getCategoryList}
goodsDetail={goodsDetail}
isDetail={isDetail}
goodsType={1}
/>
{/* 库存规格*/}
<StockSku
addOrEditSku={addOrEditSkuShowEvent}
specData={specData}
skuUnitList={skuUnitList}
deleteSku={deleteSkuEvent}
isDetail={isDetail}
/>
{/*其它信息*/}
<OtherInfo
otherServiceSelect={otherServiceSelect}
goodsDetail={goodsDetail}
isDetail={isDetail}
/>
{/*产品介绍图*/}
<GoodsIntroduce getRichText={getRichText} goodsDetail={goodsDetail} isDetail={isDetail} />
{/*库存规格,添加、编辑弹窗*/}
<AddOrEditSkuModal
currentDesc={currentDesc}
open={addOrEditSkuModalShow}
handleCancel={addOrEditSkuModalCancel}
handleOk={addOrEditSkuModalOk}
skuUnitList={skuUnitList}
curtRowData={curtRowData}
goodsType={1}
/>
<div className='goods-info-operate'>
{!isDetail && (
<Button type='primary' onClick={saveSubmit}>
保存
</Button>
)}
<Button onClick={backRoute}>返回</Button>
</div>
</div>
);
};
export default GoodsAddOrEditOrDetail;
.rent-detail-introduce{
img{
max-width: 100%;
}
}
import { useSearchParams, useNavigate } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { OrderManageAPI, RentManageAPI, SystemManageAPI } from '~/api';
import { InterDataType } from '~/api/interface';
import { leaseGoodsDetailsType } from '~/api/interface/rentManageType';
import { Badge, Button, Col, Descriptions, Image, Row, Switch, Table } from 'antd';
import { qualityList } from '~/utils/dictionary';
import './index.scss';
//租赁商品详情返回类型
type rentGoodsDetailType = InterDataType<leaseGoodsDetailsType>;
//规格表单数据类型
type specificationFormListType = {
optionList: { label: string; value: string }[];
id: number;
name: string;
addSpecificationValueShow: boolean;
specificationValueList: { name: string; id: number; specificationName: string }[];
};
const RentDetail = () => {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
//租赁-编辑-商品详情
const [rentGoodsDetails, setRentGoodsDetails] = useState<rentGoodsDetailType>();
const [skuColumns, setSkuColumns] = useState<any>([
{
title: '商品规格',
align: 'center',
children: [],
},
{
title: '是否缺货',
align: 'center',
dataIndex: 'stockOut',
render: (text: number) => <Switch checked={!!text} />,
},
{
title: '押金',
align: 'center',
dataIndex: 'cashPledge',
render: (text: number) => text.toLocaleString() + '元',
},
{
title: '库存',
align: 'center',
dataIndex: 'stock',
},
]);
const [skuTableData, setSkuTableData] = useState<
(rentGoodsDetailType['priceStock'][0] & { [x: string]: string })[]
>([]);
//全部租期
const [allLeaseTermInfoList, setAllLeaseTermInfoList] = useState<
{
label: string;
value: number;
key: string;
}[]
>([]);
const [addressOptionList, setAddressOptionList] = useState<
{ label: string; value: number; districtCode: string }[]
>([]);
const [expressOptionList, setExpressOptionList] = useState<{ label: string; value: string }[]>(
[],
);
//获取租赁商品详情
const getRentGoodsDetail = (id: number) => {
RentManageAPI.getLeaseGoodsDetails({ id }).then(({ result }) => {
if (result) {
setRentGoodsDetails({ ...result });
//转化数据
const covertSpecAttrList = result.specAttrList.map((v, index) => ({
id: v.id,
name: 'specName' + index,
optionList: [{ label: v.specName, value: v.specName }],
specificationValueList: v.specValuesList.map((i) => ({
id: i.id,
name: i.specName,
specificationName: v.specName,
})),
addSpecificationValueShow: false,
}));
const upLeaseTermInfoIndex = allLeaseTermInfoList.findIndex(
(v) => v.value === result.maxLeaseTerm,
);
const lowLeaseTermInfoIndex = allLeaseTermInfoList.findIndex(
(v) => v.value === result.minLeaseTerm,
);
const filterLeaseTermInfo = allLeaseTermInfoList.slice(
lowLeaseTermInfoIndex,
upLeaseTermInfoIndex + 1,
);
const addColumnsList: any = filterLeaseTermInfo.map((v) => ({
title: v.label,
align: 'center',
dataIndex: v.key,
width: '15%',
render: (text: number) => text.toLocaleString() + '元',
}));
skuColumns.push(...addColumnsList);
mergeTableRow(covertSpecAttrList);
const tableDataList: (rentGoodsDetailType['priceStock'][0] & { [x: string]: string })[] =
result.priceStock.map((v) => ({
id: v.id,
stockOut: !!v.stockOut,
stock: v.stock || undefined,
threeDaysRental: v.threeDaysRental || undefined,
sevenDaysRental: v.sevenDaysRental || undefined,
thirtyDaysRental: v.thirtyDaysRental || undefined,
ninetyDaysRental: v.ninetyDaysRental || undefined,
maxDaysRental: v.maxDaysRental || undefined,
cashPledge: v.cashPledge,
...Object.getOwnPropertyNames(JSON.parse(v.productSpec)).reduce(
(pre: any, cur, currentIndex) => {
pre['name' + (currentIndex + 1)] = JSON.parse(v.productSpec)[cur];
pre['specificationName' + (currentIndex + 1)] = cur;
return pre;
},
{},
),
}));
setSkuTableData(tableDataList);
}
});
};
//表头拆分及合并列
const mergeTableRow = (filterSpecificationFormList: specificationFormListType[]) => {
const columns = filterSpecificationFormList.map((v, index) => ({
title: v.optionList[0].value,
dataIndex: 'name' + (index + 1),
align: 'center',
onCell: (_: any, i: number) => {
//合并列
if (index < filterSpecificationFormList.length - 1) {
const count: number = filterSpecificationFormList
.slice(index + 1, filterSpecificationFormList.length)
.reduce((pre: number, cur) => {
return pre * cur.specificationValueList.length;
}, 1);
return {
rowSpan: count !== 1 ? ((i + 1) % count === 1 ? count : 0) : 1,
};
} else {
return {
rowSpan: 1,
};
}
},
}));
skuColumns[0].children = columns;
setSkuColumns([...skuColumns]);
};
//租赁-商品-租期信息
const getLeaseTermInfo = () => {
RentManageAPI.getLeaseTermInfo().then(({ result }) => {
if (result) {
const optionList = result.map((v, index) => ({
label: v.leaseDate,
value: v.id,
key: [
'threeDaysRental',
'sevenDaysRental',
'thirtyDaysRental',
'ninetyDaysRental',
'maxDaysRental',
][index],
}));
setAllLeaseTermInfoList(optionList);
}
});
};
//地址列表
const getAddressList = () => {
SystemManageAPI.getAddressList({}).then(({ result }) => {
if (result) {
const optionList = result.map((v) => ({
label: v.takeName + v.takePhone + `(${v.takeRegion.split('/').join('') + v.takeAddress})`,
value: v.id,
districtCode: v.districtCode,
}));
setAddressOptionList(optionList);
}
});
};
//物流公司列表
const getListExpressInfo = () => {
OrderManageAPI.listExpressInfo().then(({ result }) => {
if (result) {
const optionList = result.map((v) => ({
label: v.exName,
value: v.exCode,
}));
setExpressOptionList(optionList);
}
});
};
//返回
const backRoute = () => {
navigate(-1);
};
useEffect(() => {
getLeaseTermInfo();
getAddressList();
getListExpressInfo();
}, []);
useEffect(() => {
if (allLeaseTermInfoList.length) {
getRentGoodsDetail(Number(searchParams.get('id')));
}
}, [allLeaseTermInfoList]);
return (
<div className='rent-detail'>
<Descriptions
title='商品信息'
bordered
column={4}
extra={
<Button type='primary' onClick={backRoute}>
返回
</Button>
}
>
<Descriptions.Item label='商品标题'>{rentGoodsDetails?.tradeName}</Descriptions.Item>
<Descriptions.Item label='商品卖点'>{rentGoodsDetails?.sellingPoint}</Descriptions.Item>
<Descriptions.Item label='商品成新'>
{qualityList.find((v) => v.value === rentGoodsDetails?.level)?.label}
</Descriptions.Item>
<Descriptions.Item label='商品状态'>
<Badge
status={rentGoodsDetails?.shelfStatus ? 'processing' : 'default'}
text={rentGoodsDetails?.shelfStatus ? '上架中' : '仓库中'}
/>
</Descriptions.Item>
</Descriptions>
<Descriptions title='商品属性' bordered column={3} style={{ marginTop: '10px' }}>
<Descriptions.Item label='商品类型'>{rentGoodsDetails?.productTypeName}</Descriptions.Item>
<Descriptions.Item label='商品品牌'>{rentGoodsDetails?.brandName}</Descriptions.Item>
<Descriptions.Item label='商品型号'>{rentGoodsDetails?.deviceModeName}</Descriptions.Item>
<Descriptions.Item label='商品主图'>
<Image src={rentGoodsDetails?.resourcesList.find((v) => v.type === 0)?.url} width={50} />
</Descriptions.Item>
<Descriptions.Item label='商品副图' span={2}>
{rentGoodsDetails?.resourcesList.filter((v) => v.type === 1).length ? (
<Row>
{rentGoodsDetails?.resourcesList
.filter((v) => v.type === 1)
.map((v) => (
<Col key={v.id} offset={1}>
<Image src={v.url} width={50} height={50} />
</Col>
))}
</Row>
) : (
'暂无'
)}
</Descriptions.Item>
<Descriptions.Item label='商品视频'>
{rentGoodsDetails?.resourcesList.find((v) => v.type === 2)?.url ? (
<video
src={rentGoodsDetails?.resourcesList.find((v) => v.type === 2)?.url}
controls
style={{ width: '200px', height: '200px' }}
/>
) : (
'暂无'
)}
</Descriptions.Item>
<Descriptions.Item label='商品参数'>
{rentGoodsDetails?.productParam
? JSON.parse(rentGoodsDetails?.productParam).map(
(v: { [x: string]: string }, index: number) => (
<Row key={index} style={{ lineHeight: '40px' }}>
<Col>{Object.getOwnPropertyNames(v)[0]}</Col>
<Col>{v[Object.getOwnPropertyNames(v)[0]]}</Col>
</Row>
),
)
: '暂无'}
</Descriptions.Item>
</Descriptions>
<Descriptions title='价格库存信息' column={1} style={{ marginTop: '10px' }}>
<Descriptions.Item>
<Table
style={{ width: '100%' }}
bordered
columns={skuColumns}
dataSource={skuTableData}
pagination={false}
/>
</Descriptions.Item>
</Descriptions>
<Descriptions title='配件清单' column={1} style={{ marginTop: '10px' }}>
<Descriptions.Item>
<Table
style={{ width: '100%' }}
bordered
columns={[
{
title: '名称',
dataIndex: 'name',
align: 'center',
},
{
title: '数量',
dataIndex: 'number',
align: 'center',
},
{
title: '价格',
dataIndex: 'price',
align: 'center',
render: (text: number) => text.toLocaleString() + '元',
},
]}
dataSource={rentGoodsDetails?.leasePartsList}
pagination={false}
/>
</Descriptions.Item>
</Descriptions>
<Descriptions title='物流信息' bordered column={2} style={{ marginTop: '10px' }}>
<Descriptions.Item label='发货地址'>
{addressOptionList.find((v) => v.value === rentGoodsDetails?.shipAddress)?.label}
</Descriptions.Item>
<Descriptions.Item label='归还地址'>
{addressOptionList.find((v) => v.value === rentGoodsDetails?.returnAddress)?.label}
</Descriptions.Item>
<Descriptions.Item label='寄出物流'>
{expressOptionList.find((v) => v.value === rentGoodsDetails?.logisticsCompany)?.label}
</Descriptions.Item>
<Descriptions.Item label='配送方式'>
{rentGoodsDetails?.modeOfDeliveryInfo}
</Descriptions.Item>
</Descriptions>
<Descriptions title='商品详情页' column={1} style={{ marginTop: '10px' }}>
<Descriptions.Item>
<div
dangerouslySetInnerHTML={{ __html: rentGoodsDetails?.productDetails || '' }}
style={{ width: '100%' }}
className='rent-detail-introduce'
></div>
</Descriptions.Item>
</Descriptions>
</div>
);
};
export default RentDetail;
.rent-goods-info{
display: flex;
.info-meta{
margin-left: 5px;
word-break: break-all;
.title{
margin-bottom: 10px;
}
}
}
import SearchBox, { searchColumns } from '~/components/search-box';
import React, { useEffect, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import SearchBox, { searchColumns as searchColumnsType } from '~/components/search-box';
import { Button, Card, Image, message, Modal, Table } from 'antd';
import {
ArrowDownOutlined,
......@@ -8,23 +6,57 @@ import {
DeleteOutlined,
PlusOutlined,
} from '@ant-design/icons';
import { useEffect, useRef, useState } from 'react';
import { ColumnsType } from 'antd/es/table';
import GoodsAPI from '~/api/modules/goodsAPI';
import { InterDataType, InterReqType, PaginationProps } from '~/api/interface';
import { listGoodsType } from '~/api/interface/goodsType';
import { CategoryManageAPI } from '~/api';
import { filterObjAttr } from '~/utils';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { RentManageAPI } from '../../../../api';
import { InterDataType, InterReqListType, PaginationProps } from '../../../../api/interface';
import { leaseGoodsListType } from '../../../../api/interface/rentManageType';
import './index.scss';
import qs from 'query-string';
import { useSelector } from 'react-redux';
//租赁列表返回类型
type rentGoodsType = InterDataType<leaseGoodsListType>['list'];
//租赁列表参数类型
type rentGoodsParametersType = Exclude<InterReqListType<leaseGoodsListType>, undefined>;
//商品返回类型
type goodsType = InterDataType<listGoodsType>['list'];
//商品列表筛选类型
type goodsSearchParameters = Omit<InterReqType<listGoodsType>, 'goodsType'>;
const RentList = () => {
//筛选ref
const searchRef = useRef();
const searchRef = useRef<any>();
const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();
const { userInfo } = useSelector((state: any) => state.UserInfo);
//类型下拉列表
const [rentTypeSelectList, setRentTypeSelectList] = useState<{ label: string; value: number }[]>(
[],
);
//品牌下拉列表
const [brandInfoList, setBrandInfoList] = useState<{ label: string; value: number }[]>([]);
const searchColumns: searchColumnsType[] = [
{
label: '商品名称',
name: 'tradeName',
type: 'input',
placeholder: '请输入商品名称',
},
{
label: '商品类型',
name: 'productTypeId',
type: 'Select',
placeholder: '请选择商品类型',
options: rentTypeSelectList,
},
{
label: '商品品牌',
name: 'brandInfoId',
type: 'Select',
placeholder: '请选择商品品牌',
options: brandInfoList,
},
];
const tabList = [
{
key: '1',
......@@ -40,347 +72,292 @@ const RentList = () => {
},
];
const [activeTabKey, setActiveTabKey] = useState<string>('1');
const [searchColumns, setSearchColumns] = useState<searchColumns[]>([
const tableColumns: ColumnsType<rentGoodsType[0]> = [
{
label: '商品名称',
placeholder: '请输入商品名称',
name: 'goodsName',
type: 'input',
title: '商品名称',
width: '30%',
onHeaderCell: () => ({
style: {
textAlign: 'center',
},
}),
render: (_: any, record) => (
<div className='rent-goods-info'>
<div className='goods-img'>
<Image
src={record.resourcesList.find((v) => v.type === 0)?.url}
width={50}
height={50}
/>
</div>
<div className='info-meta'>
<div className='title'>{record.tradeName}</div>
<div className='type'>
{rentTypeSelectList.find((v) => v.value === record.productTypeId)?.label}
</div>
</div>
</div>
),
},
{
label: '所属目录',
placeholder: '请选择所属目录',
name: 'directoryId',
type: 'select',
options: [],
title: '押金范围',
align: 'center',
dataIndex: 'cashPledgeRange',
},
{
label: '创建时间',
placeholder: '请输入选择创建时间',
name: 'time',
type: 'rangePicker',
title: '租金范围(日)',
align: 'center',
dataIndex: 'rentalRange',
},
]);
const tableColumns: ColumnsType<goodsType[0]> = [
{
title: '序号',
title: '库存',
align: 'center',
render: (_text: any, _record, index: number) =>
(pagination.pageNo - 1) * pagination.pageSize + index + 1,
dataIndex: 'stock',
},
{
title: '图片',
title: '创建时间',
align: 'center',
dataIndex: 'imgUrl',
render: (text: string) => <Image src={text} width={50} height={50} />,
dataIndex: 'createTime',
},
{ title: '商品名称', align: 'center', dataIndex: 'goodsName' },
{ title: '所属目录', align: 'center', dataIndex: 'directoryName' },
{ title: '创建时间', align: 'center', dataIndex: 'createTime' },
{
title: '状态',
align: 'center',
dataIndex: 'status',
dataIndex: 'shelfStatus',
render: (text: number) => (text ? '上架' : '下架'),
},
{
title: '操作',
align: 'center',
dataIndex: 'id',
render: (id: number) => (
width: '20%',
render: (_: any, record) => (
<>
<Button type='link' onClick={() => toEditGoods(id)}>
<Button type='link' onClick={() => toRentEdit(record)}>
编辑
</Button>
<Button type='link' onClick={() => toRentGoodsDetail(id)}>
<Button type='link' onClick={() => toRentDetail(record)}>
详情
</Button>
<Button
type='link'
disabled={!!record.shelfStatus}
onClick={() => batchOnShelfOrTakeDownRequest([record.id], 1)}
>
上架
</Button>
<Button
type='link'
disabled={!record.shelfStatus}
onClick={() => batchOnShelfOrTakeDownRequest([record.id], 0)}
>
下架
</Button>
<Button type='link' danger onClick={() => batchRemoveWareInfoRequest([record.id])}>
删除
</Button>
</>
),
},
];
const [tableData, setTableData] = useState<goodsType>([]);
const [allRentGoods, setAllRentGoods] = useState<goodsType>([]);
const [loading, setLoading] = useState<boolean>(false);
//分页
const [pagination, setPagination] = useState<
PaginationProps & { totalCount: number; totalPage: number }
>({
const [tableData, setTableData] = useState<rentGoodsType>([]);
const [pagination, setPagination] = useState<PaginationProps & { totalCount: number }>({
pageNo: 1,
pageSize: 10,
totalCount: 0,
totalPage: 1,
});
//筛选
const [query, setQuery] = useState<goodsSearchParameters>({ status: undefined });
// 表格多选
const [query, setQuery] = useState<rentGoodsParametersType>({
shelfStatus: undefined,
});
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const onTabChange = (key: string) => {
pagination.pageNo = 1;
pagination.pageSize = 10;
query.status = key === '1' ? undefined : key === '2' ? 1 : 0;
setActiveTabKey(key);
query.shelfStatus = key === '1' ? undefined : key === '2' ? 1 : 0;
setSearchParams(
qs.stringify({
pageNo: 1,
pageSize: 10,
...query,
status: query.status === undefined ? 'all' : query.status,
shelfStatus: query.shelfStatus === undefined ? 'all' : query.shelfStatus,
}),
);
getGoodsList(query);
getGoodsList({ ...query, pageNo: 1, pageSize: 9999 }, true);
setQuery(query);
setActiveTabKey(key);
getRentGoodsList(query);
};
//租赁商品列表
const getRentGoodsList = (query?: rentGoodsParametersType) => {
RentManageAPI.getLeaseGoodsList({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
...query,
}).then(({ result }) => {
pagination.totalCount = result.totalCount;
setPagination({ ...pagination });
setTableData(result.list || []);
});
};
//新增,编辑租赁商品
const addOrEditClick = () => {
navigate({ pathname: '/rentManage/rentGoods/add' });
};
//分页
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageSize = pageSize;
pagination.pageNo = pageNo;
pagination.pageSize = pageSize;
getRentGoodsList(query);
setSearchParams(
qs.stringify({
pageNo: pageNo,
pageSize: pageSize,
pageNo,
pageSize,
...query,
status: query.status === undefined ? 'all' : query.status,
shelfStatus: query.shelfStatus === undefined ? 'all' : query.shelfStatus,
}),
);
getGoodsList(query);
getGoodsList({ ...query, pageNo: 1, pageSize: 9999 }, true);
};
//筛选
const searchSuccess = (data: any) => {
const searchSuccess = (value: rentGoodsParametersType) => {
pagination.pageNo = 1;
pagination.pageSize = 10;
setQuery({ ...filterObjAttr(data, ['time']), status: query.status });
getGoodsList({ ...filterObjAttr(data, ['time']), status: query.status });
getGoodsList(
{ ...filterObjAttr(data, ['time']), status: query.status, pageNo: 1, pageSize: 9999 },
true,
);
setSearchParams(
qs.stringify({
pageNo: 1,
pageSize: 10,
...filterObjAttr(data, ['time']),
status: query.status === undefined ? 'all' : query.status,
...value,
shelfStatus: query.shelfStatus === undefined ? 'all' : query.shelfStatus,
}),
);
setQuery(value);
getRentGoodsList(value);
};
//商品列表
const getGoodsList = (query?: goodsSearchParameters, isAll?: boolean) => {
setLoading(true);
GoodsAPI.getGoodsList({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
goodsType: 1,
...query,
}).then(({ result }) => {
setLoading(false);
if (isAll) {
setAllRentGoods(result.list || []);
} else {
setTableData(result.list || []);
pagination.totalCount = result.totalCount;
pagination.totalPage = result.totalPage;
setPagination(pagination);
//获取类型列表
const getRentTypeList = () => {
RentManageAPI.getTypeList({ pageNo: 1, pageSize: 99999 }).then(({ result }) => {
if (result.list) {
setRentTypeSelectList(result.list.map((v) => ({ value: v.id, label: v.name })));
}
});
};
//新增商品
const toAddRentGoods = () => {
navigate({ pathname: '/rentManage/rentGoods/add' });
};
//编辑商品
const toEditGoods = (id: number) => {
navigate({
pathname: '/rentManage/rentGoods/edit',
search: `id=${id}`,
});
};
//商品详情
const toRentGoodsDetail = (id: number) => {
navigate({
pathname: '/rentManage/rentGoods/detail',
search: `id=${id}&isDetail=1`,
//品牌列表
const getRentMakeList = () => {
RentManageAPI.getListBrandInfo({ pageNo: 1, pageSize: 99999 }).then(({ result }) => {
if (result.list) {
const optionList = result.list.map((v) => ({ label: v.brandName, value: v.id }));
setBrandInfoList(optionList);
}
});
};
// 表格多选事件
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
//跳转租赁商品编辑
const toRentEdit = (record: rentGoodsType[0]) => {
navigate({ pathname: '/rentManage/rentGoods/edit', search: `id=${record.id}` });
};
//获取目录列表
const getDirectoryList = () => {
CategoryManageAPI.getDirectoryListClone({ type: 2 }).then(({ result }) => {
if (result) {
const options = result.map((v) => ({ id: v.id, name: v.directoryName }));
searchColumns[1].options = options;
setSearchColumns([...searchColumns]);
}
});
//跳转租赁详情
const toRentDetail = (record: rentGoodsType[0]) => {
navigate({ pathname: '/rentManage/rentGoods/detail', search: `id=${record.id}` });
};
//商品-批量上下架
const batchOnShelfOrTakeDown = (status: number) => {
//批量上下架操作
const batchOnShelfOrTakeDownClick = (status: number) => {
if (selectedRowKeys.length === 0) {
return message.warning('请先选择商品');
message.warning('请先选择租赁商品');
} else {
batchOnShelfOrTakeDownRequest(selectedRowKeys as number[], status);
}
GoodsAPI.batchOnShelfOrTakeDown({ goodsIds: selectedRowKeys as number[], status }).then(
({ code }) => {
if (code === '200') {
message.success(status ? '上架成功' : '下架成功');
getGoodsList(query);
}
};
//批量上下架请求
const batchOnShelfOrTakeDownRequest = (goodsIds: number[], status: number) => {
Modal.confirm({
title: '提示',
content: status
? '是否确认上架?商品上架后,请到出租中的商品查看并管理。'
: '是否确认下架?商品下架后请到仓库中的商品查看并管理',
onOk: () => {
RentManageAPI.batchOnShelfOrTakeDown({ goodsIds, status }).then(({ code }) => {
if (code === '200') {
message.success(`${status ? '上架' : '下架'}成功`);
getRentGoodsList(query);
setSelectedRowKeys([]);
}
});
},
);
});
};
//商品-删除
const deleteGoods = () => {
//批量删除操作
const batchRemoveWareInfoClick = () => {
if (selectedRowKeys.length === 0) {
return message.warning('请先选择商品');
message.warning('请先选择租赁商品');
} else {
batchRemoveWareInfoRequest(selectedRowKeys as number[]);
}
};
//批量删除请求
const batchRemoveWareInfoRequest = (ids: number[]) => {
Modal.confirm({
title: '提示',
content: '删除后数据将会丢失,确定删除吗?',
onOk() {
GoodsAPI.batchRemoveWareInfo(selectedRowKeys as number[]).then(({ code }) => {
content: `确认删除${ids.length > 1 ? '这些' : '该'}商品`,
onOk: () => {
RentManageAPI.batchRemoveWareInfo(ids).then(({ code }) => {
if (code === '200') {
if (pagination.pageNo !== 1 && tableData.length == 1) {
if (ids.length === tableData.length && pagination.pageNo !== 1) {
pagination.pageNo -= 1;
}
getRentGoodsList(query);
message.success('删除成功');
getGoodsList(query);
}
});
},
});
};
//上移
const upGoodsRentClick = () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择商品');
} else if (selectedRowKeys.length > 1) {
message.warning('最多选择一个商品');
} else {
const index = tableData.findIndex((v) => v.id === selectedRowKeys[0]);
const allIndex = allRentGoods.findIndex((v) => v.id === selectedRowKeys[0]);
if (index === 0 && pagination.pageNo === 1) {
message.warning('位置已到最前列,无法上移');
} else {
const exReqData =
index === 0
? allRentGoods
.filter((_v, index) => index === allIndex - 1 || index === allIndex)
.map((v) => ({ id: v.id }))
: tableData
.filter((_v, i) => index - 1 === i || index === i)
.map((v) => ({ id: v.id }));
GoodsAPI.exchangeGoodsInfo({ firstId: exReqData[0].id, secondId: exReqData[1].id }).then(
({ code }) => {
if (code === '200') {
message.success('上移成功');
if (index === 0 && pagination.pageNo !== 1) {
pagination.pageNo -= 1;
setSearchParams(
qs.stringify({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
...query,
status: query.status === undefined ? 'all' : query.status,
}),
);
}
getGoodsList(query);
getGoodsList({ ...query, pageNo: 1, pageSize: 9999 }, true);
}
},
);
}
}
};
//下移
const downRentGoodsClick = () => {
if (selectedRowKeys.length === 0) {
message.warning('请选择商品');
} else if (selectedRowKeys.length > 1) {
message.warning('最多选择一个商品');
} else {
const index = tableData.findIndex((v) => v.id === selectedRowKeys[0]);
const allIndex = allRentGoods.findIndex((v) => v.id === selectedRowKeys[0]);
if (index === tableData.length - 1 && pagination.pageNo === pagination.totalPage) {
message.warning('位置已到最后,无法下移');
} else {
const exReqData =
index === tableData.length - 1
? allRentGoods
.filter((_v, index) => index === allIndex + 1 || index === allIndex)
.map((v) => ({ id: v.id }))
: tableData
.filter((_v, i) => index + 1 === i || index === i)
.map((v) => ({ id: v.id }));
GoodsAPI.exchangeGoodsInfo({ firstId: exReqData[0].id, secondId: exReqData[1].id }).then(
({ code }) => {
if (code === '200') {
message.success('下移成功');
if (index === tableData.length - 1 && pagination.pageNo !== pagination.totalPage) {
pagination.pageNo += 1;
setSearchParams(
qs.stringify({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
...query,
status: query.status === undefined ? 'all' : query.status,
}),
);
}
getGoodsList(query);
getGoodsList({ ...query, pageNo: 1, pageSize: 9999 }, true);
}
},
);
}
}
//表格-选择
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
};
useEffect(() => {
getDirectoryList();
pagination.pageNo = Number(searchParams.get('pageNo') || 1);
pagination.pageSize = Number(searchParams.get('pageSize') || 10);
const queryObj = {
goodsName: searchParams.get('goodsName') || undefined,
directoryId: searchParams.get('directoryId')
? Number(searchParams.get('directoryId'))
pagination.pageNo = Number(searchParams.get('pageNo')) || 1;
pagination.pageSize = Number(searchParams.get('pageSize')) || 10;
const searchData = {
tradeName: searchParams.get('tradeName') || undefined,
productTypeId: searchParams.get('productTypeId')
? Number(searchParams.get('productTypeId'))
: undefined,
brandInfoId: searchParams.get('brandInfoId')
? Number(searchParams.get('brandInfoId'))
: undefined,
startTime: searchParams.get('startTime') || undefined,
endTime: searchParams.get('endTime') || undefined,
status:
searchParams.get('status') === 'all' || searchParams.get('status') === null
shelfStatus: searchParams.get('shelfStatus')
? searchParams.get('shelfStatus') === 'all'
? undefined
: Number(searchParams.get('status')),
: Number(searchParams.get('shelfStatus'))
: undefined,
};
getGoodsList(queryObj);
getGoodsList({ ...queryObj, pageSize: 9999, pageNo: 1 }, true);
searchRef.current.getForm().setFieldsValue(searchData);
setActiveTabKey(
searchParams.get('status') === 'all' || searchParams.get('status') === null
? '1'
: Number(searchParams.get('status')) === 1
? '2'
: '3',
searchParams.get('shelfStatus')
? searchParams.get('shelfStatus') === 'all'
? '1'
: Number(searchParams.get('shelfStatus')) === 1
? '2'
: '3'
: '1',
);
(searchRef.current as any).getForm().setFieldsValue({
goodsName: searchParams.get('goodsName') || undefined,
directoryId: searchParams.get('directoryId')
? Number(searchParams.get('directoryId'))
: undefined,
time: searchParams.get('startTime')
? [searchParams.get('startTime'), searchParams.get('endTime')]
: undefined,
});
setQuery(searchData);
getRentGoodsList(searchData);
getRentTypeList();
getRentMakeList();
}, []);
return (
<div className='goods-list'>
<div className='rent-list'>
<SearchBox
search={searchColumns}
child={
<Button type='primary' icon={<PlusOutlined />} onClick={toAddRentGoods}>
<Button
icon={<PlusOutlined />}
type='primary'
onClick={addOrEditClick}
disabled={!!userInfo.roleInfo.superAdmin}
>
新增商品
</Button>
}
......@@ -388,58 +365,36 @@ const RentList = () => {
baseRef={searchRef}
/>
<Card tabList={tabList} activeTabKey={activeTabKey} onTabChange={onTabChange}>
<div className='header-operate' style={{ marginBottom: '10px' }}>
<div className='table-operate' style={{ marginBottom: '10px' }}>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowUpOutlined />}
onClick={upGoodsRentClick}
style={{ marginRight: '10px' }}
onClick={() => batchOnShelfOrTakeDownClick(1)}
>
上移
批量上架
</Button>
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowDownOutlined />}
onClick={downRentGoodsClick}
style={{ marginRight: '10px' }}
onClick={() => batchOnShelfOrTakeDownClick(0)}
>
下移
批量下架
</Button>
{activeTabKey !== '2' && (
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowUpOutlined />}
onClick={() => batchOnShelfOrTakeDown(1)}
>
上架
</Button>
)}
{activeTabKey !== '3' && (
<Button
type='primary'
style={{ marginRight: '10px' }}
icon={<ArrowDownOutlined />}
onClick={() => batchOnShelfOrTakeDown(0)}
>
下架
</Button>
)}
<Button danger icon={<DeleteOutlined />} onClick={deleteGoods}>
删除
<Button
type='primary'
danger
icon={<DeleteOutlined />}
onClick={batchRemoveWareInfoClick}
>
批量删除
</Button>
</div>
<Table
columns={tableColumns}
bordered
dataSource={tableData}
rowKey='id'
rowSelection={{
selectedRowKeys,
onChange: onSelectChange,
}}
loading={loading}
dataSource={tableData}
pagination={{
total: pagination.totalCount,
pageSize: pagination.pageSize,
......@@ -449,6 +404,7 @@ const RentList = () => {
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
rowSelection={{ selectedRowKeys, onChange: onSelectChange }}
/>
</Card>
</div>
......
......@@ -6,11 +6,14 @@ import { useEffect, useState } from 'react';
import { RentManageAPI } from '~/api';
import { InterDataType, PaginationProps } from '~/api/interface';
import { listBrandInfoType } from '~/api/interface/rentManageType';
import { useNavigate } from 'react-router-dom';
//品牌列表返回类型
type makeListType = InterDataType<listBrandInfoType>['list'];
const RentMake = () => {
const navigate = useNavigate();
const tableColumns: ColumnsType<makeListType[0]> = [
{
title: '品牌名称',
......@@ -23,6 +26,7 @@ const RentMake = () => {
dataIndex: 'modeInfoList',
render: (text: makeListType[0]['modeInfoList']) =>
text?.map((v) => <Tag key={v.id}>{v.modeName}</Tag>),
width: '30%',
},
{
title: '操作',
......@@ -30,11 +34,12 @@ const RentMake = () => {
width: '15%',
render: (_: any, record) => (
<>
<Button type='link'>绑定型号</Button>
<Button type='link' onClick={() => addOrEditRentMakeModalClick(record)}>
编辑
</Button>
<Button type='link'>详情</Button>
<Button type='link' onClick={() => addRentModeClick(record)}>
详情
</Button>
</>
),
},
......@@ -71,6 +76,14 @@ const RentMake = () => {
getListBrandInfo();
setAddOrEditRentMakeModalShow(false);
};
//品牌新增型号
const addRentModeClick = (record: makeListType[0]) => {
navigate({
pathname: '/rentManage/rentModel',
search: `brandInfoId=${record.id}&brandName=${record.brandName}`,
});
};
//分页
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageNo = pageNo;
......
import { Form, Input, Modal, ModalProps, Select } from 'antd';
import { Button, Col, Form, Input, message, Modal, ModalProps, Row, Select } from 'antd';
import { FC, useEffect, useState } from 'react';
import { InterDataType, InterReqType } from '~/api/interface';
import { addRentModeType, getTypeListType } from '~/api/interface/rentManageType';
import { addRentModeType, getTypeListType, rentModeListType } from '~/api/interface/rentManageType';
import { RentManageAPI } from '~/api';
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
interface selfProps {
onOk: () => void;
onCancel: () => void;
brandInfoId: number;
rentTypeList: rentTypeListType;
currentRentMode: modeListType[0] | undefined;
}
//型号新增参数类型
type addRentParameterType = Exclude<InterReqType<addRentModeType>, undefined>;
//类型类别返回类型
type rentTypeListType = InterDataType<getTypeListType>['list'];
//型号返回类型
type modeListType = InterDataType<rentModeListType>['list'];
const AddOrEditRentModeModal: FC<ModalProps & selfProps> = ({ open, onOk, onCancel }) => {
const AddOrEditRentModeModal: FC<ModalProps & selfProps> = ({
open,
onOk,
onCancel,
brandInfoId,
rentTypeList,
currentRentMode,
}) => {
const [form] = Form.useForm<addRentParameterType>();
const [rentTypeList, setRentTypeList] = useState<rentTypeListType>([]);
//标签数组
const [tagList, setTagList] = useState<{ id: number; tagName: string }[]>([
{ id: Math.random(), tagName: 'tag1' },
]);
const handleOk = () => {
form.validateFields().then((values) => {
RentManageAPI.addRentMode({ ...values }).then(({ code }) => {
form.validateFields().then((values: any) => {
const covertTagList = tagList.reduce((pre: string[], cur) => {
const tagKeyItem: string | undefined = Object.keys(values).find(
(key: string) => key === cur.tagName,
);
if (tagKeyItem && values[tagKeyItem]) {
pre.push(values[tagKeyItem]);
}
return pre;
}, []);
RentManageAPI[currentRentMode ? 'editRentMode' : 'addRentMode']({
productTypeId: values.productTypeId,
modeName: values.modeName,
tag: covertTagList.length ? covertTagList.join(',') : undefined,
brandInfoId,
id: currentRentMode ? currentRentMode.id : undefined,
}).then(({ code }) => {
if (code === '200') {
message.success(currentRentMode ? '编辑成功' : '新增成功');
form.resetFields();
setTagList([{ id: Math.random(), tagName: 'tag1' }]);
onOk();
}
});
});
};
const handleCancel = () => {
form.resetFields();
setTagList([{ id: Math.random(), tagName: 'tag1' }]);
onCancel();
};
//获取类型列表
const getRentTypeList = () => {
RentManageAPI.getTypeList({ pageNo: 1, pageSize: 99999 }).then(({ result }) => {
setRentTypeList(result.list || []);
});
//新增标签
const addTagClick = () => {
setTagList([...tagList, { id: Math.random(), tagName: `tag${tagList.length + 1}` }]);
};
//删除标签
const deleteTagClick = (index: number) => {
form.setFieldValue(tagList[index].tagName, undefined);
tagList.splice(index, 1);
setTagList([...tagList]);
};
useEffect(() => {
getRentTypeList();
}, []);
if (currentRentMode) {
form.setFieldsValue({
productTypeId: currentRentMode.productTypeId,
modeName: currentRentMode.modeName,
...currentRentMode.tag?.split(',').reduce((pre: any, cur, currentIndex) => {
pre['tag' + (currentIndex + 1)] = cur;
return pre;
}, {}),
});
setTagList(
currentRentMode.tag
?.split(',')
.map((_v, index) => ({ id: Math.random(), tagName: `tag${index + 1}` })) || [
{ id: Math.random(), tagName: 'tag1' },
],
);
}
}, [currentRentMode]);
return (
<Modal open={open} onOk={handleOk} onCancel={handleCancel} title='新增型号'>
<Form form={form}>
<Modal
open={open}
onOk={handleOk}
onCancel={handleCancel}
title={currentRentMode ? '编辑型号' : '新增型号'}
>
<Form form={form} labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
<Form.Item
label='所属类别'
name='productTypeId'
......@@ -65,6 +127,42 @@ const AddOrEditRentModeModal: FC<ModalProps & selfProps> = ({ open, onOk, onCanc
>
<Input placeholder='请输入型号名称' maxLength={30} />
</Form.Item>
{tagList.map((v, index) => (
<Row key={v.id}>
<Col span={20}>
<Form.Item
label={index ? '' : '标签'}
labelCol={{ span: 5 }}
wrapperCol={{ span: 19, offset: index ? 5 : 0 }}
name={v.tagName}
>
<Input placeholder='请输入标签名称' maxLength={30} />
</Form.Item>
</Col>
<Col span={3} offset={1}>
{index === tagList.length - 1 ? (
<Button
type='primary'
icon={<PlusOutlined />}
style={{ marginRight: '5px' }}
onClick={addTagClick}
></Button>
) : (
''
)}
{index ? (
<Button
type='primary'
icon={<MinusOutlined />}
onClick={() => deleteTagClick(index)}
></Button>
) : (
''
)}
</Col>
</Row>
))}
</Form>
</Modal>
);
......
import SearchBox, { searchColumns as searchColumnsType } from '~/components/search-box';
import { Button, Table } from 'antd';
import { Button, Modal, Table, Tag } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { ColumnsType } from 'antd/es/table/InternalTable';
import AddOrEditRentModeModal from '~/pages/rentManage/rentMode/components/addOrEditRentModeModal';
import { useEffect, useState } from 'react';
import { RentManageAPI } from '~/api';
import { PaginationProps } from '~/api/interface';
import { InterDataType, InterReqListType, PaginationProps } from '~/api/interface';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { getTypeListType, rentModeListType } from '~/api/interface/rentManageType';
//类型类别返回类型
type rentTypeListType = InterDataType<getTypeListType>['list'];
//型号返回类型
type modeListType = InterDataType<rentModeListType>['list'];
//型号列表参数类型
type modeListParametersType = InterReqListType<rentModeListType>;
const RentMode = () => {
const tableColumns: ColumnsType<any> = [
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const [rentTypeList, setRentTypeList] = useState<rentTypeListType>([]);
const tableColumns: ColumnsType<modeListType[0]> = [
{
title: '型号名称',
align: 'center',
dataIndex: 'modeName',
},
{
title: '所属类型',
align: 'center',
},
{
title: '所属品牌',
align: 'center',
dataIndex: 'productTypeId',
render: (text: number) => rentTypeList.find((v) => v.id === text)?.name || '',
},
{
title: '标签',
align: 'center',
dataIndex: 'tag',
render: (text: string) => text?.split(',').map((v, index) => <Tag key={index}>{v}</Tag>),
width: '30%',
},
{
title: '操作',
align: 'center',
width: '10%',
render: (_: any, record) => (
<>
<Button type='link' onClick={() => addOrEditRentModelClick(record)}>
编辑
</Button>
<Button type='link' onClick={() => deleteRentModelClick(record)}>
删除
</Button>
</>
),
},
];
const searchColumns: searchColumnsType[] = [
{
name: '',
label: '设备类型',
placeholder: '请选择设备类型',
name: 'productTypeId',
label: '所属类型',
placeholder: '请选择所属类型',
type: 'select',
options: [],
options: rentTypeList.map((v) => ({ id: v.id, name: v.name })),
},
];
const [brandInfoId, setBrandInfoId] = useState<number>(-1);
const [addOrEditRentModelModalShow, setAddOrEditRentModelModalShow] = useState<boolean>(false);
const [pagination, setPagination] = useState<PaginationProps & { totalCount: number }>({
pageNo: 1,
pageSize: 10,
totalCount: 0,
});
const [tableData, setTableData] = useState<modeListType>([]);
const [query, setQuery] = useState<modeListParametersType>();
const [currentRentMode, setCurrentRentMode] = useState<modeListType[0]>();
//新增,编辑型号弹窗
const addOrEditRentModelClick = () => {
const addOrEditRentModelClick = (record?: modeListType[0]) => {
setCurrentRentMode(record ? { ...record } : undefined);
setAddOrEditRentModelModalShow(true);
};
const addOrEditRentModelModalCancel = () => {
setAddOrEditRentModelModalShow(false);
};
const addOrEditRentModelModalOk = () => {
setAddOrEditRentModelModalShow(true);
setAddOrEditRentModelModalShow(false);
getModeList(brandInfoId, query);
};
//类型列表
const getModeList = () => {
//删除型号
const deleteRentModelClick = (record: modeListType[0]) => {
Modal.confirm({
title: '提示',
content: '确认删除该型号?',
onOk: () => {
RentManageAPI.deleteMode({ id: record.id }).then(({ code }) => {
if (code === '200') {
if (tableData.length === 1 && pagination.pageNo !== 1) {
pagination.pageNo -= 1;
}
getModeList(brandInfoId, query);
}
});
},
});
};
//型号列表
const getModeList = (brandInfoId: number, query?: modeListParametersType) => {
RentManageAPI.getRentModeList({
pageNo: pagination.pageNo,
pageSize: pagination.pageSize,
brandInfoId,
...query,
}).then(({ result }) => {
pagination.totalCount = result.totalCount;
setTableData(result.list || []);
setPagination({ ...pagination });
});
};
//获取类型列表
const getRentTypeList = () => {
RentManageAPI.getTypeList({ pageNo: 1, pageSize: 99999 }).then(({ result }) => {
setRentTypeList(result.list || []);
searchColumns[0].options = (result.list || []).map((v) => ({ id: v.id, name: v.name }));
});
};
//返回
const backRoute = () => {
navigate(-1);
};
//分页
const paginationChange = (pageNo: number, pageSize: number) => {
pagination.pageNo = pageNo;
pagination.pageSize = pageSize;
getModeList(brandInfoId, query);
};
//筛选
const searchSuccess = (value: modeListParametersType) => {
pagination.pageSize = 10;
pagination.pageNo = 1;
setQuery(value);
getModeList(brandInfoId, value);
};
useEffect(() => {
getModeList();
getRentTypeList();
setBrandInfoId(Number(searchParams.get('brandInfoId')));
getModeList(Number(searchParams.get('brandInfoId')));
}, []);
return (
<div className='rent-model'>
<SearchBox
search={searchColumns}
child={
<Button type='primary' icon={<PlusOutlined />} onClick={addOrEditRentModelClick}>
新增型号
<>
<span style={{ marginRight: '10px', color: '#1677ff' }}>
当前品牌:{searchParams.get('brandName')}
</span>
<Button
type='primary'
icon={<PlusOutlined />}
onClick={() => addOrEditRentModelClick()}
>
新增型号
</Button>
</>
}
otherChild={
<Button type='primary' onClick={backRoute}>
返回
</Button>
}
searchData={searchSuccess}
/>
<Table
bordered
columns={tableColumns}
dataSource={tableData}
rowKey='id'
pagination={{
total: pagination.totalCount,
pageSize: pagination.pageSize,
current: pagination.pageNo,
showSizeChanger: true,
showQuickJumper: true,
onChange: (page: number, pageSize: number) => paginationChange(page, pageSize),
showTotal: (total, range) => `当前 ${range[0]}-${range[1]} 条记录 / 共 ${total} 条数据`,
}}
/>
<Table bordered columns={tableColumns} />
<AddOrEditRentModeModal
open={addOrEditRentModelModalShow}
onCancel={addOrEditRentModelModalCancel}
onOk={addOrEditRentModelModalOk}
brandInfoId={brandInfoId}
rentTypeList={rentTypeList}
currentRentMode={currentRentMode}
/>
</div>
);
......
......@@ -66,7 +66,11 @@ const AddOrEditTypeModal: FC<ModalProps & selfProps> = ({
return (
<Modal open={open} title='新增类型' onCancel={handleCancel} onOk={handelOk}>
<Form form={form} labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
<Form.Item label='类型名称' name='name'>
<Form.Item
label='类型名称'
name='name'
rules={[{ required: true, message: '请输入类型名称' }]}
>
<Input placeholder='请输入类型名称' maxLength={30} />
</Form.Item>
<Form.Item label='图片' name='url' rules={[{ required: true, message: '请上传图片' }]}>
......
import { Cascader, Form, Input, message, Modal, ModalProps, Switch } from 'antd';
import { FC, useEffect } from 'react';
import districtData from '~/assets/json/district.json';
import { InterDataType, InterReqType } from '~/api/interface';
import { addressInsetType, addressListType } from '~/api/interface/systemManageType';
import { SystemManageAPI } from '~/api';
interface selfProps {
onOk: () => void;
onCancel: () => void;
currentAddressItem?: addressType[0] | undefined;
}
//新增地址参数类型
type addAddressParameterType = Exclude<InterReqType<addressInsetType>, undefined>;
//地址列表返回类型
type addressType = InterDataType<addressListType>;
const AddOrEditAddressModal: FC<ModalProps & selfProps> = ({
open,
onCancel,
onOk,
currentAddressItem,
}) => {
const [form] = Form.useForm<
Omit<addAddressParameterType, 'takeRegion' | 'type' | 'districtCode'> & {
takeRegion: string[];
type: boolean;
districtCode: number[];
}
>();
const handleOk = () => {
form.validateFields().then((values) => {
SystemManageAPI[currentAddressItem ? 'editAddress' : 'addressInset']({
...values,
districtCode: values.districtCode[2].toString(),
takeRegion: getDistrictName(values.districtCode),
type: !values.type ? 1 : 0,
id: currentAddressItem ? currentAddressItem.id : undefined,
}).then(({ code }) => {
if (code === '200') {
message.success('新增地址成功');
form.resetFields();
onOk();
}
});
});
};
const handleCancel = () => {
onCancel();
};
//根据省市区code查询省市区名称
const getDistrictName = (codeArr: number[]) => {
const getFlatDistrictData = (districtData: any[]) => {
return districtData.reduce((pre: any, cur: any) => {
pre.push(cur);
if (cur.childInfo) {
pre.push(...getFlatDistrictData(cur.childInfo));
}
return pre;
}, []);
};
const flatDistrictData: any = getFlatDistrictData(districtData);
return codeArr.map((v) => flatDistrictData.find((i: any) => i.id === v)?.name).join('/');
};
useEffect(() => {
if (currentAddressItem) {
form.setFieldsValue({
takeName: currentAddressItem.takeName,
takePhone: currentAddressItem.takePhone,
districtCode: [
Number(currentAddressItem.districtCode.substring(0, 2) + '0000'),
Number(currentAddressItem.districtCode.substring(0, 4) + '00'),
Number(currentAddressItem.districtCode),
],
takeAddress: currentAddressItem.takeAddress,
type: !currentAddressItem.type,
});
}
}, [currentAddressItem]);
return (
<Modal
open={open}
title={currentAddressItem ? '编辑地址' : '新增地址'}
onOk={handleOk}
onCancel={handleCancel}
>
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} form={form}>
<Form.Item label='姓名' name='takeName' rules={[{ required: true, message: '请输入姓名' }]}>
<Input placeholder='请输入姓名' maxLength={30} />
</Form.Item>
<Form.Item
label='手机号'
name='takePhone'
rules={[
{ required: true, message: '请输入手机号' },
// 校验手机号
() => ({
validator(_, value) {
if (!value || /^1[3-9]\d{9}$/.test(value)) {
return Promise.resolve();
}
return Promise.reject('请输入正确的手机号');
},
}),
]}
>
<Input placeholder='请输入手机号' maxLength={11} />
</Form.Item>
<Form.Item
label='所在地区'
name='districtCode'
rules={[{ required: true, message: '请选择所在地区' }]}
>
<Cascader
placeholder='请选择所在地区'
options={districtData}
fieldNames={{ label: 'name', value: 'id', children: 'childInfo' }}
/>
</Form.Item>
<Form.Item
label='详细地址'
name='takeAddress'
rules={[{ required: true, message: '请输入详细地址' }]}
>
<Input.TextArea rows={4} showCount placeholder='请输入详细地址' maxLength={70} />
</Form.Item>
<Form.Item label='设为默认地址' name='type' valuePropName='checked'>
<Switch />
</Form.Item>
</Form>
</Modal>
);
};
export default AddOrEditAddressModal;
import SearchBox from '~/components/search-box';
import { Button, message, Modal, Table } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { ColumnsType } from 'antd/es/table';
import AddOrEditAddressModal from '~/pages/systemManage/addressManage/components/addOrEditAddressModal';
import { useEffect, useState } from 'react';
import { SystemManageAPI } from '~/api';
import { InterDataType } from '~/api/interface';
import { addressListType } from '~/api/interface/systemManageType';
//地址列表返回类型
type addressType = InterDataType<addressListType>;
const AddressManage = () => {
const tableColumns: ColumnsType<addressType[0]> = [
{
title: '姓名',
align: 'center',
dataIndex: 'takeName',
},
{
title: '电话',
align: 'center',
dataIndex: 'takePhone',
},
{
title: '所在地区',
align: 'center',
dataIndex: 'takeRegion',
},
{
title: '详细地址',
align: 'center',
dataIndex: 'takeAddress',
},
{
title: '状态',
align: 'center',
dataIndex: 'type',
render: (text: number) => (!text ? '默认' : ''),
},
{
title: '操作',
align: 'center',
render: (_: any, record) => (
<>
<Button type='link' onClick={() => addOrEditAddressModalClick(record)}>
编辑
</Button>
<Button type='link' onClick={() => deleteAddressClick(record)}>
删除
</Button>
</>
),
},
];
const [addressList, setAddressList] = useState<addressType>([]);
const [addOrEditAddressModalShow, setAddOrEditAddressModalShow] = useState<boolean>(false);
const [currentAddressItem, setCurrentAddressItem] = useState<addressType[0]>();
const getAddressList = () => {
SystemManageAPI.getAddressList({}).then(({ result }) => {
setAddressList(result);
});
};
//新增,编辑地址
const addOrEditAddressModalClick = (record?: addressType[0]) => {
setCurrentAddressItem(record ? { ...record } : undefined);
setAddOrEditAddressModalShow(true);
};
const addOrEditAddressModalCancel = () => {
setAddOrEditAddressModalShow(false);
};
const addOrEditAddressModalOk = () => {
getAddressList();
setAddOrEditAddressModalShow(false);
};
//地址删除
const deleteAddressClick = (record: addressType[0]) => {
Modal.confirm({
title: '提示',
content: '确认删除该地址吗?',
onOk: () => {
SystemManageAPI.deleteAddress({ id: record.id }).then(({ code }) => {
if (code === '200') {
message.success('删除成功');
getAddressList();
}
});
},
});
};
useEffect(() => {
getAddressList();
}, []);
return (
<div className='address-manage'>
<SearchBox
child={
<Button
type='primary'
icon={<PlusOutlined />}
onClick={() => addOrEditAddressModalClick()}
>
新增地址
</Button>
}
/>
<Table bordered columns={tableColumns} dataSource={addressList} rowKey='id' />
{/*新增编辑地址*/}
<AddOrEditAddressModal
open={addOrEditAddressModalShow}
onCancel={addOrEditAddressModalCancel}
onOk={addOrEditAddressModalOk}
currentAddressItem={currentAddressItem}
/>
</div>
);
};
export default AddressManage;
......@@ -35,6 +35,7 @@ import {
BankOutlined,
VerifiedOutlined,
AccountBookOutlined,
EnvironmentOutlined,
} from '@ant-design/icons';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
......@@ -93,14 +94,13 @@ const ServiceListView = React.lazy(() => import('~/pages/mallManage/serviceManag
const ServiceDetailView = React.lazy(
() => import('~/pages/mallManage/serviceManage/serviceDetail'),
); //服务详情
const ServiceIntroduceView = React.lazy(
() => import('~/pages/mallManage/serviceManage/serviceIntroduce'),
); //服务介绍
// const ServiceIntroduceView = React.lazy(
// () => import('~/pages/mallManage/serviceManageOld/serviceIntroduce'),
// ); //服务介绍
const RentListView = React.lazy(() => import('~/pages/rentManage/rentGoods/rentList')); //租赁列表
const RentAddOrEditOrDetailView = React.lazy(
() => import('~/pages/rentManage/rentGoods/rentAddOrEditOrDetail'),
); //租赁新增、编辑、详情
const RentAddOrEditView = React.lazy(() => import('~/pages/rentManage/rentGoods/rentAddOrEdit')); //租赁新增、编辑
const RentDetailView = React.lazy(() => import('~/pages/rentManage/rentGoods/rentDetail')); //租赁详情
const RentTypeView = React.lazy(() => import('~/pages/rentManage/rentType')); //租赁-类型管理
const RentMakeView = React.lazy(() => import('~/pages/rentManage/rentMake')); //租赁-品牌管理
const RentModeView = React.lazy(() => import('~/pages/rentManage/rentMode')); //租赁-型号管理
......@@ -108,7 +108,7 @@ const RentModeView = React.lazy(() => import('~/pages/rentManage/rentMode')); //
const MallGoodsView = React.lazy(() => import('~/pages/mallManage/mallGoods/goodsList')); //商城商品
const MallAddOrEditOrDetailView = React.lazy(
() => import('~/pages/mallManage/mallGoods/goodsAddOrEditOrDetail'),
); //商城商品新增、编辑、租赁商品详情
); //商城商品新增、编辑
const MallGoodsDetailsView = React.lazy(() => import('~/pages/mallManage/mallGoods/goodsDetails')); //商城商品(新)
const ProduceListView = React.lazy(() => import('~/pages/mallManage/produceManage/produceList')); //产品列表
......@@ -155,6 +155,8 @@ const CompanyDetailView = React.lazy(
import AccountLimit from '~/pages/systemManage/limitManage/roleList'; //账号权限
import LimitInfo from '~/pages/systemManage/limitManage/limitInfo';
import CustomListDetail from '~/pages/customManage/customList/detail';
import ServiceCategoryDetail from '~/pages/categoryManage/serviceCategoryList/detail';
const AddressManageView = React.lazy(() => import('~/pages/systemManage/addressManage'));
// const IndustryListView = React.lazy(() => import('~/pages/mallManage/industryManage/industryList')); //行业列表
// const IndustryDetailView = React.lazy(
......@@ -601,23 +603,23 @@ export const routerList: Array<RouteObjectType> = [
element: withLoadingComponent(<ServiceDetailView />),
errorElement: <ErrorPage />,
meta: {
id: 10120,
id: 1020,
icon: <SmileOutlined />,
title: '服务详情',
hidden: true,
},
},
{
path: '/mallManage/serviceIntroduce',
element: withLoadingComponent(<ServiceIntroduceView />),
errorElement: <ErrorPage />,
meta: {
id: 10120,
icon: <SmileOutlined />,
title: '服务介绍',
hidden: true,
},
},
// {
// path: '/mallManage/serviceIntroduce',
// element: withLoadingComponent(<ServiceIntroduceView />),
// errorElement: <ErrorPage />,
// meta: {
// id: 10120,
// icon: <SmileOutlined />,
// title: '服务介绍',
// hidden: true,
// },
// },
{
path: '/mallManage/mallGoods',
element: withLoadingComponent(<MallGoodsView />),
......@@ -716,7 +718,7 @@ export const routerList: Array<RouteObjectType> = [
},
{
path: '/rentManage/rentGoods/add',
element: withLoadingComponent(<RentAddOrEditOrDetailView />),
element: withLoadingComponent(<RentAddOrEditView />),
errorElement: <ErrorPage />,
meta: {
id: 10135,
......@@ -727,7 +729,7 @@ export const routerList: Array<RouteObjectType> = [
},
{
path: '/rentManage/rentGoods/edit',
element: withLoadingComponent(<RentAddOrEditOrDetailView />),
element: withLoadingComponent(<RentAddOrEditView />),
errorElement: <ErrorPage />,
meta: {
id: 10136,
......@@ -738,10 +740,10 @@ export const routerList: Array<RouteObjectType> = [
},
{
path: '/rentManage/rentGoods/detail',
element: withLoadingComponent(<RentAddOrEditOrDetailView />),
element: withLoadingComponent(<RentDetailView />),
errorElement: <ErrorPage />,
meta: {
id: 10136,
id: 10176,
icon: <SmileOutlined />,
title: '租赁商品详情',
hidden: true,
......@@ -755,7 +757,6 @@ export const routerList: Array<RouteObjectType> = [
id: 2010,
icon: <SmileOutlined />,
title: '类型管理',
// develop: true,
},
},
{
......@@ -763,10 +764,9 @@ export const routerList: Array<RouteObjectType> = [
element: withLoadingComponent(<RentMakeView />),
errorElement: <ErrorPage />,
meta: {
id: 10138,
id: 2020,
icon: <SmileOutlined />,
title: '品牌管理',
// develop: true,
},
},
{
......@@ -776,8 +776,8 @@ export const routerList: Array<RouteObjectType> = [
meta: {
id: 10139,
icon: <SmileOutlined />,
title: '型号管理',
// develop: true,
title: '品牌管理/型号',
hidden: true,
},
},
],
......@@ -792,20 +792,9 @@ export const routerList: Array<RouteObjectType> = [
title: '分类管理',
},
children: [
{
path: '/categoryManage/jobServicesCategory/1',
element: withLoadingComponent(<CategoryManage />),
errorElement: <ErrorPage />,
meta: {
id: 1210,
title: '作业服务分类',
icon: <SendOutlined />,
},
},
// 作业服务分类(新)
// {
// path: '/categoryManage/serviceCategoryList',
// element: withLoadingComponent(<ServiceCategoryListView />),
// path: '/categoryManage/jobServicesCategory/1',
// element: withLoadingComponent(<CategoryManage />),
// errorElement: <ErrorPage />,
// meta: {
// id: 1210,
......@@ -813,6 +802,17 @@ export const routerList: Array<RouteObjectType> = [
// icon: <SendOutlined />,
// },
// },
// 作业服务分类(新)
{
path: '/categoryManage/serviceCategoryList',
element: withLoadingComponent(<ServiceCategoryListView />),
errorElement: <ErrorPage />,
meta: {
id: 1210,
title: '作业服务分类',
icon: <SendOutlined />,
},
},
{
path: '/categoryManage/jobServicesCategory/2',
element: withLoadingComponent(<CategoryManage />),
......@@ -844,6 +844,17 @@ export const routerList: Array<RouteObjectType> = [
icon: <AppstoreOutlined />,
},
},
{
path: '/categoryManage/mallCategoryList/detail',
element: withLoadingComponent(<ServiceCategoryDetail />),
errorElement: <ErrorPage />,
meta: {
id: 18600,
title: '分类详情',
icon: '',
hidden: true,
},
},
// {
// path: '/categoryManage/jobServicesCategory/4',
// element: withLoadingComponent(<CategoryManage />),
......@@ -1158,12 +1169,22 @@ export const routerList: Array<RouteObjectType> = [
element: withLoadingComponent(<CompanyDetailView />),
errorElement: <ErrorPage />,
meta: {
id: 1440,
id: 1450,
title: '单位详情',
icon: <BankOutlined />,
hidden: true,
},
},
{
path: '/systemManage/addressManage',
element: withLoadingComponent(<AddressManageView />),
errorElement: <ErrorPage />,
meta: {
id: 1440,
title: '地址管理',
icon: <EnvironmentOutlined />,
},
},
],
},
];
......@@ -37,3 +37,27 @@ export const splitCouponUseType = [
label: '店铺券',
},
];
//租赁-质量字典
export const qualityList = [
{
label: '全新',
value: 0,
},
{
label: '99新',
value: 1,
},
{
label: '95新',
value: 2,
},
{
label: '90新',
value: 3,
},
{
label: '80新',
value: 4,
},
];
......@@ -117,3 +117,18 @@ export const regPriceNumber = (value: string): boolean => {
export const isEmptyBol = (value: any): boolean => {
return _.isNull(value) || _.isNaN(value) || _.isNull(value) || _.isUndefined(value);
};
// 通用价格判断
export const commonPriceValidator = (_rules: any, value: number) => {
if (!isEmptyBol(value)) {
if (regPriceNumber(value.toString())) {
if (value > 99999999 || value < 0) {
return Promise.reject(new Error('价格最大为99999999且大于0'));
}
return Promise.resolve();
} else {
return Promise.reject(new Error('为整数且最多保留两位小数'));
}
} else {
return Promise.reject(new Error('请输入价格'));
}
};
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论