@@ -0,0 +1,214 @@ | |||||
{ | |||||
"id": 89, | |||||
"createTime": "2023-08-21 11:43:46", | |||||
"updateTime": "2023-08-21 11:43:46", | |||||
"spuCode": "1-28X7LF", | |||||
"categoryId": 1272, | |||||
"prototypeName": "TEST男装", | |||||
"costPrice": 10.000000, | |||||
"maxSyntheticNumber": 100, | |||||
"createId": 1, | |||||
"isDelete": 2, | |||||
"categoryName": "男士短裤", | |||||
"prototypeImgs": [ | |||||
{ | |||||
"id": 412, | |||||
"prototypeId": 89, | |||||
"imgId": 1991125, | |||||
"maskImgId": 1991130, | |||||
"imgName": "3.jpg", | |||||
"imgType": 2, | |||||
"imgWidth": 600.0, | |||||
"imgHeight": 600.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A056.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 394, | |||||
"prototypeImgId": 412, | |||||
"clipHeight": 139.2, | |||||
"clipWidth": 108.0, | |||||
"clipX": 230.4, | |||||
"clipY": 318.0, | |||||
"cropBoxJson": "{\"left\":317,\"top\":264.9999694824219,\"width\":90,\"height\":116.00003051757812}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 413, | |||||
"prototypeId": 89, | |||||
"imgId": 1991122, | |||||
"maskImgId": 1991131, | |||||
"imgName": "1.jpg", | |||||
"imgType": 2, | |||||
"imgWidth": 600.0, | |||||
"imgHeight": 600.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A053.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 395, | |||||
"prototypeImgId": 413, | |||||
"clipHeight": 145.2, | |||||
"clipWidth": 116.4, | |||||
"clipX": 224.4, | |||||
"clipY": 314.4001, | |||||
"cropBoxJson": "{\"left\":312,\"top\":262.00006103515625,\"width\":97,\"height\":121}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 414, | |||||
"prototypeId": 89, | |||||
"imgId": 1991127, | |||||
"imgName": "f60911abe38e82e9dd2aaa75f3292ed2.jpg", | |||||
"imgType": 2, | |||||
"imgWidth": 1000.0, | |||||
"imgHeight": 1000.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A058.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 396, | |||||
"prototypeImgId": 414, | |||||
"clipHeight": 234.0, | |||||
"clipWidth": 192.0, | |||||
"clipX": 190.0, | |||||
"clipY": 288.0, | |||||
"cropBoxJson": "{\"left\":220,\"top\":144,\"width\":96,\"height\":117}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 415, | |||||
"prototypeId": 89, | |||||
"imgId": 1991123, | |||||
"imgName": "d247c5ec0ef78f302b803c6bf00658a2.jpg", | |||||
"imgType": 2, | |||||
"imgWidth": 1000.0, | |||||
"imgHeight": 1000.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A054.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 397, | |||||
"prototypeImgId": 415, | |||||
"clipHeight": 279.9999, | |||||
"clipWidth": 226.0, | |||||
"clipX": 380.0, | |||||
"clipY": 404.0001, | |||||
"cropBoxJson": "{\"left\":315,\"top\":202.00006103515625,\"width\":113,\"height\":139.99993896484375}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 416, | |||||
"prototypeId": 89, | |||||
"imgId": 1991129, | |||||
"imgName": "b470dfa7876630bb2b6ba9ed294b58ca.jpg", | |||||
"imgType": 1, | |||||
"imgWidth": 1000.0, | |||||
"imgHeight": 1000.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A060.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 398, | |||||
"prototypeImgId": 416, | |||||
"clipHeight": 247.9999, | |||||
"clipWidth": 200.0, | |||||
"clipX": 414.0, | |||||
"clipY": 402.0001, | |||||
"cropBoxJson": "{\"left\":332,\"top\":201.00003051757812,\"width\":100,\"height\":123.99996948242188}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 417, | |||||
"prototypeId": 89, | |||||
"imgId": 1991124, | |||||
"imgName": "e870596429cb1e46065fe25a71b29070.jpg", | |||||
"imgType": 1, | |||||
"imgWidth": 1000.0, | |||||
"imgHeight": 1000.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A055.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 399, | |||||
"prototypeImgId": 417, | |||||
"clipHeight": 282.0, | |||||
"clipWidth": 216.0, | |||||
"clipX": 408.0, | |||||
"clipY": 394.0, | |||||
"cropBoxJson": "{\"left\":329,\"top\":197,\"width\":108,\"height\":141}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 418, | |||||
"prototypeId": 89, | |||||
"imgId": 1991128, | |||||
"imgName": "067b756a187026d1af97aa78b8d7d0ba.jpg", | |||||
"imgType": 1, | |||||
"imgWidth": 1000.0, | |||||
"imgHeight": 1000.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A059.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 400, | |||||
"prototypeImgId": 418, | |||||
"clipHeight": 300.0, | |||||
"clipWidth": 230.0001, | |||||
"clipX": 389.9999, | |||||
"clipY": 384.0, | |||||
"cropBoxJson": "{\"left\":319.99993896484375,\"top\":192,\"width\":115.00006103515625,\"height\":150}" | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"id": 419, | |||||
"prototypeId": 89, | |||||
"imgId": 1991126, | |||||
"imgName": "e944022ccb44a7fb14483da12f1cf8eb.jpg", | |||||
"imgType": 1, | |||||
"imgWidth": 1000.0, | |||||
"imgHeight": 1000.0, | |||||
"imgUrl": "https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A057.jpg", | |||||
"locations": [ | |||||
{ | |||||
"id": 401, | |||||
"prototypeImgId": 419, | |||||
"clipHeight": 299.9999, | |||||
"clipWidth": 206.0, | |||||
"clipX": 400.0, | |||||
"clipY": 392.0001, | |||||
"cropBoxJson": "{\"left\":325,\"top\":196.00006103515625,\"width\":103,\"height\":149.99993896484375}" | |||||
} | |||||
] | |||||
} | |||||
], | |||||
"materialClassifys": [ | |||||
{ | |||||
"id": 20, | |||||
"createTime": "2023-04-10 17:41:00", | |||||
"updateTime": "2023-04-10 17:41:06", | |||||
"parentId": 2, | |||||
"classifyName": "海贼王", | |||||
"createId": 2, | |||||
"isDelete": 2 | |||||
}, | |||||
{ | |||||
"id": 21, | |||||
"createTime": "2023-04-10 17:41:07", | |||||
"updateTime": "2023-04-10 17:41:13", | |||||
"parentId": 2, | |||||
"classifyName": "火影忍者", | |||||
"createId": 2, | |||||
"isDelete": 2 | |||||
}, | |||||
{ | |||||
"id": 22, | |||||
"createTime": "2023-04-10 17:41:16", | |||||
"updateTime": "2023-04-10 17:41:22", | |||||
"parentId": 2, | |||||
"classifyName": "死神", | |||||
"createId": 2, | |||||
"isDelete": 2 | |||||
} | |||||
] | |||||
} |
@@ -1,7 +1,7 @@ | |||||
{ | { | ||||
"name": "vogocm-web", | "name": "vogocm-web", | ||||
"private": true, | "private": true, | ||||
"version": "0.0.0", | |||||
"version": "0.0.1", | |||||
"type": "module", | "type": "module", | ||||
"scripts": { | "scripts": { | ||||
"start": "vite", | "start": "vite", | ||||
@@ -31,6 +31,7 @@ | |||||
"nprogress": "^0.2.0", | "nprogress": "^0.2.0", | ||||
"rc-resize-observer": "^1.3.1", | "rc-resize-observer": "^1.3.1", | ||||
"react": "^18.2.0", | "react": "^18.2.0", | ||||
"react-advanced-cropper": "^0.19.3", | |||||
"react-dom": "^18.2.0", | "react-dom": "^18.2.0", | ||||
"react-router-dom": "latest", | "react-router-dom": "latest", | ||||
"react-use": "^17.4.0", | "react-use": "^17.4.0", | ||||
@@ -135,13 +135,13 @@ const Header = () => { | |||||
> | > | ||||
<div className='p-[16px]'> | <div className='p-[16px]'> | ||||
<p className='text-[16px] dark:text-[rgb(237,242,247)] text-[rgb(17,25,39)] '> | <p className='text-[16px] dark:text-[rgb(237,242,247)] text-[rgb(17,25,39)] '> | ||||
{currentUser?.userName} | |||||
{currentUser?.username} | |||||
</p> | </p> | ||||
<p className='text-[rgb(108,115,127)] dark:text-[rgb(160,174,192)] mt-[10px]'> | <p className='text-[rgb(108,115,127)] dark:text-[rgb(160,174,192)] mt-[10px]'> | ||||
{currentUser?.phoneNumber} | |||||
{currentUser?.mobile} | |||||
</p> | </p> | ||||
<p className='text-[rgb(108,115,127)] dark:text-[rgb(160,174,192)] mt-[0px]'> | <p className='text-[rgb(108,115,127)] dark:text-[rgb(160,174,192)] mt-[0px]'> | ||||
{currentUser?.emailAddress} | |||||
{currentUser?.email} | |||||
</p> | </p> | ||||
</div> | </div> | ||||
<hr style={{ borderWidth: '0 0 thin' }} className='m-[0] border-solid dark:border-[rgb(45,55,72)] border-[rgb(242,244,247)]' /> | <hr style={{ borderWidth: '0 0 thin' }} className='m-[0] border-solid dark:border-[rgb(45,55,72)] border-[rgb(242,244,247)]' /> | ||||
@@ -153,8 +153,8 @@ const Header = () => { | |||||
}} | }} | ||||
> | > | ||||
<div className='btn-icon rounded-[27px] pl-[10px] pr-[14px] justify-between h-[48px] w-[92px] text-[20px] bg-[rgb(227,242,253)] text-[rgb(30,136,229)] hover:(bg-[rgb(33,150,243)] text-[rgb(227,242,253)])'> | <div className='btn-icon rounded-[27px] pl-[10px] pr-[14px] justify-between h-[48px] w-[92px] text-[20px] bg-[rgb(227,242,253)] text-[rgb(30,136,229)] hover:(bg-[rgb(33,150,243)] text-[rgb(227,242,253)])'> | ||||
{currentUser?.avatarUrl ? ( | |||||
<Avatar style={{ verticalAlign: 'middle' }} src={currentUser.avatarUrl} /> | |||||
{currentUser?.avatar ? ( | |||||
<Avatar style={{ verticalAlign: 'middle' }} src={currentUser.avatar} /> | |||||
) : ( | ) : ( | ||||
<Avatar style={{ backgroundColor: 'gold', verticalAlign: 'middle' }} icon={<IconBuguang />} /> | <Avatar style={{ backgroundColor: 'gold', verticalAlign: 'middle' }} icon={<IconBuguang />} /> | ||||
)} | )} | ||||
@@ -80,7 +80,7 @@ const BasicLayout: React.FC = () => { | |||||
const formatedMenus = formatMenus(menus.filter(o => !o.parentId), menuGroup, routes); | const formatedMenus = formatMenus(menus.filter(o => !o.parentId), menuGroup, routes); | ||||
setMenus(formatedMenus); | setMenus(formatedMenus); | ||||
console.log(components, 'components'); | |||||
console.log('components', components); | |||||
replaceRoutes('*', [ | replaceRoutes('*', [ | ||||
...routes.map(menu => ({ | ...routes.map(menu => ({ | ||||
path: `/*${menu.path}`, | path: `/*${menu.path}`, | ||||
@@ -100,9 +100,18 @@ const BasicLayout: React.FC = () => { | |||||
path: '404', | path: '404', | ||||
name: '404', | name: '404', | ||||
}, | }, | ||||
}, | |||||
{ | |||||
path: `/*/custom/product/sample/editor`, | |||||
Component: lazy(components['/custom/product/sample/editor/index.tsx']), | |||||
id: `/*/custom/product/sample/editor`, | |||||
handle: { | |||||
parentPaths: ['/custom', '/custom/product', '/custom/product/sample'], | |||||
path: '/*/custom/product/sample/editor', | |||||
name: '蒙版编辑', | |||||
}, | |||||
} | } | ||||
]); | ]); | ||||
setLoading(false); | setLoading(false); | ||||
// replace一下当前路由,为了触发路由匹配 | // replace一下当前路由,为了触发路由匹配 | ||||
@@ -73,16 +73,15 @@ export interface Menu { | |||||
export interface User { | export interface User { | ||||
id: number; | id: number; | ||||
userName: string; | |||||
nickName: string; | |||||
phoneNumber: string; | |||||
username: string; | |||||
nickname: string; | |||||
deptId:number; | |||||
postIds: number; | |||||
mobile: string; | |||||
email: string; | email: string; | ||||
createDate: string; | |||||
updateDate: string; | |||||
avatar?: any; | |||||
menus: Menu[]; | |||||
routes: any[]; | |||||
flatMenus: Menu[]; | |||||
avatarPath: string; | |||||
authList: string[]; | |||||
sex: number; | |||||
createTime: string; | |||||
loginDate: string; | |||||
avatar?: string; | |||||
status: number; | |||||
} | } |
@@ -115,7 +115,7 @@ const MaskPictureEditor: React.FC<MaskPictureProps> = (props) => { | |||||
url: item.maskImgUrl, | url: item.maskImgUrl, | ||||
}] : [] | }] : [] | ||||
return ( | return ( | ||||
<div className='flex justify-start'> | |||||
<div className='flex justify-start' key={item.id}> | |||||
<Upload | <Upload | ||||
listType="picture-card" | listType="picture-card" | ||||
fileList={mainPicture} | fileList={mainPicture} | ||||
@@ -0,0 +1,227 @@ | |||||
import React, { FC, forwardRef, useImperativeHandle } from 'react'; | |||||
import cn from 'classnames'; | |||||
import { | |||||
CardinalDirection, | |||||
OrdinalDirection, | |||||
CropperTransitions, | |||||
CropperState, | |||||
MoveDirections, | |||||
ResizeOptions, | |||||
getStencilCoordinates, | |||||
CropperInteractions, | |||||
ResizeAnchor, | |||||
isFunction, | |||||
Coordinates, | |||||
RawAspectRatio, | |||||
createAspectRatio, | |||||
SimpleLine, | |||||
SimpleHandler, | |||||
BoundingBox, | |||||
DraggableArea, | |||||
StencilWrapper, | |||||
} from 'react-advanced-cropper'; | |||||
import { Image } from 'antd'; | |||||
type HandlerComponent = FC<any>; | |||||
type LineComponent = FC<any>; | |||||
interface HandlerClassNames extends Partial<Record<OrdinalDirection, string>> { | |||||
default?: string; | |||||
disabled?: string; | |||||
hover?: string; | |||||
} | |||||
interface LineClassNames extends Partial<Record<CardinalDirection, string>> { | |||||
default?: string; | |||||
disabled?: string; | |||||
hover?: string; | |||||
} | |||||
interface DesiredCropperRef { | |||||
getState: () => CropperState | null; | |||||
getTransitions: () => CropperTransitions; | |||||
getInteractions: () => CropperInteractions; | |||||
hasInteractions: () => boolean; | |||||
resizeCoordinates: (anchor: ResizeAnchor, directions: Partial<MoveDirections>, parameters: unknown) => void; | |||||
resizeCoordinatesEnd: () => void; | |||||
moveCoordinates: (directions: Partial<MoveDirections>) => void; | |||||
moveCoordinatesEnd: () => void; | |||||
} | |||||
interface Props { | |||||
cropper: DesiredCropperRef; | |||||
coordinates?: Coordinates | ((state: CropperState | null) => Coordinates); | |||||
handlerComponent?: HandlerComponent; | |||||
handlers?: Partial<Record<OrdinalDirection, boolean>>; | |||||
handlerClassNames?: HandlerClassNames; | |||||
handlerWrapperClassNames?: HandlerClassNames; | |||||
lines?: Partial<Record<CardinalDirection, boolean>>; | |||||
lineComponent?: LineComponent; | |||||
lineClassNames?: LineClassNames; | |||||
lineWrapperClassNames?: LineClassNames; | |||||
className?: string; | |||||
movingClassName?: string; | |||||
resizingClassName?: string; | |||||
gridClassName?: string; | |||||
previewClassName?: string; | |||||
boundingBoxClassName?: string; | |||||
overlayClassName?: string; | |||||
draggableAreaClassName?: string; | |||||
minAspectRatio?: number; | |||||
maxAspectRatio?: number; | |||||
aspectRatio?: RawAspectRatio; | |||||
movable?: boolean; | |||||
resizable?: boolean; | |||||
grid?: boolean; | |||||
} | |||||
interface Methods { | |||||
aspectRatio: RawAspectRatio; | |||||
} | |||||
export const ImageStencil = forwardRef<Methods, Props>( | |||||
( | |||||
{ | |||||
cropper, | |||||
coordinates, | |||||
aspectRatio, | |||||
minAspectRatio, | |||||
maxAspectRatio, | |||||
handlerComponent = SimpleHandler, | |||||
handlers = { | |||||
eastNorth: true, | |||||
north: true, | |||||
westNorth: true, | |||||
west: true, | |||||
westSouth: true, | |||||
south: true, | |||||
eastSouth: true, | |||||
east: true, | |||||
}, | |||||
handlerClassNames = {}, | |||||
handlerWrapperClassNames = {}, | |||||
lines = { | |||||
west: true, | |||||
north: true, | |||||
east: true, | |||||
south: true, | |||||
}, | |||||
lineComponent = SimpleLine, | |||||
lineClassNames = {}, | |||||
lineWrapperClassNames = {}, | |||||
resizable = true, | |||||
movable = true, | |||||
grid, | |||||
gridClassName, | |||||
className, | |||||
movingClassName, | |||||
resizingClassName, | |||||
previewClassName, | |||||
boundingBoxClassName, | |||||
overlayClassName, | |||||
draggableAreaClassName, | |||||
}: Props, | |||||
ref, | |||||
) => { | |||||
const state = cropper.getState(); | |||||
const transitions = cropper.getTransitions(); | |||||
const interactions = cropper.getInteractions(); | |||||
useImperativeHandle(ref, () => ({ | |||||
aspectRatio: createAspectRatio( | |||||
aspectRatio || { | |||||
minimum: minAspectRatio, | |||||
maximum: maxAspectRatio, | |||||
}, | |||||
), | |||||
})); | |||||
const onMove = (directions: MoveDirections) => { | |||||
if (cropper && movable) { | |||||
cropper.moveCoordinates(directions); | |||||
} | |||||
}; | |||||
const onMoveEnd = () => { | |||||
if (cropper) { | |||||
cropper.moveCoordinatesEnd(); | |||||
} | |||||
}; | |||||
const onResize = (anchor: ResizeAnchor, directions: MoveDirections, options: ResizeOptions) => { | |||||
if (cropper && resizable) { | |||||
cropper.resizeCoordinates(anchor, directions, options); | |||||
} | |||||
}; | |||||
const onResizeEnd = () => { | |||||
if (cropper) { | |||||
cropper.resizeCoordinatesEnd(); | |||||
} | |||||
}; | |||||
const { width, height, left, top } = coordinates | |||||
? isFunction(coordinates) | |||||
? coordinates(state) | |||||
: coordinates | |||||
: getStencilCoordinates(state); | |||||
return ( | |||||
state && ( | |||||
<StencilWrapper | |||||
className={cn( | |||||
'advanced-cropper-rectangle-stencil', | |||||
className, | |||||
interactions.moveCoordinates && movingClassName, | |||||
interactions.resizeCoordinates && resizingClassName, | |||||
{ | |||||
'advanced-cropper-rectangle-stencil--movable': movable, | |||||
'advanced-cropper-rectangle-stencil--moving': interactions.moveCoordinates, | |||||
'advanced-cropper-rectangle-stencil--resizable': resizable, | |||||
'advanced-cropper-rectangle-stencil--resizing': interactions.resizeCoordinates, | |||||
}, | |||||
)} | |||||
width={width} | |||||
height={height} | |||||
left={left} | |||||
top={top} | |||||
transitions={transitions} | |||||
> | |||||
<BoundingBox | |||||
reference={state.coordinates} | |||||
className={cn(boundingBoxClassName, 'advanced-cropper-rectangle-stencil__bounding-box')} | |||||
handlers={handlers} | |||||
handlerComponent={handlerComponent} | |||||
handlerClassNames={handlerClassNames} | |||||
handlerWrapperClassNames={handlerWrapperClassNames} | |||||
lines={lines} | |||||
lineComponent={lineComponent} | |||||
lineClassNames={lineClassNames} | |||||
lineWrapperClassNames={lineWrapperClassNames} | |||||
onResize={onResize} | |||||
onResizeEnd={onResizeEnd} | |||||
disabled={!resizable} | |||||
> | |||||
<DraggableArea | |||||
disabled={!movable} | |||||
onMove={onMove} | |||||
onMoveEnd={onMoveEnd} | |||||
className={cn('advanced-cropper-rectangle-stencil__draggable-area', draggableAreaClassName)} | |||||
> | |||||
<Image | |||||
width={width} | |||||
height={height} | |||||
preview={false} | |||||
src="https://test.vogocm.com:9696/image/material/20230413162155A010.png" | |||||
/> | |||||
</DraggableArea> | |||||
</BoundingBox> | |||||
</StencilWrapper> | |||||
) | |||||
); | |||||
}, | |||||
); | |||||
ImageStencil.displayName = 'ImageStencil'; |
@@ -0,0 +1,29 @@ | |||||
import React, { useState } from 'react'; | |||||
import { CropperRef, Cropper } from 'react-advanced-cropper'; | |||||
import 'react-advanced-cropper/dist/style.css' | |||||
import { ImageStencil } from "./components/ImageStencil"; | |||||
export default () => { | |||||
const [image] = useState( | |||||
'https://test.vogocm.com:9010/eshop/eshop_img/2023/8/21/20230821114346A053.jpg', | |||||
); | |||||
const onChange = (cropper: CropperRef) => { | |||||
console.log(cropper.getCoordinates(), cropper.getCanvas()); | |||||
}; | |||||
return ( | |||||
<Cropper | |||||
src={image} | |||||
className={'cropper'} | |||||
onChange={onChange} | |||||
stencilComponent={ImageStencil} | |||||
defaultCoordinates={{ | |||||
left: 100, | |||||
top: 100, | |||||
width: 400, | |||||
height: 400, | |||||
}} | |||||
/> | |||||
) | |||||
} |
@@ -8,6 +8,7 @@ import SampleAttrEditor from './components/attr-editor' | |||||
import MaskPictureEditor from './components/mask-picture-editor'; | import MaskPictureEditor from './components/mask-picture-editor'; | ||||
import type { SampleAttribute } from './components/attr-editor' | import type { SampleAttribute } from './components/attr-editor' | ||||
import type { MaskPicture } from './components/mask-picture-editor'; | import type { MaskPicture } from './components/mask-picture-editor'; | ||||
import { useNavigate } from 'react-router-dom'; | |||||
interface DataType { | interface DataType { | ||||
id: number; | id: number; | ||||
@@ -79,7 +80,10 @@ const TablePage: React.FC = () => { | |||||
// setEditData(record); | // setEditData(record); | ||||
setAttrEditorVisible(true); | setAttrEditorVisible(true); | ||||
}}>属性设置</a> | }}>属性设置</a> | ||||
<a>编辑</a> | |||||
<a | |||||
onClick={() => { | |||||
navigate('/custom/product/sample/editor') | |||||
}}>编辑</a> | |||||
<a>删除</a> | <a>删除</a> | ||||
</Space> | </Space> | ||||
), | ), | ||||
@@ -206,7 +210,7 @@ const TablePage: React.FC = () => { | |||||
const [attrEditorVisible, setAttrEditorVisible] = useState(false); | const [attrEditorVisible, setAttrEditorVisible] = useState(false); | ||||
const [maskEditorVisible, setMaskEditorVisible] = useState(false); | const [maskEditorVisible, setMaskEditorVisible] = useState(false); | ||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); | ||||
const navigate = useNavigate(); | |||||
const cancelHandle = () => { | const cancelHandle = () => { | ||||
setAttrEditorVisible(false); | setAttrEditorVisible(false); | ||||
}; | }; | ||||
@@ -14,6 +14,7 @@ import './index.css' | |||||
const Login = () => { | const Login = () => { | ||||
const navigate = useNavigate(); | const navigate = useNavigate(); | ||||
const { runAsync: listMenus } = useRequest(userService.listMenus, { manual: true }); | const { runAsync: listMenus } = useRequest(userService.listMenus, { manual: true }); | ||||
const { runAsync: getProfile } = useRequest(userService.getProfile, { manual: true }); | |||||
const { runAsync: login, loading } = useRequest(loginService.login, { manual: true }); | const { runAsync: login, loading } = useRequest(loginService.login, { manual: true }); | ||||
const { runAsync: rerefshToken } = useRequest(loginService.rerefshToken, { manual: true }); | const { runAsync: rerefshToken } = useRequest(loginService.rerefshToken, { manual: true }); | ||||
const { setCurrentUser } = useUserStore(); | const { setCurrentUser } = useUserStore(); | ||||
@@ -28,9 +29,10 @@ const Login = () => { | |||||
// data.data.avatarUrl = 'https://test.vogocm.com:9010/eshop/eshop_img/2023/5/24/43853633d16749bfb291f81bebb73451_20230524150631A001.jpg'; | // data.data.avatarUrl = 'https://test.vogocm.com:9010/eshop/eshop_img/2023/5/24/43853633d16749bfb291f81bebb73451_20230524150631A001.jpg'; | ||||
setRefreshToken(data.refreshToken); | setRefreshToken(data.refreshToken); | ||||
setToken(data.accessToken); | setToken(data.accessToken); | ||||
const [ _, { data: menus } ] = await listMenus() | |||||
const [ _, { data: menus } ] = await listMenus(); | |||||
const [err, {data: profile}] = await getProfile(); | |||||
// const [ error, {data: tokenData}] = await rerefshToken(data.refreshToken) | // const [ error, {data: tokenData}] = await rerefshToken(data.refreshToken) | ||||
debugger | |||||
setCurrentUser(profile) | |||||
navigate('/'); | navigate('/'); | ||||
}; | }; | ||||
@@ -13,8 +13,8 @@ import { ResponseDTO } from '@/models'; | |||||
const { apiUrl = '' } = useGlobSetting(); | const { apiUrl = '' } = useGlobSetting(); | ||||
const refreshTokenUrl = '/api/auth/refresh/token'; | |||||
const loginUrl = '/auth/login'; | |||||
const refreshTokenUrl = '/app-api/member/auth/refresh-token'; | |||||
export type Response<T> = Promise<[boolean, T, AxiosResponse<T>]>; | export type Response<T> = Promise<[boolean, T, AxiosResponse<T>]>; | ||||
@@ -70,9 +70,10 @@ class Request { | |||||
const {token} = useGlobalStore.getState(); | const {token} = useGlobalStore.getState(); | ||||
if (token) { | |||||
if (token && !axiosConfig.url?.endsWith(loginUrl)) { | |||||
axiosConfig.headers.Authorization = `Bearer ${token}`; | axiosConfig.headers.Authorization = `Bearer ${token}`; | ||||
} | } | ||||
axiosConfig.headers['tenant-id'] = '1'; | |||||
return Promise.resolve(axiosConfig); | return Promise.resolve(axiosConfig); | ||||
} | } | ||||
@@ -99,6 +100,7 @@ class Request { | |||||
this.requestingCount += 1; | this.requestingCount += 1; | ||||
const {token} = useGlobalStore.getState(); | const {token} = useGlobalStore.getState(); | ||||
config.headers.Authorization = `Bearer ${token}`; | config.headers.Authorization = `Bearer ${token}`; | ||||
config.headers['tenant-id'] = '1' | |||||
resolve(config); | resolve(config); | ||||
} | } | ||||
} | } | ||||
@@ -1,8 +1,12 @@ | |||||
import request from '@/request'; | import request from '@/request'; | ||||
import { Menu } from '@/models'; | |||||
import { Menu, User } from '@/models'; | |||||
export default { | export default { | ||||
listMenus: () => { | listMenus: () => { | ||||
return request.get<Menu>('/admin-api/system/auth/list-menus'); | return request.get<Menu>('/admin-api/system/auth/list-menus'); | ||||
}, | |||||
getProfile: () => { | |||||
return request.get<User>('/admin-api/system/user/profile/get') | |||||
} | } | ||||
}; | }; |
@@ -8,24 +8,23 @@ export const components = Object.keys(modules).reduce<Record<string, () => Promi | |||||
const formatPath = path.replace('../pages', ''); | const formatPath = path.replace('../pages', ''); | ||||
prev[formatPath] = async () => { | prev[formatPath] = async () => { | ||||
try { | try { | ||||
// 这里其实就是动态加载js,如果报错了说明js资源不存在 | |||||
return await modules[path]() as any; | return await modules[path]() as any; | ||||
} catch { | } catch { | ||||
// 如果manifest已经存在了,就不用再请求了 | |||||
if (manifest) { | |||||
try { | |||||
// 有可能manifest是过期的,所以可能还会加载失败 | |||||
return await import('/' + manifest[`src/pages${formatPath}`]?.file); | |||||
} catch { | |||||
// 如果失败,重新获取一下manifest.json,拿到最新的路径 | |||||
manifest = await (await fetch('/manifest.json')).json() as any; | |||||
return await import('/' + manifest[`src/pages${formatPath}`]?.file); | |||||
} | |||||
} else { | |||||
// 如果manifest.json为空,请求manifest.json,并根据最新的路径加载对应js | |||||
manifest = await (await fetch('/manifest.json')).json() as any; | |||||
return await import('/' + manifest[`src/pages${formatPath}`]?.file); | |||||
} | |||||
// // 如果manifest已经存在了,就不用再请求了 | |||||
// if (manifest) { | |||||
// try { | |||||
// // 有可能manifest是过期的,所以可能还会加载失败 | |||||
// return await import('/' + manifest[`src/pages${formatPath}`]?.file); | |||||
// } catch { | |||||
// // 如果失败,重新获取一下manifest.json,拿到最新的路径 | |||||
// manifest = await (await fetch('/manifest.json')).json() as any; | |||||
// return await import('/' + manifest[`src/pages${formatPath}`]?.file); | |||||
// } | |||||
// } else { | |||||
// // 如果manifest.json为空,请求manifest.json,并根据最新的路径加载对应js | |||||
// manifest = await (await fetch('/manifest.json')).json() as any; | |||||
// return await import('/' + manifest[`src/pages${formatPath}`]?.file); | |||||
// } | |||||
} | } | ||||
} | } | ||||
return prev; | return prev; | ||||
@@ -1,9 +1,9 @@ | |||||
import { UserDTO } from '@/models'; | |||||
import { User } from '@/models'; | |||||
import { create } from 'zustand'; | import { create } from 'zustand'; | ||||
import { devtools } from 'zustand/middleware'; | import { devtools } from 'zustand/middleware'; | ||||
interface State { | interface State { | ||||
currentUser: UserDTO | null; | |||||
currentUser: User | null; | |||||
} | } | ||||
interface Action { | interface Action { | ||||