# @LandGIS/Frontend-SDK

🗺️ **基于OpenLayers的配置驱动GIS前端开发套件**

[![npm version](https://badge.fury.io/js/@landgis%2Ffrontend-sdk.svg)](https://badge.fury.io/js/@landgis%2Ffrontend-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## ✨ 特性

- 🎯 **配置驱动**: 90%的功能通过JSON配置实现，无需编写底层GIS代码
- 🗺️ **强依赖OpenLayers**: 基于OpenLayers 8.x构建，提供强大的地图功能
- 🔌 **服务抽象化**: 支持任意HTTP接口作为数据源，内置数据格式转换
- 🧩 **动态组件化**: 面板、工具等组件可通过配置动态加载
- 🎯 **自由拖拽**: 面板和工具栏支持自由拖拽、一键关闭
- ✅ **内置检验引擎**: 支持几何、属性、空间关系等多种检验规则
- 🎨 **现代化UI**: 支持深色模式、响应式设计
- 📱 **移动端友好**: 适配移动设备和触摸操作
- 🔧 **动态组件支持**: 支持自定义面板组件，可获取地图对象进行交互

## 🚀 快速开始

### 安装

```bash
npm install @landgis/frontend-sdk
# 或
yarn add @landgis/frontend-sdk
# 或
pnpm add @landgis/frontend-sdk
```

### 基础使用

```vue
<template>
  <GisAppContainer :config="myConfig" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { GisAppContainer } from '@landgis/frontend-sdk'

const myConfig = ref(null)

onMounted(async () => {
  // 从服务器获取配置或使用本地配置
  myConfig.value = {
    map: {
      projection: "EPSG:4549", // 西安2000坐标系
      initialView: {
        center: [113.32, 23.13],
        zoom: 12
      },
      constrainResolution: true
    },
    layers: [
      {
        id: "base_map",
        title: "矢量底图",
        type: "XYZ",
        url: "https://t0.tianditu.gov.cn/vec_c/wmts?tk=YOUR_KEY",
        isBaseLayer: true,
        visible: true
      }
    ],
    panels: {
      layerManager: { 
        enabled: true, 
        position: "left" 
      }
    },
    toolbar: ["ZoomIn", "ZoomOut", "FullScreen"],
    services: {},
    validationRules: []
  }
})
</script>
```

## 🎯 拖拽功能演示

查看完整的拖拽功能演示：

```bash
# 在浏览器中打开示例页面
open examples/draggable-panels-example.html
```

**功能特性：**
- **拖拽移动**：点击面板标题栏或工具栏手柄拖拽到任意位置
- **自由调整**：面板支持拖拽右下角调整大小
- **状态切换**：支持最小化、关闭、恢复操作
- **视觉反馈**：拖拽过程中提供流畅的视觉反馈

## 📚 核心概念

### 1. 配置驱动架构

整个应用的行为由一个 `GlobalConfig` 对象定义：

```typescript
interface GlobalConfig {
  map: MapConfig              // 地图配置
  layers: LayerConfig[]       // 图层配置
  panels: PanelConfig         // 面板配置
  toolbar: ToolbarItem[]      // 工具栏配置
  services: Record<string, ServiceEndpointConfig>  // 服务配置
  validationRules?: ValidationRule[]  // 检验规则
  theme?: ThemeConfig         // 主题配置
}
```

### 2. 图层类型支持

- **XYZ**: 瓦片服务
- **WMS**: Web地图服务
- **WFS**: Web要素服务
- **WMTS**: Web地图瓦片服务
- **VectorTile**: 矢量瓦片
- **GeoJSON**: GeoJSON数据
- **CUSTOM_API**: 自定义API数据源

### 3. 服务抽象化

通过配置连接任意第三方API：

```json
{
  "services": {
    "getEnterpriseAPI": {
      "url": "https://api.example.com/enterprises",
      "method": "GET",
      "params": {
        "bbox": "{bbox}",  // 动态参数
        "apiKey": "YOUR_KEY"
      },
      "response": {
        "dataPath": "data.records",
        "idPath": "id",
        "geometryPath": "geometry"
      }
    }
  }
}
```

### 4. 拖拽系统

所有面板和工具栏都支持自由拖拽功能：

- **🖱️ 自由拖拽**: 点击标题栏或工具栏手柄即可拖拽
- **🚧 边界限制**: 面板拖拽自动限制在容器范围内
- **📏 自由调整**: 浮动面板支持拖拽调整大小
- **🔄 状态管理**: 支持最小化、关闭、恢复等状态切换
- **🎨 流畅体验**: 防止拖拽跳动，拖拽时显示蓝色虚线框提示

```typescript
// 通过组合式函数使用拖拽功能
import { useDrag } from '@landgis/frontend-sdk'

const {
  registerElement,   // 注册可拖拽元素
  startDrag,        // 开始拖拽
  closePanel,       // 关闭面板
  toggleMinimize,   // 切换最小化
  showPanel,        // 显示面板
  getVisiblePanels  // 获取可见面板
} = useDrag()
```

### 5. 检验规则引擎

支持多种检验类型：

```json
{
  "validationRules": [
    {
      "ruleId": "AreaCheck_01",
      "title": "地块面积应大于100平方米",
      "targetLayerId": "dltb_2024",
      "type": "GEOMETRY",
      "condition": {
        "operator": "AREA_GREATER_THAN",
        "value": 100
      }
    }
  ]
}
```

### 6. 动态组件系统

支持自定义面板组件，可获取地图对象进行交互：

```typescript
// 1. 定义自定义组件
const MyCustomPanel = {
  setup(props: { mapController?: MapController }) {
    const zoomIn = () => {
      if (props.mapController) {
        const zoom = props.mapController.getZoom() || 10
        props.mapController.setZoom(zoom + 1)
      }
    }
    
    return { zoomIn }
  },
  template: `
    <div>
      <h3>自定义面板</h3>
      <button @click="zoomIn">放大地图</button>
    </div>
  `
}

// 2. 注册组件
import { registerComponent } from '@landgis/frontend-sdk'

registerComponent('MyCustomPanel', {
  name: 'MyCustomPanel',
  component: MyCustomPanel,
  needsMapController: true
})

// 3. 配置使用
const config = {
  panels: {
    myCustomPanel: {
      enabled: true,
      position: "right",
      componentType: "custom",
      componentName: "MyCustomPanel",
      title: "我的面板",
      needsMapController: true
    }
  }
}
```

## 🔧 API参考

### 组件

#### GisAppContainer

主容器组件，SDK的入口点。

```vue
<GisAppContainer
  :config="globalConfig"
  width="100%"
  height="600px"
  @ready="onMapReady"
  @error="onError"
  @map-event="onMapEvent"
  @validation-event="onValidationEvent"
/>
```

**Props:**
- `config: GlobalConfig` - 全局配置对象
- `width?: string` - 容器宽度，默认 '100%'
- `height?: string` - 容器高度，默认 '100vh'

**Events:**
- `ready(mapController)` - 地图初始化完成
- `error(error)` - 错误事件
- `map-event(event)` - 地图事件
- `validation-event(event)` - 检验事件

#### DraggablePanel

可拖拽面板包装器，为面板添加拖拽功能，支持多种插槽自定义面板内容。

```vue
<DraggablePanel
  id="my-panel"
  title="我的面板"
  :initial-position="{ x: 100, y: 100 }"
  :initial-size="{ width: 300, height: 400 }"
  :closable="true"
  :minimizable="true"
  :resizable="true"
  @close="onPanelClose"
  @minimize="onPanelMinimize"
  @resize="onPanelResize"
>
  <!-- 标题栏工具栏 -->
  <template #toolbar>
    <button class="tool-btn">🔍 查询</button>
    <button class="tool-btn">📊 统计</button>
  </template>
  
  <!-- 内容顶部 -->
  <template #content-top>
    <div class="info-bar">当前选中: 3 个要素</div>
  </template>
  
  <!-- 主要内容 -->
  <MyPanelContent />
  
  <!-- 底部状态栏 -->
  <template #footer>
    <div class="status-bar">状态：就绪</div>
  </template>
</DraggablePanel>
```

**支持的插槽：**
- `header` - 完全自定义标题栏
- `title` - 自定义标题内容
- `toolbar` - 标题栏工具栏
- `controls` - 自定义控制按钮
- `content-top` - 内容区域顶部
- `content` - 主要内容区域
- `content-bottom` - 内容区域底部
- `footer` - 面板底部

详细用法请参考 [面板插槽使用指南](./docs/panel-slots-guide.md)

#### DraggableToolbar

可拖拽工具栏包装器，为工具栏添加拖拽功能。

```vue
<DraggableToolbar
  id="my-toolbar"
  :initial-position="{ x: 10, y: 10 }"
  orientation="horizontal"
  :closable="true"
  @close="onToolbarClose"
>
  <MyToolButtons />
</DraggableToolbar>
```

### 组合式函数

#### useMap()

提供地图操作的便捷接口：

```typescript
import { useMap } from '@landgis/frontend-sdk'

const {
  getCenter,
  setCenter,
  getZoom,
  setZoom,
  zoomIn,
  zoomOut,
  zoomToExtent,
  addLayer,
  removeLayer,
  setLayerVisible
} = useMap()
```

#### useStore()

状态管理：

```typescript
import { useAppStore } from '@landgis/frontend-sdk'

const store = useAppStore()
store.setLoading(true)
store.updateLayerVisibility('layerId', false)
```

#### useDrag()

拖拽功能：

```typescript
import { useDrag } from '@landgis/frontend-sdk'

const {
  panelStates,       // 面板状态
  dragState,         // 拖拽状态
  registerElement,   // 注册元素
  startDrag,         // 开始拖拽
  closePanel,        // 关闭面板
  toggleMinimize,    // 切换最小化
  showPanel,         // 显示面板
  getVisiblePanels   // 获取可见面板
} = useDrag()
```

#### 动态组件注册

管理自定义面板组件：

```typescript
import { 
  registerComponent, 
  getComponent, 
  hasComponent, 
  getAllComponents,
  type ComponentInfo 
} from '@landgis/frontend-sdk'

// 注册组件
registerComponent('MyPanel', {
  name: 'MyPanel',
  component: MyPanelComponent,
  needsMapController: true
})

// 检查组件是否已注册
const exists = hasComponent('MyPanel')

// 获取所有注册的组件
const allComponents = getAllComponents()
```

### 核心类

#### MapController

地图控制器，封装OpenLayers功能：

```typescript
import { MapController } from '@landgis/frontend-sdk'

const mapController = new MapController()
await mapController.initialize(container, mapConfig)
```

#### LayerFactory

图层工厂，根据配置创建图层：

```typescript
import { LayerFactory } from '@landgis/frontend-sdk'

const factory = new LayerFactory(serviceAgent)
const layer = await factory.createLayer(layerConfig)
```

#### ServiceAgent

服务代理，处理第三方API：

```typescript
import { ServiceAgent } from '@landgis/frontend-sdk'

const agent = new ServiceAgent()
agent.registerService('serviceId', serviceConfig)
const data = await agent.fetchData('serviceId', params)
```

#### ValidationEngine

检验引擎：

```typescript
import { ValidationEngine } from '@landgis/frontend-sdk'

const engine = new ValidationEngine()
engine.registerRules(validationRules)
const results = await engine.validateAll()
```

## 📋 配置示例

### 完整配置示例

参见 [examples/basic-example.json](./examples/basic-example.json)

### 图层配置

```json
{
  "layers": [
    {
      "id": "dltb_2024",
      "title": "2024年度地类图斑",
      "type": "WFS",
      "url": "http://geoserver.com/wfs",
      "params": {
        "typeName": "ns:dltb_2024",
        "srsName": "EPSG:4549"
      },
      "visible": true,
      "style": {
        "fill": { "color": "rgba(255, 165, 0, 0.6)" },
        "stroke": { "color": "#FF8C00", "width": 2 }
      }
    }
  ]
}
```

### 面板配置

```json
{
  "panels": {
    "layerManager": {
      "enabled": true,
      "position": "left",
      "collapsible": true
    },
    "search": {
      "enabled": true,
      "position": "left",
      "serviceId": "searchPOI",
      "placeholder": "搜索地点..."
    },
    "validation": {
      "enabled": true,
      "position": "right",
      "autoRun": false
    }
  }
}
```

## 🛠️ 开发

### 项目结构

```
src/
├── components/           # Vue组件
│   ├── GisAppContainer.vue  # 主容器组件
│   ├── panels/             # 面板组件
│   └── tools/              # 工具组件
├── core/                 # 核心逻辑
│   ├── MapController.ts    # 地图控制器
│   ├── LayerFactory.ts     # 图层工厂
│   ├── ServiceAgent.ts     # 服务代理
│   └── ValidationEngine.ts # 检验引擎
├── composables/          # 组合式函数
│   ├── useMap.ts          # 地图操作
│   └── useStore.ts        # 状态管理
├── types/                # 类型定义
│   └── config.d.ts       # 配置类型
└── utils/               # 工具函数
```

### 本地开发

```bash
# 安装依赖
npm install

# 开发模式
npm run dev

# 构建
npm run build

# 类型检查
npm run type-check

# 代码检查
npm run lint
```

## 🤝 贡献

欢迎贡献代码！请遵循以下步骤：

1. Fork 本仓库
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request

## 🔌 插槽自定义功能

面板组件支持多种插槽，让您可以灵活自定义面板的各个部分：

```vue
<!-- 基础使用 -->
<DraggablePanel id="basic" title="基础面板">
  <div>面板内容</div>
</DraggablePanel>

<!-- 带工具栏的面板 -->
<DraggablePanel id="toolbar" title="工具面板">
  <template #toolbar>
    <button class="tool-btn">🔍 查询</button>
    <button class="tool-btn">📊 分析</button>
  </template>
  <div>主要内容</div>
</DraggablePanel>

<!-- 完全自定义标题栏 -->
<DraggablePanel id="custom">
  <template #header="{ toggleMinimize, closePanel }">
    <div class="custom-header">
      <span>🗺️ 地图工具 v2.0</span>
      <button @click="toggleMinimize">➖</button>
      <button @click="closePanel">✕</button>
    </div>
  </template>
  <div>内容区域</div>
</DraggablePanel>

<!-- 多区域自定义 -->
<DraggablePanel id="advanced" title="高级面板">
  <template #content-top>
    <div class="status">当前状态：已连接</div>
  </template>
  
  <div>主要内容区域</div>
  
  <template #content-bottom>
    <div class="actions">
      <button>保存</button>
      <button>取消</button>
    </div>
  </template>
  
  <template #footer>
    <div class="info">共 100 项 | 最后更新：刚刚</div>
  </template>
</DraggablePanel>
```

查看完整的插槽使用示例：

```bash
# 在浏览器中打开插槽示例页面
open examples/custom-panel-slots-example.html
```

## 📖 文档

- [快速开始指南](./docs/quick-start.md)
- [配置参考](./docs/configuration.md)
- [API文档](./docs/api.md)
- [插槽使用指南](./docs/panel-slots-guide.md)
- [动态组件使用指南](./docs/dynamic-components-guide.md)
- [示例集合](./examples/)

## 🐛 问题反馈

如果您发现了bug或有功能建议，请在 [Issues](https://github.com/landgis/frontend-sdk/issues) 中提交。

## 📄 许可证

本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。

## 🙏 致谢

- [OpenLayers](https://openlayers.org/) - 强大的地图引擎
- [Vue.js](https://vuejs.org/) - 渐进式JavaScript框架
- [TypeScript](https://www.typescriptlang.org/) - JavaScript的超集

---

**由 LandGIS 团队用 ❤️ 构建** 