Merge remote-tracking branch 'origin/master'
| | |
| | | * @name layout 插件 |
| | | * @doc https://umijs.org/docs/max/layout-menu |
| | | */ |
| | | title: 'Ant Design Pro', |
| | | title: 'zy-asrs-wcs', |
| | | layout: { |
| | | locale: true, |
| | | ...defaultSettings, |
| | |
| | | }, |
| | | esbuildMinifyIIFE: true, |
| | | requestRecord: {}, |
| | | // title logo |
| | | favicons: [ |
| | | '/favicon.jpg' |
| | | ] |
| | | }); |
| | |
| | | { |
| | | "name": "ant-design-pro", |
| | | "name": "zy-asrs-wcs", |
| | | "version": "6.0.0", |
| | | "private": true, |
| | | "description": "An out-of-box UI solution for enterprise applications", |
New file |
| | |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15" height="15" viewBox="0 0 15 15"> |
| | | <image id="图层_1" data-name="图层 1" width="15" height="15" xlink:href="data:img/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAA4ElEQVQokZ3TvUoDQRgF0OMiiqA2NmJlG0xjIWKR1spSES20SpNXMo02Kr6B4BOIAf9rO8FSJdrJJzOwSiDJ3G7hHtjduTPRvXpRyxR2sYMm5vGOB1zgHN+5PlmDKzjChr9ZwDK20EEb99GoUq2BywHwf9ZTr5nxNI6xNATmLOIEM4G3sTYizFmNfxN4f0yYs1fl9y9II/BcIZ6t0jmW5DPwYyF+CnxaiM8Cx+x6Y8LbjL9wiNcR4RsO0M/zjOFv4noIvEm9u3ioX4wYeyuNJm5V7D2O8QPP6fPi//R/2/gBn1UmkhfMfDoAAAAASUVORK5CYII="/> |
| | | </svg> |
| | |
| | | import { getRemoteMenu, getRoutersInfo, getUserInfo, setRemoteMenu, patchRouteWithRemoteMenus } from './services/route'; |
| | | import { getToken, setToken } from '@/utils/token-util' |
| | | import { TOKEN_HEADER_NAME, TOKEN_STORE_NAME } from '@/config/setting'; |
| | | import { API_BASE_URL } from '@/config/setting' |
| | | import { API_BASE_URL, API_TIMEOUT } from '@/config/setting' |
| | | import { message } from 'antd'; |
| | | |
| | | import logo from '../public/img/logo.png' |
| | |
| | | export const request = { |
| | | baseURL: API_BASE_URL, |
| | | ...errorConfig, |
| | | timeout: 60000, |
| | | timeout: API_TIMEOUT * 1000, |
| | | // 前置守卫 |
| | | requestInterceptors: [ |
| | | (url, options) => { |
| | |
| | | import { outLogin } from '@/services/ant-design-pro/api'; |
| | | import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons'; |
| | | import { history, useModel } from '@umijs/max'; |
| | | import { history, useModel, FormattedMessage, useIntl } from '@umijs/max'; |
| | | import { Spin } from 'antd'; |
| | | import { createStyles } from 'antd-style'; |
| | | import { stringify } from 'querystring'; |
| | |
| | | }; |
| | | |
| | | export const AvatarDropdown = ({ menu, children }) => { |
| | | const intl = useIntl(); |
| | | const { styles } = useStyles(); |
| | | const { initialState, setInitialState } = useModel('@@initialState'); |
| | | |
| | |
| | | { |
| | | key: 'logout', |
| | | icon: <LogoutOutlined />, |
| | | label: '退出登录', |
| | | label: intl.formatMessage({ id: 'common.account.logout', defaultMessage: '退出登录' }), |
| | | }, |
| | | ]; |
| | | |
| | |
| | | // 接口地址 |
| | | export const API_BASE_URL: string = 'http://127.0.0.1:9090/wcs'; |
| | | |
| | | export const API_TIMEOUT: number = 60; |
| | | |
| | | // 项目名称 |
| | | export const PROJECT_NAME: string = 'admin'; |
| | | |
| | |
| | | 'common.idcard':'ID Number', |
| | | 'common.introduction':'Introduction', |
| | | 'common.execute':'Execute', |
| | | 'common.success':'Success', |
| | | 'common.fail':'Fail', |
| | | 'common.account.logout': 'Logout', |
| | | 'common.search.placeholder': 'Please enter search content', |
| | | 'common.loading.api.message': 'Calling Server...', |
| | | '':'', |
| | | '':'', |
| | | '':'', |
| | |
| | | 'map.device.oper': 'Device Settings', |
| | | 'map.model.observer': 'Observer Pattern', |
| | | 'map.model.editor': 'Editor Pattern', |
| | | 'map.save': 'Save Map', |
| | | 'map.load': 'Load Map', |
| | | 'map.clear': 'Clear Map', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | | 'map.sensor.type.shelf': 'Shelf', |
| | | 'map.sensor.type.agv': 'Agv', |
| | | 'map.sensor.type.point': 'Point', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | | 'map.drawer.json': 'JSON', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | |
| | | 'map.settings.config.param': 'Config Parameters', |
| | | '': '', |
| | | '': '', |
| | | 'map.settings.no': 'No.', |
| | | 'map.settings.shelf.no': 'Shelf No', |
| | | 'map.settings.shelf.row': 'Row', |
| | | 'map.settings.shelf.bay': 'Bay', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | | 'map.settings.point.horizontal': 'Horizontal', |
| | | 'map.settings.point.vertical': 'Vertical', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | |
| | | 'map.settings.sub.copy.count': 'Count', |
| | | 'map.settings.sub.copy.gap': 'Gap', |
| | | 'map.settings.sub.copy.id': 'ID', |
| | | 'map.settings.sub.copy.shelf.auto-increment': 'Auto Increment', |
| | | 'map.settings.sub.copy.shelf.increment-value': 'Increment Value', |
| | | 'map.settings.sub.copy.increment.mode': 'Increment Mode', |
| | | 'map.settings.sub.copy.ascend': 'Ascending', |
| | | 'map.settings.sub.copy.descend': 'Descending', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | | 'map.settings.sub.copy.warn.config.shelf': 'Please set the shelf parameters first!', |
| | | '': '', |
| | | '': '', |
| | | '': '', |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Col, Form, Input, Row, Checkbox, Slider, Select, Drawer, Space, Button, InputNumber, Card } from 'antd'; |
| | | import { message, Form, Input, Row, Checkbox, Slider, Select, Drawer, Space, Button, InputNumber, Card } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../utils' |
| | |
| | | const { curSprite, configForm: form } = props; |
| | | |
| | | useEffect(() => { |
| | | }, []); |
| | | form.resetFields(); |
| | | if (curSprite) { |
| | | form.setFieldsValue({ |
| | | |
| | | // shelf |
| | | row: curSprite?.data?.row, |
| | | bay: curSprite?.data?.bay, |
| | | no: curSprite?.data?.no, |
| | | }) |
| | | } |
| | | }, [props, form]); |
| | | |
| | | const formValuesChange = (changeList) => { |
| | | if (curSprite && changeList && changeList.length > 0) { |
| | |
| | | const bay = form.getFieldValue('bay') |
| | | if (value && bay) { |
| | | form.setFieldsValue({ |
| | | shelfNo: Utils.pureNumStr(value) + '-' + Utils.pureNumStr(bay) |
| | | no: Utils.pureNumStr(value) + '-' + Utils.pureNumStr(bay) |
| | | }); |
| | | } else { |
| | | form.setFieldsValue({ |
| | | shelfNo: '' |
| | | no: '' |
| | | }); |
| | | } |
| | | break; |
| | |
| | | const row = form.getFieldValue('row') |
| | | if (value && row) { |
| | | form.setFieldsValue({ |
| | | shelfNo: Utils.pureNumStr(row) + '-' + Utils.pureNumStr(value) |
| | | no: Utils.pureNumStr(row) + '-' + Utils.pureNumStr(value) |
| | | }); |
| | | } else { |
| | | form.setFieldsValue({ |
| | | shelfNo: '' |
| | | no: '' |
| | | }); |
| | | } |
| | | break; |
| | | case 'vertical': |
| | | const horizontal = form.getFieldValue('horizontal') |
| | | if (value && horizontal) { |
| | | form.setFieldsValue({ |
| | | no: Utils.pureNumStr(value) + '-' + Utils.pureNumStr(horizontal) |
| | | }); |
| | | } else { |
| | | form.setFieldsValue({ |
| | | no: '' |
| | | }); |
| | | } |
| | | break; |
| | | case 'horizontal': |
| | | const vertical = form.getFieldValue('vertical') |
| | | if (value && vertical) { |
| | | form.setFieldsValue({ |
| | | no: Utils.pureNumStr(vertical) + '-' + Utils.pureNumStr(value) |
| | | }); |
| | | } else { |
| | | form.setFieldsValue({ |
| | | no: '' |
| | | }); |
| | | } |
| | | break; |
| | |
| | | const onFinishFailed = (errorInfo) => { |
| | | }; |
| | | |
| | | const handleFinish = async (values) => { |
| | | props.onSubmit({ ...values }); |
| | | const handleFinish = (values) => { |
| | | // execute where the form was finished |
| | | const confirmSettings = () => { |
| | | if (curSprite && curSprite?.data?.type) { |
| | | switch (curSprite.data.type) { |
| | | case Utils.SENSOR_TYPE.SHELF: |
| | | curSprite.data.no = values.no; // * |
| | | curSprite.data.row = values.row; |
| | | curSprite.data.bay = values.bay; |
| | | break; |
| | | case Utils.SENSOR_TYPE.POINT: |
| | | curSprite.data.no = values.no; // * |
| | | curSprite.data.horizontal = values.horizontal; |
| | | curSprite.data.vertical = values.vertical; |
| | | break; |
| | | case Utils.SENSOR_TYPE.AGV: |
| | | curSprite.data.no = values.no; // * |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | message.success(intl.formatMessage({ id: 'common.success', defaultMessage: '操作成功' })); |
| | | } |
| | | |
| | | props.onSubmit({ ...values }, confirmSettings); |
| | | } |
| | | |
| | | return ( |
| | |
| | | }} |
| | | > |
| | | <br /> |
| | | |
| | | |
| | | <Form.Item |
| | | label={intl.formatMessage({ id: 'map.settings.type', defaultMessage: '类型' })} |
| | | > |
| | | <span>{curSprite?.data?.type}</span> |
| | | </Form.Item> |
| | | <Form.Item |
| | | label={intl.formatMessage({ id: 'map.settings.uuid', defaultMessage: '编号' })} |
| | | label={intl.formatMessage({ id: 'map.settings.uuid', defaultMessage: '地图号' })} |
| | | > |
| | | <span>{curSprite?.data?.uuid}</span> |
| | | </Form.Item> |
| | |
| | | label={intl.formatMessage({ id: 'map.settings.shelf.row', defaultMessage: '排' })} |
| | | rules={[ |
| | | { |
| | | required: true, |
| | | required: false, |
| | | }, |
| | | ]} |
| | | > |
| | |
| | | label={intl.formatMessage({ id: 'map.settings.shelf.bay', defaultMessage: '列' })} |
| | | rules={[ |
| | | { |
| | | required: true, |
| | | required: false, |
| | | }, |
| | | ]} |
| | | > |
| | | <InputNumber |
| | | style={{ |
| | | width: '50%', |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | </> |
| | | )} |
| | | |
| | | {curSprite?.data?.type === Utils.SENSOR_TYPE.POINT && ( |
| | | <> |
| | | <Form.Item |
| | | name='vertical' |
| | | label={intl.formatMessage({ id: 'map.settings.point.vertical', defaultMessage: '纵向' })} |
| | | rules={[ |
| | | { |
| | | required: false, |
| | | }, |
| | | ]} |
| | | > |
| | |
| | | /> |
| | | </Form.Item> |
| | | <Form.Item |
| | | name='shelfNo' |
| | | label={intl.formatMessage({ id: 'map.settings.shelf.no', defaultMessage: '货架号' })} |
| | | name='horizontal' |
| | | label={intl.formatMessage({ id: 'map.settings.point.horizontal', defaultMessage: '横向' })} |
| | | rules={[ |
| | | { |
| | | required: true, |
| | | required: false, |
| | | }, |
| | | ]} |
| | | > |
| | | <Input |
| | | <InputNumber |
| | | style={{ |
| | | width: '50%', |
| | | }} |
| | |
| | | )} |
| | | |
| | | <Form.Item |
| | | name='no' |
| | | label={intl.formatMessage({ id: 'map.settings.no', defaultMessage: '编号' })} |
| | | rules={[ |
| | | { |
| | | required: false, |
| | | }, |
| | | ]} |
| | | > |
| | | <Input |
| | | style={{ |
| | | width: '50%', |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | |
| | | <Form.Item |
| | | wrapperCol={{ |
| | | offset: 4, |
| | | span: 16, |
| | | }}> |
| | | <Button type="primary" onClick={handleFinish}> |
| | | <Button type="primary" htmlType="submit"> |
| | | <FormattedMessage id='common.submit' defaultMessage='保存' /> |
| | | </Button> |
| | | </Form.Item> |
| | |
| | | |
| | | import agv from '/public/img/map/agv.svg' |
| | | import shelf from '/public/img/map/shelf.png' |
| | | import { Util } from '@antv/g6'; |
| | | import point from '/public/img/map/point.svg' |
| | | |
| | | const Device = (props) => { |
| | | const intl = useIntl(); |
| | | const { styles } = useStyles(); |
| | | const [dragging, setDragging] = useState(false); |
| | | const [dragSprite, setDragSprite] = useState(null); |
| | |
| | | rootStyle={{ position: "absolute" }} |
| | | mask={false} |
| | | width={378} |
| | | style={{ |
| | | opacity: .8 |
| | | }} |
| | | extra={ |
| | | <Space> |
| | | <Button onClick={() => props.onCancel()}><FormattedMessage id='common.cancel' defaultMessage='取消' /></Button> |
| | |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.AGV)} |
| | | /> |
| | | <div className={styles.title}>AGV</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.agv' defaultMessage='无人小车' /> |
| | | </div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.SHELF)} |
| | | /> |
| | | <div className={styles.title}>SHELF</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.shelf' defaultMessage='货架' /> |
| | | </div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Col className={styles.mapCol} span={8}> |
| | | <Image |
| | | src={agv} |
| | | src={point} |
| | | style={{ |
| | | }} |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.POINT)} |
| | | /> |
| | | <div>AGV</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.point' defaultMessage='定位点' /> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | <Row className={styles.mapRow}> |
| | |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.AGV)} |
| | | /> |
| | | <div>AGV</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.agv' defaultMessage='无人小车' /> |
| | | </div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | | src={shelf} |
| | | width='35px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.SHELF)} |
| | | /> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.shelf' defaultMessage='货架' /> |
| | | </div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.AGV)} |
| | | /> |
| | | <div>AGV</div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | | src={agv} |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | /> |
| | | <div>AGV</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.agv' defaultMessage='无人小车' /> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | <Row className={styles.mapRow}> |
| | |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.AGV)} |
| | | /> |
| | | <div>AGV</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.agv' defaultMessage='无人小车' /> |
| | | </div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | | src={shelf} |
| | | width='35px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.SHELF)} |
| | | /> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.shelf' defaultMessage='货架' /> |
| | | </div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | onDragStart={(e) => onDragStart(e, Utils.SENSOR_TYPE.AGV)} |
| | | /> |
| | | <div>AGV</div> |
| | | </Col> |
| | | <Col className={styles.mapCol} span={8} > |
| | | <Image |
| | | src={agv} |
| | | width='50px' |
| | | preview={false} |
| | | draggable="true" |
| | | onDragStart={(e) => onDragStart(e, 'AGV')} |
| | | /> |
| | | <div>AGV</div> |
| | | <div className={styles.title}> |
| | | <FormattedMessage id='map.sensor.type.agv' defaultMessage='无人小车' /> |
| | | </div> |
| | | </Col> |
| | | </Row> |
| | | </Drawer> |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Col, Form, Input, Row, Checkbox, Slider, Select, Drawer, Space, Button, InputNumber, Switch } from 'antd'; |
| | | import { Col, Form, Input, Row, Switch, Slider, message, Drawer, Space, Button, InputNumber, Segmented } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../utils' |
| | |
| | | const { styles } = useStyles(); |
| | | const { curSprite } = props; |
| | | const [form] = Form.useForm(); |
| | | const [autoIncrement, setAutoIncrement] = useState(false); |
| | | const [autoIncrementError, setAutoIncrementError] = useState(null); |
| | | |
| | | useEffect(() => { |
| | | setAutoIncrement(false); |
| | | form.resetFields(); |
| | | if (curSprite && props) { |
| | | form.setFieldsValue({ |
| | | ...props.values, |
| | | copyGap: 0 |
| | | }); |
| | | } |
| | | }, [form, props]); |
| | | |
| | | useEffect(() => { |
| | | if (autoIncrement === true && curSprite) { |
| | | switch (curSprite.data?.type) { |
| | | case Utils.SENSOR_TYPE.SHELF: |
| | | if (!curSprite.data?.row || !curSprite.data?.bay) { |
| | | setAutoIncrementError(intl.formatMessage({ id: 'map.settings.sub.copy.warn.config.shelf', defaultMessage: '请先设置货架参数!' })); |
| | | } else { |
| | | setAutoIncrementError(null); |
| | | } |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } else { |
| | | setAutoIncrementError(null); |
| | | } |
| | | }, [autoIncrement]) |
| | | |
| | | const handleCancel = () => { |
| | | props.onClose(); |
| | |
| | | } |
| | | |
| | | const handleFinish = (values) => { |
| | | props.submit({ ...values, ...props.values }) |
| | | } |
| | | |
| | | const formValuesChange = () => { |
| | | |
| | | props.submit({ |
| | | ...values |
| | | , ...props.values |
| | | , autoIncrement: autoIncrement |
| | | , type: curSprite?.data?.type |
| | | }) |
| | | } |
| | | |
| | | return ( |
| | |
| | | > |
| | | <Form |
| | | form={form} |
| | | onFieldsChange={formValuesChange} |
| | | initialValues={{ |
| | | copyGap: 0, |
| | | autoIncrement: false, |
| | | incrementMode: 'ascending', |
| | | }} |
| | | onFinish={handleFinish} |
| | | autoComplete="off" |
| | |
| | | style={{ |
| | | width: '60%', |
| | | }} |
| | | // min={0} |
| | | // min={0} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | |
| | | {curSprite?.data?.type === 'AGV' && ( |
| | | <Col span={24}> |
| | | <Form.Item |
| | | name='id' |
| | | label={intl.formatMessage({ id: 'map.settings.sub.copy.id', defaultMessage: '序号' })} |
| | | labelCol={{ span: 8 }} |
| | | > |
| | | <InputNumber |
| | | style={{ |
| | | width: '60%', |
| | | }} |
| | | min={0} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | {/* switch auto increment */} |
| | | <Col span={24}> |
| | | <Form.Item |
| | | label={intl.formatMessage({ id: 'map.settings.sub.copy.shelf.auto-increment', defaultMessage: '自增长' })} |
| | | labelCol={{ span: 8 }} |
| | | help={autoIncrementError} |
| | | validateStatus={autoIncrementError ? "error" : null} |
| | | > |
| | | <Switch value={autoIncrement} onChange={setAutoIncrement} /> |
| | | </Form.Item> |
| | | </Col> |
| | | |
| | | {autoIncrement && curSprite?.data?.type === Utils.SENSOR_TYPE.AGV && ( |
| | | <> |
| | | <Col span={24}> |
| | | <Form.Item |
| | | name='incrementValue' |
| | | label={intl.formatMessage({ id: 'map.settings.sub.copy.shelf.increment-value', defaultMessage: '自增长值' })} |
| | | labelCol={{ span: 8 }} |
| | | initialValue='no' |
| | | > |
| | | <Segmented |
| | | block |
| | | options={[ |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.no', defaultMessage: '编号' }), |
| | | value: 'no' |
| | | }, |
| | | ]} |
| | | onChange={(value) => { |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={24}> |
| | | <Form.Item |
| | | name='incrementMode' |
| | | label={intl.formatMessage({ id: 'map.settings.sub.copy.increment.mode', defaultMessage: '增长方式' })} |
| | | labelCol={{ span: 8 }} |
| | | > |
| | | <Segmented |
| | | block |
| | | options={[ |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.sub.copy.ascend', defaultMessage: '升序' }), |
| | | value: 'ascending' |
| | | }, |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.sub.copy.descend', defaultMessage: '降序' }), |
| | | value: 'descending' |
| | | }, |
| | | ]} |
| | | onChange={(value) => { |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | </> |
| | | )} |
| | | |
| | | {autoIncrement && curSprite?.data?.type === Utils.SENSOR_TYPE.SHELF && ( |
| | | <> |
| | | <Col span={24}> |
| | | <Form.Item |
| | | name='incrementValue' |
| | | label={intl.formatMessage({ id: 'map.settings.sub.copy.shelf.increment-value', defaultMessage: '自增长值' })} |
| | | labelCol={{ span: 8 }} |
| | | initialValue='row' |
| | | > |
| | | <Segmented |
| | | block |
| | | options={[ |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.shelf.row', defaultMessage: '排' }), |
| | | value: 'row' |
| | | }, |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.shelf.bay', defaultMessage: '列' }), |
| | | value: 'bay' |
| | | }, |
| | | ]} |
| | | onChange={(value) => { |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | <Col span={24}> |
| | | <Form.Item |
| | | name='incrementMode' |
| | | label={intl.formatMessage({ id: 'map.settings.sub.copy.increment.mode', defaultMessage: '增长方式' })} |
| | | labelCol={{ span: 8 }} |
| | | > |
| | | <Segmented |
| | | block |
| | | options={[ |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.sub.copy.ascend', defaultMessage: '升序' }), |
| | | value: 'ascending' |
| | | }, |
| | | { |
| | | label: intl.formatMessage({ id: 'map.settings.sub.copy.descend', defaultMessage: '降序' }), |
| | | value: 'descending' |
| | | }, |
| | | ]} |
| | | onChange={(value) => { |
| | | }} |
| | | /> |
| | | </Form.Item> |
| | | </Col> |
| | | </> |
| | | )} |
| | | |
| | | </Row> |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Col, Form, Input, Row, Checkbox, Slider, Select, Drawer, Space, Button, InputNumber, Card } from 'antd'; |
| | | import { Col, Form, Input, Row, message, Slider, Select, Drawer, Space, Button, InputNumber, Card } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../utils' |
| | |
| | | setLastCopiedSprites([]); |
| | | for (let i = 0; i < values.copyCount; i++) { |
| | | const copiedSprite = Utils.copySprite(curSprite); |
| | | // auto-increment-value |
| | | if (values.autoIncrement && values.type) { |
| | | switch (values.type) { |
| | | case Utils.SENSOR_TYPE.SHELF: |
| | | if (values.incrementValue === 'row') { |
| | | if (values.incrementMode === 'descending') { |
| | | copiedSprite.data.row = curSprite.data.row - i - 1; |
| | | } else { |
| | | copiedSprite.data.row = curSprite.data.row + i + 1; |
| | | } |
| | | } |
| | | if (values.incrementValue === 'bay') { |
| | | if (values.incrementMode === 'descending') { |
| | | copiedSprite.data.bay = curSprite.data.bay - i - 1; |
| | | } else { |
| | | copiedSprite.data.bay = curSprite.data.bay + i + 1; |
| | | } |
| | | } |
| | | if (copiedSprite.data.row && copiedSprite.data.bay) { |
| | | copiedSprite.data.no = Utils.pureNumStr(copiedSprite.data.row) + '-' + Utils.pureNumStr(copiedSprite.data.bay); |
| | | } |
| | | break; |
| | | case Utils.SENSOR_TYPE.AGV: |
| | | if (values.incrementValue === 'no') { |
| | | if (values.incrementMode === 'descending') { |
| | | copiedSprite.data.no = Number(curSprite.data.no) - i - 1; |
| | | } else { |
| | | copiedSprite.data.no = Number(curSprite.data.no) + i + 1; |
| | | } |
| | | } |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | // graph copy |
| | | switch (values.copyDire) { |
| | | case 'left': |
| | | copiedSprite.position.x -= (i + 1) * (values.copyGap + copiedSprite.width); |
| | |
| | | </Form.Item> |
| | | <Form.Item> |
| | | <Button |
| | | type="dashed" |
| | | type="link" |
| | | onClick={() => { |
| | | if (lastCopiedSprites) { |
| | | lastCopiedSprites.forEach(copiedSprite => { |
| | |
| | | <Button onClick={handleCancel}> |
| | | <FormattedMessage id='common.cancel' defaultMessage='取消' /> |
| | | </Button> |
| | | <Button hidden={activeTabKey === 'map'} onClick={handleOk} type="primary"> |
| | | <Button hidden={activeTabKey === 'map' || activeTabKey === 'config'} onClick={handleOk} type="primary"> |
| | | <FormattedMessage id='common.submit' defaultMessage='保存' /> |
| | | </Button> |
| | | </Space> |
New file |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Card, Form, Button } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../../utils' |
| | | import Http from '@/utils/http'; |
| | | import ShowJson from '../showJson'; |
| | | |
| | | const useStyles = createStyles(({ token, css }) => { |
| | | |
| | | }) |
| | | |
| | | const AgvDrawer = (props) => { |
| | | const intl = useIntl(); |
| | | const { styles } = useStyles(); |
| | | const [activeTabKey, setActiveTabKey] = useState('json'); |
| | | |
| | | const contentList = { |
| | | json: ( |
| | | <ShowJson |
| | | curSprite={props.curSprite} |
| | | /> |
| | | ), |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Card |
| | | className='drawer-card' |
| | | hoverable |
| | | bordered={false} |
| | | type='inner' |
| | | tabList={[ |
| | | { |
| | | key: 'json', |
| | | tab: intl.formatMessage({ id: 'map.drawer.json', defaultMessage: 'JSON' }), |
| | | }, |
| | | ]} |
| | | activeTabKey={activeTabKey} |
| | | onTabChange={(key) => { |
| | | setActiveTabKey(key) |
| | | }} |
| | | tabProps={{ |
| | | centered: true, |
| | | size: 'large', |
| | | type: "card", |
| | | style: { |
| | | } |
| | | }} |
| | | style={{ |
| | | height: '100%' |
| | | }} |
| | | > |
| | | {contentList[activeTabKey]} |
| | | </Card> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default AgvDrawer; |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Drawer } from 'antd'; |
| | | import { Drawer, Space, Button } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../utils' |
| | | import Http from '@/utils/http'; |
| | | import ShelfDrawer from './shelf'; |
| | | import AgvDrawer from './agv'; |
| | | import PointDrawer from './point' |
| | | |
| | | const useStyles = createStyles(({ token, css }) => { |
| | | |
| | |
| | | const MapDrawer = (props) => { |
| | | const intl = useIntl(); |
| | | const { styles } = useStyles(); |
| | | const { curSprite } = props; |
| | | |
| | | const handleCancel = () => { |
| | | props.onCancel(); |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Drawer |
| | | |
| | | /> |
| | | open={props.open} |
| | | onClose={handleCancel} |
| | | getContainer={props.refCurr} |
| | | rootStyle={{ position: "absolute" }} |
| | | mask={false} |
| | | width={600} |
| | | style={{ |
| | | opacity: .8 |
| | | }} |
| | | extra={ |
| | | <Space> |
| | | <Button onClick={handleCancel}> |
| | | <FormattedMessage id='common.cancel' defaultMessage='取消' /> |
| | | </Button> |
| | | </Space> |
| | | } |
| | | > |
| | | {props.curSprite?.data?.type === Utils.SENSOR_TYPE.SHELF && ( |
| | | <> |
| | | <ShelfDrawer |
| | | curSprite={curSprite} |
| | | /> |
| | | </> |
| | | )} |
| | | {props.curSprite?.data?.type === Utils.SENSOR_TYPE.POINT && ( |
| | | <> |
| | | <PointDrawer |
| | | curSprite={curSprite} |
| | | /> |
| | | </> |
| | | )} |
| | | {props.curSprite?.data?.type === Utils.SENSOR_TYPE.AGV && ( |
| | | <> |
| | | <AgvDrawer |
| | | curSprite={curSprite} |
| | | /> |
| | | </> |
| | | )} |
| | | </Drawer> |
| | | </> |
| | | ) |
| | | } |
New file |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Card, Form, Button } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../../utils' |
| | | import Http from '@/utils/http'; |
| | | import ShowJson from '../showJson'; |
| | | |
| | | const useStyles = createStyles(({ token, css }) => { |
| | | |
| | | }) |
| | | |
| | | const PointDrawer = (props) => { |
| | | const intl = useIntl(); |
| | | const { styles } = useStyles(); |
| | | const [activeTabKey, setActiveTabKey] = useState('json'); |
| | | |
| | | const contentList = { |
| | | json: ( |
| | | <ShowJson |
| | | curSprite={props.curSprite} |
| | | /> |
| | | ), |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Card |
| | | className='drawer-card' |
| | | hoverable |
| | | bordered={false} |
| | | type='inner' |
| | | tabList={[ |
| | | { |
| | | key: 'json', |
| | | tab: intl.formatMessage({ id: 'map.drawer.json', defaultMessage: 'JSON' }), |
| | | }, |
| | | ]} |
| | | activeTabKey={activeTabKey} |
| | | onTabChange={(key) => { |
| | | setActiveTabKey(key) |
| | | }} |
| | | tabProps={{ |
| | | centered: true, |
| | | size: 'large', |
| | | type: "card", |
| | | style: { |
| | | } |
| | | }} |
| | | style={{ |
| | | height: '100%' |
| | | }} |
| | | > |
| | | {contentList[activeTabKey]} |
| | | </Card> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default PointDrawer; |
New file |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Card, Form, Button } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../../utils' |
| | | import Http from '@/utils/http'; |
| | | import ShowJson from '../showJson'; |
| | | |
| | | const useStyles = createStyles(({ token, css }) => { |
| | | |
| | | }) |
| | | |
| | | const ShelfDrawer = (props) => { |
| | | const intl = useIntl(); |
| | | const { styles } = useStyles(); |
| | | const [activeTabKey, setActiveTabKey] = useState('json'); |
| | | |
| | | const contentList = { |
| | | json: ( |
| | | <ShowJson |
| | | curSprite={props.curSprite} |
| | | /> |
| | | ), |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <Card |
| | | className='drawer-card' |
| | | hoverable |
| | | bordered={false} |
| | | type='inner' |
| | | tabList={[ |
| | | { |
| | | key: 'json', |
| | | tab: intl.formatMessage({ id: 'map.drawer.json', defaultMessage: 'JSON' }), |
| | | }, |
| | | ]} |
| | | activeTabKey={activeTabKey} |
| | | onTabChange={(key) => { |
| | | setActiveTabKey(key) |
| | | }} |
| | | tabProps={{ |
| | | centered: true, |
| | | size: 'large', |
| | | type: "card", |
| | | style: { |
| | | } |
| | | }} |
| | | style={{ |
| | | height: '100%' |
| | | }} |
| | | > |
| | | {contentList[activeTabKey]} |
| | | </Card> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default ShelfDrawer; |
New file |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Card, Space, Button } from 'antd'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { createStyles } from 'antd-style'; |
| | | import * as Utils from '../utils' |
| | | import Http from '@/utils/http'; |
| | | |
| | | const useStyles = createStyles(({ token, css }) => { |
| | | let dark = token.colorBgBase === '#000'; |
| | | return { |
| | | jsonBox: { |
| | | height: '100%', |
| | | border: dark ? '2px solid #747d8c' : '2px solid #535c68', |
| | | borderRadius: '5px', |
| | | padding: '5px', |
| | | cursor: 'text' |
| | | }, |
| | | jsonContent: { |
| | | height: '100%', |
| | | overflowY: 'auto', |
| | | width: '100%', |
| | | border: 'none', |
| | | backgroundColor: 'transparent', |
| | | resize: 'none', |
| | | fontFamily: '"Courier New", monospace', |
| | | fontWeight: 'bold', |
| | | fontSize: '1em', |
| | | lineHeight: '1.5', |
| | | color: dark ? '#eee' : '#333', |
| | | '&:focus': { |
| | | outline: 'none' |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | |
| | | const ShowJSON = (props) => { |
| | | const { styles } = useStyles(); |
| | | const { curSprite } = props; |
| | | |
| | | const formattedJSON = JSON.stringify(curSprite.data, null, 2); |
| | | |
| | | return ( |
| | | <> |
| | | <div className={styles.jsonBox}> |
| | | <textarea |
| | | readOnly |
| | | className={styles.jsonContent} |
| | | value={formattedJSON} |
| | | /> |
| | | </div> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default ShowJSON; |
New file |
| | |
| | | import React, { useState, useRef, useEffect } from 'react'; |
| | | import { Select, AutoComplete } from 'antd'; |
| | | import { FormattedMessage, useIntl } from '@umijs/max'; |
| | | import { CloseOutlined } from '@ant-design/icons'; |
| | | import * as Utils from '../utils' |
| | | |
| | | const renderTitle = (title, uuid) => ( |
| | | <> |
| | | <span style={{ fontWeight: 'bold' }} >{title}</span> |
| | | <span style={{ float: 'right', opacity: .3 }} >{uuid}</span> |
| | | </> |
| | | ); |
| | | |
| | | const sensorTypeSelectOptionsFn = (intl) => { |
| | | let options = []; |
| | | Object.entries(Utils.SENSOR_TYPE).forEach(([key, value]) => { |
| | | switch (key) { |
| | | case Utils.SENSOR_TYPE.SHELF: |
| | | options.push({ |
| | | value: value, |
| | | label: |
| | | ( |
| | | <> |
| | | <span style={{ fontWeight: 'bold' }} >{intl.formatMessage({ id: 'map.sensor.type.shelf', defaultMessage: '货架' })}</span> |
| | | </> |
| | | ) |
| | | }) |
| | | break; |
| | | case Utils.SENSOR_TYPE.AGV: |
| | | options.push({ |
| | | value: value, |
| | | label: |
| | | ( |
| | | <> |
| | | <span style={{ fontWeight: 'bold' }} >{intl.formatMessage({ id: 'map.sensor.type.agv', defaultMessage: '无人小车' })}</span> |
| | | </> |
| | | ) |
| | | }) |
| | | break; |
| | | case Utils.SENSOR_TYPE.POINT: |
| | | options.push({ |
| | | value: value, |
| | | label: |
| | | ( |
| | | <> |
| | | <span style={{ fontWeight: 'bold' }} >{intl.formatMessage({ id: 'map.sensor.type.point', defaultMessage: '定位点' })}</span> |
| | | </> |
| | | ) |
| | | }) |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | }) |
| | | return options; |
| | | } |
| | | |
| | | function getAllSensorList(curSensorType) { |
| | | let sensorListAll = []; |
| | | Utils.getMapContainer().children.forEach(child => { |
| | | if (child?.data?.type === curSensorType && child?.data?.no) { |
| | | sensorListAll.push({ |
| | | value: child.data.no, |
| | | label: renderTitle(child.data.no, child.data.uuid) |
| | | }) |
| | | } |
| | | }); |
| | | return sensorListAll; |
| | | } |
| | | |
| | | const MapSearch = (props) => { |
| | | const intl = useIntl(); |
| | | const { |
| | | curSprite: curSensor, |
| | | setCurSPrite: setCurSensor, |
| | | setSpriteBySettings, |
| | | model, |
| | | setModel, |
| | | ModelEnum, |
| | | } = props; |
| | | |
| | | const sensorTypeSelectOptions = sensorTypeSelectOptionsFn(intl); |
| | | const [curSensorType, setCurSensorType] = React.useState(sensorTypeSelectOptions?.[0]?.value); |
| | | |
| | | const [sensorList, setSensorList] = React.useState([]); |
| | | const [filterSensorList, setFilterSensorList] = React.useState([]); |
| | | const [curSensorLabel, setCurSensorLabel] = React.useState(null); |
| | | |
| | | // first select |
| | | React.useEffect(() => { |
| | | if (!Utils.getMapContainer()) { return; } |
| | | let sensorListAll = getAllSensorList(curSensorType); |
| | | setSensorList(sensorListAll); |
| | | setFilterSensorList(sensorListAll); |
| | | setCurSensorLabel(null); |
| | | }, [curSensorType]) |
| | | |
| | | // second select |
| | | React.useEffect(() => { |
| | | if (!Utils.getMapContainer()) { return; } |
| | | if ((curSensorLabel !== null || curSensorLabel != undefined) |
| | | && sensorList && sensorList.length > 0) { |
| | | setFilterSensorList(sensorList.filter(item => item.value.includes(curSensorLabel))); |
| | | } |
| | | }, [curSensorLabel]) |
| | | |
| | | const onSecondSelect = (value, option) => { |
| | | const uuid = option.label?.props?.children?.[1].props.children; |
| | | const selectSensor = Utils.findSpriteByUuid(uuid); |
| | | if (selectSensor) { |
| | | Utils.beCenter(selectSensor); |
| | | } |
| | | switch (model) { |
| | | case ModelEnum.OBSERVER_MODEL: |
| | | setCurSensor(selectSensor); |
| | | break; |
| | | case ModelEnum.MOVABLE_MODEL: |
| | | setModel(ModelEnum.SETTINGS_MODEL); |
| | | setSpriteBySettings(selectSensor); |
| | | break; |
| | | case ModelEnum.SETTINGS_MODEL: |
| | | setSpriteBySettings(selectSensor); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return ( |
| | | <> |
| | | <Select |
| | | className='map-header-select' |
| | | variant='filled' |
| | | style={{ |
| | | width: 160, |
| | | }} |
| | | size={'large'} |
| | | options={sensorTypeSelectOptions} |
| | | value={curSensorType} |
| | | onChange={setCurSensorType} |
| | | /> |
| | | <AutoComplete |
| | | className='map-header-select' |
| | | variant='filled' |
| | | style={{ |
| | | width: 360, |
| | | }} |
| | | size={'large'} |
| | | placeholder={intl.formatMessage({ id: 'common.search.placeholder', defaultMessage: '请输入搜索内容' })} |
| | | allowClear={{ |
| | | clearIcon: <CloseOutlined /> |
| | | }} |
| | | popupMatchSelectWidth={500} |
| | | options={filterSensorList} |
| | | value={curSensorLabel} |
| | | onSelect={onSecondSelect} |
| | | onChange={setCurSensorLabel} |
| | | /> |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | export default MapSearch; |
| | |
| | | * { |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | |
| | | .map-header-select .ant-select-selector { |
| | | border-radius: 0px !important; |
| | | } |
| | | |
| | | .map-header-button { |
| | | border-radius: 0px !important; |
| | | font-weight: bolder !important; |
| | | } |
| | | |
| | | .map-header-select.ant-select .ant-select-selector .ant-select-selection-item { |
| | | font-weight: bolder !important; |
| | | } |
| | | |
| | | .map-header-select .ant-select-selector .ant-select-selection-search .ant-select-selection-search-input { |
| | | font-weight: bolder !important; |
| | | } |
| | | |
| | | .drawer-card .ant-card-body { |
| | | height: 95%; |
| | | padding: 12px; |
| | | } |
| | |
| | | import * as React from 'react' |
| | | import * as PIXI from 'pixi.js'; |
| | | import { FormattedMessage, useIntl, useModel } from '@umijs/max'; |
| | | import { Layout, Button, Flex, Row, Col, FloatButton, Select, Spin } from 'antd'; |
| | | import { Layout, Button, Flex, Row, Col, FloatButton, Select, Spin, AutoComplete } from 'antd'; |
| | | const { Header, Content } = Layout; |
| | | import { |
| | | AppstoreAddOutlined, |
| | | FileAddOutlined, |
| | | CompressOutlined, |
| | | SettingOutlined, |
| | | CloseOutlined |
| | | } from '@ant-design/icons'; |
| | | import './index.css' |
| | | import { createStyles } from 'antd-style'; |
| | |
| | | import Settings from './components/settings' |
| | | import * as Utils from './utils' |
| | | import Player from './player'; |
| | | import MapSearch from './header/search'; |
| | | import MapDrawer from './drawer'; |
| | | |
| | | const useStyles = createStyles(({ token }) => { |
| | |
| | | select: { |
| | | color: 'red', |
| | | fontWeight: 'bold', |
| | | }, |
| | | headerCol: { |
| | | paddingLeft: '50px' |
| | | } |
| | | }; |
| | | }); |
| | |
| | | const mapRef = React.useRef(); |
| | | const contentRef = React.useRef(); |
| | | |
| | | const [model, setModel] = React.useState(() => MapModel.OBSERVER_MODEL); |
| | | const [model, setModel] = React.useState(null); |
| | | const [deviceVisible, setDeviceVisible] = React.useState(false); |
| | | const [settingsVisible, setSettingsVisible] = React.useState(false); |
| | | const [windowSize, setWindowSize] = React.useState({ |
| | |
| | | const [didClickSprite, setDidClickSprite] = React.useState(false); |
| | | const [spriteBySettings, setSpriteBySettings] = React.useState(null); |
| | | const prevSpriteBySettingsRef = React.useRef(); |
| | | const [drawerVisible, setDrawerVisible] = React.useState(false); |
| | | const [dataFetched, setDataFetched] = React.useState(false); |
| | | const [curSprite, setCurSPrite] = React.useState(null); |
| | | const prevCurSpriteRef = React.useRef(); |
| | | |
| | | // init func |
| | | React.useEffect(() => { |
| | | player = new Player(mapRef.current, styles.dark, didClickSprite); |
| | | setApp(player.app); |
| | | setMapContainer(player.mapContainer); |
| | | Utils.syncApp(player.app); |
| | | Utils.syncMapContainer(player.mapContainer); |
| | | const initialize = async () => { |
| | | player = new Player(mapRef.current, styles.dark, didClickSprite); |
| | | setApp(player.app); |
| | | setMapContainer(player.mapContainer); |
| | | Utils.syncApp(player.app); |
| | | Utils.syncMapContainer(player.mapContainer); |
| | | |
| | | const handleResize = () => { |
| | | setWindowSize({ |
| | | width: window.innerWidth, |
| | | height: window.innerHeight, |
| | | }); |
| | | }; |
| | | window.addEventListener('resize', handleResize); |
| | | const handleResize = () => { |
| | | setWindowSize({ |
| | | width: window.innerWidth, |
| | | height: window.innerHeight, |
| | | }); |
| | | }; |
| | | window.addEventListener('resize', handleResize); |
| | | await Utils.fetchMapData(intl); |
| | | setDataFetched(true); |
| | | setModel(MapModel.OBSERVER_MODEL) |
| | | setTimeout(() => { |
| | | player.adaptScreen(); |
| | | }, 200) |
| | | } |
| | | initialize(); |
| | | }, []); |
| | | |
| | | // resize |
| | |
| | | const height = contentRef.current.offsetHeight; |
| | | app.renderer.resize(width, height); |
| | | if (model !== MapModel.OBSERVER_MODEL) { |
| | | player.hideGridlines(); |
| | | player.showGridlines(); |
| | | } |
| | | }, [app, mapContainer, windowSize]) |
| | | |
| | | // model |
| | | React.useEffect(() => { |
| | | if (!mapContainer) { |
| | | if (!mapContainer && !dataFetched) { |
| | | return; |
| | | } |
| | | switch (model) { |
| | |
| | | player.activateMapEvent(null); |
| | | |
| | | Utils.removeSelectedEffect(); |
| | | setCurSPrite(null); |
| | | setDeviceVisible(false); |
| | | setSettingsVisible(false); |
| | | |
| | | mapContainer.children.forEach(child => { |
| | | child.off('pointerup'); |
| | | child.off('pointermove'); |
| | | child.off('pointerdown'); |
| | | child.off('click'); |
| | | Utils.viewFeature(child, setCurSPrite); |
| | | }) |
| | | break |
| | | case MapModel.MOVABLE_MODEL: |
| | |
| | | Utils.removeSelectedEffect(); |
| | | setSpriteBySettings(null); |
| | | setSettingsVisible(false); |
| | | setDrawerVisible(false); |
| | | |
| | | mapContainer.children.forEach(child => { |
| | | Utils.beMovable(child, setDidClickSprite); |
| | |
| | | player.activateMapEvent(null); |
| | | |
| | | setDeviceVisible(false); |
| | | setDrawerVisible(false); |
| | | |
| | | mapContainer.children.forEach(child => { |
| | | Utils.beSettings(child, setSpriteBySettings, setDidClickSprite); |
| | |
| | | mapContainer.addChild(sprite); |
| | | Utils.beMovable(sprite, setDidClickSprite); |
| | | }; |
| | | |
| | | // watch curSprite |
| | | React.useEffect(() => { |
| | | if (!mapContainer) { |
| | | return; |
| | | } |
| | | prevCurSpriteRef.current = curSprite; |
| | | if (curSprite && prevCurSprite !== curSprite) { |
| | | Utils.removeSelectedEffect(); |
| | | } |
| | | if (curSprite) { |
| | | if (model === MapModel.OBSERVER_MODEL) { |
| | | Utils.showSelectedEffect(curSprite) |
| | | setDrawerVisible(true) |
| | | } |
| | | } else { |
| | | Utils.removeSelectedEffect(); |
| | | } |
| | | }, [curSprite]); |
| | | const prevCurSprite = prevCurSpriteRef.current; |
| | | |
| | | // didClickSprite, stop triggers both sprite click and play's selection boxs |
| | | React.useEffect(() => { |
| | |
| | | }, [spriteBySettings]) |
| | | const prevSpriteBySettings = prevSpriteBySettingsRef.current; |
| | | |
| | | const settingsFinish = () => { |
| | | setSettingsVisible(false); |
| | | setSpriteBySettings(null); |
| | | const settingsFinish = (values, fn) => { |
| | | fn(); |
| | | // setSettingsVisible(false); |
| | | // setSpriteBySettings(null); |
| | | } |
| | | |
| | | return ( |
| | |
| | | <Layout className={styles.layout}> |
| | | <Header className={styles.header}> |
| | | <Row style={{ height: '100%' }}> |
| | | <Col span={8} style={{ backgroundColor: '#dcdde1' }}> |
| | | |
| | | |
| | | <Select |
| | | defaultValue="agv" |
| | | style={{ |
| | | width: 120, |
| | | }} |
| | | size={'large'} |
| | | onChange={(value, option) => { |
| | | console.log(value, option); |
| | | }} |
| | | options={[ |
| | | { |
| | | value: 'agv', |
| | | label: 'agv', |
| | | }, |
| | | { |
| | | value: 'crn', |
| | | label: 'crn', |
| | | }, |
| | | ]} |
| | | /> |
| | | |
| | | <Select |
| | | |
| | | // notFoundContent={loading ? <Spin size="small" /> : null} |
| | | |
| | | /> |
| | | |
| | | |
| | | <Col className={styles.headerCol} span={12} style={{}}> |
| | | {dataFetched && ( |
| | | <MapSearch |
| | | model={model} |
| | | setModel={setModel} |
| | | ModelEnum={MapModel} |
| | | curSprite={curSprite} |
| | | setCurSPrite={setCurSPrite} |
| | | setSpriteBySettings={setSpriteBySettings} |
| | | /> |
| | | )} |
| | | </Col> |
| | | <Col span={16} style={{ backgroundColor: '#3C40C6' }}> |
| | | <Col span={12} style={{ backgroundColor: styles.dark ? '#2C3A47' : '#4a69bd' }}> |
| | | <Flex className={styles.flex} gap={'large'} justify={'flex-end'} align={'center'}> |
| | | |
| | | {model === MapModel.OBSERVER_MODEL && ( |
| | | <> |
| | | <Button |
| | | className='map-header-button' |
| | | size={'large'} |
| | | onClick={async () => { |
| | | await Utils.fetchMapData(intl); |
| | | |
| | | player.hideGridlines(); |
| | | player.hideStarryBackground(); |
| | | |
| | | player.activateMapEvent(null); |
| | | |
| | | Utils.removeSelectedEffect(); |
| | | setCurSPrite(null); |
| | | setDeviceVisible(false); |
| | | setSettingsVisible(false); |
| | | setDrawerVisible(false); |
| | | |
| | | mapContainer.children.forEach(child => { |
| | | Utils.viewFeature(child, setCurSPrite); |
| | | }) |
| | | |
| | | }} |
| | | > |
| | | <FormattedMessage id='map.load' defaultMessage='加载地图' /> |
| | | </Button> |
| | | </> |
| | | )} |
| | | |
| | | {model !== MapModel.OBSERVER_MODEL && ( |
| | | <> |
| | | <Button |
| | | className='map-header-button' |
| | | size={'large'} |
| | | onClick={() => { |
| | | Utils.clearMapData(intl); |
| | | }} |
| | | > |
| | | <FormattedMessage id='map.clear' defaultMessage='清除地图' /> |
| | | </Button> |
| | | <Button |
| | | className='map-header-button' |
| | | size={'large'} |
| | | onClick={() => { |
| | | Utils.saveMapData(intl); |
| | | }} |
| | | > |
| | | <FormattedMessage id='map.save' defaultMessage='保存地图' /> |
| | | </Button> |
| | | </> |
| | | )} |
| | | |
| | | <Select |
| | | className={styles.select} |
| | | className='map-header-select' |
| | | size={'large'} |
| | | defaultValue={MapModel.OBSERVER_MODEL} |
| | | style={{ |
| | |
| | | > |
| | | <FloatButton |
| | | icon={<CompressOutlined />} |
| | | onClick={() => { |
| | | player.adaptScreen(); |
| | | }} |
| | | /> |
| | | <FloatButton.BackTop visibilityHeight={0} /> |
| | | </FloatButton.Group> |
| | | |
| | | <FloatButton.Group |
| | |
| | | </FloatButton.Group> |
| | | </div> |
| | | </Content> |
| | | </Layout> |
| | | </Layout > |
| | | |
| | | <MapDrawer |
| | | open={drawerVisible} |
| | | curSprite={curSprite} |
| | | refCurr={mapRef.current} |
| | | onCancel={() => { |
| | | setCurSPrite(null); |
| | | setDrawerVisible(false); |
| | | }} |
| | | /> |
| | | |
| | | <Edit |
| | | open={deviceVisible} |
| | |
| | | this.activateMapScale(); |
| | | this.activateMapPan(); |
| | | this.showCoordinates(); |
| | | this.appTicker(); |
| | | this.getStartedTicker(); |
| | | } |
| | | |
| | | activateMapEvent = (leftEvent, rightEvent) => { |
| | |
| | | } |
| | | |
| | | activateMapScale = () => { |
| | | this.scale = 1; // 缩放 |
| | | this.scale = 1; |
| | | this.app.view.addEventListener('wheel', (event) => { |
| | | event.preventDefault(); |
| | | if (this.scale !== this.mapContainer.scale.x) { |
| | | this.scale = this.mapContainer.scale.x; |
| | | } |
| | | |
| | | const delta = Math.sign(event.deltaY); |
| | | |
| | | if (delta === 1) { |
| | | this.scale *= 0.9; |
| | | } else if (delta === -1) { |
| | | this.scale *= 1.1; |
| | | } |
| | | const mousePosition = new PIXI.Point(); |
| | | this.app.renderer.plugins.interaction.mapPositionToPoint(mousePosition, event.clientX, event.clientY); |
| | | |
| | | const diffPositionX = mousePosition.x - this.mapContainer.x; |
| | | const diffPositionY = mousePosition.y - this.mapContainer.y; |
| | | |
| | | const newScale = this.scale * (delta === 1 ? 0.9 : 1.1); |
| | | const scaleFactor = newScale / this.scale; |
| | | |
| | | this.mapContainer.x = mousePosition.x - diffPositionX * scaleFactor; |
| | | this.mapContainer.y = mousePosition.y - diffPositionY * scaleFactor; |
| | | |
| | | this.scale = newScale; |
| | | |
| | | this.mapContainer.scale.set(this.scale); |
| | | |
| | | this.mapContainer.children.forEach(child => { |
| | | // child.scale.set(1 / this.scale); // 防止图标变小 |
| | | }) |
| | | }); |
| | | }); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | showGridlines = () => { |
| | | this.hideGridlines(); |
| | | if (!this.gridLineContainer) { |
| | | this.gridLineContainer = generatePixiContainer('gridLineContainer'); |
| | | this.app.stage.addChild(this.gridLineContainer); |
| | | } |
| | | |
| | | const inte = 30; |
| | | const lineDefaultAlpha = .5;; |
| | | const lineDefaultAlpha = .1;; |
| | | const lineDefaultColor = 0x000000; |
| | | for (let i = 0; i < this.app.view.width / inte; i++) { |
| | | const graphics = new PIXI.Graphics(); |
| | | graphics.lineStyle(.3, lineDefaultColor, lineDefaultAlpha); |
| | | graphics.lineStyle(1, lineDefaultColor, lineDefaultAlpha); |
| | | graphics.beginFill(lineDefaultColor); |
| | | graphics.moveTo(i * inte, 0); |
| | | graphics.lineTo(i * inte, this.app.view.height); |
| | |
| | | |
| | | for (let i = 0; i < this.app.view.height / inte; i++) { |
| | | const graphics = new PIXI.Graphics(); |
| | | graphics.lineStyle(.3, lineDefaultColor, lineDefaultAlpha); |
| | | graphics.lineStyle(1, lineDefaultColor, lineDefaultAlpha); |
| | | graphics.beginFill(lineDefaultColor); |
| | | graphics.moveTo(0, i * inte); |
| | | graphics.lineTo(this.app.view.width, i * inte); |
| | |
| | | warpSpeed = warpSpeed > 0 ? 0 : 1; |
| | | }, 5000); |
| | | |
| | | |
| | | this.starryTicker = (delta) => { |
| | | speed += (warpSpeed - speed) / 20; |
| | | cameraZ += delta * 10 * (speed + baseSpeed); |
| | |
| | | } |
| | | |
| | | hideStarryBackground = () => { |
| | | if(this.starryTicker) { |
| | | if (this.starryTicker) { |
| | | this.app.ticker.remove(this.starryTicker); |
| | | this.starryTicker = null; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | adaptScreen = () => { |
| | | if (!this.mapContainer || !this.app) { |
| | | return; |
| | | } |
| | | |
| | | this.mapContainer.scale.set(1); |
| | | this.mapContainer.position.set(0, 0); |
| | | if (this.mapContainer.children.length === 0) { |
| | | return; |
| | | } |
| | | |
| | | let minX, maxX, minY, maxY; |
| | | for (let sprite of this.mapContainer.children) { |
| | | if (sprite?.data?.uuid) { |
| | | let bounds = sprite.getBounds(); |
| | | minX = minX !== undefined ? Math.min(minX, bounds.x) : bounds.x; |
| | | minY = minY !== undefined ? Math.min(minY, bounds.y) : bounds.y; |
| | | maxX = maxX !== undefined ? Math.max(maxX, bounds.x + bounds.width) : bounds.x + bounds.width; |
| | | maxY = maxY !== undefined ? Math.max(maxY, bounds.y + bounds.height) : bounds.y + bounds.height; |
| | | } |
| | | } |
| | | |
| | | this.scale = Math.min( |
| | | this.app.renderer.width / (maxX - minX) * 0.8, |
| | | this.app.renderer.height / (maxY - minY) * 0.8 |
| | | ); |
| | | |
| | | let centerPoint = { |
| | | x: (minX + maxX) / 2 * this.mapContainer.scale.x, |
| | | y: (minY + maxY) / 2 * this.mapContainer.scale.y |
| | | }; |
| | | |
| | | new TWEEDLE.Tween(this.mapContainer.scale).easing(TWEEDLE.Easing.Quadratic.Out) |
| | | .to({ |
| | | x: this.scale, |
| | | y: this.scale |
| | | }, 200).start(); |
| | | |
| | | new TWEEDLE.Tween(this.mapContainer.position).easing(TWEEDLE.Easing.Quadratic.Out) |
| | | .to({ |
| | | x: this.app.renderer.width / 2 - centerPoint.x * this.scale, |
| | | y: this.app.renderer.height / 2 - centerPoint.y * this.scale |
| | | }, 200).start(); |
| | | } |
| | | |
| | | updateDidClickSprite = (value) => { |
| | | this.didClickSprite = value; |
| | | } |
| | | |
| | | appTicker = () => { |
| | | TWEEDLE.Group.shared.update(); |
| | | getStartedTicker = () => { |
| | | this.app.ticker.add((delta) => { |
| | | TWEEDLE.Group.shared.update(); |
| | | }); |
| | | } |
| | | |
| | | } |
| | |
| | | import * as PIXI from 'pixi.js'; |
| | | import * as TWEEDLE from 'tweedle.js'; |
| | | import Http from '@/utils/http'; |
| | | import { message } from 'antd'; |
| | | import { API_TIMEOUT } from '@/config/setting' |
| | | import agv from '/public/img/map/agv.svg' |
| | | import shelf from '/public/img/map/shelf.png' |
| | | import point from '/public/img/map/point.svg' |
| | | |
| | | let app = null; |
| | | let mapContainer = null; |
| | |
| | | }) |
| | | |
| | | export const SENSOR_TYPE = Object.freeze({ |
| | | AGV: "AGV", |
| | | SHELF: "SHELF", |
| | | POINT: "POINT", |
| | | AGV: "AGV", |
| | | }) |
| | | |
| | | export const getRealPosition = (x, y, mapContainer) => { |
| | |
| | | }; |
| | | } |
| | | |
| | | // show sprite feature from sprite click event |
| | | export const viewFeature = (sprite, setCurSPrite) => { |
| | | sprite.off('pointerup'); |
| | | sprite.off('pointermove'); |
| | | sprite.off('pointerdown'); |
| | | sprite.off('click'); |
| | | |
| | | sprite.on("click", onClick); |
| | | |
| | | function onClick(event) { |
| | | setCurSPrite(sprite); |
| | | } |
| | | |
| | | } |
| | | |
| | | // sprite be movable from sprite click event |
| | | export const beMovable = (sprite, setDidClickSprite) => { |
| | | sprite.off('pointerup'); |
| | |
| | | |
| | | let dragTarget; |
| | | function onDragStart(event) { |
| | | setDidClickSprite(true); |
| | | dragTarget = event.currentTarget; |
| | | mapContainer.parent.off('pointermove'); |
| | | mapContainer.parent.on('pointermove', onDragMove, dragTarget); |
| | | if (event.button === 0) { |
| | | setDidClickSprite(true); |
| | | dragTarget = event.currentTarget; |
| | | mapContainer.parent.off('pointermove'); |
| | | mapContainer.parent.on('pointermove', onDragMove, dragTarget); |
| | | |
| | | mapContainer.parent.off('pointerup'); |
| | | mapContainer.parent.on('pointerup', onDragEnd.bind(mapContainer)); |
| | | mapContainer.parent.off('pointerup'); |
| | | mapContainer.parent.on('pointerup', onDragEnd.bind(mapContainer)); |
| | | } |
| | | } |
| | | |
| | | function onDragMove(event) { |
| | |
| | | } else { |
| | | return ''; |
| | | } |
| | | } |
| | | |
| | | export const rotationToNum = (rotation) => { |
| | | let res = rotation * 180 / Math.PI; |
| | | if (res < 0) { |
| | | res += 360; |
| | | } else if (res > 360) { |
| | | res -= 360; |
| | | } |
| | | return res; |
| | | } |
| | | |
| | | export const rotationParseNum = (num) => { |
| | | return num * Math.PI / 180; |
| | | } |
| | | |
| | | export const findSpriteByUuid = (uuid) => { |
| | | return mapContainer?.children?.find(child => child?.data?.uuid === uuid); |
| | | } |
| | | |
| | | export const sensorTypeSelectOptions = (intl) => { |
| | | let options = []; |
| | | Object.entries(SENSOR_TYPE).forEach(([key, value]) => { |
| | | switch (key) { |
| | | case SENSOR_TYPE.SHELF: |
| | | options.push({ |
| | | value: value, |
| | | label: intl.formatMessage({ id: 'map.sensor.type.shelf', defaultMessage: '货架' }) |
| | | }) |
| | | break; |
| | | case SENSOR_TYPE.AGV: |
| | | options.push({ |
| | | value: value, |
| | | label: intl.formatMessage({ id: 'map.sensor.type.agv', defaultMessage: '无人小车' }) |
| | | }) |
| | | break; |
| | | case SENSOR_TYPE.POINT: |
| | | options.push({ |
| | | value: value, |
| | | label: intl.formatMessage({ id: 'map.sensor.type.point', defaultMessage: '定位点' }) |
| | | }) |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | |
| | | }) |
| | | return options; |
| | | } |
| | | |
| | | export const fetchMapData = async (intl) => { |
| | | clearMapData(); |
| | | await Http.doPostPromise('api/map/list', {}, (res) => { |
| | | const mapItemList = res.data.itemList; |
| | | mapItemList.forEach(item => { |
| | | let sprite; |
| | | switch (item.type) { |
| | | case SENSOR_TYPE.SHELF: |
| | | sprite = PIXI.Sprite.from(shelf); |
| | | break; |
| | | case SENSOR_TYPE.AGV: |
| | | sprite = PIXI.Sprite.from(agv); |
| | | break; |
| | | case SENSOR_TYPE.POINT: |
| | | sprite = PIXI.Sprite.from(point); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | if (sprite) { |
| | | initSprite(sprite, item.type); |
| | | // data |
| | | sprite.data.uuid = item.uuid; |
| | | sprite.data.no = item.no; |
| | | |
| | | // graph |
| | | sprite.position.set(item.positionX, item.positionY); |
| | | sprite.scale.set(item.scaleX, item.scaleY); |
| | | sprite.rotation = rotationParseNum(item.rotation); |
| | | mapContainer.addChild(sprite); |
| | | } |
| | | }) |
| | | |
| | | }).catch((error) => { |
| | | console.error(error); |
| | | }) |
| | | |
| | | } |
| | | |
| | | export const saveMapData = async (intl) => { |
| | | if (!mapContainer) { |
| | | return; |
| | | } |
| | | |
| | | let mapItemList = []; |
| | | mapContainer?.children.forEach(child => { |
| | | if (child.data?.uuid) { |
| | | mapItemList.push({ |
| | | // data |
| | | type: child.data.type, |
| | | uuid: child.data.uuid, |
| | | no: child.data.no, |
| | | |
| | | // graph |
| | | positionX: child.position.x, |
| | | positionY: child.position.y, |
| | | scaleX: child.scale.x, |
| | | scaleY: child.scale.y, |
| | | rotation: rotationToNum(child.rotation) |
| | | }) |
| | | } |
| | | }) |
| | | |
| | | const closeLoading = message.loading({ content: intl.formatMessage({ id: 'common.loading.api.message', defaultMessage: '等待服务器......' }), duration: API_TIMEOUT }); |
| | | await Http.doPostPromise('api/map/save', { itemList: mapItemList }, (res) => { |
| | | closeLoading(); |
| | | }).catch((error) => { |
| | | closeLoading(); |
| | | console.error(error); |
| | | }) |
| | | } |
| | | |
| | | export const clearMapData = (intl) => { |
| | | if (!mapContainer) { |
| | | return; |
| | | } |
| | | let childList = []; |
| | | mapContainer.children.forEach(child => { |
| | | if (child.data?.uuid) { |
| | | childList.push(child); |
| | | } |
| | | }) |
| | | if (childList.length > 0) { |
| | | childList.forEach(child => { |
| | | mapContainer.removeChild(child); |
| | | child.destroy({ children: true, texture: false, baseTexture: false }); |
| | | }) |
| | | childList.forEach((child, index) => { |
| | | childList[index] = null; |
| | | }); |
| | | childList = []; |
| | | } |
| | | } |
| | | |
| | | export const beCenter = (sprite) => { |
| | | if (!sprite || !app || !mapContainer) { |
| | | return; |
| | | } |
| | | |
| | | mapContainer.scale.set(1); |
| | | mapContainer.position.set(0, 0); |
| | | |
| | | let bounds = sprite.getBounds(); |
| | | let centerPoint = { |
| | | x: bounds.x + bounds.width / 2, |
| | | y: bounds.y + bounds.height / 2 |
| | | }; |
| | | |
| | | let targetPos = { |
| | | x: app.renderer.width / 3 - centerPoint.x * mapContainer.scale.x, |
| | | y: app.renderer.height / 3 - centerPoint.y * mapContainer.scale.y |
| | | }; |
| | | |
| | | new TWEEDLE.Tween(mapContainer.position).easing(TWEEDLE.Easing.Quadratic.Out) |
| | | .to(targetPos, 500).start(); |
| | | } |
New file |
| | |
| | | package com.zy.asrs.wcs.core.map.controller; |
| | | |
| | | import com.zy.asrs.framework.common.R; |
| | | import com.zy.asrs.wcs.core.map.controller.param.MapDataParam; |
| | | import com.zy.asrs.wcs.core.map.service.MapService; |
| | | import com.zy.asrs.wcs.system.controller.BaseController; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * Created by vincent on 3/15/2024 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/map") |
| | | public class MapController extends BaseController { |
| | | |
| | | @Autowired |
| | | private MapService mapService; |
| | | |
| | | // @PreAuthorize("hasAuthority('core:map:list')") |
| | | @PostMapping("/list") |
| | | public R mapList() { |
| | | return R.ok().add(mapService.getMapData(getLoginUserId())); |
| | | } |
| | | |
| | | // @PreAuthorize("hasAuthority('core:map:save')") |
| | | @PostMapping("/save") |
| | | @Transactional |
| | | public R mapSave(@RequestBody MapDataParam param) { |
| | | mapService.saveMapData(param, getLoginUserId()); |
| | | return R.ok(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.zy.asrs.wcs.core.map.controller.param; |
| | | |
| | | import com.zy.asrs.wcs.core.map.entity.MapItem; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * Created by vincent on 3/15/2024 |
| | | */ |
| | | @Data |
| | | public class MapDataParam { |
| | | |
| | | public List<MapItem> itemList; |
| | | |
| | | } |
New file |
| | |
| | | package com.zy.asrs.wcs.core.map.entity; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * Created by vincent on 3/15/2024 |
| | | */ |
| | | @Data |
| | | public class MapItem { |
| | | |
| | | private String type; |
| | | |
| | | private String uuid; |
| | | |
| | | private String no; |
| | | |
| | | |
| | | private Double positionX; |
| | | |
| | | private Double positionY; |
| | | |
| | | private Double scaleX; |
| | | |
| | | private Double scaleY; |
| | | |
| | | private Double rotation; |
| | | |
| | | } |
New file |
| | |
| | | package com.zy.asrs.wcs.core.map.service; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.zy.asrs.framework.common.Cools; |
| | | import com.zy.asrs.framework.exception.CoolException; |
| | | import com.zy.asrs.wcs.core.map.controller.param.MapDataParam; |
| | | import com.zy.asrs.wcs.system.entity.User; |
| | | import com.zy.asrs.wcs.system.service.UserService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | /** |
| | | * Created by vincent on 3/15/2024 |
| | | */ |
| | | @Service |
| | | public class MapService { |
| | | |
| | | @Autowired |
| | | private UserService userService; |
| | | |
| | | public MapDataParam getMapData(Long userId) { |
| | | User user = userService.getById(userId); |
| | | if (Cools.isEmpty(user.getMemo())) { |
| | | return new MapDataParam(); |
| | | } |
| | | return JSON.parseObject(user.getMemo(), MapDataParam.class); |
| | | } |
| | | |
| | | public void saveMapData(MapDataParam param, Long userId) { |
| | | User user = userService.getById(userId); |
| | | user.setMemo(JSON.toJSONString(param)); |
| | | if (!userService.updateById(user)) { |
| | | throw new CoolException("服务器内部错误"); |
| | | } |
| | | } |
| | | |
| | | } |