Commit 0c3f31c1 by wanghao

同步基础项目代码

parent a4af167d
......@@ -99,7 +99,7 @@ module.exports = {
"no-dupe-keys": "error", // 禁止对象字面量中出现重复的 key
"no-func-assign": "error", // 禁止对 function 声明重新赋值
"no-nested-ternary": "error", // 禁用嵌套的三元表达式
"no-multiple-empty-lines": ["error", { max: 3 }], // 空行最多不能超过两
"no-multiple-empty-lines": ["error", { max: 4 }], // 空行最多不能超过max
"new-cap": "off" // 关闭eslint fromDegrees方法报错
}
}
......@@ -8,21 +8,21 @@
"clean-cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean-lib": "rimraf node_modules",
"preview": "vite preview",
"build": "npm run lint && vite build",
"build": "vite build",
"serve:dist": "http-server ./dist",
"lint": "npm run eslint",
"eslint": "eslint ./src/**/*.{js,jsx,vue,ts,tsx} --fix",
"prepare": "husky install"
"eslint": "eslint ./src/**/*.{js,jsx,vue,ts,tsx} --fix"
},
"dependencies": {
"@icon-park/svg": "^1.4.0",
"@marsgis/editor": "^1.0.10",
"@marsgis/editor": "^1.0.11",
"classnames": "^2.3.1",
"antd": "^4.19.3",
"axios": "^0.26.1",
"echarts": "^5.3.2",
"lodash": "^4.17.21",
"mars3d": "^3.3.0",
"monaco-editor": "^0.33.0",
"monaco-editor": "^0.29.1",
"nprogress": "^0.2.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
......@@ -52,7 +52,6 @@
"eslint-config-react-app": "^6.0.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"less": "^4.1.2",
"rollup-plugin-copy": "^3.4.0",
"typescript": "^4.5.4",
......@@ -84,10 +83,5 @@
],
"author": "火星科技",
"license": "Apache-2.0",
"homepage": "http://mars3d.cn",
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"npm run eslint"
]
}
"homepage": "http://mars3d.cn"
}
This source diff could not be displayed because it is too large. You can view the blob instead.
import { Button } from "antd"
import type { ButtonProps } from "antd/lib/button"
import classNames from "classnames"
import "./index.less"
export const MarsButton = ({ className, ...props }: ButtonProps & { className?: string }) => {
return <Button className={["mars-button", className].join(" ")} {...props}></Button>
return (
<Button
className={classNames({
"mars-button": true,
[className]: className
})}
{...props}
></Button>
)
}
export const MarsButtonGroup = Button.Group
import type { DatePickerProps } from "antd/lib/date-picker"
import { DatePicker } from "antd"
import moment from "moment"
import { DatePicker, ConfigProvider } from "antd"
// import zhCN from "antd/lib/date-picker/locale/zh_CN"
// import locale from "antd/lib/locale/zh_CN"
import zh_CN from "antd/lib/locale-provider/zh_CN"
import "./index.less"
import "dayjs/locale/zh-cn"
import locale from "antd/es/date-picker/locale/zh_CN"
import moment from "moment"
import "moment/locale/zh-cn"
moment.locale("zh-cn")
export const MarsDatePicker = (props: DatePickerProps) => {
return (
<DatePicker
locale={locale}
className="mars-date-picker"
dropdownClassName="mars-datepicker-dropdown"
defaultValue={moment(new Date(), "YYYY-MM-DD")}
{...props}
/>
<ConfigProvider locale={zh_CN}>
<DatePicker
// locale={zhCN}
className="mars-date-picker"
dropdownClassName="mars-datepicker-dropdown"
defaultValue={moment(new Date(), "YYYY-MM-DD")}
{...props}
/>
</ConfigProvider>
)
}
......@@ -42,7 +42,7 @@
.mars-dialog__body {
width: 100%;
height: calc(100% - 40px);
padding: 0 5px 10px 10px;
padding: 10px;
overflow: hidden;
}
.content {
......@@ -64,7 +64,7 @@
bottom: 0;
display: flex;
align-items: center;
background: @background-base;
background: @mars-bg-base;
}
}
......
import { forwardRef, useState, useRef, useEffect, useCallback, useMemo, ReactElement } from "react"
import { forwardRef, useRef, useEffect, useCallback, useMemo, ReactElement } from "react"
import { createPortal } from "react-dom"
import { MarsIcon } from "@mars/components/MarsUI"
import { getConfig } from "../index"
import "./index.less"
......@@ -34,13 +35,14 @@ interface Props {
children?: any
footer?: ReactElement
icon?: ReactElement
onClose?: () => void
}
const defaultHandles = ["x", "y", "xy"]
const DialogElement = forwardRef<any, Props>(
({ handles = true, width = 200, minWidth = 100, minHeight = 100, maxWidth = 1000, maxHeight = 1000, ...props }, ref) => {
({ handles = true, width = 200, minWidth = 100, minHeight = 100, maxWidth = 1000, maxHeight = 1000, icon, ...props }, ref) => {
const pannelBox = useRef<HTMLDivElement>()
useEffect(() => {
const pannelStyle = pannelBox.current.style
......@@ -187,15 +189,17 @@ const DialogElement = forwardRef<any, Props>(
return (
<div className="mars-dialog" ref={pannelBox}>
<div className="mars-dialog__header" onMouseDown={drag}>
<span className="icon">
<slot name="icon"></slot>
</span>
<span className="icon">{icon && icon}</span>
<span className="title"> {props.title} </span>
<MarsIcon icon="close" width="18" className="close-btn" onClick={close}></MarsIcon>
</div>
<div className="mars-dialog__body">
<div className={`mars-dialog__body ${props.footer ? "" : "full-content"}`}>
<div className="content">{props.children}</div>
<div className="footer">{props.footer && <div className="footer-content">{props.footer}</div>}</div>
{props.footer && (
<div className="footer">
<div className="footer-content">{props.footer}</div>
</div>
)}
</div>
{actualHandles.map((handle) => (
<div key={handle} className={`handle handle-${handle}`} onMouseDown={(e) => setSize(handle, e)}></div>
......@@ -205,12 +209,22 @@ const DialogElement = forwardRef<any, Props>(
}
)
export const MarsDialog = forwardRef<any, Props>(({ warpper, ...props }, ref) => {
if (warpper) {
const domNode = document.querySelector(warpper)
return createPortal(<DialogElement warpper={warpper} {...props} ref={ref}></DialogElement>, domNode)
export const MarsDialog = forwardRef<any, Props>(({ warpper = "", ...props }, ref) => {
const CONFIG = getConfig()
let newWarpper = warpper
let globalConfig: Record<string, any> = {}
if (CONFIG.dialog) {
globalConfig = CONFIG.dialog
}
if (!newWarpper) {
newWarpper = globalConfig.warpper || "mars-main-view"
}
if (newWarpper) {
const domNode = document.querySelector(`#${newWarpper}`)
return createPortal(<DialogElement warpper={newWarpper} {...props} ref={ref}></DialogElement>, domNode)
}
return <DialogElement warpper="sanbox-warpper" {...props} ref={ref}></DialogElement>
return <DialogElement warpper="mars-main-view" {...props} ref={ref}></DialogElement>
})
// 处理传入的单位问题
......
// 下拉菜单
.mars-dropdown-menu {
.ant-dropdown-menu {
border-bottom: 1px solid #008aff70;
border-left: 1px solid #008aff70;
border-right: 1px solid #008aff70;
box-shadow: 0px 4px 15px 1px rgba(2, 33, 59, 0.7);
border-radius: 0px;
background: linear-gradient(to left, @mars-content-color, @mars-content-color) left bottom no-repeat,
linear-gradient(to bottom, @mars-content-color, @mars-content-color) left bottom no-repeat,
linear-gradient(to left, @mars-content-color, @mars-content-color) right bottom no-repeat,
linear-gradient(to left, @mars-content-color, @mars-content-color) right bottom no-repeat;
background-size: 1px 10px, 10px 1px, 1px 10px, 10px 1px;
background-color: @mars-bg-base !important;
.mars-icon {
vertical-align: middle;
margin-right: 4px;
}
}
.ant-dropdown-menu-title-content {
color: @mars-base-color;
}
.ant-dropdown-menu-item,
.ant-dropdown-menu-submenu-title {
&:hover {
background: @mars-list-active;
}
}
.ant-dropdown-menu-submenu-title {
padding: 6px 20px !important;
}
}
import { Menu, Dropdown } from "antd"
import type { DropdownProps } from "antd/lib/dropdown"
import "./dropdown.less"
export const MarsMenu = Menu
export const MarsDropdown = ({ className, ...props }: DropdownProps & { className?: string }) => {
return <Dropdown overlayClassName="mars-dropdown-menu" {...props}></Dropdown>
}
import { useEffect, useMemo } from "react"
import { useMemo } from "react"
import _ from "lodash"
import * as svgModule from "@icon-park/svg"
import classnames from "classnames"
import "./index.less"
interface MarsIconProps extends Record<string, any> {
icon: string
color?: string
width?: string | number
className?: string
}
console.log(svgModule)
export const MarsIcon = ({ icon, color, fill, width = "14", theme = "outline", size, ...props }: MarsIconProps) => {
export const MarsIcon = ({ icon, color, fill, width = "14", theme = "outline", size, className, ...props }: MarsIconProps) => {
const iconName = useMemo(() => _.upperFirst(_.camelCase(icon)), [icon])
const svgComponent = useMemo(
......@@ -21,8 +23,11 @@ export const MarsIcon = ({ icon, color, fill, width = "14", theme = "outline", s
size: size || width,
...props
}),
[iconName]
[iconName, theme, color, fill, size, width, props]
)
return <span className="mars-icon" dangerouslySetInnerHTML={{ __html: svgComponent }} {...props}></span>
const classes = classnames({
"mars-icon": true,
[className]: className
})
return <span className={classes} dangerouslySetInnerHTML={{ __html: svgComponent }} {...props}></span>
}
import { forwardRef, useCallback, useEffect, useMemo, useRef } from "react"
import { forwardRef, useCallback, useEffect, useState } from "react"
import { Input, Space } from "antd"
import type { PasswordProps, SearchProps, InputProps, InputRef } from "antd/lib/input"
import type { TextAreaRef, TextAreaProps } from "antd/lib/input/TextArea"
import classNames from "classnames"
import "./index.less"
export const MarsInput = forwardRef<InputRef, InputProps>((props, ref) => {
return <Input className="mars-input" ref={ref} {...props}></Input>
export const MarsInput = forwardRef<InputRef, InputProps & { className?: string }>(({ className, ...props }, ref) => {
return (
<Input
className={classNames({
"mars-input": true,
[className]: className
})}
ref={ref}
{...props}
></Input>
)
})
interface MarsInputGroupProps {
......@@ -14,21 +24,32 @@ interface MarsInputGroupProps {
onChange?: (v: any) => void
}
export const MarsInputGroup = forwardRef<any, MarsInputGroupProps>(({ value = [], units = [], onChange }, ref) => {
const values = useRef(value)
const [values, setValues] = useState(() => value)
useEffect(() => {
values.current = value
setValues(value)
}, [value])
const itemChange = useCallback((v, i) => {
values.current[i] = v
onChange && onChange(values.current)
}, [onChange])
const itemChange = useCallback(
(v) => {
setValues(v)
onChange && onChange(v)
},
[onChange]
)
return (
<Space>
{values.current.map((item, i) => (
<MarsInput defaultValue={item} key={i} suffix={units[i]} onChange={(e) => itemChange(e.target.value, i)}></MarsInput>
{values.map((item, i) => (
<MarsInput
value={item}
key={i}
suffix={units[i]}
onChange={(e) => {
values[i] = e.target.value
itemChange(values)
}}
></MarsInput>
))}
</Space>
)
......
// 分页
.mars-pagination.ant-pagination {
* {
color: @mars-base-color !important;
}
.ant-pagination-item,
.ant-pagination-prev,
.ant-pagination-next {
background: none;
background-color: transparent !important;
.ant-pagination-item-link {
background: none;
background-color: transparent !important;
}
}
.ant-pagination-simple-pager {
input {
background: none;
background-color: transparent !important;
}
}
}
import { Pagination } from "antd"
import type { PaginationProps } from "antd/lib/pagination"
import classNames from "classnames"
import "./index.less"
export const MarsPagination = ({ className, ...props }: PaginationProps & { className?: string }) => {
return (
<Pagination
className={classNames({
"mars-pagination": true,
[className]: className
})}
{...props}
></Pagination>
)
}
......@@ -9,6 +9,7 @@
background-repeat: no-repeat;
.pannel-content {
overflow-y: auto;
width: 100%;
height: 100%;
}
......
import { forwardRef, useState, useRef, useEffect, useCallback } from "react"
import { forwardRef, useRef, useEffect, useCallback } from "react"
import { createPortal } from "react-dom"
import { MarsIcon } from "@mars/components/MarsUI"
......@@ -80,7 +80,7 @@ const PannelElement = forwardRef<any, Props>(({ beforeClose, onClose, ...props }
pannelStyle.maxHeight = antoUnit(props.maxHeight)
}
}
}, [props.width, props.height, props.top, props.bottom])
}, [props.width, props.height, props.top, props.bottom, props.maxHeight])
const close = useCallback(() => {
pannelBox.current.style.display = "none"
......@@ -116,9 +116,7 @@ const PannelElement = forwardRef<any, Props>(({ beforeClose, onClose, ...props }
return (
<div className={`mars-pannel ${props.customClass || ""}`} ref={pannelBox}>
<div className="pannel-content" style={{ overflowY: props.height ? "auto" : "visible" }}>
{props.children}
</div>
<div className="pannel-content">{props.children}</div>
{props.closeable && (
<div className="pannel-close-icon" onClick={closeModel}>
<MarsIcon icon="close-one" width="20"></MarsIcon>
......
......@@ -2,6 +2,7 @@
color: @mars-base-color;
background-color: transparent !important;
background: none;
width: 100%;
.ant-select-arrow {
color: @mars-base-color;
}
......
.mars-slider.ant-slider {
margin: 0px 6px 2px 6px;
margin: 0px 10px 6px 6px;
.ant-slider-mark-text {
color: @mars-base-color !important;
top: 5px;
......
......@@ -40,4 +40,68 @@
.ant-table-row:nth-last-child(1) .ant-table-cell {
border-bottom: none;
}
// 分页
.ant-pagination {
* {
color: @mars-base-color !important;
}
.ant-pagination-item,
.ant-pagination-item-link,
.ant-pagination-prev,
.ant-pagination-next {
background: none !important;
background-color: transparent !important;
}
.ant-pagination-simple-pager {
input {
background: none;
background-color: transparent !important;
}
}
.ant-select {
color: @mars-base-color;
background-color: transparent !important;
background: none;
.ant-select-selector {
border-color: @mars-base-border-color !important;
background: none;
background-color: transparent !important;
&:hover,
&:focus {
border-color: #4db3ff !important;
}
}
.ant-select-arrow {
color: @mars-base-color !important;
}
}
.ant-select-dropdown {
background-color: red;
padding: 0 !important;
.mars-drop-bg();
.ant-select-item-option-active:not(.ant-select-item-option-disabled) {
background: @mars-list-active;
}
.ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
font-weight: 700;
background: @mars-list-active;
}
.ant-select-item {
transition: none;
color: @mars-base-color !important;
text-align: center;
}
}
}
}
.mars-main-view {
// 树控件
.ant-tree {
background: none;
color: @mars-base-color;
padding-bottom: 2px;
}
// 树控件
.ant-tree.mars-tree {
background: none;
color: @mars-base-color;
padding-bottom: 2px;
.ant-tree-checkbox {
margin-right: 0;
}
.ant-tree-show-line .ant-tree-switcher,
.ant-tree-switcher,
.ant-tree-checkbox-inner {
background: none !important;
}
......@@ -35,6 +33,10 @@
width: 20px;
}
.ant-tree-node-selected {
background: none !important;
}
.ant-tree-switcher-noop::before {
content: "";
display: inline-block;
......@@ -52,6 +54,9 @@
border-right: 1px dotted @mars-base-color;
}
}
.ant-tree .ant-tree-treenode-disabled .ant-tree-node-content-wrapper {
color: #ffffff;
}
.ant-tree-switcher-noop::after {
content: "";
......
......@@ -57,36 +57,99 @@
.mars-main-view {
color: @mars-base-color;
.mars-primary-table {
width: 100%;
td {
border: 1px solid @border-color-base;
padding: 2px;
}
// 卡片按钮样式
.ant-radio-button-wrapper {
background: rgba(32, 160, 255, 0.2);
color: @mars-base-color;
box-shadow: none !important;
}
// 分页
.ant-pagination {
* {
color: @mars-base-color !important;
}
// 卡片选中按钮
.ant-radio-button-checked {
background-color: #1890ff;
}
//进度条
.ant-progress-text {
color: @mars-base-border !important;
}
// 表格滑动条
.ant-table-cell-scrollbar {
display: none;
}
/* 卡片 */
.mars-ant-card {
background: none;
border: 1px solid @mars-base-color;
color: @mars-base-color;
.ant-card-head {
border-color: @mars-base-color;
min-height: auto;
padding: 0 10px;
color: white;
.ant-pagination-item,
.ant-pagination-prev,
.ant-pagination-next {
background: none;
background-color: transparent !important;
.ant-pagination-item-link {
background: none;
background-color: transparent !important;
.ant-card-head-title {
padding: 8px 0;
}
}
.ant-pagination-simple-pager {
input {
background: none;
background-color: transparent !important;
}
.ant-card-body {
padding: 10px;
}
}
}
.mars-primary-table {
width: 100%;
td {
border: 1px solid @border-color-base;
padding: 2px;
}
}
// 滚动条
::-webkit-scrollbar-button {
height: 0;
width: 0;
display: none;
}
::-webkit-scrollbar-track {
background: #173147;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-thumb {
border: 0;
}
::-webkit-scrollbar {
height: 10px;
width: 4px;
background: #134875;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
padding-top: 100px;
background-color: #134875;
min-height: 28px;
border-radius: 4px;
background-clip: padding-box;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-thumb {
border: 0;
}
::-webkit-scrollbar-thumb:hover {
-webkit-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25);
background-color: rgba(0, 0, 0, 0.4);
}
::-webkit-scrollbar-thumb:active {
-webkit-box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.35);
background-color: rgba(0, 0, 0, 0.5);
}
......@@ -429,5 +429,5 @@
display: inline-block;
text-align: right;
margin-right: 10px;
width: 60px;
}
\ No newline at end of file
min-width: 60px;
}
......@@ -22,6 +22,8 @@ export * from "./MarsDatePicker"
export * from "./MarsTabs"
export * from "./MarsCollapse"
export * from "./MarsTable"
export * from "./MarsDrowdown"
export * from "./MarsPagination"
export const $alert = MarsAlert
export const $notify = MarsNotify
......@@ -36,10 +38,11 @@ export const setConfig = (config) => {
}
export const getConfig = () => {
return marsUIConfig
return marsUIConfig || {}
}
export default () => {
export default (config?: any) => {
setConfig(config || {})
window.$alert = MarsAlert
window.$notify = MarsNotify
window.$message = MarsMessage
......
......@@ -26,7 +26,15 @@ let inited = false
let reactApp
marsEditor.on("loaded", (exampleConfig) => {
if (!reactApp) {
MarsUIInstall()
MarsUIInstall({
dialog: {
position: {
left: 50,
bottom: 50
},
warpper: "sanbox-warpper"
}
})
}
if (inited) {
destoryUI()
......
......@@ -39,8 +39,4 @@ const ListRenderer = () => {
}
const reactApp = createRoot(document.getElementById("root"))
reactApp.render(
<React.StrictMode>
<ListRenderer />
</React.StrictMode>
)
reactApp.render(<ListRenderer />)
......@@ -27,7 +27,15 @@ let inited = false
let reactApp
marsEditor.on("loaded", (exampleConfig) => {
if (!reactApp) {
MarsUIInstall()
MarsUIInstall({
dialog: {
position: {
left: 50,
bottom: 50
},
warpper: "sanbox-warpper"
}
})
}
if (inited) {
destoryUI()
......
import {
MarsCollapse,
MarsCollapsePanel,
MarsInput,
MarsInputNumber,
MarsSwitch,
MarsSlider,
MarsColor,
MarsTextArea,
MarsSelect
} from "@mars/components/MarsUI"
import { isBoolean, isNumber } from "lodash"
import { useMemo } from "react"
const components = {
number: MarsInputNumber,
radio: MarsSwitch,
slider: MarsSlider,
color: MarsColor,
combobox: MarsSelect,
textarea: MarsTextArea,
label: "span",
text: MarsInput
}
function AttrItem({ type, ...props }) {
const Component = components[type]
return <Component {...props}></Component>
}
interface MarsAttrProps {
attrs: any
onChange?: (value?: any) => void
}
export default function MarsAttr({ attrs, onChange = () => {} }: MarsAttrProps) {
const attrComps = useMemo(() => {
const ac: any[] = Object.keys(attrs)
.filter((k) => !["id", "name", "remark"].includes(k))
.map((k) => {
let type = "text"
if (isBoolean(attrs[k])) {
type = "radio"
}
if (isNumber(attrs[k])) {
type = "number"
}
return { name: k, label: k, type }
})
console.log("ac", ac)
return [
{ name: "id", label: "主键", type: "label", defval: "" },
{ name: "name", label: "名称", type: "text", defval: "" },
{ name: "remark", label: "备注", type: "textarea", defval: "" }
].concat(ac)
}, [attrs])
return (
<MarsCollapse activeKey={["1"]}>
<MarsCollapsePanel key="1" showArrow={false} header="属性信息">
<table className="mars-primary-table">
<tbody>
{attrComps.map((item, i) => (
<tr key={i}>
<td>{item.label}</td>
<td>
{item.type === "lable" ? (
attrs[item.name]
) : (
<AttrItem
type={item.type}
value={attrs[item.name]}
onChange={(data) => {
onChange({
[item.name]: data.target ? data.target.value : data
})
}}
></AttrItem>
)}
</td>
</tr>
))}
</tbody>
</table>
</MarsCollapsePanel>
</MarsCollapse>
)
}
import { MarsCollapse, MarsCollapsePanel, MarsIcon, MarsInputNumber } from "@mars/components/MarsUI"
import { Space } from "antd"
import { Fragment, useCallback, useEffect, useRef } from "react"
import { cloneDeep } from "lodash"
interface MarsAttrProps {
positions: any
onChange?: (value?: any) => void
graphic: any
}
function PointPosition({ value = [], onChange }) {
const values = useRef(value)
useEffect(() => {
values.current = value
}, [value])
const itemChange = useCallback(
(v, i) => {
values.current[i] = v
onChange && onChange(values.current)
},
[values, onChange]
)
return (
<table className="mars-primary-table">
<tbody>
<tr>
<td width="80">经度</td>
<td>
<MarsInputNumber size="small" value={value[0]} step="0.000001" onChange={(value) => itemChange(value, 0)}></MarsInputNumber>
</td>
</tr>
<tr>
<td>纬度</td>
<td>
<MarsInputNumber size="small" value={value[1]} step="0.000001" onChange={(value) => itemChange(value, 1)}></MarsInputNumber>
</td>
</tr>
<tr>
<td>高程</td>
<td>
<MarsInputNumber size="small" value={value[2]} step="0.1" onChange={(value) => itemChange(value, 2)}></MarsInputNumber>
</td>
</tr>
</tbody>
</table>
)
}
export default function MarsPosition({ positions, graphic, onChange = () => {} }: MarsAttrProps) {
const minNum = getMinPointNum(graphic)
const maxNum = getMaxPointNum(graphic)
const pointChange = useCallback((value, i) => {
positions[i] = value
onChange(positions)
}, [])
return (
<MarsCollapse activeKey={["1", "2"]}>
<MarsCollapsePanel key="1" showArrow={false} header="属性信息">
<table className="mars-primary-table">
<tbody>
<tr>
<td width="100">在原值上增加</td>
<td>
<MarsInputNumber
size="small"
step="1"
onChange={(value) => {
onChange(
positions.map((p) => {
p[2] = p[2] + value
return p
})
)
}}
></MarsInputNumber>
</td>
</tr>
<tr>
<td>全部修改为</td>
<td>
<MarsInputNumber
size="small"
step="1"
onChange={(value) => {
onChange(
positions.map((p) => {
p[2] = value
return p
})
)
}}
></MarsInputNumber>
</td>
</tr>
</tbody>
</table>
</MarsCollapsePanel>
<MarsCollapsePanel key="2" showArrow={false} header="坐标列表">
{positions.map((item, i) => (
<Fragment key={i}>
<div className="position-title">
<span>{i + 1}</span>
<Space className="position-title__subfix">
{positions.length < maxNum && (
<MarsIcon
icon="add-one"
width="16"
onClick={() => {
const lonlats = cloneDeep(positions)
lonlats.splice(i, 0, cloneDeep(item))
onChange(lonlats)
}}
></MarsIcon>
)}
{positions.length > minNum && (
<MarsIcon
icon="delete"
width="16"
onClick={() => {
const lonlats = cloneDeep(positions)
lonlats.splice(i, 1)
onChange(lonlats)
}}
></MarsIcon>
)}
</Space>
</div>
<PointPosition value={item} onChange={(value) => pointChange(value, i)}></PointPosition>
</Fragment>
))}
</MarsCollapsePanel>
</MarsCollapse>
)
}
function getMaxPointNum(gp: any): number {
if (gp && gp._maxPointNum) {
return gp._maxPointNum
}
return 999
}
function getMinPointNum(gp: any): number {
if (gp && gp._minPointNum) {
return gp._minPointNum
}
return 3
}
......@@ -11,8 +11,8 @@ import {
$message
} from "@mars/components/MarsUI"
import styleConfig from "./attr.json"
import { useCallback, useEffect, useMemo, useState } from "react"
import { cloneDeep, uniq, isArray } from "lodash"
import { useCallback, useMemo } from "react"
import { cloneDeep, isArray } from "lodash"
const components = {
number: MarsInputNumber,
......@@ -38,7 +38,7 @@ interface MarsAttrProps {
export default function MarsStyle({ style, graphic, onChange = () => {} }: MarsAttrProps) {
const styleOptions = useMemo(() => {
const defaultOption = styleConfig[graphic.options.edittype || graphic.type] || {}
const defaultOption = styleConfig[graphic.options.styleType || graphic.type] || {}
return defaultOption
}, [graphic])
......
.top-handle-bar {
padding: 5px 0 2px 0;
.mars-icon {
color: #ffffff;
}
}
.attr-editor-main {
height: calc(100% - 40px);
......@@ -11,6 +14,13 @@
.mars-select {
width: 100%;
}
td {
height: 40px;
padding-left: 6px !important;
padding-right: 6px !important;
font-size: 12px !important;
}
}
.position-title {
......
import { MarsDialog, MarsIcon, MarsTabs, MarsTabPane } from "@mars/components/MarsUI"
import { MarsDialog, MarsIcon } from "@mars/components/MarsUI"
import { Space } from "antd"
import * as mars3d from "mars3d"
import { useCallback, useEffect, useState } from "react"
import MarsPosition from "./MarsPosition"
import { useEffect, useState } from "react"
import MarsStyle from "./MarsStyle"
import MarsAttr from "./MarsAttr"
import _ from "lodash"
import "./index.less"
function GraphicEditor({ currentWidget, ...props }) {
const [graphic, setGraphic] = useState(null)
const [style, setStyle] = useState(null)
const [positions, setPositions] = useState(null)
const [attrs, setAttrs] = useState(null)
useEffect(() => {
console.log("编辑面板接收到了graphic对象更新:", currentWidget)
const gp = currentWidget.data.graphic
setGraphic(gp)
setStyle(_.cloneDeep(gp.style))
setPositions(_.cloneDeep(gp.coordinates))
setAttrs(_.cloneDeep(gp.attr))
}, [currentWidget])
const [acTab, setAcTab] = useState("style")
const tabChange = useCallback((key: string) => {
setAcTab(key)
}, [])
}, [currentWidget.data.graphic])
return (
<MarsDialog
title="属性编辑"
width="260"
top="60"
bottom="40"
left="10"
minWidth={200}
{...props}
footer={
<MarsTabs tabPosition="bottom" activeKey={acTab} type="card" onTabClick={tabChange}>
<MarsTabPane key="attr" tab="属性"></MarsTabPane>
<MarsTabPane key="coord" tab="坐标"></MarsTabPane>
<MarsTabPane key="style" tab="样式"></MarsTabPane>
</MarsTabs>
}
>
<MarsDialog title="属性编辑" width="260" top="60" bottom="40" left="10" minWidth={200} {...props}>
{graphic && (
<>
<div className="top-handle-bar">
......@@ -67,37 +40,15 @@ function GraphicEditor({ currentWidget, ...props }) {
</Space>
</div>
<div className="attr-editor-main">
{acTab === "attr" && (
<MarsAttr
attrs={attrs}
onChange={(data) => {
console.log("更新属性:", data)
graphic.attr = data
}}
></MarsAttr>
)}
{acTab === "coord" && (
<MarsPosition
positions={positions}
graphic={graphic}
onChange={(data) => {
console.log("更新positions:", data)
graphic.positions = data
setPositions(_.cloneDeep(data))
}}
></MarsPosition>
)}
{acTab === "style" && (
<MarsStyle
style={style}
graphic={graphic}
onChange={(data) => {
console.log("更新style:", data)
graphic.setStyle(data)
setStyle(_.cloneDeep(data))
}}
></MarsStyle>
)}
<MarsStyle
style={style}
graphic={graphic}
onChange={(data) => {
console.log("更新style:", data)
graphic.setStyle(data)
setStyle(_.cloneDeep(data))
}}
></MarsStyle>
</div>
</>
)}
......
.layer-pictrue {
position: absolute;
bottom: 30px;
left: 60px;
.layer-picture_img {
height: 180px;
}
}
.tree-slider {
display: inline-block;
width: 70px;
margin-left: 5px;
vertical-align: middle;
}
\ No newline at end of file
import { MarsIcon, MarsDialog, MarsTree, MarsSlider } from "@mars/components/MarsUI"
import { activate, disable } from "@mars/widgets/common/store/widget"
import { useEffect, useRef, useState } from "react"
import { Space } from "antd"
import * as mapWork from "./map"
import { useLifecycle } from "@mars/widgets/common/uses/useLifecycle"
import styles from "./index.module.less"
export default function (props) {
useLifecycle(mapWork)
const [treeData, setTreeData] = useState([])
const [checkedKeys, setCheckedKeys] = useState([])
const [checked, setChecked] = useState({
isChecked: false,
id: ""
}) // 树节点是否被选中
const layersObj = useRef({})
const opacityObj = useRef({})
useEffect(() => {
initTree()
}, [])
function initTree() {
const newTreeData = []
const layers = mapWork.getLayers()
for (let i = layers.length - 1; i >= 0; i--) {
const layer = layers[i] // 创建图层
if (layer.isPrivate) {
continue
}
if (!layer._hasMapInit && layer.pid === -1 && layer.id !== 99) {
layer.pid = 99 // 示例中创建的图层都放到99分组下面
}
layersObj.current[layer.id] = layers
if (layer && layer.pid === -1) {
const node: any = {
index: i,
title: layer.name || `未命名(${layer.type})`,
key: layer.id,
id: layer.id,
pId: layer.pid,
uuid: layer.uuid,
hasZIndex: layer.hasZIndex,
hasOpacity: layer.hasOpacity,
opacity: 100 * (layer.opacity || 0),
group: layer.type === "group"
}
if (layer.hasOpacity) {
opacityObj.current[layer.id] = 100 * (layer.opacity || 0)
}
node.children = findChild(node, layers)
newTreeData.push(node)
}
}
newTreeData.forEach((data: any) => {
data.children.forEach((item: any) => {
if (item.children) {
item.children.forEach((chil: any) => {
if (layersObj.current[chil.key].options.radio) {
chil.parent.disabled = true
}
})
}
})
})
setTreeData(newTreeData)
setCheckedKeys(checkedKeys)
}
function findChild(parent: any, list: any[]) {
return list
.filter((item: any) => item.pid === parent.id)
.reverse()
.map((item: any, i: number) => {
const node: any = {
index: i,
title: item.name || `未命名(${item.type})`,
key: item.id,
id: item.id,
pId: item.pid,
uuid: item.uuid,
hasZIndex: item.hasZIndex,
hasOpacity: item.hasOpacity,
opacity: 100 * (item.opacity || 0),
parent: parent,
group: item.type === "group"
}
if (item.hasOpacity) {
opacityObj.current[item.id] = 100 * (item.opacity || 0)
}
layersObj.current[item.id] = item
node.children = findChild(node, list)
if (item.isAdded && item.show) {
checkedKeys.push(node.key)
}
return node
})
}
const lastWidget = useRef("")
function checkedChange(keys: string[], e: any) {
const layer = layersObj.current[e.node.id]
setChecked({
isChecked: e.checked,
id: e.node.uuid
})
// console.log("点击的矢量图层", layer)
if (layer) {
if (layer.isAdded === false) {
mapWork.addLayer(layer)
}
// 特殊处理同目录下的单选的互斥的节点,可在config对应图层节点中配置"radio":true即可
if (layer.options && layer.options.radio && e.checked) {
// 循环所有的图层
for (const i in layersObj.current) {
const item = layersObj.current[i]
// 循环所有的打开的图层
keys.forEach((key, index) => {
// 在所有图层中筛选与打开图层对应key值的图层 以及 与当前操作的图层的pid相同的图层
if (item === layersObj.current[key] && layer.pid === layersObj.current[key].pid) {
// 筛选出不是当前的其他图层进行图层隐藏以及移除
if (item !== layer) {
keys.splice(index, 1)
item.show = false
}
}
})
}
}
// 处理图层的关联事件
if (layer.options && layer.options.onWidget) {
if (e.checked) {
if (lastWidget.current) {
disable(lastWidget.current)
}
activate({
name: layer.options.onWidget
})
lastWidget.current = layer.options.onWidget
} else {
disable(layer.options.onWidget)
}
}
// 处理子节点
if (e.node.children && e.node.children.length) {
renderChildNode(keys, e.node.children)
}
if (keys.indexOf(e.node.id) !== -1) {
layer.show = true
layer.readyPromise &&
layer.readyPromise.then(function (layer) {
layer.flyTo()
})
} else {
layer.show = false
}
// 处理图层构件树控件
if (layer.options && layer.options.scenetree) {
initLayerTree(layer)
}
}
setCheckedKeys(keys)
}
function renderChildNode(keys: string[], children: any[]) {
children.forEach((child) => {
const layer = layersObj.current[child.id]
if (layer) {
if (!layer.isAdded) {
mapWork.addLayer(layer)
}
if (keys.indexOf(child.id) !== -1) {
layer.show = true
} else {
layer.show = false
}
if (child.children) {
renderChildNode(keys, child.children)
}
if (layer.options.scenetree) {
initLayerTree(layer)
}
}
})
}
const lastBindClickLayer = useRef<any>()
function initLayerTree(layer: any) {
disable("layerComponent")
if (lastBindClickLayer.current) {
lastBindClickLayer.current.off("click", onClickBimLayer)
lastBindClickLayer.current = null
}
// 处理图层构件树控件
if (layer.options.scenetree) {
layer.on("click", onClickBimLayer)
lastBindClickLayer.current = layer
}
}
function onClickBimLayer(event: any) {
const layer = event.layer
const url = layer.options.url
const id = layer.id
activate({
name: "layerComponent",
data: { url, id }
})
}
const flyTo = (item: any) => {
if (checked) {
const layer = layersObj.current[item.id]
layer.flyTo()
}
}
const opcityChange = (node: any, val: number) => {
const id = node.id
const layer = layersObj.current[id]
if (layer) {
layer.opacity = val / 100
}
}
const renderNode = (node: any) => {
return (
<>
<span onDoubleClick={() => flyTo(node)}>{node.title}</span>
{node.hasOpacity && checked.isChecked && node.uuid === checked.id ? (
<span className={`${styles["tree-slider"]}`}>
<MarsSlider
defaultValue={opacityObj.current[node.id]}
min={0}
max={100}
step={1}
onChange={(val: number) => opcityChange(node, val)}
></MarsSlider>
</span>
) : (
""
)}
</>
)
}
return (
<MarsDialog title="图层" width={280} minWidth={250} left={50} top={50} bottom={40} {...props}>
{treeData.length && (
<MarsTree
checkable
treeData={treeData}
defaultExpandAll
checkedKeys={checkedKeys}
onCheck={checkedChange}
titleRender={(node) => renderNode(node)}
></MarsTree>
)}
</MarsDialog>
)
}
import { MarsDialog, MarsTree, $message } from "@mars/components/MarsUI"
import { useEffect, useRef, useState } from "react"
import axios from "axios"
import * as mapWork from "./map"
export default function ({ currentWidget, ...props }) {
const keyVal = useRef(0)
const [treeData, setTreeData] = useState([])
useEffect(() => {
;(async () => {
const newTreeData = []
const url = currentWidget.data.url
const scenetree = url.substring(0, url.lastIndexOf("/") + 1) + "scenetree.json"
const scene: any = await axios.get(scenetree)
if (scene.data) {
let item = scene.data
if (scene.data.scenes) {
item = scene.data.scenes[0]
}
const childeren = isHaveChildren(item.children)
newTreeData.push({
title: item.name,
key: keyVal,
id: item.id,
sphere: item.sphere ? item.sphere : null,
children: childeren
})
}
setTreeData(newTreeData)
})()
}, [currentWidget])
function isHaveChildren(arr: any) {
if (!arr) {
return []
}
keyVal.current++
const childeren: any[] = []
arr.forEach((item: any) => {
const childrenObject: any = {}
childrenObject.title = item.name ? item.name : "element"
childrenObject.key = keyVal + "-" + Math.random()
childrenObject.id = item.id
childrenObject.sphere = item.sphere
const childOne: any = isHaveChildren(item.children)
if (childOne.length !== 0) {
childrenObject.children = childOne
}
return childeren.push(childrenObject)
})
return childeren
}
// 点击节点 定位
function flytoModelNode(selectedKeys: any, selected: any) {
const id = currentWidget.data.id
mapWork.flytoModelNode(id, selected.node.sphere)
}
// 选中节点 修改样式
const onModelChecked = (keys: string[], e: any) => {
const id = currentWidget.data.id
// 判断
if (keys.length > 2000) {
$message(`勾选数据${keys.length}大于2000,请减少勾选数量。`)
return
}
mapWork.checkModelStyle(id, e.checkedNodes)
}
return (
<MarsDialog title="模型构件" width={320} minWidth={320} left={10} top={70} bottom={50} {...props}>
{treeData.length && <MarsTree checkable treeData={treeData} onCheck={onModelChecked} onSelect={flytoModelNode}></MarsTree>}
</MarsDialog>
)
}
import * as mapWork from "./map"
import { useLifecycle } from "@mars/widgets/common/uses/useLifecycle"
import styles from "./index.module.less"
import image from "./img/guihua.jpg"
export default function (props) {
useLifecycle(mapWork)
return (
<div className={`${styles["layer-pictrue"]}`}>
<img className={`${styles["layer-picture_img"]}`} alt="" src={image} />
</div>
)
}
import * as mapWork from "./map"
import { useLifecycle } from "@mars/widgets/common/uses/useLifecycle"
import styles from "./index.module.less"
import image from "./img/heatmap.png"
export default function (props) {
useLifecycle(mapWork)
return (
<div className={`${styles["layer-pictrue"]}`}>
<img className={`${styles["layer-picture_img"]}`} alt="" src={image} />
</div>
)
}
/**
* 图层管理
* @copyright 火星科技 mars3d.cn
* @author 火星吴彦祖 2022-01-10
*/
import * as mars3d from "mars3d"
const Cesium = mars3d.Cesium
let map: mars3d.Map // 地图对象
/**
* 初始化地图业务,生命周期钩子函数(必须)
* 框架在地图初始化完成后自动调用该函数
* @param {mars3d.Map} mapInstance 地图对象
* @returns {void} 无
*/
export function onMounted(mapInstance: mars3d.Map) {
map = mapInstance // 记录首次创建的map
}
/**
* 释放当前地图业务的生命周期函数
* @returns {void} 无
*/
export function onUnmounted() {
map = null
}
export function addLayer(layer: mars3d.layer.BaseLayer) {
map.addLayer(layer)
}
export function getLayers() {
return map.getLayers({
basemaps: true, // 是否取config.json中的basempas
layers: true // 是否取config.json中的layers
})
}
// ********************************** 图层的结构树控件 ****************************** //
export function flytoModelNode(nodeid: number, nodesphere: any) {
if (!nodesphere || nodesphere[3] <= 0) {
return
}
// 构件节点位置
let center = new Cesium.Cartesian3(nodesphere[0], nodesphere[1], nodesphere[2])
const tiles3dLayer: mars3d.layer.TilesetLayer = map.getLayerById(nodeid)
// 获取构件节点位置,现对于原始矩阵变化后的新位置
center = tiles3dLayer.getPositionByOrginMatrix(center)
// 飞行过去
const sphere = new Cesium.BoundingSphere(center, nodesphere[3])
map.camera.flyToBoundingSphere(sphere, {
offset: new Cesium.HeadingPitchRange(map.camera.heading, map.camera.pitch, nodesphere[3] * 1.5),
duration: 0.5
})
}
export function checkModelStyle(layerid: number, arrIds: any) {
// 设置tileset的样式
if (!map) {
return
}
const tiles3dLayer = map.getLayerById(layerid)
let titleStyle = ""
if (!arrIds.length) {
tiles3dLayer.style = null
return
}
// 勾选
arrIds.forEach((element: any, index: number) => {
if (index === 0) {
titleStyle += "${id} ==='" + element.id + "' "
} else {
titleStyle += "|| ${id} ==='" + element.id + "'"
}
})
tiles3dLayer.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
[titleStyle, "rgb(255, 255, 255)"],
["true", "rgba(255, 200, 200,0.2)"]
]
}
})
}
.basemap-card {
width: 75px;
list-style-type: none;
margin-top: 10px;
margin-left: 10px;
float: left;
text-align: center;
cursor: pointer;
font-size: 12px;
color: #fff;
&:hover {
.active-card();
}
.icon {
border: 1px solid #4db3ff78;
width: 75px;
height: 70px;
padding: 1px;
}
}
.active-card {
color: #337fe5 !important;
.icon {
border-color: #337fe5;
}
}
import * as mapWork from "./map"
import { withLifeCyle } from "@mars/widgets/common/uses/useLifecycle"
import { MarsDialog, MarsSwitch } from "@mars/components/MarsUI"
import { useEffect, useState } from "react"
import styles from "./index.module.less"
import classNames from "classnames"
function initData(e: any) {
let activeBaseMap: string
const baseMapData = e.baseMaps.map((m: any) => {
if (m.isAdded && m.show) {
activeBaseMap = m.uuid
}
return {
name: m.name,
uuid: m.uuid,
options: m.options
}
})
const chkHasTerrain = e.hasTerrain || false
return { baseMapData, chkHasTerrain, activeBaseMap }
}
export default withLifeCyle(function (props) {
// useLifecycle(mapWork)
const [baseMaps, stateBaseMaps] = useState<any[]>([]) // 底图
const [terrain, stateTerrain] = useState<boolean>() // 地形
const [active, stateActive] = useState<string>() // 选中样式
useEffect(() => {
const layer = mapWork.getLayers()
const { baseMapData, chkHasTerrain, activeBaseMap } = initData(layer)
stateBaseMaps(baseMapData)
stateTerrain(chkHasTerrain)
stateActive(activeBaseMap)
}, [])
return (
<MarsDialog
title="底图"
footer={
<>
<MarsSwitch
checked={terrain}
onChange={(value) => {
stateTerrain(value)
mapWork.changeTerrain(value)
}}
></MarsSwitch>
<span className="f-ml">显示地形</span>
</>
}
width={380}
position={{ top: "auto", bottom: 50, left: 50 }}
{...props}
>
<ul>
{baseMaps.map((item, i) => (
<li
key={i}
className={classNames({
[styles.basemapCard]: true,
[styles.activeCard]: active === item.uuid
})}
onClick={() => {
stateActive(item.uuid)
mapWork.changeBaseMaps(item.uuid)
}}
>
<img className={styles.icon} src={item.options.icon || "img/basemaps/bingAerial.png"} alt="" />
<span className="title">{item.name}</span>
</li>
))}
</ul>
</MarsDialog>
)
}, mapWork)
/**
* 底图控制
* @copyright 火星科技 mars3d.cn
* @author 火星渣渣灰 2022-06-01
*/
import * as mars3d from "mars3d"
let map: mars3d.Map // 地图对象
// 初始化当前业务
export function onMounted(mapInstance: mars3d.Map): void {
console.log("onMounted")
map = mapInstance // 记录map
}
// 释放当前业务
export function onUnmounted(): void {
console.log("onUnmounted")
map = null
}
export function changeBaseMaps(id: string) {
map.basemap = id
}
export function changeTerrain(value: boolean) {
map.hasTerrain = value
}
export function getLayers() {
return {
baseMaps: map.getBasemaps(true),
hasTerrain: map.hasTerrain
}
}
.query-poi-pannel {
background: none !important;
border: none !important;
padding: 0 !important;
overflow: visible !important;
:global(.ant-input-clear-icon) {
color: @mars-content-color !important;
}
:global(.ant-pagination-simple-pager) {
input {
width: 50px;
}
}
}
.query-poi {
padding: 0;
color: #fff;
.query-poi__search {
display: flex;
justify-content: flex-start;
align-items: center;
width: 320px;
height: 44px;
border: 1px solid @mars-primary-color;
background: @mars-bg-base;
.search-input {
border: none;
background: none;
height: 44px;
outline: none;
padding-left: 10px;
flex-grow: 1;
:global(.ant-input) {
font-size: 16px;
color: @mars-base-color !important;
}
}
.search-button {
height: 44px;
width: 55px;
:global(.mars-icon) {
line-height: 1;
margin: 0;
}
}
}
}
.search-list {
min-height: 100px;
width: 100%;
box-shadow: 0px 4px 15px 1px rgba(2, 33, 59, 0.7);
border-radius: 0px;
background: linear-gradient(to left, @mars-base-color, @mars-base-color) left bottom no-repeat,
linear-gradient(to bottom, @mars-base-color, @mars-base-color) left bottom no-repeat,
linear-gradient(to left, @mars-base-color, @mars-base-color) right bottom no-repeat,
linear-gradient(to left, @mars-base-color, @mars-base-color) right bottom no-repeat;
background-size: 1px 10px, 10px 1px, 1px 10px, 10px 1px;
background-color: @mars-bg-base !important;
position: absolute;
.search-list__item {
height: 36px;
line-height: 36px;
padding-left: 10px;
cursor: pointer;
&:hover {
background: @mars-list-active;
}
}
}
.query-site {
position: absolute;
border-top: none;
padding: 10px 20px;
padding-top: 0;
width: 100%;
border-bottom: 1px solid #008aff70;
border-left: 1px solid #008aff70;
border-right: 1px solid #008aff70;
z-index: 100;
background: linear-gradient(to left, @mars-content-color, @mars-content-color) left bottom no-repeat,
linear-gradient(to bottom, @mars-content-color, @mars-content-color) left bottom no-repeat,
linear-gradient(to left, @mars-content-color, @mars-content-color) right bottom no-repeat,
linear-gradient(to left, @mars-content-color, @mars-content-color) right bottom no-repeat;
background-size: 1px 10px, 10px 1px, 1px 10px, 10px 1px;
background-color: @mars-bg-base;
.query-site__item {
height: 80px;
display: flex;
justify-content: flex-start;
align-items: center;
&:hover {
background: @mars-list-active;
}
.query-site__context {
flex-grow: 1;
.query-site-text {
font-size: 16px;
width: 200px;
font-family: Source Han Sans CN;
font-weight: 400;
color: @mars-base-color;
}
.query-site-sub {
font-size: 14px;
width: 200px;
font-family: Source Han Sans CN;
font-weight: 400;
color: @mars-content-color;
}
}
.query-site__more {
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: @mars-content-color;
}
}
.query-site__page {
display: flex;
justify-content: space-between;
padding: 10px 0;
.query-site-allcount {
font-size: 14px;
}
}
}
import { MarsPannel, MarsIcon, MarsInput, MarsButton, MarsPagination, $showLoading, $hideLoading, $message, $alert } from "@mars/components/MarsUI"
import { useRef, useState } from "react"
import { isLonLat } from "@mars/utils/mars-util"
import * as mapWork from "./map"
import { useLifecycle } from "@mars/widgets/common/uses/useLifecycle"
import styles from "./index.module.less"
const storageName = "mars3d_queryGaodePOI"
const url = "//www.amap.com/detail/"
export default function (props) {
useLifecycle(mapWork)
const [searchTxt, setSearchTxt] = useState("")
const [allCount, setAllCount] = useState(0)
const [dataSource, setDataSource] = useState([])
const [siteSource, setSiteSource] = useState([])
const [searchListShow, setSearchListShow] = useState(false)
const [siteListShow, setSiteListShow] = useState(false)
let timer = useRef<any>()
const startCloseSearch = () => {
timer.current = setTimeout(() => {
setSearchListShow(false)
clearTimeout(timer.current)
timer = null
}, 100)
}
// 搜寻输入框数据之前的提示数据 以及搜寻过的历史数据 通过列表展现
const handleSearch = (val: string) => {
if (val === "") {
showHistoryList()
mapWork.clearLayers()
return
}
if (isLonLat(val)) {
mapWork.centerAtLonLat(val)
return
}
setSearchListShow(false)
mapWork.queryData(val).then((result: any) => {
const list: { value: string }[] = []
result.list.forEach((item: any) => {
if (list.every((l) => l.value !== item.name)) {
list.push({
value: item.name
})
}
})
setDataSource(list)
setSearchListShow(true)
})
}
// 展示搜寻过的历史数据
const showHistoryList = () => {
if (searchTxt) {
return
}
const historys = JSON.parse(localStorage.getItem(storageName)!)
if (historys) {
setDataSource((historys || []).map((item: any) => ({ value: item })))
setSearchListShow(true)
}
if (timer) {
clearTimeout(timer.current)
}
setSiteListShow(false)
}
// 开始查询并加载数据
const selectPoint = async (value: any) => {
if (!searchTxt) {
setSearchTxt(value)
}
$showLoading()
addHistory(value)
await querySiteList(value, 1)
$hideLoading()
setSiteListShow(true)
setSearchListShow(false)
}
async function querySiteList(text: string, page: number) {
const result = await mapWork.querySiteList(text, page)
if (!result.list || result.list.length <= 0) {
$message("暂无数据")
}
setSiteSource(result.list || [])
setAllCount(result.allcount)
mapWork.showPOIArr(result.list || [])
return result
}
/**
* 将需要搜查的关键字记录进历史数据中
* @param {any} data 输入框输入的关键字
* @returns {void} 无
*/
function addHistory(data: any) {
try {
const arrHistory = JSON.parse(localStorage.getItem(storageName)!) || []
if (!arrHistory.includes(data)) {
arrHistory.unshift(data)
}
localStorage.setItem(storageName, JSON.stringify(arrHistory.slice(0, 10)))
} catch (err: any) {
throw new Error(err)
}
}
// 定位至矢量图层
function flyTo(item: any) {
const graphic = item._graphic
if (graphic === null) {
return $alert(item.name + " 无经纬度坐标信息!")
}
mapWork.flyToGraphic(graphic, { radius: 2000 })
}
return (
<MarsPannel left={10} top={10} customClass={styles["query-poi-pannel"]} {...props}>
<div className={styles["query-poi"]}>
<div className={styles["query-poi__search"]}>
<MarsInput
placeholder="搜索 地点"
value={searchTxt}
className={styles["search-input"]}
onBlur={startCloseSearch}
onFocus={showHistoryList}
allowClear
onChange={(e) => {
setSearchTxt(e.target.value)
handleSearch(e.target.value)
}}
></MarsInput>
<MarsButton className={styles["search-button"]}>
<MarsIcon icon="search" width="20" color="#fff" click="selectPoint(searchTxt)"></MarsIcon>
</MarsButton>
</div>
{searchListShow && (
<ul className={styles["search-list"]}>
{dataSource.map((item, i) => (
<li key={i} className={styles["search-list__item"]} onClick={() => selectPoint(item.value)}>
{item.value}
</li>
))}
</ul>
)}
{siteListShow && (
<div className={styles["query-site"]}>
{siteSource && siteSource.length && (
<>
<ul>
{siteSource.map((item, i) => (
<li key={i} className={styles["query-site__item"]} onClick={() => flyTo(item)}>
<div className={styles["query-site__context"]}>
<p className={`${styles["query-site-text"]} f-toe`} title={item.name}>
{i + 1}{item.name}
</p>
<p className={`${styles["query-site-sub"]} f-toe`}>{item.type}</p>
</div>
<a href={url + item.id} rel="noreferrer" target="_blank" className={styles["query-site__more"]}>
更多&gt;&gt;
</a>
</li>
))}
</ul>
<div className={styles["query-site__page"]}>
<p className={styles["query-site-allcount"]}>{allCount}条结果</p>
<MarsPagination
onChange={(page: number) => querySiteList(searchTxt, page)}
size="small"
total={allCount}
pageSize={6}
simple={true}
/>
</div>
</>
)}
</div>
)}
</div>
</MarsPannel>
)
}
/**
* 高德POI 查询栏 (左上角)
* @copyright 火星科技 mars3d.cn
* @author 火星吴彦祖 2022-01-10
*/
import * as mars3d from "mars3d"
const Cesium = mars3d.Cesium
let map: mars3d.Map // 地图对象
let graphicLayer: mars3d.layer.GraphicLayer
let queryPoi: mars3d.query.GaodePOI // GaodePOI查询
let address: any = null
// 初始化当前业务
export function onMounted(mapInstance: mars3d.Map): void {
map = mapInstance // 记录map
queryPoi = new mars3d.query.GaodePOI({
// city: '合肥市',
})
graphicLayer = new mars3d.layer.GraphicLayer({
name: "PIO查询",
pid: 99 // 图层管理 中使用,父节点id
})
graphicLayer.bindPopup(function (event: any) {
const item = event.graphic?.attr
if (!item) {
return
}
let inHtml = `<div class="mars3d-template-titile"><a href="https://www.amap.com/detail/${item.id}" target="_black" style="color: #ffffff; ">${item.name}</a></div><div class="mars3d-template-content" >`
if (item.tel !== "") {
inHtml += "<div><label>电话:</label>" + item.tel + "</div>"
}
if (item.address) {
inHtml += "<div><label>地址:</label>" + item.address + "</div>"
}
if (item.type) {
const fl = item.type
if (fl !== "") {
inHtml += "<div><label>类别:</label>" + fl + "</div>"
}
}
inHtml += "</div>"
return inHtml
})
map.addLayer(graphicLayer)
map.on(mars3d.EventType.cameraChanged, cameraChanged)
}
function cameraChanged() {
queryPoi.getAddress({
location: map.getCenter(),
success: (result: any) => {
address = result
}
})
}
// 释放当前业务
export function onUnmounted(): void {
map.removeLayer(graphicLayer)
map.off(mars3d.EventType.cameraChanged, cameraChanged)
graphicLayer.remove()
queryPoi = null
address = null
map = null
}
// 查询数据
export function queryData(val: string): Promise<any> {
return new Promise((resolve) => {
queryPoi.autoTip({
text: val,
city: address?.city,
location: map.getCenter(),
success: (result: any) => {
resolve(result)
}
})
})
}
export function querySiteList(text: string, page: number): Promise<any> {
return new Promise((resolve) => {
queryPoi.queryText({
text,
count: 6,
page: page - 1,
city: address?.city,
success: (result: any) => {
resolve(result)
}
})
})
}
/**
* 加载查询之后的数据,通过矢量数据展示出来
* @param {any} arr 查询之后的数据
* @returns {void} 无
*/
export function showPOIArr(arr: any): void {
clearLayers()
arr.forEach((item: any) => {
const jd = Number(item.lng)
const wd = Number(item.lat)
if (isNaN(jd) || isNaN(wd)) {
return
}
item.lng = jd
item.lat = wd
// 添加实体
const graphic = new mars3d.graphic.PointEntity({
position: Cesium.Cartesian3.fromDegrees(jd, wd),
style: {
pixelSize: 10,
color: "#3388ff",
outline: true,
outlineColor: "#ffffff",
outlineWidth: 2,
scaleByDistance: new Cesium.NearFarScalar(1000, 1, 1000000, 0.1),
clampToGround: true, // 贴地
visibleDepth: false, // 是否被遮挡
label: {
text: item.name,
font_size: 20,
color: "rgb(240,255,255)",
outline: true,
outlineWidth: 2,
outlineColor: Cesium.Color.BLACK,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffsetY: -10, // 偏移量
distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 200000),
clampToGround: true, // 贴地
visibleDepth: false // 是否被遮挡
}
},
attr: item
})
graphicLayer.addGraphic(graphic)
item._graphic = graphic
})
if (arr.length > 1) {
graphicLayer.flyTo()
}
}
/**
* 判断是否为经纬度值,
* 若是,加载为矢量数据且定位至该矢量数据
* 若否,返回
* @param {string} text 输入框输入的关键字
* @returns {void} 无
*/
export function centerAtLonLat(text: string): void {
const arr = text.split(",")
if (arr.length !== 2) {
return
}
const jd = Number(arr[0])
const wd = Number(arr[1])
if (isNaN(jd) || isNaN(wd)) {
return
}
// 添加实体
const graphic = new mars3d.graphic.PointEntity({
position: Cesium.Cartesian3.fromDegrees(jd, wd),
style: {
color: "#3388ff",
pixelSize: 10,
outline: true,
outlineColor: "#ffffff",
outlineWidth: 2,
scaleByDistance: new Cesium.NearFarScalar(1000, 1, 1000000, 0.1),
clampToGround: true, // 贴地
visibleDepth: false // 是否被遮挡
}
})
graphicLayer.addGraphic(graphic)
graphic.bindPopup(`<div class="mars3d-template-titile">坐标定位</div>
<div class="mars3d-template-content" >
<div><label>经度</label> ${jd}</div>
<div><label>纬度</label>${wd}</div>
</div>`)
graphic.openHighlight()
graphic.flyTo({
radius: 1000, // 点数据:radius控制视距距离
scale: 1.5, // 线面数据:scale控制边界的放大比例
complete: () => {
graphic.openPopup()
}
})
}
export function flyToGraphic(graphic: mars3d.graphic.BaseGraphic, option: any): void {
map.flyToGraphic(graphic, { ...option, complete: () => graphicLayer.openPopup(graphic) })
}
export function clearLayers(): void {
graphicLayer.closePopup()
graphicLayer.clear()
}
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="22" height="22" preserveAspectRatio="xMidYMid meet" viewBox="0 0 64 64"><g transform="rotate(180 32 32)"><path fill="#dfe9ef" d="M21.6 56.6L2 58.2L8.1 5l19-1.2z"/><path fill="#bbc3c7" d="m43.4 62l-21.8-5.4l5.5-52.8l13.8 6z"/><path fill="#dfe9ef" d="M62 55.2L43.4 62L40.9 9.8l15-7.8z"/><path fill="#5df6ff" d="M22.1 52.1L6.8 53.3l4.7-44.8l15.2-.9z"/><path fill="#00c0cf" d="m43.2 58l-21.1-5.9l4.6-44.5l14.4 6z"/><path fill="#5df6ff" d="M57.9 52.5L43.2 58l-2.1-44.4L53 7.9z"/><g opacity=".7"><path fill="#f361f5" d="M21.6 36c-.3.1-.6.3-.8.5l.1.1C19 40.1 22 41.3 22 41.3c-1.2 6.1 1.3 8.1 1.3 8.1c-.6-4.6 3-7.6 3-7.6c1-4.7-3.3-5.6-4.7-5.8"/><path fill="#edba53" d="M20.4 35.6c-1.8-1.2-1.8-3.1-1.8-3.1c3 0 6.3-4.4 6.3-4.4c-2.2-7-3.1-1.1-3.1-1.1c-3.2-3.4.9-5 .9-5c-2.5-1-3.6-3.8-3.6-3.8c-.1 1.8-.9 1.7-.9 1.7c-5.7-3.1-6.7 1.9-6.7 1.9c.9 2.7-1 3-1 3c1.9.3 2.9-2.1 2.9-2.1c3.9 1.8 1.8 8.4 1.8 8.4c1.5.8 1.9 3.6 1.9 3.6c1.1.3 2.1.9 2.9 1.4c.1-.3.2-.4.4-.5"/><path fill="#eef8fb" d="M26.4 16.1s-1.7-1.1-4.5.6c0 0-.7 6.3 4.5-.6"/><path fill="#5f5cff" d="M30.6 17.5c-2.2-3.6-5.8 1.3-5.8 1.3c1.3 1 1.4 1.9 1.2 2.7c-.9 3 .3 3.9.3 3.9c.7-2.5 3.1-2.9 3.1-2.9c-.3-2.3 1.2-5 1.2-5"/><path fill="#ff7033" d="M36.7 33.7c-.2.1-.4.1-.6 0c-.5-.5-.8-1.1-1.1-1.7c-.1-.1-.1-.2-.1-.3c-1.7-.6-5-1.2-5.5 3.4c0 0 .6 3.1 2.7 1.5c0 0 2.3.4 1.4 4.5c0 0 .4 3.2 2.4 2.9c0 0 .5-4.6 3.1-8.2c-.1.1-1.1 1.1-2.3-2.1"/><path fill="#6c732d" d="M53.3 18.1s-4.9 1.7-10.3 1.2c0 0 .5-1.6-1.4-1.8c0 0-2.5 3-1.4 4.7c0 0-.5 3-1.1-1.4c0 0-1 2.6-2.5 2c0 0-.4 2.6-1.3.9c0 0 .7.3.9-.5c0 0-2.1-2.1-3.3 1.1c0 0-1 .4-.6 1.8c0 0 .7-1.2 1.1.6c0 0 .4-.3.5-2.6c0 0 1-.5.2 1.4c0 0 2.2 0-1.2 1.9c0 0-.2 0-.3-.7c0 0-.5 1.7-1.6 2.3c0 0 .3.7-.5 1c0 0-.4 1.1.6 1.1c0 0 .9-2.5 2.6-.5c0 0 .3-.1-.2-1.1c0 0 .9.3 1.2 1.6c0 0 .5-.1.6-1.2c0 0 .7-1.5 1.8.3c0 0-1.5-.4-1.9 1c0 0 .7-.3 1.1.1c0 0 0 .7-.4.9c.2.3.4.7.7.9c.2.2.2.4.1.6c.8 1.3 2.2 2.9 3.3.3c0 0-1.4.7-1.8-1.3c0 0 3.5.4 4.4 3.9c0 0 1-.7 1.3-3c0 0 1.2.1 2.6 3c0 0 .6-.1.3-2c0 0 2.3-.9 1-4.6c0 0 1.9 2.5 1.3-.7c0 0 2.1-2.4-.4-4c0 0 .1-1.8 1.6-1.5c0 0 1.5-3.7.8.7c0 0 0 .9 1 1.8c0 0 .4-1.4-.3-3c.2-.2 1.7-2.9 1.5-5.2"/><path fill="#f361f5" d="M31.9 27.8c.4-.9-.9-1.8-.9-1.8c.2 2 .5 2.6.9 1.8m-1.6.1s.6-.1.5-.8c0 0-.5 0-.5.8m-.2-4.1s-1.6 0-1 .8c.5.7 1-.8 1-.8"/><path fill="#edff54" d="M38.2 42.5s.5-.9.5-2.4c0 0-1.6 1.7-.5 2.4"/><path fill="#eef8fb" d="M48.4 36.6s-1.1 1-.9 1.9c.2.9 1.2-.2.9-1.9"/><path fill="#edff54" d="M53 39.8c.5-.8-2.7-1.8-2.7-1.8c.6.8 2.2 2.5 2.7 1.8"/><path fill="#f361f5" d="M49.3 39.9c.8-1.2-2.3-.6-2.3-.6s2 1.1 2.3.6"/><path fill="#f33" d="M50.5 40.2s-.6 1.3-2.9 1.8c0 0 .5 1 0 3c0 0 2.3-2.7 3.6 1.2c0 0 1.1.6 1.9-3c0 0-1-1.3-1.3-3.1c.1 0-.2 2.3-1.3.1m-1-8.7s2.6-.9 1.5-3c.1-.1.3-.2.3-.2c-1.1-1.7-.7.7-.7.7c.1-.2.2-.3.2-.4c0 .8-.1 1.9-1.3 2.9m-26.9-8s1.9 2.7 2.1-.3c.1-.7-.2-.8-.5-.8c-.1-2-1.5-2.6-1.5-2.6c-.9.5-.8 1.2-.8 1.2c.8.1 1.3 1.1 1.6 1.9c-.1.3-.4.6-.9.6"/><path fill="#edff54" d="M31.3 15.9c.6-1.2-1.2-2.6-1.2-2.6c.2 2.9.6 3.8 1.2 2.6"/><path fill="#fff" d="M29.8 14.8s-.7.1-.7 1.2c0 0 .9-.1.7-1.2"/></g></g></svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="15" height="15" viewBox="0 0 48 62" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" fill="white" fill-opacity="0.01"/>
<path d="M4 11.9143L24 19L44 11.9143L24 5L4 11.9143Z" fill="#fdfdfd" stroke="#fdfdfd" stroke-width="4" stroke-linejoin="round"/>
<path d="M4 20L24 27L44 20" stroke="#fdfdfd" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 27.9999L24 34.9999L44 27.9999" stroke="#fdfdfd" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4 36L24 43L44 36" stroke="#fdfdfd" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
import { Component, ReactNode } from "react"
import { withLifeCyle } from "@mars/widgets/common/uses/useLifecycle"
import { isActive, activate, disable } from "@mars/widgets/common/store/widget"
import * as mapWork from "./map"
class ToolButton extends Component {
constructor(props) {
super(props)
mapWork.eventTarget.on("openManageLayer", () => {
if (!isActive("layers")) {
activate("layers")
} else {
disable("layers")
}
})
mapWork.eventTarget.on("openManageBasemaps", () => {
if (!isActive("manage-basemap")) {
activate("manage-basemap")
} else {
disable("manage-basemap")
}
})
}
render(): ReactNode {
return <></>
}
}
export default withLifeCyle(ToolButton, mapWork)
/**
* 电子沙盘
*
* @copyright 火星科技 mars3d.cn
* @author 火星渣渣灰 2022-01-05
*/
import * as mars3d from "mars3d"
import iconLayer from "./icon/manager-layers.svg"
import iconBasemaps from "./icon/manager-basemaps.svg"
export const eventTarget = new mars3d.BaseClass()
let map: mars3d.Map // mars3d.Map三维地图对象
let layersTool: mars3d.control.ToolButton
let basemapsTool: mars3d.control.ToolButton
/**
* 初始化地图业务,生命周期钩子函数(必须)
* 框架在地图初始化完成后自动调用该函数
* @param {mars3d.Map} mapInstance 地图对象
* @returns {void} 无
*/
export function onMounted(mapInstance: mars3d.Map): void {
if (!mapInstance) {
return
}
map = mapInstance // 记录map
layersTool = new mars3d.control.ToolButton({
title: "图层控制",
icon: iconLayer,
insertIndex: 1, // 插入的位置顺序, 1是home按钮后面
click: () => {
eventTarget.fire("openManageLayer")
}
})
map.addControl(layersTool)
if (!map.controls.baseLayerPicker && map.options.basemaps?.length > 0) {
basemapsTool = new mars3d.control.ToolButton({
title: "底图切换",
icon: iconBasemaps,
insertIndex: 1, // 插入的位置顺序, 1是home按钮后面
click: () => {
eventTarget.fire("openManageBasemaps")
}
})
map.addControl(basemapsTool)
}
}
/**
* 释放当前地图业务的生命周期函数
* @returns {void} 无
*/
export function onUnmounted() {
console.log("卸载了")
eventTarget.off()
map.removeControl(basemapsTool)
map.removeControl(layersTool)
basemapsTool = null
layersTool = null
map = null
}
import { useEffect, useMemo } from "react"
import { useEffect, useMemo, Component } from "react"
export function useLifecycle(mapWork: any) {
const mapInstance = window._mapInstance
useMemo(() => {
if (mapWork.onMounted) {
mapWork.onMounted(mapInstance)
mapWork.onMounted(window._mapInstance)
}
}, [])
......@@ -17,3 +16,26 @@ export function useLifecycle(mapWork: any) {
[]
)
}
export function withLifeCyle(WrappedComponent: any, mapWork) {
class WithLifeCyle extends Component {
constructor(props) {
console.log("WithLifeCyle onMounted执行")
super(props)
if (mapWork.onMounted) {
mapWork.onMounted(window._mapInstance)
}
}
componentWillUnmount() {
if (mapWork.onUnmounted) {
mapWork.onUnmounted()
}
}
render() {
return <WrappedComponent {...this.props} />
}
}
return WithLifeCyle
}
......@@ -6,9 +6,29 @@ const widgetState: WidgetState = {
{
component: lazy(() => import("@mars/widgets/basic/GraphicEditor")),
name: "GraphicEditor"
},
{
component: lazy(() => import("@mars/widgets/basic/SearchPoi")),
name: "SearchPoi"
},
{
component: lazy(() => import("@mars/widgets/basic/ManageBasemap")),
name: "manage-basemap",
group: "manage"
},
{
component: lazy(() => import("@mars/widgets/basic/Layer")),
name: "layers",
group: "manage"
},
{
component: lazy(() => import("@mars/widgets/basic/ToolButton")),
name: "tools-button",
disableOther: false,
autoDisable: false
}
],
openAtStart: []
openAtStart: ["tools-button"]
}
export default widgetState
......@@ -50,6 +50,9 @@ export default ({ mode }: ConfigEnv) => {
javascriptEnabled: true,
additionalData: `@import "${path.resolve(__dirname, "src/components/MarsUI/base.less")}";`
}
},
modules: {
localsConvention: "camelCase"
}
},
build: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论