diff --git a/assetsMap/boundary-doc.md b/assetsMap/boundary-doc.md new file mode 100644 index 0000000..0d7a470 --- /dev/null +++ b/assetsMap/boundary-doc.md @@ -0,0 +1,149 @@ +# 资产边界数据配置说明 + +## 数据格式 + +边界数据存储在表单字段 **"资产边界数据"** 中,格式为 JSON 字符串。 + +## 整体结构 + +```json +{ + "regions": [ 区域1, 区域2, ... ], + "color": "#ff0000" +} +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `regions` | 数组 | 区域列表,每个区域是一个独立的外圈(可带孔洞) | +| `color` | 字符串 | 边界颜色,十六进制色值,如 `#ff0000`(红色)、`#3498db`(蓝色) | + +## 单个区域结构 + +```json +{ + "id": 1780905134732976, + "outer": [ 坐标点1, 坐标点2, ... ], + "holes": [ 孔洞1, 孔洞2, ... ] +} +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `id` | 数字 | 区域唯一标识(系统自动生成的随机整数),手动配置时可随意填一个不重复的数字 | +| `outer` | 数组 | 外圈边界坐标,至少 3 个点 | +| `holes` | 数组 | 孔洞列表,每个孔洞也是一个坐标数组(至少 3 个点),可为空数组 `[]` | + +## 坐标格式 + +每个坐标点为 **`[纬度, 经度]`**,注意顺序是 **纬度在前,经度在后**。 + +示例:`[30.68239, 111.31176]` + +## 配置示例 + +### 1. 单个简单区域(无孔洞) + +```json +{ + "regions": [ + { + "id": 100001, + "outer": [ + [30.68, 111.31], + [30.69, 111.38], + [30.74, 111.38], + [30.74, 111.34], + [30.74, 111.30], + [30.71, 111.28] + ], + "holes": [] + } + ], + "color": "#ff0000" +} +``` + +### 2. 单个区域带一个孔洞 + +```json +{ + "regions": [ + { + "id": 100002, + "outer": [ + [30.68, 111.31], + [30.69, 111.38], + [30.74, 111.38], + [30.74, 111.30], + [30.71, 111.28] + ], + "holes": [ + [ + [30.70, 111.33], + [30.72, 111.37], + [30.73, 111.35], + [30.72, 111.32] + ] + ] + } + ], + "color": "#ff0000" +} +``` + +### 3. 多个独立区域 + +```json +{ + "regions": [ + { + "id": 100003, + "outer": [ + [30.68, 111.31], + [30.69, 111.38], + [30.74, 111.38], + [30.71, 111.28] + ], + "holes": [] + }, + { + "id": 100004, + "outer": [ + [30.68, 111.43], + [30.67, 111.50], + [30.74, 111.52], + [30.75, 111.44], + [30.71, 111.39] + ], + "holes": [ + [ + [30.69, 111.45], + [30.72, 111.50], + [30.74, 111.48], + [30.72, 111.41] + ] + ] + } + ], + "color": "#ff0000" +} +``` + +## 注意事项 + +1. **坐标顺序**:必须是 `[纬度, 经度]`,即 `[lat, lng]` +2. **最少点数**:每个外圈和孔洞至少需要 **3 个坐标点** +3. **孔洞范围**:孔洞必须完全位于其所属外圈内部 +4. **区域 ID**:`id` 为任意不重复的数字,建议使用时间戳+随机数 +5. **颜色值**:支持标准十六进制格式,如 `#ff0000`、`#3498db`、`#27ae60` +6. **多区域共用一个颜色**:所有区域使用同一个 `color` 值 +7. **字段名称**:存储在表单字段 **"资产边界数据"** 中 + +## 坐标获取方式 + +推荐使用坐标拾取工具获取经纬度: +- **高德坐标拾取器**:https://lbs.amap.com/tools/picker +- **百度坐标拾取器**:https://api.map.baidu.com/lbsapi/getpoint/index.html + +在地图上依次点击区域顶点,复制坐标填入即可。 diff --git a/assetsMap/map.html b/assetsMap/map.html index 755254d..f6126d3 100644 --- a/assetsMap/map.html +++ b/assetsMap/map.html @@ -1,609 +1,1001 @@ - - - 资产地图(聚合版) - - - - - - - - - - - - - - + + +
+
- .searchItem { - padding: 10px; - border-bottom: 1px solid #eee; - cursor: pointer; - } - - .searchItem:hover { - background: #f5f5f5 - } - - /* 右侧面板 */ - #assetPanel { - position: absolute; - top: 0; - right: 0; - width: 360px; - height: 100vh; - background: #fff; - box-shadow: -2px 0 10px rgba(0, 0, 0, 0.15); - transform: translateX(100%); - transition: 0.3s; - display: flex; - flex-direction: column; - z-index: 9999; - } - - #assetPanel.show { - transform: translateX(0); - } - - .panelHeader { - padding: 15px; - background: #f7f8fa; - border-bottom: 1px solid #eee; - } - - .panelTitle { - font-size: 17px; - font-weight: 800; - } - - .closeBtn { - float: right; - cursor: pointer; - font-size: 18px; - color: #888; - } - - .panelMeta { - padding: 10px 15px; - font-size: 13px; - color: #666; - border-bottom: 1px solid #eee; - } - - .assetList { - flex: 1; - overflow: auto; - } - - .assetItem { - cursor: pointer; - padding: 14px 16px; - border-bottom: 1px solid #f0f0f0; - } - - .marker-cluster-small { - background-color: rgba(231, 76, 60, 0.6) !important; - } - - .marker-cluster-small div { - background-color: rgba(231, 76, 60, 0.8) !important; - } - - - .marker-cluster-medium { - background-color: rgba(231, 76, 60, 0.6) !important; - } - - .marker-cluster-medium div { - background-color: rgba(231, 76, 60, 0.8) !important; - } - - - .marker-cluster-large { - background-color: rgba(192, 57, 43, 0.6) !important; - } - - .marker-cluster-large div { - background-color: rgba(192, 57, 43, 0.85) !important; - } - - .marker-cluster-custom { - background: transparent !important; - border: none !important; - } - - .marker-cluster-custom .cluster-inner { - width: 40px; - height: 40px; - border-radius: 50%; - background: rgba(231, 76, 60, 0.85); - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); - } - - .marker-cluster-custom .cluster-inner span { - color: #fff; - font-weight: bold; - font-size: 14px; - } - - .assetTenant, - .assetManager, - .assetOwner, - .assetStatus, - .assetPurpose, - .assetAddress, - .assetRemark, - .assetArea { - font-size: 16px; - color: #666; - margin-top: 3px; - } - - - - - -
- - - - -
-
- -
- 总资产: {{allAssetsCount}} +
+ +
+
+ 筛选条件 + +
+
+
+ +
+
+
+
+ {{ node.name }} + + +
+
+ +
+
- -
- -
- 已出租: {{rentedCount}} +
+ {{ filterOwner || '点击选择单位...' }}
- -
-
- 未出租: {{vacantCount}} -
- - - - - - +
+
+ + +
+
+ + +
+
+ +
- - -
-
- {{item.assetName || item.address}} -
-
- - -
- - -
-
- {{currentRegion.address}} - -
- -
- 资产数量:{{currentRegion.assets ? currentRegion.assets.length : 0}} -
- -
-
-
运营单位:{{a.manager}}
-
产权单位:{{a.owner}}
-
用途:{{a.purpose}}
-
地址:{{a.address}}
-
资产状态:{{a.status || 未知}}
-
面积:{{a.area}}㎡
-
备注:{{a.remark || '暂无'}}
- -
-
-
-
- + // ===================================================================== + // 筛选选项加载 + // ===================================================================== - - \ No newline at end of file + /** + * 并行加载筛选选项(所属单位 + 资产分类) + * @returns {Promise} 等待两个接口都完成 + */ + loadFilterOptions() { + var self = this; + return Promise.all([ + // 加载部门树 + fetch(this.host + "/seeyon/rest/assetsRegion/getDeptOptions") + .then(r => r.json()).then(d => { + if (d.code === 0) { + self.deptTree = d.data || []; + self.deptOptions = self.flattenDeptTree(self.deptTree); + } + }), + // 加载资产分类 + fetch(this.host + "/seeyon/rest/assetsRegion/getCategoryOptions") + .then(r => r.json()).then(d => { if (d.code === 0) self.categoryOptions = d.data || {}; }) + ]); + }, + + /** + * 将树形部门数据扁平化为 {name: id} 映射 + * 用于筛选下拉框的选项值 + * @param {Array} tree - 树形数据 + * @returns {Object} 扁平化映射 + */ + flattenDeptTree(tree) { + var result = {}; + function walk(nodes) { + if (!nodes) return; + nodes.forEach(function(node) { + result[node.name] = node.id; + if (node.children && node.children.length > 0) walk(node.children); + }); + } + walk(tree); + return result; + }, + + // ===================================================================== + // 级联选择器交互 + // ===================================================================== + + /** + * 级联选择器节点点击 + * @param {Object} node - 当前点击的节点 + * @param {number} level - 当前层级(0=根) + */ + cascadeClick(node, level) { + this.cascadeActiveId = node.id; + if (node.children && node.children.length > 0) { + // 有子级:展开下一级列 + var newChildren = this.cascadeChildren.slice(0, level); + newChildren[level] = node.children; + this.cascadeChildren = newChildren; + } else { + // 无子级:选中该单位并关闭选择器 + this.filterOwner = node.name; + this.showCascade = false; + } + }, + + /** 清除级联选择器选中状态 */ + clearCascadeSelect() { + this.filterOwner = ''; + this.cascadeActiveId = null; + this.cascadeChildren = []; + }, + + // ===================================================================== + // 筛选与查询 + // ===================================================================== + + /** 重置所有筛选条件并清空地图标点和边界 */ + resetFilter() { + this.filterOwner = ''; this.filterCatagory = ''; this.keyword = ''; + this.assetList = []; this.queried = false; this.total = 0; + this.markerGroup.clearLayers(); + this.clearAllBoundLayers(); + this.markerMap = {}; + this.clearActiveMarker(); + this.activeAssetId = null; + }, + + /** + * 清除所有marker的边界图层 + * 遍历 markerBoundLayers 中每个 layerGroup 并从地图移除 + */ + clearAllBoundLayers() { + var self = this; + Object.keys(this.markerBoundLayers).forEach(function(key) { + self.map.removeLayer(self.markerBoundLayers[key]); + }); + this.markerBoundLayers = {}; + }, + + /** + * 查询资产列表 + * - 清空现有标点和边界 + * - 调用 queryAssetList 接口获取分页数据 + * - 成功后构建标点 + */ + queryAssets() { + var self = this; + this.queried = true; + // 清空现有图层 + this.markerGroup.clearLayers(); + this.clearAllBoundLayers(); + this.markerMap = {}; + this.clearActiveMarker(); + this.activeAssetId = null; + + // 构建查询参数 + var body = { + owner: this.filterOwner, + level3: this.filterCatagory, + keyword: this.keyword, + pageNum: 1, + pageSize: 20 + }; + + fetch(this.host + "/seeyon/rest/assetsRegion/queryAssetList", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body) + }).then(r => r.json()).then(d => { + if (d.code === 0) { + self.assetList = d.data.records || []; + self.total = d.data.total || 0; + self.buildMarkers(); + } + }); + }, + + // ===================================================================== + // 标点构建(按经纬度分组) + // ===================================================================== + + /** + * 按经纬度分组构建标点 + * 核心逻辑: + * 1. 将资产按 lat,lng 分组(toFixed(8) 避免浮点精度问题) + * 2. 每组创建一个标点,标点上挂载 _assets 数组 + * 3. 绑定点击事件:选中 + 弹框 + 高亮边界 + * 4. 自动加载该标点所有资产的边界 + */ + buildMarkers() { + this.markerGroup.clearLayers(); + this.markerMap = {}; + this.markerBoundLayers = {}; + var self = this; + + // Step 1: 按经纬度分组 + var groups = {}; + this.assetList.forEach(function(a) { + var lat = parseFloat(a.lat); + var lng = parseFloat(a.lng); + if (isNaN(lat) || isNaN(lng)) return; + var key = lat.toFixed(8) + ',' + lng.toFixed(8); + if (!groups[key]) groups[key] = { lat: lat, lng: lng, assets: [] }; + groups[key].assets.push(a); + }); + + // Step 2: 为每组创建标点 + Object.keys(groups).forEach(function(key) { + var group = groups[key]; + var m = L.marker([group.lat, group.lng], { icon: self.markerIcon }); + m._groupKey = key; // 分组key,用于关联边界图层 + m._assets = group.assets; // 该位置的所有资产 + m._activeIdx = 0; // 当前展示的资产索引 + + // Step 3: 标点点击事件 + m.on('click', function() { + self.highlightMarker(m); + var activeIdx = m._activeIdx || 0; + // 注释:切换单资产边界逻辑已屏蔽,改为一次性加载所有资产边界 + // self.loadMarkerBound(m, activeIdx); + self.activeAssetId = m._assets[activeIdx].id; + self.highlightBoundForAsset(m._assets[activeIdx].id); + self.showAssetPopup(m, m._assets[activeIdx]); + self.scrollListToAsset(m._assets[activeIdx].id); + // 多资产时额外弹出切换弹框 + if (m._assets.length > 1) { + self.showSwitchPopup(m); + } + }); + + m.addTo(self.markerGroup); + self.markerMap[key] = m; + + // Step 4: 自动加载该标点所有资产的边界 + self.loadAllMarkerBounds(m); + }); + }, + + // ===================================================================== + // 边界加载与高亮 + // ===================================================================== + + /** + * 为指定资产的边界添加斜线填充效果 + * 选中:区域内部填充斜线纹理(SVG pattern),边框加粗 + * 非选中:恢复默认纯色填充 + * @param {string} assetId - 要高亮的资产ID + */ + highlightBoundForAsset(assetId) { + this._ensureHatchPattern(); + var self = this; + Object.keys(this.markerBoundLayers).forEach(function(key) { + var layerGroup = self.markerBoundLayers[key]; + layerGroup.eachLayer(function(layer) { + if (!layer.setStyle) return; + if (layer._assetId === assetId) { + // 选中:斜线填充 + 加粗边框 + layer.setStyle({ weight: 3, fillOpacity: 1 }); + layer.bringToFront(); + // 用SVG pattern替换填充为斜线纹理 + if (layer._path) { + layer._path.setAttribute('fill', 'url(#hatch)'); + } + } else { + // 非选中:恢复默认纯色填充 + layer.setStyle({ weight: 2, fillOpacity: 0.3 }); + if (layer._path) { + layer._path.setAttribute('fill', layer.options.fillColor || layer.options.color); + } + } + }); + }); + }, + + /** + * 加载标点内所有资产的边界(一次性全部加载) + * 每个资产的边界独立请求,渲染到同一个 layerGroup + * polygon 通过 _assetId 属性关联到对应资产,用于后续高亮 + * @param {L.Marker} m - 标点对象,包含 _assets 数组和 _groupKey + */ + loadAllMarkerBounds(m) { + var self = this; + // 清除该marker旧的边界图层 + if (self.markerBoundLayers[m._groupKey]) { + self.map.removeLayer(self.markerBoundLayers[m._groupKey]); + } + var layerGroup = L.layerGroup().addTo(self.map); + self.markerBoundLayers[m._groupKey] = layerGroup; + + // 遍历每个资产,异步加载边界 + m._assets.forEach(function(asset) { + var assetId = asset.id || asset.assetCode; + if (!assetId) return; + var url = self.host + "/seeyon/rest/assetsRegion/getAssetBound?assetId=" + encodeURIComponent(assetId); + fetch(url) + .then(function(r) { return r.json(); }) + .then(function(d) { + if (d.code === 0 && d.data && d.data.boundData) { + var bound = typeof d.data.boundData === 'string' ? JSON.parse(d.data.boundData) : d.data.boundData; + var regions = self.normalizeBoundary(bound); + var color = bound.color || '#e74c3c'; + regions.forEach(function(region) { + if (!region.outer || region.outer.length < 3) return; + // rings: [外圈坐标, ...孔洞坐标] + var rings = [region.outer].concat(region.holes || []); + var polygon = L.polygon(rings, { + color: color, fillColor: color, weight: 2, fillOpacity: 0.3 + }); + polygon._assetId = asset.id; // 标记归属资产,用于高亮匹配 + layerGroup.addLayer(polygon); + }); + } + }) + .catch(function(err) { console.error('[map] 加载边界失败:', err); }); + }); + }, + + /** + * 加载指定marker的单个资产的边界(暂保留,供后续切换边界功能使用) + * 当前已屏蔽调用,改为 loadAllMarkerBounds 一次性加载 + * @param {L.Marker} m - 标点对象 + * @param {number} assetIdx - 资产在 _assets 数组中的索引 + */ + loadMarkerBound(m, assetIdx) { + var self = this; + var asset = m._assets[assetIdx]; + if (!asset) { console.warn('[map] asset为空, idx:', assetIdx); return; } + var assetId = asset.id || asset.assetCode; + if (!assetId) { console.warn('[map] assetId为空:', asset); return; } + + // 清除该marker旧的边界图层 + if (self.markerBoundLayers[m._groupKey]) { + self.map.removeLayer(self.markerBoundLayers[m._groupKey]); + } + var layerGroup = L.layerGroup().addTo(self.map); + self.markerBoundLayers[m._groupKey] = layerGroup; + m._activeIdx = assetIdx; + self.activeAssetId = asset.id; + + var url = self.host + "/seeyon/rest/assetsRegion/getAssetBound?assetId=" + encodeURIComponent(assetId); + console.log('[map] loadMarkerBound, assetId:', assetId, 'assetName:', asset.assetName); + fetch(url) + .then(function(r) { return r.json(); }) + .then(function(d) { + console.log('[map] getAssetBound返回:', d); + if (d.code === 0 && d.data && d.data.boundData) { + var bound = typeof d.data.boundData === 'string' ? JSON.parse(d.data.boundData) : d.data.boundData; + console.log('[map] 解析后的边界:', bound); + var regions = self.normalizeBoundary(bound); + console.log('[map] regions:', regions.length, regions); + var color = bound.color || '#e74c3c'; + regions.forEach(function(region) { + if (!region.outer || region.outer.length < 3) { + console.warn('[map] region.outer不足3个点:', region.outer); + return; + } + var rings = [region.outer].concat(region.holes || []); + var polygon = L.polygon(rings, { + color: color, fillColor: color, weight: 2, fillOpacity: 0.3 + }); + polygon._assetId = asset.id; + layerGroup.addLayer(polygon); + console.log('[map] polygon已添加, 点数:', region.outer.length); + }); + } else { + console.warn('[map] 无boundData, code:', d.code, 'data:', d.data); + } + }) + .catch(function(err) { console.error('[map] 加载边界失败:', err); }); + }, + + // ===================================================================== + // 多资产切换弹框 + // ===================================================================== + + /** + * 显示资产切换弹框(多资产标点点击时触发) + * 弹框内列出该位置所有资产,点击可切换信息弹框和列表高亮 + * 注:边界已一次性加载,切换时只更新信息和高亮,不重新加载边界 + * @param {L.Marker} m - 标点对象 + */ + showSwitchPopup(m) { + var self = this; + var activeIdx = m._activeIdx || 0; + + // 构建弹框HTML + var html = '
'; + html += '
该位置有 ' + m._assets.length + ' 个资产
'; + m._assets.forEach(function(a, idx) { + var isActive = idx === activeIdx; + html += '
'; + html += '
' + (a.assetName || '-') + '
'; + html += '
编码:' + (a.assetCode || a.id) + '
'; + html += '
'; + }); + html += '
'; + + // 打开弹框(Leaflet popup) + L.popup({ closeButton: true, className: 'switch-popup-container', offset: [0, -24] }) + .setLatLng(m.getLatLng()) + .setContent(html) + .openOn(self.map); + + // 延迟绑定点击事件(等待DOM渲染完成) + setTimeout(function() { + var items = document.querySelectorAll('.switch-popup .sp-item'); + items.forEach(function(item) { + item.addEventListener('click', function() { + var idx = parseInt(this.getAttribute('data-idx')); + // 注释:切换边界逻辑已屏蔽,改为一次性加载所有资产边界 + // if (idx !== activeIdx) { + // self.loadMarkerBound(m, idx); + // } + m._activeIdx = idx; + self.activeAssetId = m._assets[idx].id; + self.highlightBoundForAsset(m._assets[idx].id); + self.scrollListToAsset(m._assets[idx].id); + // showAssetPopup 会替换当前弹框,不需要手动 close + self.showAssetPopup(m, m._assets[idx]); + }); + }); + }, 50); + }, + + // ===================================================================== + // 列表交互 + // ===================================================================== + + /** + * 从右侧列表点击资产时触发 + * - 高亮对应标点 + * - 高亮对应边界 + * - 显示信息弹框 + * - 地图定位到该资产位置 + * @param {Object} asset - 资产对象 + */ + locateFromList(asset) { + var lat = parseFloat(asset.lat); + var lng = parseFloat(asset.lng); + if (isNaN(lat) || isNaN(lng)) return; + + this.activeAssetId = asset.id; + var key = lat.toFixed(8) + ',' + lng.toFixed(8); + var m = this.markerMap[key]; + if (m) { + this.highlightMarker(m); + // 找到该资产在marker中的索引 + var idx = m._assets.findIndex(function(a) { return a.id === asset.id; }); + if (idx < 0) idx = 0; + // 注释:切换边界逻辑已屏蔽 + // this.loadMarkerBound(m, idx); + m._activeIdx = idx; + this.highlightBoundForAsset(asset.id); + this.showAssetPopup(m, asset); + } + // 仅平移到资产位置,不改变缩放层级 + this.map.panTo([lat, lng]); + }, + + /** + * 滚动右侧列表到指定资产项 + * 使用 $nextTick 确保DOM已更新后再滚动 + * @param {string} assetId - 资产ID + */ + scrollListToAsset(assetId) { + this.$nextTick(function() { + var ref = this.$refs['asset-' + assetId]; + if (ref && ref[0]) { + ref[0].scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }); + }, + + // ===================================================================== + // 边界数据解析 + // ===================================================================== + + /** + * 标准化边界数据格式 + * 兼容两种格式: + * 1. 新格式:{ regions: [{id, outer, holes}], color } + * 2. 旧格式:{ polygon: [...], color }(polygon 可能是单层或多层嵌套) + * @param {Object} bound - 原始边界数据 + * @returns {Array} 标准化后的 regions 数组 [{id, outer, holes}] + */ + normalizeBoundary(bound) { + if (!bound) return []; + var self = this; + // 新格式:直接取 regions 数组 + if (bound.regions && Array.isArray(bound.regions)) { + return bound.regions.map(r => ({ id: r.id, outer: r.outer || [], holes: r.holes || [] })); + } + // 旧格式:从 polygon 字段解析 + if (!bound.polygon || bound.polygon.length === 0) return []; + var p = bound.polygon; + // 单个多边形:polygon[0] = [lat, lng] + if (typeof p[0][0] === 'number') return [{ id: 0, outer: p, holes: [] }]; + // 单个带孔洞:polygon[0] = outer, polygon[1..] = holes + if (typeof p[0][0][0] === 'number') return [{ id: 0, outer: p[0], holes: p.slice(1) }]; + // 多个多边形 + return p.map(function(r, i) { + if (typeof r[0][0] === 'number') return { id: i, outer: r, holes: [] }; + return { id: i, outer: r[0], holes: r.slice(1) }; + }); + }, + + // ===================================================================== + // 弹框与标点高亮 + // ===================================================================== + + /** + * 显示资产基本信息弹框(在标点位置弹出) + * @param {L.Marker} m - 标点对象(用于定位弹框位置) + * @param {Object} asset - 资产对象 + */ + showAssetPopup(m, asset) { + var area = asset.area || '-'; + var html = + '资产编码:' + (asset.assetCode || asset.id) + '
' + + '资产名称:' + (asset.assetName || '-') + '
' + + '坐落位置:' + (asset.address || '-') + '
' + + '面积:' + area + '㎡
' + + '管理单位:' + (asset.manager || '-'); + L.popup({ offset: [0, -24] }) + .setLatLng(m.getLatLng()) + .setContent(html) + .openOn(this.map); + }, + + /** + * 高亮指定标点(红色发光 + 放大效果) + * 同时取消上一个高亮标点 + * @param {L.Marker} m - 要高亮的标点 + */ + highlightMarker(m) { + if (this.activeMarker && this.activeMarker !== m) { + this.unhighlightMarker(this.activeMarker); + } + var el = m.getElement(); + if (el) { + var img = el.querySelector('img') || el; + img.style.filter = 'drop-shadow(0 0 6px #e74c3c) drop-shadow(0 0 12px #e74c3c) drop-shadow(0 0 20px rgba(231,76,60,0.6))'; + img.style.transform = 'scale(2)'; + img.style.transformOrigin = 'center center'; + el.style.zIndex = 1000; + } + this.activeMarker = m; + }, + + /** + * 取消标点高亮(恢复原始样式) + * @param {L.Marker} m - 要取消高亮的标点 + */ + unhighlightMarker(m) { + var el = m.getElement(); + if (el) { + var img = el.querySelector('img') || el; + img.style.filter = ''; + img.style.transform = ''; + el.style.zIndex = ''; + } + }, + + /** 清除当前活跃标点的高亮状态 */ + clearActiveMarker() { + if (this.activeMarker) { + this.unhighlightMarker(this.activeMarker); + this.activeMarker = null; + } + this.clearBoundHighlight(); + }, + + /** 清除所有边界的斜线填充效果,恢复默认纯色填充 */ + clearBoundHighlight() { + var self = this; + Object.keys(this.markerBoundLayers).forEach(function(key) { + var layerGroup = self.markerBoundLayers[key]; + layerGroup.eachLayer(function(layer) { + if (!layer.setStyle) return; + layer.setStyle({ weight: 2, fillOpacity: 0.3 }); + if (layer._path) { + layer._path.setAttribute('fill', layer.options.fillColor || layer.options.color); + } + }); + }); + }, + + // ===================================================================== + // URL穿透定位 + // ===================================================================== + + /** + * 根据资产编号(URL参数)定位到指定资产 + * 流程:调 getByBounds 接口 → 构建标点 → 地图定位 → 自动选中目标资产 + * @param {string} assetId - 资产编号(来自URL参数) + */ + loadAssetById(assetId) { + var self = this; + fetch(this.host + "/seeyon/rest/assetsRegion/getByBounds", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ assetId: assetId }) + }).then(function(r) { return r.json(); }).then(function(d) { + if (d.code === 0 && d.data && d.data.length > 0) { + var region = d.data[0]; + var lat = parseFloat(region.lat), lng = parseFloat(region.lng); + if (!isNaN(lat) && !isNaN(lng)) { + self.assetList = region.assets || []; + self.total = self.assetList.length; + self.queried = true; + self.buildMarkers(); + self.map.setView([lat, lng], 18); + // 自动定位到目标资产 + var target = self.assetList.find(function(a) { return a.assetCode === assetId || a.id === assetId; }); + if (target) self.locateFromList(target); + } + } + }); + }, + + // ===================================================================== + // 跳转详情 + // ===================================================================== + + /** + * 跳转到资产详情页 + * 先根据资产类型获取详情页URL模板,再替换资产ID打开新窗口 + * @param {Object} item - 资产对象 + */ + goToDetail(item) { + var host = this.host; + fetch(this.host + "/seeyon/rest/assetsRegion/getDetailBaseUrlByAssetsType?assetsType=" + item.assetType) + .then(r => r.json()).then(d => { + if (d.code === 0 && d.data) { + window.open(host + d.data.replace(/\{assetsId\}/g, item.id)); + } + }); + } + } + }); + + + diff --git a/assetsMap/mapbak.html b/assetsMap/mapbak.html new file mode 100644 index 0000000..1bf7f5a --- /dev/null +++ b/assetsMap/mapbak.html @@ -0,0 +1,1069 @@ + + + + + + 资产地图(双模式边界:手绘+预配置经纬度) + + + + + + + + + + + + + + + +
+
+ 资产数量: {{filteredCount}} ▼ +
+
+
+ +
资产数量: {{filteredCount}}
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+ {{item.assetName || item.address}} +
+
+ +
+ +
+
+ {{currentRegion.address}} + +
+
资产数量:{{currentRegion.assets?.length || 0}}
+
+
+
资产编码:{{a.assetCode || a.id}}
+
资产名称:{{a.assetName || '-'}}
+
坐落位置:{{a.address || '-'}}
+
地点:{{a.owner || '-'}}
+
面积:{{a.area || '-'}}㎡
+
管理单位:{{a.manager || '-'}}
+
备注:{{a.remark || '暂无'}}
+ +
+ + +
+ +
+ + + + +
+
+
+
+
+ + + + diff --git a/assetsMap/markicon.png b/assetsMap/markicon.png new file mode 100644 index 0000000..bec5c1f Binary files /dev/null and b/assetsMap/markicon.png differ diff --git a/seeyon/WEB-INF/assetsmap/spring/spring.xml b/seeyon/WEB-INF/assetsmap/spring/spring.xml index b3b8c1c..e833a05 100644 --- a/seeyon/WEB-INF/assetsmap/spring/spring.xml +++ b/seeyon/WEB-INF/assetsmap/spring/spring.xml @@ -4,4 +4,7 @@ + + + \ No newline at end of file diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/css/goMapBtn.css b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/css/goMapBtn.css new file mode 100644 index 0000000..e5c4ae0 --- /dev/null +++ b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/css/goMapBtn.css @@ -0,0 +1,36 @@ +.customButton_class_box { + width: 35%; + line-height: 24px; + height:24px; + color: #1f85ec; + cursor: pointer; + font-family: "Microsoft YaHei"!important; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-break:keep-all; + box-sizing: border-box; + -webkit-box-sizing : border-box; + -moz-box-sizing : border-box; + text-align: center; + outline: none; + border: 1px solid #1f85ec; + background-color: #fff; + border-radius: 15px; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; +} +.customInputArea{ + width: 65%; + height:auto; + color: #1f85ec; + font-family: "Microsoft YaHei"!important; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-break:keep-all; +} +.customButton_box_content{ + width: 100%; + height: auto; +} diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/css/img/icon16.png b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/css/img/icon16.png new file mode 100644 index 0000000..e43def3 Binary files /dev/null and b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/css/img/icon16.png differ diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/boundConfig.html b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/boundConfig.html new file mode 100644 index 0000000..7acb593 --- /dev/null +++ b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/boundConfig.html @@ -0,0 +1,498 @@ + + + + + + 配置资产边界 + + + + + + + + +
+
+
+
配置资产区域边界
+
+ +
+ + + {{color}} +
+ + +
+ 区域列表 ({{regions.length}}) + +
+ +
+ 暂无区域,点击"新增区域"开始 +
+ +
+
+ 区域 {{ri + 1}} ID:{{r.id}} + × +
+
+ 外圈:{{r.outer ? r.outer.length : 0}} 个点 + | 孔洞:{{r.holesCount}} 个 +
+
+ 孔洞共 {{r.holesCount}} 个 +
+
+ + +
+
+ +
提示:点击区域卡片选中后,可在地图上看到高亮显示
+
+ + +
+
+ + + + diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/location.html b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/location.html new file mode 100644 index 0000000..416b8af --- /dev/null +++ b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/location.html @@ -0,0 +1,191 @@ + + + + + + 坐标定位 + + + + + + +
+
+ +
+
+
坐标定位
+
+ + +
+
+ + +
+ +
+ +
+
当前坐标
+
+ 纬度:{{lat}}
+ 经度:{{lng}} +
+
+
+
+ + + + diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/markicon.png b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/markicon.png new file mode 100644 index 0000000..bec5c1f Binary files /dev/null and b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/html/markicon.png differ diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/boundConfig.js b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/boundConfig.js new file mode 100644 index 0000000..16dd98c --- /dev/null +++ b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/boundConfig.js @@ -0,0 +1,177 @@ +(function (factory) { + var nameSpace = 'field_17179214358792461983'; + if (!window[nameSpace]) { + console.log("开始实例化边界区域配置按钮") + var Builder = factory(); + window[nameSpace] = { instance: {} }; + window[nameSpace].init = function (options) { + window[nameSpace].instance[options.privateId] = new Builder(options); + }; + } +})(function () { + function App(options) { + var self = this; + self.initParams(options); + self.initDom(); + self.events(); + } + + App.prototype = { + initParams: function (options) { + var self = this; + self.adaptation = options.adaptation; + self.privateId = options.privateId; + self.preUrl = options.url_prefix; + self.adaptation.formMessage = options.formMessage; + self.messageObj = options.getData; + }, + initDom: function () { + var self = this; + // 修复:避免重复加载CSS + if (!document.querySelector('link[href="' + self.preUrl + '/css/goMapBtn.css' + '"]')) { + dynamicLoading.css(self.preUrl + '/css/goMapBtn.css'); + } + self.appendChildDom(); + }, + events: function () { + var self = this; + self.adaptation.ObserverEvent.listen('Event' + self.privateId, function () { + self.messageObj = self.adaptation.childrenGetData(self.privateId); + self.appendChildDom(); + }); + }, + + openBoundConfig: function (privateId,messageObj,adaptation) { + var self = this; + messageObj = adaptation.childrenGetData(privateId); + + var targetObj = messageObj.formdata.formmains[messageObj.formdata.alldata.tableInfo.formmain.tableName]; + var boundData = ''; + var assetsId = ''; + if (targetObj) { + for (var key in targetObj) { + if (targetObj.hasOwnProperty(key) && !/^auxiliary/.test(key)) { + var display = targetObj[key].display; + // 修复:去掉未定义的 fieldMap + if (display === "资产边界数据") { + boundData = targetObj[key].showValue; + } + if (display === "资产编号") { + assetsId = targetObj[key].showValue; + } + } + } + } + var dialog = $.dialog({ + id: 'boundConfigDialog', + url: self.preUrl + '/html/boundConfig.html', + width: 1100, + height: 750, + title: '配置资产区域边界', + transParams: { oldBoundConfig: boundData, assetsId: assetsId }, + type: 'panel', + checkMax: true, + closeParam: { + 'show': false, + autoClose: false, + handler: function () { } + }, + buttons: [{ + text: "确认保存", + handler: function () { + var result = dialog.getReturnValue(); + console.log("资产边界数据: " + result) + // 修复:参数顺序与回填对应 + backFill(result, messageObj.id, "资产边界数据", privateId, messageObj, adaptation); + dialog.close() + } + }, { + text: "取消", + handler: function () { + dialog.close(); + } + }] + }); + }, + + appendChildDom: function () { + var self = this; + var doJump = function () { + + self.openBoundConfig(self.privateId, self.messageObj, self.adaptation); + }; + + var domStructure = '
' + + '
' + self.messageObj.display.escapeHTML() + '
' + + '
'; + + var $container = document.querySelector('#' + self.privateId); + if (!$container) return; + + $container.innerHTML = domStructure; + + var $btn = document.querySelector('.' + self.privateId); + if ($btn) { + $btn.removeEventListener('click', doJump); + $btn.addEventListener('click', doJump); + } + + if (self.messageObj.auth === 'hide') { + $container.innerHTML = '
***
'; + } + } + }; + + function backFill(boundConfig, fieldName, fieldDisplay, privateId, messageObj, adaptation) { + var param = {}; + param.masterId = messageObj.formdata.content.contentDataId; + param.value = boundConfig; + param.formId = messageObj.formdata.content.contentTemplateId; + param.fieldName = fieldName; + param.fieldDisplay = fieldDisplay; + + $.ajax({ + type: "POST", + url: '/seeyon/rest/assetsRegion/formBackFill', + data: JSON.stringify(param), + dataType: "json", + contentType: 'application/json;charset=UTF-8', + success: function (res) { + if (res.code == 0) { + var backfillData = {}; + backfillData.tableName = adaptation.formMessage.tableName; + backfillData.tableCategory = 'formmain'; + backfillData.updateData = res.data; + adaptation.backfillFormControlData(backfillData, privateId); + } else { + $.alert(res.message); + } + }, + error: function (e) { + top.$.error(e.responseText || '保存失败'); + } + }); + } + + var dynamicLoading = { + css: function (path) { + if (!path) return; + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.href = path; + link.rel = 'stylesheet'; + link.type = 'text/css'; + head.appendChild(link); + }, + js: function (path) { + if (!path) return; + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.src = path; + script.type = 'text/javascript'; + head.appendChild(script); + } + } + + return App; +}); \ No newline at end of file diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/goMapInit.js b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/goMapInit.js new file mode 100644 index 0000000..9a85477 --- /dev/null +++ b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/goMapInit.js @@ -0,0 +1,114 @@ +(function (factory) { + var nameSpace = 'field_17179215134928371056'; + if (!window[nameSpace]) { + console.log("开始实例化跳转地图按钮") + var Builder = factory(); + window[nameSpace] = { + instance: {} + }; + window[nameSpace].init = function (options) { + window[nameSpace].instance[options.privateId] = new Builder(options); + }; + } +})(function () { + function App(options) { + var self = this; + self.initParams(options); + //初始化dom + self.initDom(); + //事件 + self.events(); + } + + App.prototype = { + initParams: function (options) { + console.log("开始初始化参数") + var self = this; + self.adaptation = options.adaptation; + self.privateId = options.privateId; + self.preUrl = options.url_prefix; + self.adaptation.formMessage = options.formMessage; + self.messageObj = options.getData; + console.log(self.messageObj) + }, + initDom: function () { + var self = this; + console.log("开始渲染dom") + dynamicLoading.css(self.preUrl + '/css/goMapBtn.css'); + self.appendChildDom(); + }, + events: function () { + var self = this; + // 监听是否数据刷新 + console.log("设置事件监听") + self.adaptation.ObserverEvent.listen('Event' + self.privateId, function () { + self.messageObj = self.adaptation.childrenGetData(self.privateId); + self.appendChildDom(); + }); + }, + goMap: function (privateId, messageObj, adaptation) { + // 获取表单中的资产编号,跳转到资产地图 + console.log("privateId:" + privateId) + console.log("messageObj:" + messageObj) + console.log(adaptation) + messageObj = adaptation.childrenGetData(privateId); + var targetObj = messageObj.formdata.formmains[adaptation.formMessage.tableName]; + if (!targetObj) return; + var assetNo; + for (var key in targetObj) { + if (targetObj.hasOwnProperty(key) && !/^auxiliary/.test(key)) { + if (targetObj[key].display === "资产编号") { + assetNo = targetObj[key].showValue; + break; + } + } + } + if (assetNo) { + var baseUrl = window.location.origin + '/seeyon/seeyonExtend/assetsMap/map.html'; + window.open(baseUrl + '?assetId=' + encodeURIComponent(assetNo), '_blank'); + } else { + $.alert('未找到资产编号字段'); + } + }, + appendChildDom: function () { + var self = this; + var doJump = function () { + self.goMap(self.privateId, self.messageObj, self.adaptation) + } + var domStructure = '
' + + '
' + self.messageObj.display.escapeHTML() + '
' + + '
'; + document.querySelector('#' + self.privateId).innerHTML = domStructure; + var content = self.messageObj.formdata.content + document.querySelector('.' + self.privateId).removeEventListener('click', doJump); + document.querySelector('.' + self.privateId).addEventListener('click', doJump); + + } + + }; + + var dynamicLoading = { + css: function (path) { + if (!path || path.length === 0) { + throw new Error('argument "path" is required !'); + } + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.href = path; + link.rel = 'stylesheet'; + link.type = 'text/css'; + head.appendChild(link); + }, + js: function (path) { + if (!path || path.length === 0) { + throw new Error('argument "path" is required !'); + } + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.src = path; + script.type = 'text/javascript'; + head.appendChild(script); + } + } + return App; +}); \ No newline at end of file diff --git a/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/location.js b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/location.js new file mode 100644 index 0000000..f38e6d5 --- /dev/null +++ b/seeyon/apps_res/cap/customeCtrlResources/goMapBtnResources/js/location.js @@ -0,0 +1,166 @@ +(function(factory) { + var nameSpace = 'field_17179214358792461984'; + if (!window[nameSpace]) { + console.log("开始实例化资产定位按钮") + var Builder = factory(); + window[nameSpace] = { + instance: {} + }; + window[nameSpace].init = function(options) { + window[nameSpace].instance[options.privateId] = new Builder(options); + }; + } +})(function() { + function App(options) { + var self = this; + self.initParams(options); + //初始化dom + self.initDom(); + //事件 + self.events(); + } + + App.prototype = { + initParams: function(options) { + console.log("开始初始化参数") + var self = this; + self.adaptation = options.adaptation; + self.privateId = options.privateId; + self.preUrl = options.url_prefix; + self.adaptation.formMessage = options.formMessage; + self.messageObj = options.getData; + console.log(self.messageObj) + }, + initDom: function() { + var self = this; + console.log("开始渲染dom") + dynamicLoading.css(self.preUrl + '/css/goMapBtn.css'); + self.appendChildDom(); + }, + events: function() { + var self = this; + // 监听是否数据刷新 + console.log("设置事件监听") + self.adaptation.ObserverEvent.listen('Event' + self.privateId, function() { + self.messageObj = self.adaptation.childrenGetData(self.privateId); + self.appendChildDom(); + }); + }, + goLocation: function(privateId,messageObj,adaptation) { + var privateId1; + var privateId2; + messageObj = adaptation.childrenGetData(privateId); + var tableName = messageObj.formdata.alldata.tableInfo.formmain.tableName + var dialog = $.dialog({ + id: 'dialog', + url: this.preUrl + '/html/location.html', + width: 1080, + height: 720, + title: '资产位置定位', + type: 'panel', + checkMax: true, + closeParam: { + 'show': false, + autoClose: false, + handler: function() {} + }, + buttons: [{ + text: "确认", + handler: function() { + var latlng = dialog.getReturnValue(); + // 回填数据 + const [lat, lng] = latlng.split(','); + backFill(lat, messageObj.id, "纬度-", privateId,messageObj, adaptation,tableName); + backFill(lng, messageObj.id, "经度-", privateId,messageObj, adaptation,tableName); + dialog.close() + } + }, { + text: "取消", + handler: function() { + dialog.close() + } + }] + }); + }, + appendChildDom: function() { + var self = this; + var doBiz = function() { + self.goLocation(self.privateId, self.messageObj, self.adaptation) + } + + var domStructure = '
' + + '
' + self.messageObj.display.escapeHTML() + '
' + + '
'; + document.querySelector('#' + self.privateId).innerHTML = domStructure; + + var content = self.messageObj.formdata.content + document.querySelector('.' + self.privateId).removeEventListener('click', doBiz); + document.querySelector('.' + self.privateId).addEventListener('click', doBiz); + //渲染隐藏权限 + if (self.messageObj.auth === 'hide') { + document.querySelector('#' + self.privateId).innerHTML = + '
***
'; + } + } + + }; +function backFill(latlng, fieldName, fieldDisplay, privateId, messageObj, adaptation,tableName) { + var param = new Object(); + param.masterId = messageObj.formdata.content.contentDataId; + param.value = latlng; + param.formId = messageObj.formdata.content.contentTemplateId; + param.fieldDisplay = fieldDisplay; + $.ajax({ + type: "POST", + url: '/seeyon/rest/assetsRegion/formBackFill', + data: JSON.stringify(param), + dataType: "json", + contentType: 'application/json;charset=UTF-8', + success: function(res) { + // 后台解析数据后 将数据填写到表单中 + if (res.code == 0) { + var backfill = {}; + backfill.tableName = tableName; + // 回填主表 + backfill.tableCategory = 'formmain'; + // 后台组装的data数据 + console.log(res.data) + backfill.updateData = res.data; + adaptation.backfillFormControlData(backfill, privateId); + } else { + // 报错 + $.alert(res.message); + } + }, + complete: function() {}, + error: function(e) { + top.$.error(e.responseText); + } + }); + } + var dynamicLoading = { + css: function(path) { + if (!path || path.length === 0) { + throw new Error('argument "path" is required !'); + } + var head = document.getElementsByTagName('head')[0]; + var link = document.createElement('link'); + link.href = path; + link.rel = 'stylesheet'; + link.type = 'text/css'; + head.appendChild(link); + }, + js: function(path) { + if (!path || path.length === 0) { + throw new Error('argument "path" is required !'); + } + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.src = path; + script.type = 'text/javascript'; + head.appendChild(script); + } + } + return App; +}); \ No newline at end of file diff --git a/src/main/java/com/seeyon/apps/assetsmap/constants/AssetsMapConstans.java b/src/main/java/com/seeyon/apps/assetsmap/constants/AssetsMapConstans.java index 8f476bb..43fd5d9 100644 --- a/src/main/java/com/seeyon/apps/assetsmap/constants/AssetsMapConstans.java +++ b/src/main/java/com/seeyon/apps/assetsmap/constants/AssetsMapConstans.java @@ -3,6 +3,7 @@ package com.seeyon.apps.assetsmap.constants; public enum AssetsMapConstans { plugin("assetsmap",""), ASSETSFORMNO("assetsformno","资产表单"), + ASSETSDETAILURLCONFIGFORMNO("","资产详情URL配置表单"), ; private String defaultValue; diff --git a/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/BoundConfigFieldCtrl.java b/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/BoundConfigFieldCtrl.java new file mode 100644 index 0000000..45864db --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/BoundConfigFieldCtrl.java @@ -0,0 +1,54 @@ +package com.seeyon.apps.assetsmap.fieldCtrl; + +import com.seeyon.cap4.form.bean.ParamDefinition; +import com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrl; +import com.seeyon.cap4.form.util.Enums; + +public class BoundConfigFieldCtrl extends FormFieldCustomCtrl { + @Override + public String getPCInjectionInfo() { + return "{path:'apps_res/cap/customCtrlResources/goMapBtnResources',jsUri:'/js/boundConfig.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}"; + } + + @Override + public String getMBInjectionInfo() { + return "{path:'http://mapResource.v5.cmp/v1.0.0/',weixinpath:'invoice',jsUri:'js/location.js',initMethod:'init',nameSpace:'"+getNameSpace()+"'}"; + } + + @Override + public String getKey() { + return "17179214358792461983"; + } + + @Override + public boolean canUse(Enums.FormType formType) { + return true; + } + + @Override + public String[] getDefaultVal(String s) { + return new String[0]; + } + + public String getNameSpace() { + return "field_" + this.getKey(); + } + + public String getFieldLength() { + return "20"; + } + + @Override + public String getText() { + return "绘制资产边界"; + } + + @Override + public void init() { + setPluginId("assetsmap"); + setIcon("cap-icon-custom-button"); + ParamDefinition param = new ParamDefinition(); + param.setParamType(Enums.ParamType.button); + addDefinition(param); + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/GoMapFieldCtrl.java b/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/GoMapFieldCtrl.java new file mode 100644 index 0000000..e3d3ebb --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/GoMapFieldCtrl.java @@ -0,0 +1,61 @@ +package com.seeyon.apps.assetsmap.fieldCtrl; + +import com.seeyon.cap4.form.bean.ParamDefinition; +import com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrl; +import com.seeyon.cap4.form.util.Enums; + + +public class GoMapFieldCtrl extends FormFieldCustomCtrl { + @Override + public String getPCInjectionInfo() { + return "{path:'apps_res/cap/customCtrlResources/goMapBtnResources',jsUri:'/js/goMapInit.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}"; + } + + @Override + public String getMBInjectionInfo() { + return "{path:'http://mapResource.v5.cmp/v1.0.0/',weixinpath:'invoice',jsUri:'js/location.js',initMethod:'init',nameSpace:'"+getNameSpace()+"'}"; + } + + @Override + public String getKey() { + return "17179215134928371056"; + } + + @Override + public boolean canUse(Enums.FormType formType) { + return true; + } + + @Override + public String[] getDefaultVal(String s) { + return new String[0]; + } + + public String getNameSpace() { + return "field_" + this.getKey(); + } + + public String getFieldLength() { + return "20"; + } + @Override + public String getText() { + return "跳转资产地图按钮"; + } + + /** + * 控件初始化接口,此接口在控件初始化的时候,会调用,主要用于定义控件所属插件id、在表单编辑器中的图标、表单编辑器中有哪些属性可以设置。 + * 使用举例:在接口中定义自定义控件在在表单编辑器中有哪些控件属性需要配置 + */ + @Override + public void init() { + //设置图标和插件ID + setPluginId("assetsmap"); + setIcon("cap-icon-custom-button"); + // 自定义参数 + + ParamDefinition esignContractCompareBtnParam = new ParamDefinition(); + esignContractCompareBtnParam.setParamType(Enums.ParamType.button); + addDefinition(esignContractCompareBtnParam); + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/LocationFieldCtrl.java b/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/LocationFieldCtrl.java new file mode 100644 index 0000000..18341bb --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/fieldCtrl/LocationFieldCtrl.java @@ -0,0 +1,54 @@ +package com.seeyon.apps.assetsmap.fieldCtrl; + +import com.seeyon.cap4.form.bean.ParamDefinition; +import com.seeyon.cap4.form.bean.fieldCtrl.FormFieldCustomCtrl; +import com.seeyon.cap4.form.util.Enums; + +public class LocationFieldCtrl extends FormFieldCustomCtrl { + @Override + public String getPCInjectionInfo() { + return "{path:'apps_res/cap/customCtrlResources/goMapBtnResources',jsUri:'/js/location.js',initMethod:'init',nameSpace:'" + getNameSpace() + "'}"; + } + + @Override + public String getMBInjectionInfo() { + return getPCInjectionInfo(); + } + + @Override + public String getKey() { + return "17179214358792461984"; + } + + @Override + public boolean canUse(Enums.FormType formType) { + return true; + } + + @Override + public String[] getDefaultVal(String s) { + return new String[0]; + } + + public String getNameSpace() { + return "field_" + this.getKey(); + } + + public String getFieldLength() { + return "20"; + } + + @Override + public String getText() { + return "资产定位按钮"; + } + + @Override + public void init() { + setPluginId("assetsmap"); + setIcon("cap-icon-custom-button"); + ParamDefinition param = new ParamDefinition(); + param.setParamType(Enums.ParamType.button); + addDefinition(param); + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/kit/A8FolderKit.java b/src/main/java/com/seeyon/apps/assetsmap/kit/A8FolderKit.java new file mode 100644 index 0000000..a6fda3e --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/kit/A8FolderKit.java @@ -0,0 +1,49 @@ +package com.seeyon.apps.assetsmap.kit; + +import com.seeyon.ctp.common.SystemEnvironment; + +/** + * Description + *
获取A8产品下面的文件夹路径
+ * Date 2019年10月11日 上午9:42:49
+ * Copyright(c) Beijing Seeyon Software Co.,LTD + */ +public class A8FolderKit { + + private static String A8_APPLICAION_FLODER = ""; + + + /** + * Description: + *
获取到A8的安装模目录,ApacheJetspeed目录的父目录
+ * @return + */ + public static String getApplicationFolder() { + if("".equals(A8_APPLICAION_FLODER)) { + String installPath = SystemEnvironment.getApplicationFolder(); + A8_APPLICAION_FLODER = installPath.split("ApacheJetspeed")[0].replaceAll("\\\\", "/"); + } + return A8_APPLICAION_FLODER; + } + + /** + * Description: + *
获取配置文件的地址
+ * @return + */ + public static String getCodePropertiesPath() { + String path = getApplicationFolder() + "ApacheJetspeed/webapps/seeyon/WEB-INF/classes/code.properties"; + return path; + } + + + public static String getPropertiesPath(String fileName) { + String path = getApplicationFolder() + "ApacheJetspeed/webapps/seeyon/WEB-INF/classes/" + fileName; + return path; + } + + public static String getZSJCodePropertiesPath() { + String path = getApplicationFolder() + "ApacheJetspeed/webapps/seeyon/WEB-INF/classes/ZSJcode.properties"; + return path; + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/kit/CAP4FormKit.java b/src/main/java/com/seeyon/apps/assetsmap/kit/CAP4FormKit.java new file mode 100644 index 0000000..616fad1 --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/kit/CAP4FormKit.java @@ -0,0 +1,196 @@ +package com.seeyon.apps.assetsmap.kit; + +import com.seeyon.cap4.form.bean.*; +import com.seeyon.cap4.form.service.CAP4FormManager; +import com.seeyon.ctp.common.exceptions.BusinessException; +import com.seeyon.ctp.common.log.CtpLogFactory; +import org.apache.commons.logging.Log; + +import java.util.List; +import java.util.Map; + +/** + * Description + *
操作CAP4表单和字段
+ * @author FanGaowei
+ * Copyright(c) Beijing Seeyon Software Co.,LTD + */ +public class CAP4FormKit { + + private static final Log LOGGER = CtpLogFactory.getLog(CAP4FormKit.class); + + public static FormBean getFormBean(CAP4FormManager cap4FormManager, String code) { + FormBean formBean = null; + try { + formBean = cap4FormManager.getFormByFormCode(code); + } catch(BusinessException e) { + LOGGER.error("获取表单发生异常,编号:" + code, e); + } + return formBean; + } + + /** + * 根据表单的显示名称获取字段的值 + * @param bean + * @param disPlay + * @return + */ + public static Object getFieldValue(FormDataBean bean, String disPlay) { + if(bean == null) { + return null; + } + FormTableBean table = bean.getFormTable(); + if(table == null) { + return null; + } + FormFieldBean field = table.getFieldBeanByDisplay(disPlay); + if(field == null) { + return null; + } + return bean.getFieldValue(field.getName()); + } + + + public static int getIntValue(FormDataBean bean, String disPlay) { + Object value = getFieldValue(bean, disPlay); + return StrKit.toInteger(value); + } + + public static String getFieldStrValue(FormDataBean bean, String disPlay) { + Object value = getFieldValue(bean, disPlay); + return StrKit.str(value); + } + + public static Object getFieldValueByName(FormDataBean bean, String fieldName) { + return bean.getFieldValue(fieldName); + } + + /** + * 根据表单的显示名称获取字段是 field000? + * @param bean + * @param disPlay + * @return + */ + public static String getFieldTaleId(FormBean bean, String disPlay) { + if(bean == null) { + return null; + } + FormTableBean table = bean.getMasterTableBean(); + return getFieldTaleId(table, disPlay); + } + + /** + * 直接根据table来获取 + * @param table + * @param disPlay + * @return + */ + public static String getFieldTaleId(FormTableBean table, String disPlay) { + if(table == null) { + return null; + } + FormFieldBean field = table.getFieldBeanByDisplay(disPlay); + if(field == null) { + return null; + } + return field.getName(); + } + + public static String getFieldTaleId(FormDataBean bean, String disPlay) { + if(bean == null) { + return null; + } + FormTableBean table = bean.getFormTable(); + if(table == null) { + return null; + } + FormFieldBean field = table.getFieldBeanByDisplay(disPlay); + if(field == null) { + return null; + } + return field.getName(); + } + + public static FormFieldBean getFieldBean(FormDataBean bean, String disPlay) { + if(bean == null) { + return null; + } + FormTableBean table = bean.getFormTable(); + if(table == null) { + return null; + } + return table.getFieldBeanByDisplay(disPlay); + } + + public static FormFieldBean getFieldBean(FormTableBean bean, String disPlay) { + if(bean == null) { + return null; + } + return bean.getFieldBeanByDisplay(disPlay); + } + + + /** + * Description: + *

+	 * @param bean 这里的bean必须是getMasterBean() 方法获取到的bean
+	 * @param disPlay 
+	 * @param value
+	 */
+	public static void setCellValue(FormDataBean bean, String disPlay, Object value) {
+		FormFieldBean cell = CAP4FormKit.getFieldBean(bean, disPlay);
+		if(cell != null) {
+			bean.addFieldValue(cell.getName(), value);
+		}
+	}
+	
+	public static void setCellValue(FormDataSubBean bean, String disPlay, Object value) {
+        FormFieldBean cell = CAP4FormKit.getFieldBean(bean, disPlay);
+        if(cell != null) {
+            bean.addFieldValue(cell.getName(), value);
+        }
+    }
+	
+	/**
+	 * Description:
+	 * 
只适用于只有一个子表的表单
+ * @param colManager + * @param data + * @return + * @throws Exception + */ + public static List getSubBeans(FormDataMasterBean master) throws Exception { + Map> subs = master.getSubTables(); + + if(null != subs && subs.size() > 0) { + for(String key : subs.keySet()) { + return subs.get(key); + } + } + return null; + } + + public static List getSubBeansByDisPlay(FormDataMasterBean master, String display) throws Exception { + Map> subs = master.getSubTables(); + + if(null != subs && subs.size() > 0) { + for(String key : subs.keySet()) { + if(display.equals(subs.get(key).get(0).getFormTable().getDisplay())) { + return subs.get(key); + } + } + } + return null; + } + + /** + * Description: + *
获取从表字段
+ * @param sub + * @param disPlay + * @return + */ + public static Object getSubFieldValue(FormDataSubBean sub, String disPlay) { + return getFieldValue(sub, disPlay); + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/kit/DBKit.java b/src/main/java/com/seeyon/apps/assetsmap/kit/DBKit.java new file mode 100644 index 0000000..1345539 --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/kit/DBKit.java @@ -0,0 +1,92 @@ +package com.seeyon.apps.assetsmap.kit; + +import com.seeyon.ctp.common.exceptions.BusinessException; +import com.seeyon.ctp.util.JDBCAgent; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + *
+ * 直接操作数据库的类
+ * 
+ * @date 2018年11月8日 上午11:10:15 + * @Copyright(c) Beijing Seeyon Software Co.,LTD + */ +public class DBKit { + + @SuppressWarnings("unchecked") + public static List> excuteSQL(String sql, List params) throws Exception { + List> list = new ArrayList<>(); + JDBCAgent jdbc = new JDBCAgent(true, false); + try { + if(null == params) { + jdbc.execute(sql); + } else { + jdbc.execute(sql, params); + } + if(!sql.startsWith("update")) { + list = jdbc.resultSetToList(); + } + } catch(Exception e) { + e.printStackTrace(); + throw e; + } finally { + jdbc.close(); + } + return list; + } + + public static int updateBatch(String sql, List> params) throws BusinessException { + JDBCAgent agent = new JDBCAgent(true, false); + try { + agent.batch1Prepare(sql); + for(List param : params) { + agent.batch2Add(param); + } + return agent.batch3Execute(); + } catch(Exception e) { + throw new BusinessException(e); + } finally { + agent.close(); + } + } + + /** + * 不成熟,按照这个自己进行sql的编写,只需要获取字段是 数据库的 field000? 就行 根据cap4formkit。getFieldTableId + * sub表也一样,只是多一步判断而已,如果只有一个重复表就不存在 + * @param formbean 根据表单编号获取 + * @param fields field1, "商品编号" 会解析成 field000x as field1, 如果不存在则直接用后面的 + * @param conditions + * @param params + * @return + * @throws Exception + */ + /*public static List> selectSQL(FormBean formbean, Map fields, List conditions) throws Exception { + List params = new ArrayList(); + StringBuffer sb = new StringBuffer(); + sb.append("select "); + for(String key : fields.keySet()) { + String field = CAP4FormKit.getFieldTaleId(formbean, fields.get(key)); + if(StrKit.isNull(field)) { + field = fields.get(key); + } + sb.append(" ").append(field).append(" as ").append(key).append(","); + } + sb.deleteCharAt(sb.lastIndexOf(",")); + sb.append(" from ").append(formbean.getMasterTableBean().getTableName()); + sb.append(" where 1 = 1 "); + if(!StrKit.isNull(conditions)) { + for(FormCondition c : conditions) { + sb.append(ConditionKit.getWhere(formbean, c)); + params.add(c.getParam1()); + if(null != c.getParam2()) { + params.add(c.getParam2()); + } + } + } + return excuteSQL(sb.toString(), params); + }*/ + +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/kit/DateKit.java b/src/main/java/com/seeyon/apps/assetsmap/kit/DateKit.java new file mode 100644 index 0000000..c63cd38 --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/kit/DateKit.java @@ -0,0 +1,293 @@ +package com.seeyon.apps.assetsmap.kit; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 日期处理工具类 + * @Copyright Beijing Seeyon Software Co.,LTD + */ +public class DateKit { + + private static SimpleDateFormat simple = new SimpleDateFormat("yyyyMMdd"); + + private static SimpleDateFormat sixFormat = new SimpleDateFormat("yyMMdd"); + + private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + private static SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd"); + + private static SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM"); + + private static SimpleDateFormat weekDay = new SimpleDateFormat("MM-dd"); + + private static SimpleDateFormat hour = new SimpleDateFormat("HH:mm"); + + private static SimpleDateFormat detailDate = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + + private static final String dayNames[] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五","星期六" }; + + public static int getYear(Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + return cal.get(Calendar.YEAR); + } + + public static String getSixDate() { + return getSixDate(null); + } + + public static String getSixDate(Date date) { + if(null == date) { + return sixFormat.format(new Date()); + } + return sixFormat.format(date); + } + + /** + * 获取这个时间属于一年的第几周 + * @param date + * @return + */ + public static int getWeekNum(Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + int week = cal.get(Calendar.WEEK_OF_YEAR); + // 如果是星期天,那么需要减1 + if("星期日".equals(DateKit.getDayWeekName(date))) { + week -= 1; + } + return week; + } + + /** + * Description: + * + *
+     * 获取一周
+     * 
+ * + * @param date + * @return + */ + public static void getWeekInterval(Map param) { + Calendar cal = Calendar.getInstance(); + // 判断要计算的日期是否是周日,如果是则减一天计算周六的,否则会出问题,计算到下一周去了 + int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天 + if(1 == dayWeek) { + cal.add(Calendar.DAY_OF_MONTH, -1); + } + // System.out.println("要计算日期为:" + sdf.format(cal.getTime())); // 输出要计算日期 + // 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一 + cal.setFirstDayOfWeek(Calendar.MONDAY); + // 获得当前日期是一个星期的第几天 + int day = cal.get(Calendar.DAY_OF_WEEK); + // 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值 + cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day); + param.put("startDate", cal.getTime()); + // System.out.println("所在周星期一的日期:" + imptimeBegin); + cal.add(Calendar.DATE, 6); + param.put("endDate", cal.getTime()); + // System.out.println("所在周星期日的日期:" + imptimeEnd); + } + + /** + * Description: + * + *
+     * 根据传入的日期,获取当前日期所在的一周时间段
+     * 
+ * + * @param date + * @return + */ + public static void getWeekInterval(Map param, Date now) { + Calendar cal = Calendar.getInstance(); + cal.setTime(now); + // 判断要计算的日期是否是周日,如果是则减一天计算周六的,否则会出问题,计算到下一周去了 + int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天 + if(1 == dayWeek) { + cal.add(Calendar.DAY_OF_MONTH, -1); + } + // System.out.println("要计算日期为:" + sdf.format(cal.getTime())); // 输出要计算日期 + // 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一 + cal.setFirstDayOfWeek(Calendar.MONDAY); + // 获得当前日期是一个星期的第几天 + int day = cal.get(Calendar.DAY_OF_WEEK); + // 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值 + cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day); + String time = DateKit.getDayDate(cal.getTime()) + " 00:00:00"; + try { + param.put("beginDate", DateKit.getdayDate(time)); + } catch(Exception e) { + // TODO + } + // System.out.println("所在周星期一的日期:" + imptimeBegin); + cal.add(Calendar.DATE, 6); + time = DateKit.getDayDate(cal.getTime()) + " 23:59:59"; + try { + param.put("endDate", DateKit.getdayDate(time)); + } catch(Exception e) { + // + } + // System.out.println("所在周星期日的日期:" + imptimeEnd); + } + + public static String getSimpleDate(Date date) { + if(date == null) { + return ""; + } + return simple.format(date); + } + + /** + * 给日志格式化使用 + * @param date + * @return + */ + public static String getDate4Cal(Date date) { + if(date == null) { + return ""; + } + return hour.format(date); + } + + public static String getDateString(Date date) { + if(date == null) { + return ""; + } + return sdf.format(date); + } + + public static String getDayDate(Date date) { + if(date == null) { + return ""; + } + return dayFormat.format(date); + } + + public static String getWeekDay(Date date) { + if(date == null) { + return ""; + } + return weekDay.format(date); + } + + /** + * Description: + * + *
+     * 获取多少天以后的日期
+     * 
+ * + * @param day + * @return + */ + public static Date getDayAfter(int day) { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, day); + return cal.getTime(); + } + // 获得某天的几天后 + public static Date getDayAfter(Date d, int day) { + Calendar cal = Calendar.getInstance(); + cal.setTime(d); + cal.add(Calendar.DATE, day); + return cal.getTime(); + } + + /** + * 获取一个月有多少天 + * @param year + * @param month + * @return + */ + public static int getDaysByYearMonth(int year, int month) { + Calendar a = Calendar.getInstance(); + a.set(Calendar.YEAR, year); + a.set(Calendar.MONTH, month); + a.set(Calendar.DATE, 1); + a.roll(Calendar.DATE, -1); + int maxDate = a.get(Calendar.DATE); + return maxDate; + } + + /** + * 获取今天是星期几 + * @param date + * @return + */ + public static String getDayWeekName(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; + if(dayOfWeek < 0) + dayOfWeek = 0; + return dayNames[dayOfWeek]; + } + + /** + * 解析时间,先解析yyyy-MM-dd HH:mm:ss + * @param dateStr + * @return + * @throws ParseException + * @throws Exception + */ + public static Date getdayDate(String dateStr) throws Exception { + try { + return sdf.parse(dateStr); + } catch(Exception e) { + try { + return detailDate.parse(dateStr); + } catch(Exception e1) { + try { + return dayFormat.parse(dateStr); + } catch(Exception e2) { + try { + return simple.parse(dateStr); + } + catch(Exception e3) { + return monthFormat.parse(dateStr); + } + } + } + } + } + + /** + * 根据时间获取周区间字符串 + * @param now + * @return 第XX周:2018-08-27至2018-09-02 + */ + public static String getWeekNumAndDateZone(Date now) { + StringBuffer sb = new StringBuffer(); + sb.append("第"); + sb.append(getWeekNum(now)); + sb.append("周:"); + Map param = new HashMap(); + getWeekInterval(param, now); + Date beginDate = (Date)param.get("beginDate"); + Date endDate = (Date)param.get("endDate"); + sb.append(dayFormat.format(beginDate)); + sb.append("至"); + sb.append(dayFormat.format(endDate)); + return sb.toString(); + } + + /** + * 根据日期和时间获取一个返回该日期该时间的date + * @param d 日期 + * @param time 时间 HH:mm格式 + * @return d日期+time时间 + * @throws ParseException + */ + public static Date getDateFromTime(Date d, String time) throws ParseException { + String dateTime = dayFormat.format(d) + " " + time; + return detailDate.parse(dateTime); + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/kit/StrKit.java b/src/main/java/com/seeyon/apps/assetsmap/kit/StrKit.java new file mode 100644 index 0000000..ea873ea --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/kit/StrKit.java @@ -0,0 +1,115 @@ +package com.seeyon.apps.assetsmap.kit; + +import org.apache.tools.ant.util.DateUtils; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * @date 2018年5月23日上午9:05:01 + * @Copyright Beijing Seeyon Software Co.,LTD + */ +public class StrKit { + + public static String str(Object o) { + if(o == null) { + return ""; + } + if(o instanceof Date) { + return DateUtils.format((Date) o, "yyyy-MM-dd HH:mm:ss"); + } + if(o instanceof String) { + return (String)o; + } + return o.toString(); + } + + public static float toFloat(Object o) { + if(o == null) { + return 0f; + } else if(o instanceof Float) { + return (Float)o; + } else if(o instanceof String) { + return Float.valueOf((String)o); + } else if(o instanceof BigDecimal) { + return ((BigDecimal)o).floatValue(); + } + return 0f; + } + + public static Long toLong(Object o) { + if(null == o) { + return 0L; + } else if(o instanceof Long) { + return (Long)o; + } else if(o instanceof String) { + if("".equals(o)) { + return 0L; + } + return Long.valueOf((String)o); + } else if(o instanceof BigDecimal) { + return ((BigDecimal)o).longValue(); + } + return 0L; + } + + /** + * 取int值,为空返回0 + * @param obj 对象 + * @return + */ + public static Integer toInteger(Object obj) { + if(obj == null) { + return 0; + } else if(obj instanceof Long){ + return ((Long)obj).intValue(); + } else if(obj instanceof BigDecimal) { + return ((BigDecimal)obj).intValue(); + }else if(obj instanceof String) { + String o = (String) obj; + if("".equals(o)) { + return 0; + } else { + try { + return Integer.valueOf((String) obj); + } catch(Exception e) { + return 0; + } + } + } else if(obj instanceof Integer) { + return (Integer) obj; + } + return 0; + } + + public static List toList(Object o) { + if(o == null) { + return null; + } else if(o instanceof List) { + return (List) o; + } else { + return null; + } + } + + /** + * 判断对象是否为空 + * @param o + * @return + */ + public static boolean isNull(Object o) { + if(null == o) { + return true; + } + if(o instanceof String) { + return "".equals((String) o); + } + if(o instanceof Collection) { + // 集合数量为0 则为空 + return ((Collection) o).size() == 0; + } + return false; + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/service/AssetsQueryService.java b/src/main/java/com/seeyon/apps/assetsmap/service/AssetsQueryService.java index c1b577a..cfc2cd4 100644 --- a/src/main/java/com/seeyon/apps/assetsmap/service/AssetsQueryService.java +++ b/src/main/java/com/seeyon/apps/assetsmap/service/AssetsQueryService.java @@ -1,158 +1,469 @@ package com.seeyon.apps.assetsmap.service; +import cn.hutool.log.Log; import com.seeyon.apps.assetsmap.config.AssetsMapConfigProvider; import com.seeyon.apps.assetsmap.constants.AssetsMapConstans; +import com.seeyon.apps.assetsmap.vo.AssetsCatagory; import com.seeyon.apps.assetsmap.vo.AssetsItem; +import com.seeyon.apps.assetsmap.vo.BoundsQueryVo; import com.seeyon.apps.assetsmap.vo.RegionVo; import com.seeyon.ctp.common.AppContext; import com.seeyon.ctp.common.exceptions.BusinessException; -import com.seeyon.ctp.organization.bo.V3xOrgDepartment; +import com.seeyon.ctp.common.po.ctpenumnew.CtpEnumItem; +import com.seeyon.ctp.organization.bo.V3xOrgAccount; import com.seeyon.ctp.organization.manager.OrgManager; +import com.seeyon.ctp.util.JDBCAgent; import com.seeyon.utils.form.*; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang.StringUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; public class AssetsQueryService { private AssetsMapConfigProvider configProvider = (AssetsMapConfigProvider) AppContext.getBean("assetsMapConfigProvider"); private OrgManager orgManager = (OrgManager) AppContext.getBean("orgManager"); + private static final Log log = Log.get(AssetsQueryService.class); + // 部门名称缓存,避免 N+1 查询 + private Map deptCache = new HashMap<>(); private String getFormNo(){ return configProvider.getBizConfigByKey(AssetsMapConstans.ASSETSFORMNO); } + private TableContext getMasterTableContext() throws Exception { + return FormTableExecutor.master(getFormNo()); + } + public List queryAllZoneAssets() throws Exception { - System.out.println("表单编码为:" + getFormNo()); TableContext tableContext = FormTableExecutor.master(getFormNo()); - List formColumns = FormTableExecutor.query(tableContext,null, null, true); - List regionVos = new ArrayList<>(); + List conditions = buildQueryCondition(); + List formColumns = FormTableExecutor.query(tableContext, null, conditions, true); System.out.println("查询出的数据条数:" + formColumns.size()); + + // 用 Map 做 O(1) 查找,替代 List 遍历 + Map regionMap = new HashMap<>(); + for (FormColumn formColumn : formColumns) { Map fieldsMap = formColumn.getFieldsMap(); - if(fieldsMap == null) { + if (fieldsMap == null) { continue; } - upsertRegionVos(regionVos,fieldsMap); + upsertRegionVos(regionMap, fieldsMap); } - return regionVos; + return new ArrayList<>(regionMap.values()); } - public void upsertRegionVos(List regionVos, Map fieldsMap) { + private List buildQueryCondition() { + long currentAccountId = AppContext.currentAccountId(); + List conditions = new ArrayList<>(); + conditions.add(FormWhereCondition.build().display("所属单位-").value(currentAccountId).concatFactor(ClauseFactor.OR)); + conditions.add(FormWhereCondition.build().display("管理单位-").value(currentAccountId).concatFactor(ClauseFactor.OR)); + return conditions; + } - String latStr = (String) fieldsMap.get("纬度"); - String lngStr = (String) fieldsMap.get("经度"); + /** + * 视口懒加载:通过数据库层经纬度范围过滤,只查询视口内的资产 + * 生成 SQL: WHERE 纬度 >= south AND 纬度 <= north AND 经度 >= west AND 经度 <= east + */ + public List queryAssetsByBounds(BoundsQueryVo vo) throws Exception { + TableContext tableContext = FormTableExecutor.master(getFormNo()); + List conditions = new ArrayList<>(); + // 如果传了 assetId,按 资产编号 精确查询,忽略经纬度范围 + if (StringUtils.isNotBlank(vo.getAssetId())) { + conditions.add(FormWhereCondition.build().display("资产编号").value(vo.getAssetId())); + } else { + conditions.add(FormWhereCondition.build().display("经度-").between(vo.getWest(), vo.getEast())); + conditions.add(FormWhereCondition.build().display("纬度-").between(vo.getSouth(), vo.getNorth())); + } + conditions.add(FormWhereCondition.build().display("经度-").clauseFactor(ClauseFactor.NOT_NULL)); + conditions.add(FormWhereCondition.build().display("纬度-").clauseFactor(ClauseFactor.NOT_NULL)); + // 代码层过滤权限,避免 LIKE 导致索引失效 + boolean showAll = showAll(); + log.info("是否允许查看所有数据: " + showAll); + final String permIdStr = showAll ? null : (AppContext.currentAccountId() + ""); + if (StringUtils.isNotBlank(vo.getOwner())) { + conditions.add(FormWhereCondition.build().display("所属单位").value(vo.getOwner())); + } - // 1️⃣ 经纬度必须有 - if (StringUtils.isAnyBlank(latStr, lngStr)) { + if (StringUtils.isNotBlank(vo.getLevel2())) { + conditions.add(FormWhereCondition.build().display("资产类型二级").value(vo.getLevel2())); + } + if (StringUtils.isNotBlank(vo.getLevel1())) { + conditions.add(FormWhereCondition.build().display("资产类型一级").value(vo.getLevel1())); + } + if (StringUtils.isNotBlank(vo.getLevel3())) { + conditions.add(FormWhereCondition.build().display("资产分类-").value(vo.getLevel3())); + } + System.out.println("开始查询资产数据"); + List formColumns = FormTableExecutor.query(tableContext, null, conditions, true); + + // 代码层过滤权限(当前单位及下级单位) + if (!showAll && formColumns != null) { + List unitIds = getCurrentAndChildUnitIds(); + formColumns = formColumns.stream() + .filter(col -> { + Map fields = col.getFieldsMap(); + if (fields == null) return false; + Object permObj = fields.get("查看权限-选单位"); + String perm = permObj == null ? null : permObj.toString(); + return hasPermission(perm, unitIds); + }) + .collect(Collectors.toList()); + } + log.info("权限过滤后的数据条数: " + formColumns.size()); + Map regionMap = new HashMap<>(); + for (FormColumn formColumn : formColumns) { + Map fieldsMap = formColumn.getFieldsMap(); + if (fieldsMap == null) continue; + upsertRegionVos(regionMap, fieldsMap); + } + log.info("返回资产数据结果"); + return new ArrayList<>(regionMap.values()); + } + + private boolean showAll() throws Exception { + V3xOrgAccount currentAccount = orgManager.getAccountById(AppContext.currentAccountId()); + if(currentAccount == null) { + return false; + }else { + return currentAccount.getName().contains("国清办") + || currentAccount.getName().contains("国资局") + || currentAccount.getName().contains("软件开发"); + } + } + + /** + * 获取当前登录者单位及其所有下级单位的ID列表 + */ + private List getCurrentAndChildUnitIds() { + List ids = new ArrayList<>(); + long currentAccountId = AppContext.currentAccountId(); + JDBCAgent jdbcAgent = new JDBCAgent(); + try { + // 查当前单位的 path + jdbcAgent.execute("select path from org_unit where id = ?", currentAccountId); + List list = jdbcAgent.resultSetToList(); + if (list == null || list.isEmpty()) { + ids.add(String.valueOf(currentAccountId)); + return ids; + } + Map unitMap = (Map) list.get(0); + String rootPath = unitMap.get("path") + ""; + + // 查所有子单位 + jdbcAgent.execute("select id from org_unit where path like ?", rootPath + "%"); + list = jdbcAgent.resultSetToList(); + if (list != null) { + for (Object o : list) { + Map map = (Map) o; + ids.add(map.get("id") + ""); + } + } + } catch (Exception e) { + e.printStackTrace(); + ids.add(String.valueOf(currentAccountId)); + } finally { + jdbcAgent.close(); + } + return ids; + } + + /** + * 检查权限字段是否包含当前单位或下级单位 + * permField: 逗号分隔的单位ID字符串,如 "100,200,300" + * unitIds: 当前单位及下级单位ID列表 + */ + private boolean hasPermission(String permField, List unitIds) { + if (StringUtils.isBlank(permField) || unitIds == null || unitIds.isEmpty()) { + return false; + } + // 用逗号分隔后逐一匹配 + String[] permParts = permField.split(","); + for (String part : permParts) { + String trimmed = part.trim(); + if (unitIds.contains(trimmed)) { + return true; + } + } + return false; + } + + /** + * 保存资产边界数据到表单字段"资产边界数据" + * polygonData: JSON格式,包含polygon坐标和color颜色 + */ + public void saveAssetPolygon(String assetId, String polygonData) throws Exception { + if (StringUtils.isBlank(assetId)) { + throw new IllegalArgumentException("assetId不能为空"); + } + List updateFields = new ArrayList<>(); + updateFields.add(FormUpdateField.build().display("资产边界数据").value(polygonData)); + List conditions = new ArrayList<>(); + conditions.add(FormWhereCondition.build().display("id").value(assetId)); + FormTableExecutor.update(getMasterTableContext(),updateFields,conditions); + } + + + public String getDetailBaseUrlByAssetsType(String assetsType) throws Exception { + TableContext master = FormTableExecutor.master(configProvider.getBizConfigByKey(AssetsMapConstans.ASSETSDETAILURLCONFIGFORMNO)); + List conditions = new ArrayList<>(); + if(StringUtils.isBlank(assetsType)) { + conditions.add(FormWhereCondition.build().display("资产类型").value("不动产")); + }else { + conditions.add(FormWhereCondition.build().display("资产类型").value(assetsType)); + } + FormColumn formColumn = FormTableExecutor.queryOne(master, conditions, true); + if(formColumn == null) { + return null; + } + Object urlObj = formColumn.getFieldsMap().get("前置地址"); + return urlObj == null ? null : urlObj.toString(); + } + + /** + * 查询单个资产的边界数据 + */ + public Map getAssetBound(String assetId) throws Exception { + if (StringUtils.isBlank(assetId)) { + return null; + } + TableContext tableContext = FormTableExecutor.master(getFormNo()); + List conditions = new ArrayList<>(); + conditions.add(FormWhereCondition.build().display("id").value(assetId)); + FormColumn formColumn = FormTableExecutor.queryOne(tableContext, conditions, true); + if (formColumn == null) { + log.info("getAssetBound: 未查到记录, assetId=" + assetId); + return null; + } + Map fieldsMap = formColumn.getFieldsMap(); + if (fieldsMap == null) { + return null; + } + log.info("getAssetBound: 查到记录, assetId=" + assetId + ", 资产编号=" + fieldsMap.get("资产编号") + ", 资产名称=" + fieldsMap.get("资产名称")); + log.info("getAssetBound: 资产边界数据字段值=" + fieldsMap.get("资产边界数据") + ", 类型=" + (fieldsMap.get("资产边界数据") == null ? "null" : fieldsMap.get("资产边界数据").getClass().getName())); + Object boundObj = fieldsMap.get("资产边界数据"); + String boundData = boundObj == null ? null : boundObj.toString(); + Map result = new HashMap<>(); + result.put("boundData", boundData); + return result; + } + + /** + * 返回当前登录者单位的树形结构 + * path 每4字符代表一个层级,如 0000 → 00000001 → 000000010001 + */ + public List> getDeptSelectOptions() throws BusinessException { + long currentAccountId = AppContext.currentAccountId(); + JDBCAgent jdbcAgent = new JDBCAgent(); + try { + // 查当前单位的 path + jdbcAgent.execute("select path from org_unit where id = ?", currentAccountId); + List list = jdbcAgent.resultSetToList(); + if (list == null || list.isEmpty()) { + return new ArrayList<>(); + } + Map unitMap = (Map) list.get(0); + String rootPath = unitMap.get("path") + ""; + + // 查所有子单位 + jdbcAgent.execute("select id, name, path from org_unit where path like ?", rootPath + "%"); + list = jdbcAgent.resultSetToList(); + + // 用 LinkedHashMap 保持插入顺序,path → node + Map> nodeMap = new LinkedHashMap<>(); + for (Object o : list) { + Map map = (Map) o; + String path = map.get("path") + ""; + Map node = new LinkedHashMap<>(); + node.put("id", map.get("id") + ""); + node.put("name", map.get("name") + ""); + node.put("path", path); + node.put("children", new ArrayList>()); + nodeMap.put(path, node); + } + + // 按 path 长度排序(浅层先处理),逐个挂到父节点 + List> roots = new ArrayList<>(); + List sortedPaths = new ArrayList<>(nodeMap.keySet()); + Collections.sort(sortedPaths, Comparator.comparingInt(String::length)); + + for (String path : sortedPaths) { + Map node = nodeMap.get(path); + if (path.equals(rootPath) || path.length() <= 4) { + roots.add(node); + } else { + // 父节点 path = 去掉最后4字符 + String parentPath = path.substring(0, path.length() - 4); + Map parent = nodeMap.get(parentPath); + if (parent != null) { + ((List>) parent.get("children")).add(node); + } else { + // 找不到父节点,作为根节点 + roots.add(node); + } + } + } + return roots; + } catch (Exception e) { + e.printStackTrace(); + return new ArrayList<>(); + } finally { + jdbcAgent.close(); + } + } + + public Map getAssetsCatagorySelectOptions() throws Exception { + TableContext tableContext = FormTableExecutor.master(getFormNo()); + Map enumItemMap = EnumMapUtils.getEnumItemMap(tableContext.getTableBean(), "资产分类"); + if(enumItemMap == null) { + return new HashMap<>(); + } + Map resultMap = new HashMap<>(); + for (String key : enumItemMap.keySet()) { + CtpEnumItem tempItem = enumItemMap.get(key); + AssetsCatagory assetsCatagory = new AssetsCatagory(); + assetsCatagory.setId(tempItem.getId() + ""); + assetsCatagory.setShowValue(key); + resultMap.put(key,assetsCatagory); + } + return resultMap; + } + + /** + * 生成 region 的唯一 key,用于 Map 查找 + */ + private String regionKey(double lat, double lng, String address) { + return lat + "_" + lng + "_" + address; + } + + /** + * 优化:接收 Map 而非 List,O(1) 查找替代 O(n) 遍历 + */ + public void upsertRegionVos(Map regionMap, Map fieldsMap) { + + BigDecimal latObj = (BigDecimal) fieldsMap.get("纬度-"); + BigDecimal lngObj = (BigDecimal) fieldsMap.get("经度-"); + + if (latObj == null || lngObj == null) { return; } - double lat = Double.parseDouble(latStr); - double lng = Double.parseDouble(lngStr); + double lat = Double.parseDouble(latObj.toString()); + double lng = Double.parseDouble(lngObj.toString()); - String address = (String) fieldsMap.get("位置详情"); - String owner = (String) fieldsMap.get("权属公司"); + Object addrObj = fieldsMap.get("所在位置"); + String address = addrObj == null ? null : addrObj.toString(); - // 2️⃣ 地址 & 权属公司建议也做非空控制(关键!) - if (StringUtils.isAnyBlank(address)) { + if (StringUtils.isBlank(address)) { return; } - String assetId = getStringValue(fieldsMap,"id"); + String assetId = getStringValue(fieldsMap, "id"); AssetsItem newAsset = buildAssetsItem(fieldsMap); - Long managerId = fieldsMap.get("运营单位") == null ? null : Long.parseLong(fieldsMap.get("运营单位") + ""); - String deptName = getDeptName(managerId); - // 3️⃣ 查找区域 - for (RegionVo regionVo : regionVos) { - // 3.1 经纬度必须存在 - if (regionVo.getLat() == null || regionVo.getLng() == null) { - continue; + String key = regionKey(lat, lng, address); + RegionVo regionVo = regionMap.get(key); + + if (regionVo != null) { + // 判断资产是否已存在 + boolean exists = regionVo.getAssets().stream() + .anyMatch(a -> a.getId().equals(assetId)); + if (!exists) { + regionVo.getAssets().add(newAsset); } + } else { + // 新建区域 + RegionVo newRegion = new RegionVo(); + newRegion.setLat(lat); + newRegion.setLng(lng); + newRegion.setAddress(address); + newRegion.setManager(getStringValue(fieldsMap,"管理单位")); - boolean sameLocation = - Double.compare(regionVo.getLat(), lat) == 0 && - Double.compare(regionVo.getLng(), lng) == 0; + List assets = new ArrayList<>(); + assets.add(newAsset); + newRegion.setAssets(assets); - // 3.2 地址 & 权属公司必须非空才参与匹配 - if (!sameLocation || - StringUtils.isAnyBlank(regionVo.getAddress())) { - continue; - } - - boolean sameAddress = address.equals(regionVo.getAddress()); - - if (sameAddress) { - - // 4️⃣ 判断资产是否存在 - boolean exists = regionVo.getAssets().stream() - .anyMatch(a -> a.getId().equals(assetId) ); - - if (!exists) { - regionVo.getAssets().add(newAsset); - } - return; - } + regionMap.put(key, newRegion); } - - // 5️⃣ 新建区域(这里可以放心,因为关键字段都有值) - RegionVo newRegion = new RegionVo(); - newRegion.setLat(lat); - newRegion.setLng(lng); - newRegion.setAddress(address); - newRegion.setOwner(owner); - newRegion.setManager(deptName); - - List assets = new ArrayList<>(); - assets.add(newAsset); - newRegion.setAssets(assets); - - regionVos.add(newRegion); } - private String getDeptName(Long deptId) { - if(deptId != null) { - try { - V3xOrgDepartment department = orgManager.getDepartmentById(deptId); - if(department != null) { - return department.getName(); - } - }catch (Exception e) { - - } + /** + * 优化:带缓存的单位名称查询 + */ + private String getUnitName(Long unitId) { + if (unitId == null) { + return null; + } + if (deptCache.containsKey(unitId)) { + return deptCache.get(unitId); + } + try { + V3xOrgAccount account = orgManager.getAccountById(unitId); + String name = account != null ? account.getName() : null; + deptCache.put(unitId, name); + return name; + } catch (Exception e) { + deptCache.put(unitId, null); + return null; } - return null; } private AssetsItem buildAssetsItem(Map fieldsMap) { AssetsItem assetsItem = new AssetsItem(); - assetsItem.setId(getStringValue(fieldsMap,"id")); - assetsItem.setArea(fieldsMap.get("资产面积") == null ? null : Double.parseDouble(fieldsMap.get("资产面积") + "")); - assetsItem.setTenant(getStringValue(fieldsMap,"租户名称")); - assetsItem.setPurpose(getStringValue(fieldsMap,"资产用途")); - assetsItem.setRemark(getStringValue(fieldsMap,"备注")); - assetsItem.setOwner(getStringValue(fieldsMap,"权属单位")); - assetsItem.setAssetName(getStringValue(fieldsMap,"资产名称")); - assetsItem.setAddress(getStringValue(fieldsMap,"位置详情")); - String certNo = fieldsMap.get("权证号码") == null ? null : (String)fieldsMap.get("权证号码"); -// assetsItem.setManager(fieldsMap.get("运营单位") == null ? null : (String)fieldsMap.get("运营单位") + ""); - Long managerId = fieldsMap.get("运营单位") == null ? null : Long.parseLong(fieldsMap.get("运营单位") + ""); - assetsItem.setLat(getStringValue(fieldsMap,"纬度")); - assetsItem.setLng(getStringValue(fieldsMap,"经度")); - assetsItem.setManager(getDeptName(managerId)); - assetsItem.setCertNo(certNo); - assetsItem.setStatus(getStringValue(fieldsMap,"资产状态")); + assetsItem.setId(getStringValue(fieldsMap, "id")); + assetsItem.setArea(fieldsMap.get("数量面积") == null ? null : Double.parseDouble(fieldsMap.get("数量面积") + "")); + assetsItem.setAssetCode(getStringValue(fieldsMap, "资产编号")); + assetsItem.setRemark(getStringValue(fieldsMap, "备注")); + assetsItem.setAssetName(getStringValue(fieldsMap, "资产名称")); + assetsItem.setAddress(getStringValue(fieldsMap, "所在位置")); + assetsItem.setLat(Optional.ofNullable(fieldsMap.get("纬度-")).map(Object::toString).orElse(null)); + assetsItem.setLng(Optional.ofNullable(fieldsMap.get("经度-")).map(Object::toString).orElse(null)); + assetsItem.setManager(getStringValue(fieldsMap,"管理单位")); + assetsItem.setAssetType(getStringValue(fieldsMap, "资产类型一级")); return assetsItem; } private String getStringValue(Map fieldsMap, String key) { - return fieldsMap.get(key) == null ? null : (String)fieldsMap.get(key); + Object val = fieldsMap.get(key); + return val == null ? null : val.toString(); } - public Integer countAssets(String type) throws BusinessException { + /** + * 优化:一次查询全量数据,内存中分类计数,替代3次独立查询 + */ + public Map countAllStats() throws Exception { + TableContext tableContext = FormTableExecutor.master(getFormNo()); + List formColumns = FormTableExecutor.query(tableContext, null, null, true); + + int total = 0; + int rented = 0; + int vacant = 0; + + String rentedValue = EnumMapUtils.getEnumItemValueByDisplayValue(tableContext.getTableBean(), "资产状态", "已租"); + String vacantValue = EnumMapUtils.getEnumItemValueByDisplayValue(tableContext.getTableBean(), "资产状态", "待租"); + + for (FormColumn col : formColumns) { + Map fields = col.getFieldsMap(); + if (fields == null) continue; + total++; + String status = fields.get("资产状态") == null ? null : fields.get("资产状态") + ""; + if (rentedValue != null && rentedValue.equals(status)) { + rented++; + } else if (vacantValue != null && vacantValue.equals(status)) { + vacant++; + } + } + + Map result = new HashMap<>(); + result.put("total", total); + result.put("rented", rented); + result.put("vacant", vacant); + return result; + } + + public Integer countAssets(String type) throws Exception { TableContext tableContext = FormTableExecutor.master(getFormNo()); List conditions = new ArrayList(); if(type == null || "0".equals(type)) { @@ -168,10 +479,76 @@ public class AssetsQueryService { return formColumns.size(); } - public List queryAssetsByKeyword(String keyword) throws BusinessException { + /** + * 按筛选条件分页查询资产列表(不走视口),用于地图右侧列表展示 + * 返回格式: {records: [...], total: n} + */ + public Map queryAssetList(BoundsQueryVo vo) throws Exception { + TableContext tableContext = FormTableExecutor.master(getFormNo()); + List conditions = new ArrayList<>(); + if (StringUtils.isNotBlank(vo.getOwner())) { + conditions.add(FormWhereCondition.build().display("所属单位").value(vo.getOwner())); + } + if (StringUtils.isNotBlank(vo.getLevel1())) { + conditions.add(FormWhereCondition.build().display("资产类型一级").value(vo.getLevel1())); + } + if (StringUtils.isNotBlank(vo.getLevel2())) { + conditions.add(FormWhereCondition.build().display("资产类型二级").value(vo.getLevel2())); + } + if (StringUtils.isNotBlank(vo.getLevel3())) { + conditions.add(FormWhereCondition.build().display("资产分类-").value(vo.getLevel3())); + } + if (StringUtils.isNotBlank(vo.getKeyword())) { + conditions.add(FormWhereCondition.build().display("资产名称").value(vo.getKeyword()).clauseFactor(ClauseFactor.LIKE) + .concatFactor(ClauseFactor.OR)); + conditions.add(FormWhereCondition.build().display("资产编号").value(vo.getKeyword()).clauseFactor(ClauseFactor.LIKE)); + } + + // 先查全量用于权限过滤 + List formColumns = FormTableExecutor.query(tableContext, null, conditions, true); + + // 权限过滤(当前单位及下级单位) + boolean showAll = showAll(); + if (!showAll) { + List unitIds = getCurrentAndChildUnitIds(); + formColumns = formColumns.stream() + .filter(col -> { + Map fields = col.getFieldsMap(); + if (fields == null) return false; + String perm = (String) fields.get("查看权限-选单位"); + return hasPermission(perm, unitIds); + }) + .collect(Collectors.toList()); + } + + int total = formColumns.size(); + + // 分页 + int pageNum = vo.getPageNum() != null && vo.getPageNum() > 0 ? vo.getPageNum() : 1; + int pageSize = vo.getPageSize() != null && vo.getPageSize() > 0 ? vo.getPageSize() : 10; + int fromIndex = (pageNum - 1) * pageSize; + int toIndex = Math.min(fromIndex + pageSize, total); + + List records = new ArrayList<>(); + if (fromIndex < total) { + List pageColumns = formColumns.subList(fromIndex, toIndex); + for (FormColumn col : pageColumns) { + Map fieldsMap = col.getFieldsMap(); + if (fieldsMap == null) continue; + records.add(buildAssetsItem(fieldsMap)); + } + } + + Map result = new HashMap<>(); + result.put("records", records); + result.put("total", total); + return result; + } + + public List queryAssetsByKeyword(String keyword) throws Exception { List conditions = new ArrayList(); if(StringUtils.isNotBlank(keyword)){ - conditions.add(FormWhereCondition.build().display("位置详情").value(keyword).concatFactor(ClauseFactor.OR).clauseFactor(ClauseFactor.LIKE)); + conditions.add(FormWhereCondition.build().display("所在位置").value(keyword).concatFactor(ClauseFactor.OR).clauseFactor(ClauseFactor.LIKE)); } TableContext tableContext = FormTableExecutor.master(getFormNo()); List formColumns = FormTableExecutor.pageQuery(tableContext,null, conditions, 1,10,true); diff --git a/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsCatagory.java b/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsCatagory.java new file mode 100644 index 0000000..96cbdf4 --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsCatagory.java @@ -0,0 +1,33 @@ +package com.seeyon.apps.assetsmap.vo; + +import java.util.Map; + +public class AssetsCatagory { + private String id; + private String showValue; + private Map children; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getShowValue() { + return showValue; + } + + public void setShowValue(String showValue) { + this.showValue = showValue; + } + + public Map getChildren() { + return children; + } + + public void setChildren(Map children) { + this.children = children; + } +} diff --git a/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsItem.java b/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsItem.java index 72246f7..d95c175 100644 --- a/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsItem.java +++ b/src/main/java/com/seeyon/apps/assetsmap/vo/AssetsItem.java @@ -2,6 +2,7 @@ package com.seeyon.apps.assetsmap.vo; public class AssetsItem { private String id; + private String assetCode; private String assetName; private Long regionId; private String purpose; @@ -15,6 +16,9 @@ public class AssetsItem { private String lng; private String lat; private String address; + private String assetType; + private String assetCategory; + private String boundData; public String getId() { return id; @@ -24,6 +28,14 @@ public class AssetsItem { this.id = id; } + public String getAssetCode() { + return assetCode; + } + + public void setAssetCode(String assetCode) { + this.assetCode = assetCode; + } + public Long getRegionId() { return regionId; } @@ -127,4 +139,28 @@ public class AssetsItem { public void setAddress(String address) { this.address = address; } + + public String getAssetType() { + return assetType; + } + + public void setAssetType(String assetType) { + this.assetType = assetType; + } + + public String getAssetCategory() { + return assetCategory; + } + + public void setAssetCategory(String assetCategory) { + this.assetCategory = assetCategory; + } + + public String getBoundData() { + return boundData; + } + + public void setBoundData(String boundData) { + this.boundData = boundData; + } } \ No newline at end of file diff --git a/src/main/java/com/seeyon/apps/assetsmap/vo/BoundsQueryVo.java b/src/main/java/com/seeyon/apps/assetsmap/vo/BoundsQueryVo.java new file mode 100644 index 0000000..d18a068 --- /dev/null +++ b/src/main/java/com/seeyon/apps/assetsmap/vo/BoundsQueryVo.java @@ -0,0 +1,41 @@ +package com.seeyon.apps.assetsmap.vo; + +public class BoundsQueryVo { + private Double south; + private Double west; + private Double north; + private Double east; + private String owner; + private String level1; + private String level2; + private String level3; + private String assetId; + private String keyword; + private Integer pageNum; + private Integer pageSize; + + public Double getSouth() { return south; } + public void setSouth(Double south) { this.south = south; } + public Double getWest() { return west; } + public void setWest(Double west) { this.west = west; } + public Double getNorth() { return north; } + public void setNorth(Double north) { this.north = north; } + public Double getEast() { return east; } + public void setEast(Double east) { this.east = east; } + public String getOwner() { return owner; } + public void setOwner(String owner) { this.owner = owner; } + public String getLevel1() { return level1; } + public void setLevel1(String level1) { this.level1 = level1; } + public String getLevel2() { return level2; } + public void setLevel2(String level2) { this.level2 = level2; } + public String getLevel3() { return level3; } + public void setLevel3(String level3) { this.level3 = level3; } + public String getAssetId() { return assetId; } + public void setAssetId(String assetId) { this.assetId = assetId; } + public String getKeyword() { return keyword; } + public void setKeyword(String keyword) { this.keyword = keyword; } + public Integer getPageNum() { return pageNum; } + public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } + public Integer getPageSize() { return pageSize; } + public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } +} diff --git a/src/main/java/com/seeyon/ctp/rest/resources/assetsmap/AssetsRegionResources.java b/src/main/java/com/seeyon/ctp/rest/resources/assetsmap/AssetsRegionResources.java index 55165a6..f4a94ca 100644 --- a/src/main/java/com/seeyon/ctp/rest/resources/assetsmap/AssetsRegionResources.java +++ b/src/main/java/com/seeyon/ctp/rest/resources/assetsmap/AssetsRegionResources.java @@ -2,7 +2,15 @@ package com.seeyon.ctp.rest.resources.assetsmap; import cn.hutool.log.Log; import com.alibaba.fastjson.JSONObject; +import com.seeyon.aicloud.common.JsonUtils; +import com.seeyon.apps.assetsmap.kit.CAP4FormKit; import com.seeyon.apps.assetsmap.service.AssetsQueryService; +import com.seeyon.apps.assetsmap.vo.AssetsCatagory; +import com.seeyon.apps.assetsmap.vo.BoundsQueryVo; +import com.seeyon.cap4.form.bean.FormDataMasterBean; +import com.seeyon.cap4.form.bean.FormFieldBean; +import com.seeyon.cap4.form.service.CAP4FormManager; +import com.seeyon.cap4.template.util.CAPFormUtil; import com.seeyon.ctp.common.AppContext; import com.seeyon.ctp.rest.resources.BaseResource; @@ -10,12 +18,15 @@ import javax.servlet.http.HttpServletRequest; import javax.ws.rs.*; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; +import java.util.HashMap; +import java.util.Map; @Path("/assetsRegion") @Produces({"application/json", "application/xml"}) public class AssetsRegionResources extends BaseResource { private static final Log log = Log.get(AssetsRegionResources.class); + private CAP4FormManager cap4FormManager = (CAP4FormManager) AppContext.getBean("cap4FormManager"); private AssetsQueryService assetsQueryService = (AssetsQueryService) AppContext.getBean("assetsQueryService"); @@ -34,12 +45,82 @@ public class AssetsRegionResources extends BaseResource { } @POST - @Path("/search") + @Path("/getByBounds") @Produces({"application/json"}) @Consumes({"application/json"}) - public Response search(JSONObject params) { + public Response getByBounds(BoundsQueryVo vo){ try { - return this.success(this.assetsQueryService.queryAssetsByKeyword(params.getString("keyword"))); + log.info("按视口查询资产"); + return success(assetsQueryService.queryAssetsByBounds(vo)); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } + + @POST + @Path("/queryAssetList") + @Produces({"application/json"}) + @Consumes({"application/json"}) + public Response queryAssetList(BoundsQueryVo vo){ + try { + return success(assetsQueryService.queryAssetList(vo)); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } + + @GET + @Path("/getDeptOptions") + @Produces({"application/json"}) + public Response getDeptOptions(){ + try { + return success(assetsQueryService.getDeptSelectOptions()); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } + + @GET + @Path("/getCategoryOptions") + @Produces({"application/json"}) + public Response getCategoryOptions(){ + try { + log.info("查询分类"); + Map resMap = assetsQueryService.getAssetsCatagorySelectOptions(); + log.info("查询结果: " + JsonUtils.toJSONString(resMap)); + return success(resMap); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } + + @GET + @Path("/getAssetBound") + @Produces({"application/json"}) + public Response getAssetBound(@QueryParam("assetId") String assetId){ + try { + return success(assetsQueryService.getAssetBound(assetId)); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } + + @GET + @Path("/search") + @Produces({"application/json"}) + public Response search(@QueryParam("keyword") String keyword) { + try { + return this.success(this.assetsQueryService.queryAssetsByKeyword(keyword)); } catch (Exception e) { log.error(e.getMessage(), new Object[]{e}); e.printStackTrace(); @@ -47,6 +128,48 @@ public class AssetsRegionResources extends BaseResource { } } + @POST + @Path("/formBackFill") + @Produces({"application/json"}) + @Consumes({"application/json"}) + public Response boundRefCallBack(Map params) { + try { + String masterId = (String) params.get("masterId"); + String fieldDisplay = (String) params.get("fieldDisplay"); + Object value = params.get("value"); + + FormDataMasterBean cacheFormData = cap4FormManager.getSessioMasterDataBean(Long.parseLong(masterId)); + FormFieldBean fieldBean = CAP4FormKit.getFieldBean(cacheFormData, fieldDisplay); + Map displayValueMap = CAPFormUtil.getDisplayValueMap(value, fieldBean, null); + cacheFormData.addFieldValue(fieldBean.getName(), value); + + Map mainData = new HashMap<>(); + mainData.put(fieldBean.getName(), displayValueMap); + + return success(mainData); + } catch (Exception e) { + log.error(e.getMessage(), e); + return fail("保存失败:" + e.getMessage()); + } + } + + @POST + @Path("/saveAssetPolygon") + @Produces({"application/json"}) + @Consumes({"application/json"}) + public Response saveAssetPolygon(JSONObject params){ + try { + String assetId = params.getString("assetId"); + String polygonData = params.getString("polygonData"); + assetsQueryService.saveAssetPolygon(assetId, polygonData); + return success("保存成功"); + } catch (Exception e) { + log.error(e.getMessage(), e); + e.printStackTrace(); + return fail("保存失败:" + e.getMessage()); + } + } + @Path("/countAssets") @GET @Produces({"application/json"}) @@ -60,4 +183,30 @@ public class AssetsRegionResources extends BaseResource { return fail("查询失败:" + e.getMessage()); } } + + @Path("/countAllStats") + @GET + @Produces({"application/json"}) + public Response countAllStats(){ + try { + return success(assetsQueryService.countAllStats()); + }catch (Exception e) { + log.error(e.getMessage(),e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } + + @Path("/getDetailBaseUrlByAssetsType") + @GET + @Produces({"application/json"}) + public Response getDetailBaseUrlByAssetsType(@QueryParam("assetsType")String assetsType){ + try { + return success(assetsQueryService.getDetailBaseUrlByAssetsType(assetsType)); + }catch (Exception e) { + log.error(e.getMessage(),e); + e.printStackTrace(); + return fail("查询失败:" + e.getMessage()); + } + } }