refactor(components): 重构业务组件结构和功能实现

- 将格式化函数提取到公共工具文件,避免重复定义
- 优化成本数据处理逻辑,完善数据映射和去重操作
- 统一拖拽组件分页配置,增强分页功能一致性
- 修复类型导入问题,提升代码健壮性
- 新增经销商列表公司返点字段展示与表单控制
- 重构发货单组件为订单发货组件,优化状态管理和搜索功能
- 创建新的采购订单和供应商订单组件,完善订单状态tab
- 调整组件导出路径,统一归类到Order目录下
- 扩展用户头像功能,支持角色切换能力
This commit is contained in:
shenyifei 2025-12-16 22:37:03 +08:00
parent 3b4942cc44
commit 8c169fb4ca
36 changed files with 2228 additions and 909 deletions

View File

@ -6,7 +6,7 @@ export default defineConfig({
'/api': {
target: 'http://api.erp.xunhong168.test/',
changeOrigin: true,
pathRewrite: { '^/': '' },
pathRewrite: { '^/api': '' },
},
},
plugins: ['@umijs/max-plugin-openapi'],

View File

@ -19,8 +19,6 @@ import './global.less';
interface InitialStateProps {
currentUser?: AuthAPI.UserVO;
currentAdmin?: AuthAPI.AdminVO;
currentUserRole?: AuthAPI.UserRoleVO[];
currentUserPermission?: AuthAPI.PermissionVO[];
platformId: string;
channel?: AuthAPI.ChannelVO;
tip: any;
@ -120,11 +118,78 @@ export const render = async (oldRender: () => void) => {
return;
}
const { data: employeeVO } = await auth.user.userEmployee({
employeeShowQry: {},
});
let platformId = '1769220738064285698';
let roleIdList: string[] = [];
if (employeeVO) {
window.localStorage.setItem('employee', JSON.stringify(employeeVO));
const userRoleVOList = employeeVO.userRoleList?.filter(
(item) => item.platformId === platformId,
);
if (!userRoleVOList?.length) {
message.error('请先绑定角色');
window.localStorage.removeItem('employee');
window.localStorage.removeItem('admin');
window.localStorage.removeItem('roleSlug');
// 未登录,跳转到登录页面
window.location.href =
process.env.UMI_APP_SSO_LOGIN_URL +
encodeURIComponent(process.env.UMI_APP_OPERATION_URL || '');
return;
}
window.localStorage.setItem(
'userRoleVOList',
JSON.stringify(userRoleVOList),
);
// 根据 localStorage 判断有没有选择角色,如果没有选择则默认选择第一个
if (!window.localStorage.getItem('roleSlug')) {
window.localStorage.setItem('roleSlug', userRoleVOList?.[0]?.slug);
roleIdList = [userRoleVOList?.[0]?.roleId];
} else {
const roleSlug = window.localStorage.getItem('roleSlug');
const role = userRoleVOList?.find((item) => item.slug === roleSlug);
if (!role) {
message.error('请先绑定角色');
window.localStorage.removeItem('employee');
window.localStorage.removeItem('admin');
window.localStorage.removeItem('roleSlug');
// 未登录,跳转到登录页面
window.location.href =
process.env.UMI_APP_SSO_LOGIN_URL +
encodeURIComponent(process.env.UMI_APP_OPERATION_URL || '');
return;
}
roleIdList = [role?.roleId];
}
} else {
const { data: adminVO } = await auth.user.userAdmin({
adminShowQry: {},
});
window.localStorage.setItem('admin', JSON.stringify(adminVO));
window.localStorage.setItem(
'userRoleVOList',
JSON.stringify([]),
);
window.localStorage.setItem('roleSlug', 'operation');
}
// 获取服务端路由配置
await auth.user
.userMenu({
roleMenuTreeQry: {
platformId: '1769220738064285698',
platformId: platformId,
roleId: roleIdList,
},
})
.then(({ data: serverRoutes }) => {
@ -141,22 +206,22 @@ export async function getInitialState(): Promise<InitialStateProps> {
let current: any;
const { data: channel } = await auth.channel.selectChannelByDomain({
domain: "operation.erp.qilincloud168.com",
domain: 'operation.erp.qilincloud168.com',
});
const { data: userAuthVO } = await auth.user.userInfo();
const { data: adminVO } = await auth.user.userAdmin({
adminShowQry: {
userId: userAuthVO?.userId,
},
});
// 根据 localStorage 获取当前用户信息 admin 和 employee
const adminVO = JSON.parse(window.localStorage.getItem('admin') || '{}');
const employeeVO = JSON.parse(
window.localStorage.getItem('employee') || '{}',
);
current = {
channel,
platformId: '1769220738064285698',
currentUser: userAuthVO?.userVO,
currentAdmin: adminVO,
currentUser: {
...employeeVO,
...adminVO,
},
};
return current;
@ -353,7 +418,9 @@ export const request: RequestConfig = {
const { success, data, errCode, errMessage, showType } = res;
if (!success) {
if (errCode === '401' || errCode === '403') {
window.localStorage.removeItem('user-data');
window.localStorage.removeItem('employee');
window.localStorage.removeItem('admin');
window.localStorage.removeItem('roleSlug');
// 未登录,跳转到登录页面
window.location.href =
process.env.UMI_APP_SSO_LOGIN_URL +
@ -432,11 +499,14 @@ export const request: RequestConfig = {
headers: any;
},
) => {
const roleSlug = window.localStorage.getItem('roleSlug');
options.headers = {
...options.headers,
'Xh-Role-Slug': "operation",
};
if (roleSlug) {
options.headers = {
...options.headers,
'Xh-Role-Slug': roleSlug,
};
}
console.debug('请求拦截器', url, options);
const apiUrl = '/api' + url;

View File

@ -16,6 +16,7 @@ import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import { ProFormUploadMaterial } from '@chageable/components';
import React, { useRef, useState } from 'react';
import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
import { formatBankCard, formatIdCard, formatPhone } from '@/utils/format';
interface IMelonFarmerListProps {
ghost?: boolean;
@ -43,24 +44,6 @@ export default function MelonFarmerList(props: IMelonFarmerListProps) {
const [showBankCard, setShowBankCard] = useState<Record<string, boolean>>({});
const [showPhone, setShowPhone] = useState<Record<string, boolean>>({});
const formatIdCard = (idCard: string, showFull: boolean = false) => {
if (showFull) return idCard;
if (idCard.length < 8) return idCard;
return `${idCard.substring(0, 4)}********${idCard.substring(idCard.length - 4)}`;
};
const formatBankCard = (bankCard: string, showFull: boolean = false) => {
if (showFull) return bankCard;
if (bankCard.length < 4) return bankCard;
return `**** **** **** ${bankCard.substring(bankCard.length - 4)}`;
};
const formatPhone = (phone: string, showFull: boolean = false) => {
if (showFull) return phone;
if (phone.length < 4) return phone;
return `${phone.substring(0, 3)}****${phone.substring(phone.length - 4)}`;
};
const columns: ProColumns<BusinessAPI.SupplierVO, BizValueType>[] = [
{
title: intl.formatMessage({ id: intlPrefix + '.column.name' }),
@ -76,17 +59,23 @@ export default function MelonFarmerList(props: IMelonFarmerListProps) {
key: 'idCard',
render: (_, record) => (
<div className="flex items-center">
<span>{formatIdCard(record.idCard, showIdCard[record.supplierId])}</span>
<span>
{formatIdCard(record.idCard, showIdCard[record.supplierId])}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowIdCard(prev => ({
setShowIdCard((prev) => ({
...prev,
[record.supplierId]: !prev[record.supplierId]
[record.supplierId]: !prev[record.supplierId],
}));
}}
>
{showIdCard[record.supplierId] ? <EyeTwoTone /> : <EyeInvisibleOutlined />}
{showIdCard[record.supplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
@ -101,13 +90,17 @@ export default function MelonFarmerList(props: IMelonFarmerListProps) {
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowPhone(prev => ({
setShowPhone((prev) => ({
...prev,
[record.supplierId]: !prev[record.supplierId]
[record.supplierId]: !prev[record.supplierId],
}));
}}
>
{showPhone[record.supplierId] ? <EyeTwoTone /> : <EyeInvisibleOutlined />}
{showPhone[record.supplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
@ -118,17 +111,23 @@ export default function MelonFarmerList(props: IMelonFarmerListProps) {
key: 'bankCard',
render: (_, record) => (
<div className="flex items-center">
<span>{formatBankCard(record.bankCard, showBankCard[record.supplierId])}</span>
<span>
{formatBankCard(record.bankCard, showBankCard[record.supplierId])}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowBankCard(prev => ({
setShowBankCard((prev) => ({
...prev,
[record.supplierId]: !prev[record.supplierId]
[record.supplierId]: !prev[record.supplierId],
}));
}}
>
{showBankCard[record.supplierId] ? <EyeTwoTone /> : <EyeInvisibleOutlined />}
{showBankCard[record.supplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),

View File

@ -185,12 +185,12 @@ export default function ProductDataList(props: IProductDataListProps) {
)
.map((item: BusinessAPI.CostVO) => {
return {
costItemId: item.costItemId,
costId: item.costId,
name: item.name,
type: item.type,
price: item.price,
unit: item.unit,
belong: item.belong,
};
});
@ -207,9 +207,11 @@ export default function ProductDataList(props: IProductDataListProps) {
item && item.type === 'MATERIAL_TYPE',
)
.filter(
(item, index, self) =>
(item: BusinessAPI.CostItemVO, index: number, self: any) =>
index ===
self.findIndex((t) => t.costItemId === item.costItemId),
self.findIndex(
(t: any) => t.costItemId === item.costItemId,
),
)
.map((item: BusinessAPI.CostItemVO) => ({
costItemId: item.costItemId,
@ -232,9 +234,11 @@ export default function ProductDataList(props: IProductDataListProps) {
item && item.type === 'ARTIFICIAL_TYPE',
)
.filter(
(item, index, self) =>
(item: BusinessAPI.CostItemVO, index: number, self: any) =>
index ===
self.findIndex((t) => t.costItemId === item.costItemId),
self.findIndex(
(t: any) => t.costItemId === item.costItemId,
),
)
.map((item: BusinessAPI.CostItemVO) => ({
costItemId: item.costItemId,
@ -450,7 +454,7 @@ export default function ProductDataList(props: IProductDataListProps) {
transform: async (data) => {
return {
...data,
...JSON.parse(data.costTemplate),
...JSON.parse(data.costTemplate || '{}'),
};
},
}}

View File

@ -4,6 +4,7 @@ import { DragSortTable, DragTableProps } from '@ant-design/pro-components';
import { Drawer } from 'antd';
import { useState } from 'react';
import { BizDragProps, BizValueType } from './typing';
import { pagination } from '@/utils/pagination';
export default function BizDrag<
BizVO extends Record<string, any>,
@ -34,7 +35,7 @@ export default function BizDrag<
persistenceType: 'sessionStorage',
persistenceKey: method + 'ColumnStateKey',
defaultValue: {
...fieldProps?.columnsState?.defaultValue
...fieldProps?.columnsState?.defaultValue,
},
},
dragSortKey: 'sort',
@ -113,7 +114,10 @@ export default function BizDrag<
};
}}
onDragSortEnd={handleDragSortEnd}
pagination={false}
pagination={{
...pagination(),
position: ['bottomRight'],
}}
{...rest}
columns={[
{

View File

@ -1,4 +1,4 @@
import { ProFormFieldItemProps, ProFormItem } from '@ant-design/pro-components';
import { ProFormFieldProps, ProFormItem } from '@ant-design/pro-components';
import { Form, Space } from 'antd';
import React from 'react';
import {
@ -12,7 +12,7 @@ interface BizDragDropProps<T> {
/**
*
*/
fieldProps?: ProFormFieldItemProps;
fieldProps?: ProFormFieldProps;
/**
* ID
*/

View File

@ -5,12 +5,12 @@ import {
ModeType,
} from '@/components';
import { business } from '@/services';
import { formatParam } from '@/utils/formatParam';
import { useIntl } from '@@/exports';
import { ProColumns, ProFormText } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import React from 'react';
import { formatParam } from '@/utils/formatParam';
import { ProFormUploadMaterial } from '@chageable/components';
import React from 'react';
interface ICompanyListProps {
ghost?: boolean;
@ -277,7 +277,9 @@ export default function CompanyList(props: ICompanyListProps) {
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
container={{}}
container={{
ghost,
}}
remark={{
mode: 'editor',
}}

View File

@ -232,6 +232,37 @@ export default function DealerList(props: IDealerListProps) {
</span>
),
},
// 新增字段展示 - 公司返点
{
title: (
<Tooltip
title={intl.formatMessage({
id: intlPrefix + '.column.enableLoss.tooltip',
})}
>
<span>
{intl.formatMessage({
id: intlPrefix + '.column.enableLoss',
})}
</span>
</Tooltip>
),
dataIndex: 'enableLoss',
key: 'enableLoss',
search: false,
render: (_, record) => (
<span>
{record.enableLoss ? (
<CheckCircleOutlined style={{ color: '#52c41a' }} />
) : (
<CloseCircleOutlined style={{ color: '#ff4d4f' }} />
)}
{record.enableLoss && record.lossAmount
? ` ${record.lossAmount}`
: ''}
</span>
),
},
];
const formContext = [
@ -451,6 +482,52 @@ export default function DealerList(props: IDealerListProps) {
);
}}
</ProFormDependency>,
// 新增字段表单 - 产地损耗
<ProFormSwitch
key={'enableLoss'}
name={'enableLoss'}
label={intl.formatMessage({
id: intlPrefix + '.form.enableLoss.label',
})}
fieldProps={{
checkedChildren: intl.formatMessage({ id: 'common.yes' }),
unCheckedChildren: intl.formatMessage({ id: 'common.no' }),
}}
/>,
<ProFormDependency key={'enableLoss'} name={['enableLoss']}>
{({ enableLoss }) => {
return (
enableLoss && (
<ProFormDigit
key={'lossAmount'}
name={'lossAmount'}
label={intl.formatMessage({
id: intlPrefix + '.form.lossAmount.label',
})}
fieldProps={{
precision: 2,
step: 0.01,
min: 0,
max: 9999,
suffix: '元',
}}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.lossAmount.placeholder',
})}
dependencies={['enableLoss']}
rules={[
{
required: true,
message: intl.formatMessage({
id: intlPrefix + '.form.lossAmount.required',
}),
},
]}
/>
)
);
}}
</ProFormDependency>,
];
const detailColumns: ProDescriptionsItemProps<

View File

@ -1,40 +1,35 @@
import { BizContainer, BizValueType, ModeType } from '@/components';
import { business } from '@/services';
import { useIntl } from '@@/exports';
import {
ProColumns,
ProFormDatePicker,
ProFormDigit,
ProFormSelect,
ProFormText,
ProFormTextArea,
} from '@ant-design/pro-components';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import { Tag } from 'antd';
import React from 'react';
import React, { useState } from 'react';
interface IShipOrderListProps {
interface IOrderShipListProps {
ghost?: boolean;
shipOrderId?: BusinessAPI.ShipOrderVO['shipOrderId'];
orderShipId?: BusinessAPI.OrderShipVO['orderShipId'];
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;
trigger?: () => React.ReactNode;
}
export default function ShipOrderList(props: IShipOrderListProps) {
export default function OrderShipList(props: IOrderShipListProps) {
const {
ghost = false,
shipOrderId,
orderShipId,
search = true,
mode = 'page',
trigger,
onValueChange,
} = props;
const intl = useIntl();
const intlPrefix = 'shipOrder';
const intlPrefix = 'orderShip';
const columns: ProColumns<BusinessAPI.ShipOrderVO, BizValueType>[] = [
const [activeKey, setActiveKey] = useState<string>('ALL');
const columns: ProColumns<BusinessAPI.OrderShipVO, BizValueType>[] = [
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderSn' }),
dataIndex: 'orderSn',
@ -48,8 +43,11 @@ export default function ShipOrderList(props: IShipOrderListProps) {
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.vehicleNo' }),
dataIndex: 'vehicleNo',
dataIndex: ['orderVehicle', 'vehicleNo'],
key: 'vehicleNo',
render: (_, record) => (
<span> {record.orderVehicle?.vehicleNo} </span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.shippingDate' }),
@ -62,36 +60,6 @@ export default function ShipOrderList(props: IShipOrderListProps) {
dataIndex: 'state',
key: 'state',
valueType: 'select',
valueEnum: {
WAIT_PAYMENT: {
text: intl.formatMessage({
id: intlPrefix + '.column.state.waitPayment',
}),
status: 'Default',
},
PARTIAL_PAYMENT: {
text: intl.formatMessage({
id: intlPrefix + '.column.state.partialPayment',
}),
status: 'Processing',
},
FULL_PAYMENT: {
text: intl.formatMessage({
id: intlPrefix + '.column.state.fullPayment',
}),
status: 'Success',
},
REJECT_FINISH: {
text: intl.formatMessage({
id: intlPrefix + '.column.state.rejectFinish',
}),
status: 'Error',
},
FINISH: {
text: intl.formatMessage({ id: intlPrefix + '.column.state.finish' }),
status: 'Success',
},
},
render: (_, record) => {
const stateText = intl.formatMessage({
id: `${intlPrefix}.column.state.${record.state?.toLowerCase() || 'unknown'}`,
@ -99,6 +67,12 @@ export default function ShipOrderList(props: IShipOrderListProps) {
let color = 'default';
switch (record.state) {
case 'DRAFT':
color = 'default';
break;
case 'WAIT_SHIPMENT':
color = 'default';
break;
case 'WAIT_PAYMENT':
color = 'default';
break;
@ -121,13 +95,6 @@ export default function ShipOrderList(props: IShipOrderListProps) {
return <Tag color={color}>{stateText}</Tag>;
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.totalAmount' }),
dataIndex: 'totalAmount',
key: 'totalAmount',
valueType: 'money',
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.createdAt' }),
dataIndex: 'createdAt',
@ -137,107 +104,27 @@ export default function ShipOrderList(props: IShipOrderListProps) {
},
];
const formContext = [
<ProFormText
key={'orderSn'}
name={'orderSn'}
label={intl.formatMessage({ id: intlPrefix + '.form.orderSn.label' })}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.orderSn.placeholder',
})}
/>,
<ProFormText
key={'dealerName'}
name={'dealerName'}
label={intl.formatMessage({ id: intlPrefix + '.form.dealerName.label' })}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.dealerName.placeholder',
})}
/>,
<ProFormDatePicker
key={'shippingDate'}
name={'shippingDate'}
label={intl.formatMessage({
id: intlPrefix + '.form.shippingDate.label',
})}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.shippingDate.placeholder',
})}
/>,
<ProFormText
key={'vehicleNo'}
name={'vehicleNo'}
label={intl.formatMessage({ id: intlPrefix + '.form.vehicleNo.label' })}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.vehicleNo.placeholder',
})}
/>,
<ProFormSelect
key={'state'}
name={'state'}
label={intl.formatMessage({ id: intlPrefix + '.form.state.label' })}
valueEnum={{
WAIT_PAYMENT: intl.formatMessage({
id: intlPrefix + '.column.state.waitPayment',
}),
PARTIAL_PAYMENT: intl.formatMessage({
id: intlPrefix + '.column.state.partialPayment',
}),
FULL_PAYMENT: intl.formatMessage({
id: intlPrefix + '.column.state.fullPayment',
}),
REJECT_FINISH: intl.formatMessage({
id: intlPrefix + '.column.state.rejectFinish',
}),
FINISH: intl.formatMessage({ id: intlPrefix + '.column.state.finish' }),
}}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.state.placeholder',
})}
/>,
<ProFormDigit
key={'totalAmount'}
name={'totalAmount'}
label={intl.formatMessage({ id: intlPrefix + '.form.totalAmount.label' })}
fieldProps={{
precision: 2,
step: 0.01,
}}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.totalAmount.placeholder',
})}
/>,
<ProFormTextArea
key={'remark'}
name={'remark'}
label={intl.formatMessage({ id: intlPrefix + '.form.remark.label' })}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.remark.placeholder',
})}
/>,
];
const detailColumns: ProDescriptionsItemProps<
BusinessAPI.ShipOrderVO,
BusinessAPI.OrderShipVO,
BizValueType
>[] = columns as ProDescriptionsItemProps<
BusinessAPI.ShipOrderVO,
BusinessAPI.OrderShipVO,
BizValueType
>[];
return (
<BizContainer<
typeof business.shipOrder,
BusinessAPI.ShipOrderVO,
BusinessAPI.ShipOrderPageQry,
BusinessAPI.ShipOrderCreateCmd,
BusinessAPI.ShipOrderUpdateCmd
typeof business.orderShip,
BusinessAPI.OrderShipVO,
BusinessAPI.OrderShipPageQry,
BusinessAPI.OrderShipCreateCmd,
BusinessAPI.OrderShipUpdateCmd
>
rowKey={'shipOrderId'}
rowKey={'orderShipId'}
permission={'operation-ship-order'}
func={business.shipOrder}
method={'shipOrder'}
methodUpper={'ShipOrder'}
func={business.orderShip}
method={'orderShip'}
methodUpper={'OrderShip'}
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
@ -251,23 +138,79 @@ export default function ShipOrderList(props: IShipOrderListProps) {
ghost,
//@ts-ignore
search,
params: {
...(activeKey !== 'ALL' && {
state: activeKey! as BusinessAPI.OrderShipVO['state'],
}),
},
toolbar: {
menu: {
type: 'tab',
activeKey: activeKey,
items: [
// 全部
{
key: 'ALL',
label: intl.formatMessage({
id: intlPrefix + '.tab.all',
}),
},
{
key: 'DRAFT',
label: intl.formatMessage({
id: intlPrefix + '.tab.draft',
}),
},
{
key: 'WAIT_SHIPMENT',
label: intl.formatMessage({
id: intlPrefix + '.tab.waitShipment',
}),
},
{
key: 'WAIT_PAYMENT',
label: intl.formatMessage({
id: intlPrefix + '.tab.waitPayment',
}),
},
{
key: 'PARTIAL_PAYMENT',
label: intl.formatMessage({
id: intlPrefix + '.tab.partialPayment',
}),
},
{
key: 'FULL_PAYMENT',
label: intl.formatMessage({
id: intlPrefix + '.tab.fullPayment',
}),
},
{
key: 'REJECT_FINISH',
label: intl.formatMessage({
id: intlPrefix + '.tab.rejectFinish',
}),
},
{
key: 'FINISH',
label: intl.formatMessage({
id: intlPrefix + '.tab.finish',
}),
},
],
onChange: (key) => {
setActiveKey(key as string);
},
},
},
},
columns,
}}
create={{
formType: 'drawer',
formContext,
initValues: {
status: true,
},
}}
update={{
formType: 'drawer',
formContext,
}}
destroy={{}}
create={false}
update={false}
destroy={false}
detail={{
rowId: shipOrderId,
rowId: orderShipId,
formType: 'drawer',
columns: detailColumns,
trigger,

View File

@ -0,0 +1,210 @@
import { BizContainer, BizValueType, ModeType } from '@/components';
import { business } from '@/services';
import { useIntl } from '@@/exports';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import { Tag } from 'antd';
import React, { useState } from 'react';
interface IOrderSupplierListProps {
ghost?: boolean;
orderId?: BusinessAPI.PurchaseOrderVO['orderId'];
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;
trigger?: () => React.ReactNode;
}
export default function OrderSupplierList(props: IOrderSupplierListProps) {
const {
ghost = false,
orderId,
search = true,
mode = 'page',
trigger,
onValueChange,
} = props;
const intl = useIntl();
const intlPrefix = 'orderSupplier';
const [activeKey, setActiveKey] = useState<string>('ALL');
const columns: ProColumns<BusinessAPI.PurchaseOrderVO, BizValueType>[] = [
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderSn' }),
dataIndex: 'orderSn',
key: 'orderSn',
renderText: (text: string) => <span className="font-medium">{text}</span>,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.origin' }),
dataIndex: ['orderVehicle', 'origin'],
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.destination' }),
dataIndex: ['orderVehicle', 'destination'],
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.dealerName' }),
dataIndex: ['orderVehicle', 'dealerName'],
key: 'dealerName',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.plate' }),
dataIndex: ['orderVehicle', 'plate'],
key: 'plate',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.driver' }),
dataIndex: ['orderVehicle', 'driver'],
key: 'driver',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.phone' }),
dataIndex: ['orderVehicle', 'phone'],
key: 'phone',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.state' }),
dataIndex: 'state',
key: 'state',
valueType: 'select',
render: (_, record) => {
const stateText = intl.formatMessage({
id: `${intlPrefix}.column.state.${record.state?.toLowerCase() || 'unknown'}`,
});
let color = 'default';
switch (record.state) {
case 'DRAFT':
color = 'default';
break;
case 'WAITING_AUDIT':
color = 'processing';
break;
case 'COMPLETED':
color = 'success';
break;
case 'REJECTED':
color = 'error';
break;
default:
color = 'default';
}
return <Tag color={color}>{stateText}</Tag>;
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.createdAt' }),
dataIndex: 'createdAt',
key: 'createdAt',
valueType: 'dateTime',
search: false,
},
];
const detailColumns: ProDescriptionsItemProps<
BusinessAPI.PurchaseOrderVO,
BizValueType
>[] = columns as ProDescriptionsItemProps<
BusinessAPI.PurchaseOrderVO,
BizValueType
>[];
return (
<BizContainer<
typeof business.purchaseOrder,
BusinessAPI.PurchaseOrderVO,
BusinessAPI.PurchaseOrderPageQry,
BusinessAPI.PurchaseOrderCreateCmd,
BusinessAPI.PurchaseOrderUpdateCmd
>
rowKey={'orderId'}
permission={'operation-purchase-order'}
func={business.purchaseOrder}
method={'purchaseOrder'}
methodUpper={'PurchaseOrder'}
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
container={{
ghost,
}}
status={false}
page={{
fieldProps: {
bordered: true,
ghost,
//@ts-ignore
search,
params: {
...(activeKey !== 'ALL' && {
state: activeKey! as BusinessAPI.PurchaseOrderVO['state'],
}),
},
toolbar: {
menu: {
type: 'tab',
activeKey: activeKey,
items: [
// 全部
{
key: 'ALL',
label: intl.formatMessage({
id: intlPrefix + '.tab.all',
}),
},
{
key: 'DRAFT',
label: intl.formatMessage({
id: intlPrefix + '.tab.draft',
}),
},
{
key: 'WAITING_AUDIT',
label: intl.formatMessage({
id: intlPrefix + '.tab.waitingAudit',
}),
},
{
key: 'COMPLETED',
label: intl.formatMessage({
id: intlPrefix + '.tab.completed',
}),
},
{
key: 'REJECTED',
label: intl.formatMessage({
id: intlPrefix + '.tab.rejected',
}),
},
{
key: 'CLOSED',
label: intl.formatMessage({
id: intlPrefix + '.tab.closed',
}),
},
],
onChange: (key) => {
setActiveKey(key as string);
},
},
},
},
columns,
}}
create={false}
update={false}
destroy={false}
detail={{
rowId: orderId,
formType: 'drawer',
columns: detailColumns,
trigger,
}}
/>
);
}

View File

@ -0,0 +1,301 @@
import {
BizContainer,
BizValueType,
CompanyList,
ModeType,
PurchaseOrderList,
} from '@/components';
import { business } from '@/services';
import { formatBankCard, formatIdCard, formatPhone } from '@/utils/format';
import { useIntl } from '@@/exports';
import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import React, { useState } from 'react';
interface IOrderSupplierListProps {
ghost?: boolean;
orderId?: BusinessAPI.PurchaseOrderVO['orderId'];
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;
trigger?: () => React.ReactNode;
}
export default function OrderSupplierList(props: IOrderSupplierListProps) {
const {
ghost = false,
orderId,
search = true,
mode = 'page',
trigger,
onValueChange,
} = props;
const intl = useIntl();
const intlPrefix = 'orderSupplier';
const [showIdCard, setShowIdCard] = useState<Record<string, boolean>>({});
const [showBankCard, setShowBankCard] = useState<Record<string, boolean>>({});
const [showPhone, setShowPhone] = useState<Record<string, boolean>>({});
const [activeKey, setActiveKey] = useState<string>('ALL');
const columns: ProColumns<BusinessAPI.OrderSupplierVO, BizValueType>[] = [
{
title: intl.formatMessage({ id: intlPrefix + '.column.name' }),
dataIndex: 'name',
key: 'name',
},
{
title: intl.formatMessage({
id: intlPrefix + '.column.idCard',
}),
dataIndex: 'idCard',
key: 'idCard',
search: false,
render: (_, record) => (
<div className="flex items-center">
<span>
{formatIdCard(record.idCard, showIdCard[record.orderSupplierId])}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowIdCard((prev) => ({
...prev,
[record.orderSupplierId]: !prev[record.orderSupplierId],
}));
}}
>
{showIdCard[record.orderSupplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.phone' }),
dataIndex: 'phone',
key: 'phone',
search: false,
render: (_, record) => (
<div className="flex items-center">
<span>
{formatPhone(record.phone, showPhone[record.orderSupplierId])}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowPhone((prev) => ({
...prev,
[record.orderSupplierId]: !prev[record.orderSupplierId],
}));
}}
>
{showPhone[record.orderSupplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.bankCard' }),
dataIndex: 'bankCard',
key: 'bankCard',
search: false,
render: (_, record) => (
<div className="flex items-center">
<span>
{formatBankCard(
record?.bankCard || '',
showBankCard[record.orderSupplierId],
)}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowBankCard((prev) => ({
...prev,
[record.orderSupplierId]: !prev[record.orderSupplierId],
}));
}}
>
{showBankCard[record.orderSupplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.purchaseOrder' }),
dataIndex: 'purchaseOrderVO',
key: 'poOrderSn',
render: (_, record) => {
return (
<PurchaseOrderList
ghost={true}
mode={'detail'}
orderId={record.purchaseOrderVO.orderId}
trigger={() => <a href={'#'}>{record.purchaseOrderVO.orderSn}</a>}
/>
);
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderVehicle' }),
dataIndex: 'orderVehicle',
key: 'ovVehicleNo',
render: (_, record) => {
return (
<span>
{record.orderVehicle.origin} {record.orderVehicle.destination}
</span>
);
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderCompany' }),
dataIndex: 'orderCompany',
valueType: 'select',
request: async (params) => {
const { data } = await business.company.listCompany({
companyListQry: {
...params,
},
});
return (
data?.map((item) => {
return {
label: item.fullName,
value: item.companyId,
};
}) || []
);
},
render: (_, record) => {
return (
<CompanyList
ghost={true}
mode={'detail'}
companyId={record.orderCompany.companyId}
trigger={() => <a href={'#'}>{record.orderCompany.fullName}</a>}
/>
);
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.isPaid' }),
dataIndex: 'isPaid',
key: 'isPaid',
valueType: 'select',
valueEnum: {
true: {
text: intl.formatMessage({
id: intlPrefix + '.column.isPaid.paid',
}),
status: 'success',
},
false: {
text: intl.formatMessage({
id: intlPrefix + '.column.isPaid.unpaid',
}),
status: 'processing',
},
},
},
];
const detailColumns: ProDescriptionsItemProps<
BusinessAPI.OrderSupplierVO,
BizValueType
>[] = columns as ProDescriptionsItemProps<
BusinessAPI.OrderSupplierVO,
BizValueType
>[];
return (
<BizContainer<
typeof business.orderSupplier,
BusinessAPI.OrderSupplierVO,
BusinessAPI.OrderSupplierPageQry
>
rowKey={'orderSupplierId'}
permission={'operation-order-supplier'}
func={business.orderSupplier}
method={'orderSupplier'}
methodUpper={'OrderSupplier'}
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
container={{
ghost,
}}
status={false}
page={{
fieldProps: {
bordered: true,
ghost,
//@ts-ignore
search,
params: {
...(activeKey !== 'ALL' && {
isPaid: activeKey! as any,
}),
poStates: ['COMPLETED'],
},
toolbar: {
menu: {
type: 'tab',
activeKey: activeKey,
items: [
// 全部
{
key: 'ALL',
label: intl.formatMessage({
id: intlPrefix + '.tab.all',
}),
},
// 已支付
{
key: 'true',
label: intl.formatMessage({
id: intlPrefix + '.tab.paid',
}),
},
// 未支付
{
key: 'false',
label: intl.formatMessage({
id: intlPrefix + '.tab.unpaid',
}),
},
],
onChange: (key) => {
setActiveKey(key as string);
},
},
},
},
columns,
}}
create={false}
update={false}
destroy={false}
detail={{
rowId: orderId,
formType: 'drawer',
columns: detailColumns,
trigger,
}}
/>
);
}

View File

@ -0,0 +1,210 @@
import { BizContainer, BizValueType, ModeType } from '@/components';
import { business } from '@/services';
import { useIntl } from '@@/exports';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import { Tag } from 'antd';
import React, { useState } from 'react';
interface IPurchaseOrderListProps {
ghost?: boolean;
orderId?: BusinessAPI.PurchaseOrderVO['orderId'];
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;
trigger?: () => React.ReactNode;
}
export default function PurchaseOrderList(props: IPurchaseOrderListProps) {
const {
ghost = false,
orderId,
search = true,
mode = 'page',
trigger,
onValueChange,
} = props;
const intl = useIntl();
const intlPrefix = 'purchaseOrder';
const [activeKey, setActiveKey] = useState<string>('ALL');
const columns: ProColumns<BusinessAPI.PurchaseOrderVO, BizValueType>[] = [
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderSn' }),
dataIndex: 'orderSn',
key: 'orderSn',
renderText: (text: string) => <span className="font-medium">{text}</span>,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.origin' }),
dataIndex: ['orderVehicle', 'origin'],
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.destination' }),
dataIndex: ['orderVehicle', 'destination'],
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.dealerName' }),
dataIndex: ['orderVehicle', 'dealerName'],
key: 'dealerName',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.plate' }),
dataIndex: ['orderVehicle', 'plate'],
key: 'plate',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.driver' }),
dataIndex: ['orderVehicle', 'driver'],
key: 'driver',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.phone' }),
dataIndex: ['orderVehicle', 'phone'],
key: 'phone',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.state' }),
dataIndex: 'state',
key: 'state',
valueType: 'select',
render: (_, record) => {
const stateText = intl.formatMessage({
id: `${intlPrefix}.column.state.${record.state?.toLowerCase() || 'unknown'}`,
});
let color = 'default';
switch (record.state) {
case 'DRAFT':
color = 'default';
break;
case 'WAITING_AUDIT':
color = 'processing';
break;
case 'COMPLETED':
color = 'success';
break;
case 'REJECTED':
color = 'error';
break;
default:
color = 'default';
}
return <Tag color={color}>{stateText}</Tag>;
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.createdAt' }),
dataIndex: 'createdAt',
key: 'createdAt',
valueType: 'dateTime',
search: false,
},
];
const detailColumns: ProDescriptionsItemProps<
BusinessAPI.PurchaseOrderVO,
BizValueType
>[] = columns as ProDescriptionsItemProps<
BusinessAPI.PurchaseOrderVO,
BizValueType
>[];
return (
<BizContainer<
typeof business.purchaseOrder,
BusinessAPI.PurchaseOrderVO,
BusinessAPI.PurchaseOrderPageQry,
BusinessAPI.PurchaseOrderCreateCmd,
BusinessAPI.PurchaseOrderUpdateCmd
>
rowKey={'orderId'}
permission={'operation-purchase-order'}
func={business.purchaseOrder}
method={'purchaseOrder'}
methodUpper={'PurchaseOrder'}
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
container={{
ghost,
}}
status={false}
page={{
fieldProps: {
bordered: true,
ghost,
//@ts-ignore
search,
params: {
...(activeKey !== 'ALL' && {
state: activeKey! as BusinessAPI.PurchaseOrderVO['state'],
}),
},
toolbar: {
menu: {
type: 'tab',
activeKey: activeKey,
items: [
// 全部
{
key: 'ALL',
label: intl.formatMessage({
id: intlPrefix + '.tab.all',
}),
},
{
key: 'DRAFT',
label: intl.formatMessage({
id: intlPrefix + '.tab.draft',
}),
},
{
key: 'WAITING_AUDIT',
label: intl.formatMessage({
id: intlPrefix + '.tab.waitingAudit',
}),
},
{
key: 'COMPLETED',
label: intl.formatMessage({
id: intlPrefix + '.tab.completed',
}),
},
{
key: 'REJECTED',
label: intl.formatMessage({
id: intlPrefix + '.tab.rejected',
}),
},
{
key: 'CLOSED',
label: intl.formatMessage({
id: intlPrefix + '.tab.closed',
}),
},
],
onChange: (key) => {
setActiveKey(key as string);
},
},
},
},
columns,
}}
create={false}
update={false}
destroy={false}
detail={{
rowId: orderId,
formType: 'drawer',
columns: detailColumns,
trigger,
}}
/>
);
}

View File

@ -1 +1,2 @@
export { default as PurchaseOrderList } from './PurchaseOrderList';
export { default as OrderSupplierList } from './OrderSupplierList';

View File

@ -1,3 +0,0 @@
export default function PurchaseOrderList() {
return <></>;
}

View File

@ -15,7 +15,7 @@ export * from './Menu';
export * from './Permission';
export { default as PhoneContainer } from './PhoneContainer';
export * from './Platform';
export * from './Purchase';
export * from './Order';
export * from './Remark';
export * from './Role';
export * from './Setting';

View File

@ -1,36 +1,120 @@
import { LogoutOutlined, SettingOutlined } from '@ant-design/icons';
import {
LogoutOutlined,
SettingOutlined,
UserSwitchOutlined,
} from '@ant-design/icons';
import { history, useModel } from '@umijs/max';
import { Dropdown } from 'antd';
import React, { ReactNode } from 'react';
import { Dropdown, message, Modal, Radio } from 'antd';
import React, { ReactNode, useEffect, useState } from 'react';
const Avatar: React.FC<{ children: ReactNode }> = ({ children }) => {
const { logout } = useModel('useUser', (model) => ({
logout: model.logout,
}));
const [userRoleVOList, setUserRoleVOList] = useState<
BusinessAPI.UserRoleVO[]
>([]);
const [isModalVisible, setIsModalVisible] = useState(false);
const [selectedRoleId, setSelectedRoleId] = useState<string>('');
useEffect(() => {
const roles = JSON.parse(
localStorage.getItem('userRoleVOList') || '[]',
) as BusinessAPI.UserRoleVO[];
setUserRoleVOList(roles);
// 获取当前选中的角色
const currentRoleSlug = localStorage.getItem('roleSlug') || '';
const currentRole = roles.find((role) => role.slug === currentRoleSlug);
if (currentRole) {
setSelectedRoleId(currentRole.roleId);
}
}, []);
const handleSwitchRole = () => {
setIsModalVisible(true);
};
const handleModalOk = () => {
// 保存选中的角色到localStorage
const selectedRole = userRoleVOList.find(
(role) => role.roleId === selectedRoleId,
);
if (selectedRole) {
localStorage.setItem('roleSlug', selectedRole.slug);
message.success('角色切换成功,请重新加载页面');
setIsModalVisible(false);
// 重新加载页面以应用新的角色权限
window.location.reload();
}
};
const handleModalCancel = () => {
setIsModalVisible(false);
};
return (
<Dropdown
menu={{
items: [
{
key: 'setting',
icon: <SettingOutlined />,
label: '账号设置',
onClick: async () => {
history.push('/operation/settings/account');
<>
<Dropdown
menu={{
items: [
...(userRoleVOList.length > 1
? [
{
key: 'switch',
icon: <UserSwitchOutlined />,
label: '切换角色',
onClick: handleSwitchRole,
},
]
: []),
{
key: 'setting',
icon: <SettingOutlined />,
label: '账号设置',
onClick: async () => {
history.push('/operation/settings/account');
},
},
},
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
onClick: async () => logout(),
},
],
}}
>
{children}
</Dropdown>
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
onClick: async () => logout(),
},
],
}}
>
{children}
</Dropdown>
<Modal
title="切换角色"
open={isModalVisible}
onOk={handleModalOk}
onCancel={handleModalCancel}
okText="确认"
cancelText="取消"
>
<Radio.Group
onChange={(e) => setSelectedRoleId(e.target.value)}
value={selectedRoleId}
style={{ width: '100%' }}
>
<div className="space-y-2">
{userRoleVOList.map((role) => (
<div
key={role.roleId}
className="flex items-center p-2 hover:bg-gray-50 rounded"
>
<Radio value={role.roleId}>{role.name}</Radio>
</div>
))}
</div>
</Radio.Group>
</Modal>
</>
);
};

View File

@ -703,6 +703,9 @@ export default {
enableCompanyRebate: '公司返点',
'enableCompanyRebate.tooltip': '是否开启公司返点',
companyRebateRatio: '公司返点比例',
enableLoss: '产地损耗',
'enableLoss.tooltip': '是否开启产地损耗',
lossAmount: '产地损耗金额',
remark: '备注',
status: '状态',
'status.enum.enabled': '正常',
@ -762,6 +765,13 @@ export default {
label: '公司返点比例',
placeholder: '请输入公司返点比例',
},
enableLoss: {
label: '是否开启产地损耗',
},
lossAmount: {
label: '产地损耗金额',
placeholder: '请输入产地损耗金额',
},
remark: {
label: '备注',
placeholder: '请输入备注',
@ -2025,52 +2035,136 @@ export default {
},
},
},
shipOrder: {
purchaseOrder: {
tab: {
all: '全部',
draft: '草稿',
waitingAudit: '审核中',
completed: '已完成',
rejected: '已驳回',
closed: '已关闭',
},
column: {
orderSn: '采购单编号',
plate: '车牌号',
deliveryTime: '采购日期',
dealerName: '经销商名称',
frameInfo: '瓜农信息',
origin: '发货地',
destination: '收货地',
driver: '司机名称',
phone: '联系电话',
state: '状态',
'state.draft': '草稿',
'state.waiting_audit': '审核中',
'state.completed': '已完成',
'state.rejected': '已驳回',
'state.closed': '已关闭',
createdAt: '创建时间',
option: '操作',
},
modal: {
create: {
title: '创建采购单',
button: '创建采购单',
success: '创建采购单成功',
},
update: {
title: '更新采购单',
button: '编辑',
success: '更新采购单成功',
},
delete: {
success: '删除采购单成功',
button: '删除',
confirm: {
title: '确认删除',
content: '您确定要删除该采购单吗?',
okText: '确定',
cancelText: '取消',
},
},
view: {
title: '查看发货单',
button: '详情',
},
},
},
orderShip: {
tab: {
all: '全部',
draft: '草稿',
waitShipment: '待发货',
waitPayment: '待回款',
partialPayment: '部分回款',
fullPayment: '已回款',
rejectFinish: '拒收完结',
finish: '已完结',
},
column: {
orderSn: '发货单编号',
dealerName: '经销商名称',
vehicleNo: '车次号',
shippingDate: '发货日期',
state: '状态',
'state.waitPayment': '待回款',
'state.partialPayment': '部分回款',
'state.fullPayment': '已回款',
'state.rejectFinish': '拒收完结',
'state.draft': '草稿',
'state.wait_shipment': '待发货',
'state.wait_payment': '待回款',
'state.partial_payment': '部分回款',
'state.full_payment': '已回款',
'state.reject_finish': '拒收完结',
'state.finish': '已完结',
totalAmount: '合计金额(元)',
createdAt: '创建时间',
option: '操作',
},
form: {
orderSn: {
label: '发货单编号',
placeholder: '请输入发货单编号',
modal: {
create: {
title: '创建发货单',
button: '创建发货单',
success: '创建发货单成功',
},
dealerName: {
label: '经销商名称',
placeholder: '请输入经销商名称',
update: {
title: '更新发货单',
button: '编辑',
success: '更新发货单成功',
},
shippingDate: {
label: '发货日期',
placeholder: '请选择发货日期',
delete: {
success: '删除发货单成功',
button: '删除',
confirm: {
title: '确认删除',
content: '您确定要删除该发货单吗?',
okText: '确定',
cancelText: '取消',
},
},
vehicleNo: {
label: '车次号',
placeholder: '请输入车次号',
},
state: {
label: '状态',
placeholder: '请选择状态',
},
totalAmount: {
label: '合计金额(元)',
placeholder: '请输入合计金额',
},
remark: {
label: '备注',
placeholder: '请输入备注',
view: {
title: '查看发货单',
button: '详情',
},
},
},
orderSupplier: {
tab: {
all: '全部',
paid: '已付款',
unpaid: '待付款',
},
column: {
name: '瓜农姓名',
idCard: '瓜农身份证',
phone: '瓜农手机号',
bankCard: '瓜农银行卡号',
purchaseOrder: '关联采购单',
orderVehicle: '关联车辆',
orderCompany: '所属公司',
isPaid: '是否付款',
'isPaid.paid': '已付款',
'isPaid.unpaid': '待付款',
paidAt: '付款时间',
createdAt: '创建时间',
option: '操作',
},
modal: {
create: {
title: '创建发货单',

View File

@ -1,31 +1,6 @@
import auth from '@/services/auth';
import { useModel } from '@umijs/max';
import { getRedirectUrl } from '@/utils/getRedirectUrl';
const useUser = () => {
const { initialState } = useModel('@@initialState');
/**
*
* @param loginForm
*/
const passwordLogin = async (loginForm: any): Promise<void> => {
await auth.userAuth.passwordLogin(loginForm);
await getRedirectUrl();
};
const getAdminInfo = (): AuthAPI.AdminVO | undefined => {
if (initialState?.currentAdmin) {
return initialState?.currentAdmin;
} else {
window.localStorage.removeItem('user-data');
window.location.href =
process.env.UMI_APP_SSO_LOGIN_URL +
encodeURIComponent(process.env.UMI_APP_OPERATION_URL || '');
}
};
/**
* 退
*/
@ -33,7 +8,9 @@ const useUser = () => {
const response = await auth.userAuth.logout();
if (response.success) {
window.localStorage.removeItem('user-data');
window.localStorage.removeItem('employee');
window.localStorage.removeItem('admin');
window.localStorage.removeItem('roleSlug');
window.location.href =
process.env.UMI_APP_SSO_LOGIN_URL +
@ -43,8 +20,6 @@ const useUser = () => {
return {
logout,
getAdminInfo,
passwordLogin,
};
};

View File

@ -0,0 +1,5 @@
import { MelonFarmerList } from '@/components';
export default function Page() {
return <MelonFarmerList />;
}

View File

@ -0,0 +1,5 @@
import { MelonFarmerList } from '@/components';
export default function Page() {
return <MelonFarmerList />;
}

View File

@ -0,0 +1,5 @@
import { OrderSupplierList } from '@/components';
export default function Page() {
return <OrderSupplierList />;
}

View File

@ -6,9 +6,10 @@ import * as captcha from './captcha';
import * as channel from './channel';
import * as user from './user';
import * as userAuth from './userAuth';
export default {
user,
userAuth,
captcha,
user,
channel,
};

View File

@ -64,6 +64,47 @@ declare namespace AuthAPI {
wxOpenId?: string;
};
type EmployeeShowQry = {
/** 员工信息ID */
employeeId?: string;
/** 用户ID */
userId?: string;
};
type EmployeeUpdateAvatarCmd = {
/** 员工信息ID */
employeeId: string;
/** 头像 */
avatar?: string;
};
type EmployeeVO = {
/** 员工信息 */
employeeId: string;
/** 姓名 */
name: string;
/** 工号 */
number: string;
/** 头像 */
avatar?: string;
/** 性别 */
gender: number;
/** 手机号 */
phone: string;
/** 备注 */
remark?: string;
/** 状态 */
status?: boolean;
/** 创建时间 */
createdAt?: string;
/** 用户ID */
userId: string;
/** 角色ID */
roleIdList: number[];
/** 角色信息 */
userRoleList?: UserRoleVO[];
};
type getRedirectUrlParams = {
redirect: string;
};
@ -170,6 +211,13 @@ declare namespace AuthAPI {
data?: AdminVO;
};
type SingleResponseBoolean = {
success?: boolean;
errCode?: string;
errMessage?: string;
data?: boolean;
};
type SingleResponseChannelVO = {
success?: boolean;
errCode?: string;
@ -177,6 +225,13 @@ declare namespace AuthAPI {
data?: ChannelVO;
};
type SingleResponseEmployeeVO = {
success?: boolean;
errCode?: string;
errMessage?: string;
data?: EmployeeVO;
};
type SingleResponseString = {
success?: boolean;
errCode?: string;
@ -235,6 +290,10 @@ declare namespace AuthAPI {
meta: Meta;
};
type userEmployeeParams = {
employeeShowQry: EmployeeShowQry;
};
type userMenuParams = {
roleMenuTreeQry: RoleMenuTreeQry;
};
@ -246,6 +305,33 @@ declare namespace AuthAPI {
slug: string;
/** 角色ID */
roleId: string;
/** 用户ID */
userId: string;
/** 描述 */
description: string;
};
type UserUpdateCmd = {
/** 会员名称 */
name?: string;
/** 会员昵称 */
nickname?: string;
/** 头像 */
avatar?: string;
/** 会员手机号 */
phone?: string;
/** 会员生日 */
birthday?: string;
/** 会员性别 */
gender?: number;
/** 会员卡号生成方式1 系统生成 2 自定义 */
cardType?: number;
/** 备注 */
remark?: string;
/** 客户标签 */
labelId?: number[];
/** 用户ID */
userId: string;
};
type UserVO = {
@ -263,51 +349,52 @@ declare namespace AuthAPI {
birthday: string;
/** 手机号 */
phone: string;
/** 余额 */
balance: string;
/** 积分 */
point: number;
/** 备注 */
remark?: string;
/** 状态 */
state: 'ENABLE' | 'DISABLE';
/** 是否是管理员 */
isAdmin: boolean;
/** 是否是小区合伙人 */
isPartner: boolean;
/** 创建时间 */
createdAt: string;
/** 绑定手机号码时间 */
bindPhoneAt: string;
/** 最后一次登录时间 */
lastLoginAt: string;
/** 是否绑定微信 */
isBindWechat: boolean;
/** 来源渠道 */
sourceChannel:
| 'WECHAT'
| 'MANAGE_BACKEND'
| 'BOSS_END'
| 'DOUYIN'
| 'INTRODUCE';
/** 来源方式 */
sourceWay:
| 'MANUAL_ENTRY'
| 'EXTERNAL_IMPORT'
| 'NINE_GRID_LOTTERY'
| 'ENTER_COUPON_PAGE'
| 'ENTER_HOME_PAGE';
/** 自定义字段 */
customField: { empty?: boolean };
/** 上次跟进时间 */
lastFollowUpAt: string;
/** 上次跟进天数 */
userFollowUpDay: number;
/** 是否签单 */
isSign: boolean;
/** 门店id */
storeIdList?: number[];
/** 课程名称 */
spuName?: string;
/** 课程已参与次数 */
useNum?: number;
/** 微信公众号openid */
mpOpenid: string;
/** 微信小程序openid */
maOpenid: string;
/** 开放平台unionid */
unionid: string;
};
type WxMaOpenidQry = {
/** 微信code */
code: string;
};
type WxMaPhoneBindCmd = {
/** 微信code */
code: string;
/** 微信encryptedData */
encryptedData: string;
/** 微信iv */
iv: string;
};
type wxMaPhoneParams = {
wxMaPhoneQry: WxMaPhoneQry;
};
type WxMaPhoneQry = {
/** 微信code */
code: string;
/** 微信encryptedData */
encryptedData: string;
/** 微信iv */
iv: string;
};
}

View File

@ -2,6 +2,14 @@
/* eslint-disable */
import request from '../request';
/** 判断是否关注公众号 POST /auth/isSubscribe */
export async function isSubscribe(options?: { [key: string]: any }) {
return request<AuthAPI.SingleResponseBoolean>('/auth/isSubscribe', {
method: 'POST',
...(options || {}),
});
}
/** 管理员详情 GET /auth/userAdmin */
export async function userAdmin(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
@ -19,6 +27,38 @@ export async function userAdmin(
});
}
/** 员工详情 GET /auth/userEmployee */
export async function userEmployee(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: AuthAPI.userEmployeeParams,
options?: { [key: string]: any },
) {
return request<AuthAPI.SingleResponseEmployeeVO>('/auth/userEmployee', {
method: 'GET',
params: {
...params,
employeeShowQry: undefined,
...params['employeeShowQry'],
},
...(options || {}),
});
}
/** 更新员工头像 POST /auth/userEmployeeUpdateAvatar */
export async function userEmployeeUpdateAvatar(
body: AuthAPI.EmployeeUpdateAvatarCmd,
options?: { [key: string]: any },
) {
return request<AuthAPI.Response>('/auth/userEmployeeUpdateAvatar', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 用户信息 GET /auth/userInfo */
export async function userInfo(options?: { [key: string]: any }) {
return request<AuthAPI.SingleResponseUserAuthVO>('/auth/userInfo', {
@ -43,3 +83,50 @@ export async function userMenu(
...(options || {}),
});
}
/** 修改用户信息 POST /auth/userUpdate */
export async function userUpdate(
body: AuthAPI.UserUpdateCmd,
options?: { [key: string]: any },
) {
return request<AuthAPI.Response>('/auth/userUpdate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 绑定微信手机号码 POST /auth/wxMaPhone */
export async function wxMaPhone(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: AuthAPI.wxMaPhoneParams,
options?: { [key: string]: any },
) {
return request<AuthAPI.SingleResponseString>('/auth/wxMaPhone', {
method: 'POST',
params: {
...params,
wxMaPhoneQry: undefined,
...params['wxMaPhoneQry'],
},
...(options || {}),
});
}
/** 绑定微信手机号码 POST /auth/wxMaPhoneBind */
export async function wxMaPhoneBind(
body: AuthAPI.WxMaPhoneBindCmd,
options?: { [key: string]: any },
) {
return request<AuthAPI.Response>('/auth/wxMaPhoneBind', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

View File

@ -92,3 +92,18 @@ export async function updatePassword(
...(options || {}),
});
}
/** 微信登录 POST /auth/wxMaCodeLogin */
export async function wxMaCodeLogin(
body: AuthAPI.WxMaOpenidQry,
options?: { [key: string]: any },
) {
return request<AuthAPI.SingleResponseUserAuthVO>('/auth/wxMaCodeLogin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

View File

@ -22,6 +22,7 @@ import * as giftBox from './giftBox';
import * as material from './material';
import * as materialCategory from './materialCategory';
import * as menu from './menu';
import * as orderShip from './orderShip';
import * as orderSupplier from './orderSupplier';
import * as permission from './permission';
import * as platform from './platform';
@ -29,19 +30,18 @@ import * as product from './product';
import * as purchaseOrder from './purchaseOrder';
import * as role from './role';
import * as setting from './setting';
import * as shipOrder from './shipOrder';
import * as supplier from './supplier';
import * as user from './user';
export default {
user,
supplier,
shipOrder,
setting,
purchaseOrder,
product,
platform,
orderSupplier,
orderShip,
menu,
material,
materialCategory,

View File

@ -0,0 +1,168 @@
// @ts-ignore
/* eslint-disable */
import request from '../request';
/** 创建发货单 POST /operation/createOrderShip */
export async function createOrderShip(
body: BusinessAPI.OrderShipCreateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseOrderShipVO>(
'/operation/createOrderShip',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单删除 DELETE /operation/destroyOrderShip */
export async function destroyOrderShip(
body: BusinessAPI.OrderShipDestroyCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>('/operation/destroyOrderShip', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 发货单生成单据 PUT /operation/generateDocumentOrderShip */
export async function generateDocumentOrderShip(
body: BusinessAPI.OrderShipGenerateDocumentCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>(
'/operation/generateDocumentOrderShip',
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单生成单据 PATCH /operation/generateDocumentOrderShip */
export async function generateDocumentOrderShip1(
body: BusinessAPI.OrderShipGenerateDocumentCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>(
'/operation/generateDocumentOrderShip',
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单列表 GET /operation/listOrderShip */
export async function listOrderShip(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.listOrderShipParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.MultiResponseOrderShipVO>(
'/operation/listOrderShip',
{
method: 'GET',
params: {
...params,
orderShipListQry: undefined,
...params['orderShipListQry'],
},
...(options || {}),
},
);
}
/** 发货单列表 GET /operation/pageOrderShip */
export async function pageOrderShip(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.pageOrderShipParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.PageResponseOrderShipVO>(
'/operation/pageOrderShip',
{
method: 'GET',
params: {
...params,
orderShipPageQry: undefined,
...params['orderShipPageQry'],
},
...(options || {}),
},
);
}
/** 发货单详情 GET /operation/showOrderShip */
export async function showOrderShip(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.showOrderShipParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseOrderShipVO>(
'/operation/showOrderShip',
{
method: 'GET',
params: {
...params,
orderShipShowQry: undefined,
...params['orderShipShowQry'],
},
...(options || {}),
},
);
}
/** 发货单更新 PUT /operation/updateOrderShip */
export async function updateOrderShip(
body: BusinessAPI.OrderShipUpdateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseOrderShipVO>(
'/operation/updateOrderShip',
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单更新 PATCH /operation/updateOrderShip */
export async function updateOrderShip1(
body: BusinessAPI.OrderShipUpdateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseOrderShipVO>(
'/operation/updateOrderShip',
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}

View File

@ -2,6 +2,21 @@
/* eslint-disable */
import request from '../request';
/** 批量上传供应商发票 POST /operation/batchUploadInvoice */
export async function batchUploadInvoice(
body: BusinessAPI.OrderSupplierBatchInvoiceUploadCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>('/operation/batchUploadInvoice', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 订单供应商列表 GET /operation/pageOrderSupplier */
export async function pageOrderSupplier(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)

View File

@ -1,168 +0,0 @@
// @ts-ignore
/* eslint-disable */
import request from '../request';
/** 创建发货单 POST /operation/createShipOrder */
export async function createShipOrder(
body: BusinessAPI.ShipOrderCreateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseShipOrderVO>(
'/operation/createShipOrder',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单删除 DELETE /operation/destroyShipOrder */
export async function destroyShipOrder(
body: BusinessAPI.ShipOrderDestroyCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>('/operation/destroyShipOrder', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 发货单生成单据 PUT /operation/generateDocumentShipOrder */
export async function generateDocumentShipOrder(
body: BusinessAPI.ShipOrderGenerateDocumentCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>(
'/operation/generateDocumentShipOrder',
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单生成单据 PATCH /operation/generateDocumentShipOrder */
export async function generateDocumentShipOrder1(
body: BusinessAPI.ShipOrderGenerateDocumentCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.Response>(
'/operation/generateDocumentShipOrder',
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单列表 GET /operation/listShipOrder */
export async function listShipOrder(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.listShipOrderParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.MultiResponseShipOrderVO>(
'/operation/listShipOrder',
{
method: 'GET',
params: {
...params,
shipOrderListQry: undefined,
...params['shipOrderListQry'],
},
...(options || {}),
},
);
}
/** 发货单列表 GET /operation/pageShipOrder */
export async function pageShipOrder(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.pageShipOrderParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.PageResponseShipOrderVO>(
'/operation/pageShipOrder',
{
method: 'GET',
params: {
...params,
shipOrderPageQry: undefined,
...params['shipOrderPageQry'],
},
...(options || {}),
},
);
}
/** 发货单详情 GET /operation/showShipOrder */
export async function showShipOrder(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: BusinessAPI.showShipOrderParams,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseShipOrderVO>(
'/operation/showShipOrder',
{
method: 'GET',
params: {
...params,
shipOrderShowQry: undefined,
...params['shipOrderShowQry'],
},
...(options || {}),
},
);
}
/** 发货单更新 PUT /operation/updateShipOrder */
export async function updateShipOrder(
body: BusinessAPI.ShipOrderUpdateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseShipOrderVO>(
'/operation/updateShipOrder',
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}
/** 发货单更新 PATCH /operation/updateShipOrder */
export async function updateShipOrder1(
body: BusinessAPI.ShipOrderUpdateCmd,
options?: { [key: string]: any },
) {
return request<BusinessAPI.SingleResponseShipOrderVO>(
'/operation/updateShipOrder',
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
},
);
}

File diff suppressed because it is too large Load Diff

View File

@ -3,3 +3,21 @@
export const formatCurrency = (value: number) => {
return Number(value || 0)?.toLocaleString();
};
export const formatIdCard = (idCard: string, showFull: boolean = false) => {
if (showFull) return idCard;
if (idCard.length < 8) return idCard;
return `${idCard.substring(0, 4)}********${idCard.substring(idCard.length - 4)}`;
};
export const formatBankCard = (bankCard: string, showFull: boolean = false) => {
if (showFull) return bankCard;
if (bankCard.length < 4) return bankCard;
return `**** **** **** ${bankCard.substring(bankCard.length - 4)}`;
};
export const formatPhone = (phone: string, showFull: boolean = false) => {
if (showFull) return phone;
if (phone.length < 4) return phone;
return `${phone.substring(0, 3)}****${phone.substring(phone.length - 4)}`;
};

View File

@ -6,7 +6,7 @@ export default defineConfig({
'/api': {
target: 'http://api.erp.xunhong168.test/',
changeOrigin: true,
pathRewrite: { '^/': '' },
pathRewrite: { '^/api': '' },
},
},
plugins: ['@umijs/max-plugin-openapi'],

View File

@ -11,18 +11,8 @@ const useUser = () => {
await getRedirectUrl();
};
/**
*
* @param loginForm
*/
const scanLogin = async (loginForm: any) => {
await auth.userAuth.wxMpScanLogin(loginForm);
await getRedirectUrl();
};
return {
passwordLogin,
scanLogin,
};
};

View File

@ -154,7 +154,7 @@ declare namespace AuthAPI {
type RoleMenuTreeQry = {
/** 角色权限 */
roleId?: number[];
roleId?: string[];
/** 平台ID */
platformId: string;
};
@ -304,7 +304,7 @@ declare namespace AuthAPI {
/** 是否签单 */
isSign: boolean;
/** 门店id */
storeIdList?: number[];
storeIdList?: string[];
/** 课程名称 */
spuName?: string;
/** 课程已参与次数 */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long