智处-防灭火资源表格

main
滕嵩 2025-03-26 16:41:00 +08:00
parent 0b2697fab4
commit d76a4d0423
23 changed files with 4623 additions and 1224 deletions

View File

@ -98,6 +98,7 @@
"axios": "^1.6.4",
"codemirror": "^5.65.16",
"color": "^4.2.3",
"coordtransform": "^2.1.2",
"cropperjs": "^1.6.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

View File

@ -0,0 +1,67 @@
import cloneDeep from 'lodash/cloneDeep'
import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { chartInitConfig } from '@/settings/designSetting'
import { TableFilterConfig } from './index'
import dataJson from './data.json'
const {header, source, filterset} = dataJson;
export const option = {
header: header,
dataset: source,
filterset: filterset,
dataStyle: {
colLeft: 4,
filterMarginTop: 20,
filterMarginLeft: 50,
filterItemHeight: 45,
filterTitleAlign: 'left',
filterTitleFontSize: 24,
filterTitleFontColor: '#ADFFC0',
filterContentAlign: 'left',
filterChooseFontSize: 18,
filterChooseFontColor: '#FFFFFF',
tableMarginTop: 20,
tableMarginLeft: 20,
tableBordered: false,
tableSigleColumn: true,
tableSingleLine: true,
tableStriped: false,
tableSize: 'small',
tableAlign: 'center',
// 表头
tableHeaderShow: false,
// 序号
orderBumberShow: false,
// 操作
buttonDivShow: true,
buttonDivShow1: true,
buttonDivShow2: true,
buttonWidth: 22,
buttonHeight: 22,
// 宽度
orderDivWidth: 50,
buttonDivWidth: 100,
// 字体
tableHeaderBackgroud: '#FFFFFF',
tableHeaderFontSize: 20,
tableHeaderFontColor: '#000000',
tableDataBackgroudShow: false,
tableDataBackgroud: '#FFFFFF',
tableDataFontSize: 20,
tableDataFontColor: '#FFFFFF',
},
}
export default class Config extends PublicConfigClass implements CreateComponentType {
public key = TableFilterConfig.key
public attr = { ...chartInitConfig, w: 1060, h: 570, zIndex: -1 }
public chartConfig = cloneDeep(TableFilterConfig)
public option = cloneDeep(option)
}

View File

@ -0,0 +1,826 @@
<template>
<!-- 筛选项数据显示设置 -->
<CollapseItem name="筛选项数据显示设置" :expanded="true">
<span style="font-size: 12px; margin-left: 8px"> 筛选项字段与类型 </span>
<div style="overflow-x: auto">
<n-table
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th>标题名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in props.optionData.filterset" :key="index">
<td>
<n-input v-model:value="item.title" type="text" size="small" />
</td>
<td>
<div style="width: 70px">
<n-button
class="go-ml-2"
type="primary"
size="small"
ghost
@click="
props.optionData.filterset.splice(index + 1, 0, {
title: null,
field: null,
compareType: null,
showType: null,
value: [],
options: [{ label: null, value: null }],
})
"
>
+
</n-button>
<n-button
class="go-ml-2"
type="warning"
size="small"
ghost
:disabled="props.optionData.filterset.length === 1"
@click="
if (props.optionData.filterset.length !== 1) {
props.optionData.filterset.splice(index, 1);
}
"
>
-
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
</CollapseItem>
<CollapseItem
:expanded="false"
v-for="(filtersetItem, filtersetIndex) in optionData.filterset"
:key="filtersetIndex"
:alone="false"
:name="'筛选项【' + filtersetItem.title + '】设置'"
>
<SettingItem name="筛选类型">
<n-select
v-model:value="filtersetItem.showType"
:options="[
{ label: '多选框', value: 'checkbox' },
{ label: '单选框', value: 'radio' },
{ label: '下拉框', value: 'select' },
{ label: '下拉框(多选)', value: 'selectGroup' },
{ label: '按钮组', value: 'buttonGroup' },
{ label: '按钮组(多选)', value: 'buttonGroupList' },
{ label: '列搜索框', value: 'inputSearch' },
{ label: '全数据搜索框', value: 'inputSearchAll' },
{ label: '时间选择器', value: 'timePicker' },
{ label: '时间范围', value: 'timeRange' },
{ label: '日期范围', value: 'dateRange' },
]"
clearable
size="small"
placeholder="筛选类型"
@change="
filtersetItem.compareType = null;
filtersetItem.value = null;
"
/>
</SettingItem>
<SettingItem v-if="!['inputSearchAll'].includes(filtersetItem.showType)" name="对应字段">
<n-select
v-model:value="filtersetItem.field"
:options="columnsOptions"
size="small"
clearable
placeholder="对应字段"
/>
</SettingItem>
<SettingItem name="筛选方式">
<n-select
v-model:value="filtersetItem.compareType"
:options="getOptionsByShowType(filtersetItem)"
clearable
size="small"
placeholder="筛选方式"
/>
</SettingItem>
<!-- 默认值搜索框无默认值 -->
<SettingItem
v-if="!['inputSearch', 'inputSearchAll'].includes(filtersetItem.showType)"
name="默认值"
>
<!-- 多选框下拉框多选按钮组 多选-->
<n-select
v-if="['checkbox', 'selectGroup', 'buttonGroupList'].includes(filtersetItem.showType)"
v-model:value="filtersetItem.value"
:options="filtersetItem.options"
multiple
clearable
size="small"
placeholder="默认值"
/>
<!-- 时间选择器 -->
<n-time-picker
v-else-if="['timePicker'].includes(filtersetItem.showType)"
v-model:formatted-value="filtersetItem.value"
value-format="HH:mm:ss"
format="HH:mm:ss"
size="small"
clearable
placeholder="默认时间"
/>
<!-- 时间范围 -->
<div v-else-if="['timeRange'].includes(filtersetItem.showType)">
<n-time-picker
v-model:formatted-value="filtersetItem.value"
value-format="HH:mm:ss"
format="HH:mm:ss"
size="small"
clearable
placeholder="开始时间"
/>
<n-time-picker
v-model:formatted-value="filtersetItem.value2"
value-format="HH:mm:ss"
format="HH:mm:ss"
size="small"
clearable
placeholder="结束时间"
/>
</div>
<!-- 日期范围 -->
<n-date-picker
v-else-if="['dateRange'].includes(filtersetItem.showType)"
v-model:formatted-value="filtersetItem.value"
value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
size="small"
clearable
/>
<!-- 其他 -->
<n-select
v-else
v-model:value="filtersetItem.value"
:options="filtersetItem.options"
clearable
size="small"
placeholder="默认值"
/>
</SettingItem>
<!-- 搜索框时间日期框无选项 -->
<div style="overflow-x: auto">
<n-table
v-if="
!['inputSearch', 'inputSearchAll', 'timePicker', 'timeRange', 'dateRange'].includes(
filtersetItem.showType,
)
"
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th>名称</th>
<th></th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(optionsItem, optionsIndex) in filtersetItem.options" :key="optionsIndex">
<td>
<n-input
v-model:value="optionsItem.label"
type="text"
size="small"
placeholder="名称"
/>
</td>
<td>
<n-input
v-model:value="optionsItem.value"
type="text"
size="small"
placeholder="值"
/>
</td>
<td>
<div style="width: 70px">
<n-button
class="go-ml-2"
type="primary"
size="small"
ghost
@click="
filtersetItem.options.splice(optionsIndex + 1, 0, {
label: null,
value: null,
})
"
>
+
</n-button>
<n-button
class="go-ml-2"
type="warning"
size="small"
ghost
:disabled="optionsItem.length === 1"
@click="
if (filtersetItem.options.length !== 1) {
filtersetItem.options.splice(optionsIndex, 1);
}
"
>
-
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
</CollapseItem>
<br />
<!-- 筛选项样式设置 -->
<CollapseItem name="筛选项样式设置" :expanded="true">
<SettingItemBox :alone="false" name="整体设置">
<SettingItem :alone="false" name="上方距离">
<n-input-number
v-model:value="optionData.dataStyle.filterMarginTop"
:min="0"
size="small"
placeholder="上方距离"
/>
</SettingItem>
<SettingItem :alone="false" name="左右距离">
<n-input-number
v-model:value="optionData.dataStyle.filterMarginLeft"
:min="0"
size="small"
placeholder="左右距离"
/>
</SettingItem>
<SettingItem :alone="false" name="单个筛选项高度">
<n-input-number
v-model:value="optionData.dataStyle.filterItemHeight"
:min="0"
size="small"
placeholder="单个筛选项高度"
/>
</SettingItem>
<SettingItem name="标题对齐方式">
<n-select
v-model:value="optionData.dataStyle.filterTitleAlign"
:options="[
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' },
]"
size="small"
/>
</SettingItem>
<SettingItem name="内容对齐方式">
<n-select
v-model:value="optionData.dataStyle.filterContentAlign"
:options="[
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' },
]"
size="small"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="标题样式">
<SettingItem :alone="false" name="标题占比">
<n-space vertical>
<n-slider v-model:value="optionData.dataStyle.colLeft" :step="1" :min="0" :max="24" />
<n-input-number v-model:value="optionData.dataStyle.colLeft" size="small">
<template #suffix> / 24 </template>
</n-input-number>
</n-space>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="字体设置">
<SettingItem name="筛选标题字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.filterTitleFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="筛选标题字体大小">
<n-input-number
v-model:value="optionData.dataStyle.filterTitleFontSize"
:min="0"
size="small"
placeholder="筛选标题字体大小"
></n-input-number>
</SettingItem>
<SettingItem name="筛选内容字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.filterChooseFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="筛选内容字体大小">
<n-input-number
v-model:value="optionData.dataStyle.filterChooseFontSize"
:min="0"
size="small"
placeholder="筛选内容字体大小"
></n-input-number>
</SettingItem>
</SettingItemBox>
</CollapseItem>
<CollapseItem name="表格数据显示设置" :expanded="true">
<span style="font-size: 12px; margin-left: 8px">
表格数据请先确定数据部分再确定表格数据
</span>
<div style="overflow-x: auto">
<n-table
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th></th>
<th>表头名称</th>
<th>表格字段</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in props.optionData.header" :key="index">
<td>
{{ index + 1 }}
</td>
<td>
<n-input v-model:value="item.title" type="text" size="small" />
</td>
<td>
<n-select v-model:value="item.key" :options="columnsOptions" size="small" />
</td>
<td>
<div style="width: 70px">
<n-button class="go-ml-2" type="primary" size="small" ghost @click="add(index)">
+
</n-button>
<n-button
class="go-ml-2"
type="warning"
size="small"
ghost
:disabled="props.optionData.header.length === 1"
@click="remove(index)"
>
-
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
<SettingItemBox :alone="true" name="表格宽度">
<SettingItem :alone="true" name="不包含序列和操作列">
<n-input v-model:value="columnsWidths" type="text" size="small" />
</SettingItem>
<SettingItem :alone="false" name="序列宽度">
<n-input-number
v-model:value="optionData.dataStyle.orderDivWidth"
:min="0"
size="small"
placeholder="序列宽度"
/>
</SettingItem>
<SettingItem :alone="false" name="操作列宽度">
<n-input-number
v-model:value="optionData.dataStyle.buttonDivWidth"
:min="0"
size="small"
placeholder="操作列宽度"
/>
</SettingItem>
</SettingItemBox>
<span style="font-size: 12px; margin-left: 8px"> 表格数据前缀和后缀 </span>
<div style="overflow-x: auto">
<n-table
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th>表头名称</th>
<th>前缀</th>
<th>后缀</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in props.optionData.header" :key="index">
<td>
{{ item.title }}
</td>
<td>
<n-input v-model:value="item.prefix" size="small" placeholder="前缀" />
</td>
<td>
<n-input v-model:value="item.suffix" size="small" placeholder="后缀" />
</td>
</tr>
</tbody>
</n-table>
</div>
<br />
<span style="font-size: 12px; margin-left: 8px"> 表格数据数据格式转换 </span>
<div style="overflow-x: auto">
<n-table
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th>表头名称</th>
<th>数据格式转换</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in props.optionData.header" :key="index">
<td style="width: 70px">
{{ item.title }}
</td>
<td>
<n-select
v-model:value="item.convert"
:options="[
{ label: '小数点保留后两位', value: '0' },
{ label: '转换时间格式0小时0分钟0秒', value: '1' },
{ label: '转换日期格式2025-01-01', value: '2' },
{ label: '转换日期格式2025/01/01', value: '3' },
{ label: '转换日期格式2025-01-01 00:00:00', value: '4' },
]"
clearable
size="small"
placeholder="数据格式转换"
/>
</td>
</tr>
</tbody>
</n-table>
</div>
<SettingItemBox :alone="false" name="显示设置">
<SettingItem name="是否显示边框">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableBordered" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否使用斑马条纹">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableStriped" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否不设定行的分割线">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableSigleColumn" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否不设定列的分割线">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableSingleLine" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示表头">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableHeaderShow" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示序号列">
<n-space>
<n-switch v-model:value="optionData.dataStyle.orderBumberShow" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示操作列">
<n-space>
<n-switch v-model:value="optionData.dataStyle.buttonDivShow" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示第一按钮">
<n-space>
<n-switch v-model:value="optionData.dataStyle.buttonDivShow1" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示第二按钮">
<n-space>
<n-switch v-model:value="optionData.dataStyle.buttonDivShow2" size="small"></n-switch>
</n-space>
</SettingItem>
</SettingItemBox>
</CollapseItem>
<CollapseItem name="表格样式设置" :expanded="true">
<SettingItemBox name="整体设置">
<SettingItem name="上方距离">
<n-input-number
v-model:value="optionData.dataStyle.tableMarginTop"
:min="0"
size="small"
placeholder="上方距离"
/>
</SettingItem>
<SettingItem name="左右距离">
<n-input-number
v-model:value="optionData.dataStyle.tableMarginLeft"
:min="0"
size="small"
placeholder="左右距离"
/>
</SettingItem>
<SettingItem name="对齐方式">
<n-select
v-model:value="optionData.dataStyle.tableAlign"
:options="[
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' },
]"
size="small"
/>
</SettingItem>
<SettingItem name="表格尺寸大小">
<n-select
v-model:value="optionData.dataStyle.tableSize"
:options="[
{ label: '小', value: 'small' },
{ label: '中', value: 'medium' },
{ label: '大', value: 'large' },
]"
size="small"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="表头设置">
<SettingItem name="表头背景颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.tableHeaderBackgroud"
></n-color-picker>
</SettingItem>
<SettingItem name="表头字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.tableHeaderFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="表头字体大小">
<n-input-number
v-model:value="optionData.dataStyle.tableHeaderFontSize"
:min="0"
size="small"
placeholder="表头字体大小"
></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="表格设置">
<SettingItem name="表格背景颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.tableDataBackgroud"
></n-color-picker>
</SettingItem>
<SettingItem name="是否使用表格背景颜色">
<n-space>
<n-switch
v-model:value="optionData.dataStyle.tableDataBackgroudShow"
size="small"
></n-switch>
</n-space>
</SettingItem>
<SettingItem name="表格字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.tableDataFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="表格字体大小">
<n-input-number
v-model:value="optionData.dataStyle.tableDataFontSize"
:min="0"
size="small"
placeholder="表格字体大小"
></n-input-number>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
import { PropType, watch, ref } from 'vue';
import { option } from './config';
import { cloneDeep } from 'lodash-es';
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting';
const props = defineProps({
optionData: {
type: Object as PropType<typeof option>,
required: true,
},
});
//
const columnsOptions: any = ref([]);
watch(
() => props.optionData.dataset,
() => {
//
const field = Object.keys(props.optionData.dataset[0]);
columnsOptions.value = [];
field.forEach((item) => {
columnsOptions.value.push({
label: item,
value: item,
});
});
},
{
deep: false,
immediate: true,
},
);
//
const add = (index: number) => {
props.optionData.header.splice(index + 1, 0, {
key: '',
title: '',
align: props.optionData.dataStyle.tableAlign,
width: '200',
prefix: null,
suffix: null,
convert: null,
});
};
//
const remove = (index: number) => {
if (props.optionData.header.length !== 1) {
props.optionData.header.splice(index, 1);
}
};
//
watch(
() => props.optionData.dataStyle.tableAlign,
() => {
props.optionData.header.forEach((item) => {
item.align = props.optionData.dataStyle.tableAlign;
});
},
);
//
const columnsWidths: any = ref('');
watch(
() => props.optionData.header,
() => {
columnsWidths.value = props.optionData.header.map((item) => item.width).join(',');
},
{
deep: true,
immediate: true,
},
);
watch(
() => columnsWidths.value,
() => {
const widths = columnsWidths.value.split(',');
props.optionData.header.forEach((item, index) => {
item.width = widths[index];
});
},
{
deep: true,
immediate: true,
},
);
//
function getOptionsByShowType(filtersetItem) {
let result: any = [];
if (filtersetItem.showType) {
switch (filtersetItem.showType) {
case 'checkbox':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '含有', value: 'contain' },
];
break;
case 'radio':
//
result = [
{ label: '大于', value: 'greater' },
{ label: '小于', value: 'less' },
{ label: '大于等于', value: 'greaterAndEqual' },
{ label: '小于等于', value: 'lessAndEqual' },
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
{ label: '含有', value: 'contain' },
{ label: '不含有', value: 'notcontain' },
];
break;
case 'select':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '含有', value: 'contain' },
];
break;
case 'selectGroup':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '含有', value: 'contain' },
];
break;
case 'buttonGroup':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
{ label: '含有', value: 'contain' },
{ label: '不含有', value: 'notcontain' },
];
break;
case 'buttonGroupList':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
{ label: '含有', value: 'contain' },
{ label: '不含有', value: 'notcontain' },
];
break;
case 'inputSearch':
//
result = [{ label: '含有', value: 'contain' }];
break;
case 'inputSearchAll':
//
result = [{ label: '含有', value: 'contain' }];
break;
case 'timePicker':
//
result = [
{ label: '大于', value: 'greater' },
{ label: '小于', value: 'less' },
{ label: '大于等于', value: 'greaterAndEqual' },
{ label: '小于等于', value: 'lessAndEqual' },
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
];
break;
case 'timeRange':
//
result = [{ label: '在两者之间', value: 'between' }];
break;
case 'dateRange':
//
result = [{ label: '在两者之间', value: 'between' }];
break;
}
}
return result;
}
</script>

View File

@ -0,0 +1,186 @@
{
"header": [
{
"title": "名称",
"key": "name",
"align": "center",
"width": "250",
"prefix": null,
"suffix": null,
"convert": null
},
{
"title": "面积",
"key": "area",
"align": "center",
"width": "200",
"prefix": null,
"suffix": "m²",
"convert": null
},
{
"title": "时间",
"key": "time",
"align": "center",
"width": "200",
"prefix": null,
"suffix": "小时",
"convert": "0"
},
{
"title": "路程",
"key": "distance",
"align": "center",
"width": "200",
"prefix": "路程",
"suffix": "km",
"convert": null
},
{
"title": "预计时间",
"key": "estimatedTime",
"align": "center",
"width": "300",
"prefix": "预计",
"suffix": null,
"convert": "1"
}
],
"filterset": [
{
"title": "地图显示",
"field": "type",
"compareType": "contain",
"showType": "checkbox",
"value": [ "水源", "物资", "营房", "防火通道", "三轮通道", "步行道" ],
"options": [
{ "label": "水源", "value": "水源" },
{ "label": "物资", "value": "物资" },
{ "label": "营房", "value": "营房" },
{ "label": "防火通道", "value": "防火通道" },
{ "label": "三轮通道", "value": "三轮通道" },
{ "label": "步行道", "value": "步行道" }
]
},
{
"title": "资源列表",
"field": "type",
"compareType": "equal",
"showType": "buttonGroup",
"value": null,
"options": [
{ "label": "水源", "value": "水源" },
{ "label": "物资", "value": "物资" },
{ "label": "营房", "value": "营房" }
]
},
{
"title": "重点资源",
"field": null,
"compareType": null,
"showType": null,
"value": null,
"options": [
{ "label": null, "value": null }
]
},
{
"title": "距离",
"field": "distance",
"compareType": "lessAndEqual",
"showType": "radio",
"value": null,
"options": [
{ "label": "1km以内", "value": "1" },
{ "label": "5km以内", "value": "5" },
{ "label": "10km以内", "value": "10" },
{ "label": "20km以内", "value": "20" },
{ "label": "全部", "value": null }
]
}
],
"source": [
{
"name": "坑塘",
"type": "水源",
"area": null,
"time": null,
"distance": "21.14",
"estimatedTime": "00:05:45",
"date": "2025-03-01 00:05:45"
},
{
"name": "东蒙布袋口检查",
"type": "物资",
"area": "300",
"time": "12.50",
"distance": "14.14",
"estimatedTime": "00:05:45",
"date": "2025-03-11 00:05:45"
},
{
"name": "北刘家庄蓄水池",
"type": "营房",
"area": "300",
"time": "12.50",
"distance": "0.14",
"estimatedTime": "00:05:45",
"date": "2025/03/01 00:05:45"
},
{
"name": "防火通道",
"type": "防火通道",
"area": null,
"time": null,
"distance": "2.14",
"estimatedTime": "00:05:45",
"date": "2025-12-01 00:05:45"
},
{
"name": "步行道",
"type": "步行道",
"area": "300",
"time": "5.50",
"distance": "1.14",
"estimatedTime": "00:05:45",
"date": "2025-08-01 00:05:45"
},
{
"name": "三轮通道",
"type": "三轮通道",
"area": "300",
"time": "9.50",
"distance": "5.14",
"estimatedTime": "00:05:45",
"date": "2025-10-21 00:05:45"
},
{
"name": "坑塘",
"type": "水源",
"area": null,
"time": null,
"distance": "10.14",
"estimatedTime": "00:05:45",
"date": "2025-06-01 00:05:45"
},
{
"name": "东蒙布袋口检查",
"type": "物资",
"area": "300",
"time": "12.50",
"distance": "10.14",
"estimatedTime": "00:05:45",
"date": "2025-06-12 00:05:45"
},
{
"name": "北刘家庄蓄水池",
"type": "营房",
"area": "300",
"time": "12.50",
"distance": "19.14",
"estimatedTime": "00:05:45",
"date": "2025-05-01 00:05:45"
}
]
}

View File

@ -0,0 +1,14 @@
import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
export const TableFilterConfig: ConfigType = {
key: 'TableFilter',
chartKey: 'VTableFilter',
conKey: 'VCTableFilter',
title: '自定义筛选表格',
category: ChatCategoryEnum.TABLE,
categoryName: ChatCategoryEnumName.TABLE,
package: PackagesCategoryEnum.TABLES,
chartFrame: ChartFrameEnum.COMMON,
image: 'table_filter.png'
}

View File

@ -0,0 +1,823 @@
<template>
<div class="TableFilter">
<div class="filterDiv">
<div class="filterItem" v-for="(item, index) in option.filterset" :key="index">
<a-row>
<!-- 筛选标题 -->
<a-col :span="option.dataStyle.colLeft">
<div class="filterTitle">{{ item.title }}</div>
</a-col>
<!-- 筛选内容 -->
<!-- 多选框 -->
<a-col :span="24 - option.dataStyle.colLeft" v-if="item.showType == 'checkbox'">
<div class="filterContentDiv">
<n-checkbox-group v-model:value="item.value">
<n-checkbox
:style="{
marginRight: option.dataStyle.filterTitleFontSize + 'px',
}"
v-for="(checkboxItem, checkboxIndex) in item.options"
:key="checkboxIndex"
:value="checkboxItem.value"
>
<template #default>
<span class="filterContentFont">{{ checkboxItem.label }}</span>
</template>
</n-checkbox>
</n-checkbox-group>
</div>
</a-col>
<!-- 单选框 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'radio'">
<div class="filterContentDiv">
<n-radio-group v-model:value="item.value" name="radiogroup">
<n-space>
<n-radio
:style="{
marginRight: option.dataStyle.filterTitleFontSize + 'px',
}"
v-for="(radioItem, radioIndex) in item.options"
:key="radioIndex"
:value="radioItem.value"
>
<span class="filterContentFont">{{ radioItem.label }}</span>
</n-radio>
</n-space>
</n-radio-group>
</div>
</a-col>
<!-- 下拉框 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'select'">
<div class="filterContentDiv">
<n-select
v-model:value="item.value"
:options="item.options"
:style="{ width: '50%' }"
placeholder="请选择选项"
clearable
/>
</div>
</a-col>
<!-- 下拉框多选 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'selectGroup'">
<div class="filterContentDiv">
<n-select
v-model:value="item.value"
:options="item.options"
placeholder="请选择选项(可多选)"
clearable
multiple
/>
</div>
</a-col>
<!-- 按钮组 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'buttonGroup'">
<div class="filterContentDiv" style="display: inline-flex">
<div
:style="{
marginRight: option.dataStyle.filterTitleFontSize + 'px',
}"
v-for="(buttonItem, buttonIndex) in item.options"
:key="buttonIndex"
:class="
item.value == buttonItem.value
? 'filterContentFont_choose'
: 'filterContentFont_nochoose'
"
@click="clickFilterButton(item, buttonItem)"
>
{{ buttonItem.label }}
</div>
</div>
</a-col>
<!-- 按钮组多选 -->
<a-col
:span="24 - option.dataStyle.colLeft"
v-else-if="item.showType == 'buttonGroupList'"
>
<div class="filterContentDiv" style="display: inline-flex">
<div
:style="{
marginRight: option.dataStyle.filterTitleFontSize + 'px',
}"
v-for="(buttonItem, buttonIndex) in item.options"
:key="buttonIndex"
:class="
item.value && item.value.includes(buttonItem.value)
? 'filterContentFont_choose'
: 'filterContentFont_nochoose'
"
@click="clickFilterButtonList(item, buttonItem)"
>
{{ buttonItem.label }}
</div>
</div>
</a-col>
<!-- 列搜索框 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'inputSearch'">
<div class="filterContentDiv">
<n-input-group>
<n-input
v-model:value="item.value"
:style="{ width: '50%' }"
:placeholder="'请输入' + item.title"
clearable
/>
<n-button type="primary" ghost @click="search"> </n-button>
</n-input-group>
</div>
</a-col>
<!-- 全数据搜索框 -->
<a-col
:span="24 - option.dataStyle.colLeft"
v-else-if="item.showType == 'inputSearchAll'"
>
<div class="filterContentDiv">
<n-input-group>
<n-input
v-model:value="item.value"
:style="{ width: '50%' }"
placeholder="请输入搜索数据"
clearable
/>
<n-button type="primary" ghost @click="searchAll"> </n-button>
</n-input-group>
</div>
</a-col>
<!-- 时间选择器 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'timePicker'">
<div class="filterContentDiv">
<n-time-picker
v-model:formatted-value="item.value"
value-format="HH:mm:ss"
format="HH:mm:ss"
:style="{ width: '50%' }"
clearable
placeholder="请选择时间"
/>
</div>
</a-col>
<!-- 时间范围 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'timeRange'">
<div class="filterContentDiv" :style="{ display: 'inline-flex' }">
<n-time-picker
v-model:formatted-value="item.value"
value-format="HH:mm:ss"
format="HH:mm:ss"
:style="{ width: '40%', marginRight: '30px' }"
clearable
placeholder="请选择开始时间"
/>
<n-time-picker
v-model:formatted-value="item.value2"
value-format="HH:mm:ss"
format="HH:mm:ss"
:style="{ width: '40%' }"
clearable
placeholder="请选择结束时间"
/>
</div>
</a-col>
<!-- 日期范围 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else-if="item.showType == 'dateRange'">
<div class="filterContentDiv">
<n-date-picker
v-model:formatted-value="item.value"
value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
:style="{ width: '80%' }"
size="small"
clearable
/>
</div>
</a-col>
<!-- 其他 -->
<a-col :span="24 - option.dataStyle.colLeft" v-else>
<div class="filterContent"> </div>
</a-col>
</a-row>
</div>
</div>
<!-- 表格部分 -->
<!-- 表头 -->
<div class="theadDiv">
<n-table
:bordered="option.dataStyle.tableBordered"
:single-column="option.dataStyle.tableSigleColumn"
:single-line="option.dataStyle.tableSingleLine"
:size="option.dataStyle.tableSize"
>
<thead v-if="option.dataStyle.tableHeaderShow">
<tr
:style="{
backgroud: option.dataStyle.tableHeaderBackgroud,
}"
>
<th
class="th"
v-if="option.dataStyle.orderBumberShow"
:style="{
width: headWidths[0] + 'px',
}"
>
</th>
<th
class="th"
v-for="(item, index) in option.header"
:key="index"
:style="{
width: headWidths[option.dataStyle.orderBumberShow ? index + 1 : index] + 'px',
}"
>
{{ item.title }}
</th>
<th
class="th"
v-if="option.dataStyle.buttonDivShow"
:style="{
width: headWidths[headWidths.length - 1] + 'px',
}"
>
操作
</th>
</tr>
</thead>
</n-table>
</div>
<!-- 表格数据 -->
<div class="tbodyDiv">
<n-table
:bordered="option.dataStyle.tableBordered"
:single-column="option.dataStyle.tableSigleColumn"
:single-line="option.dataStyle.tableSingleLine"
:size="option.dataStyle.tableSize"
>
<tbody>
<tr
v-for="(dataItem, dataIndex) in filterData"
:key="dataIndex"
:class="
option.dataStyle.tableDataBackgroudShow
? 'td_background'
: dataIndex % 2 == 0
? 'td_image1'
: 'td_image2'
"
>
<!-- 序号 -->
<td
id="dataTd_order"
v-if="option.dataStyle.orderBumberShow"
:style="{
width: option.dataStyle.orderDivWidth + 'px',
}"
>
{{ dataIndex + 1 }}
</td>
<!-- 数据 -->
<td
:id="'dataTd_' + headerIndex"
v-for="(headerItem, headerIndex) in option.header"
:key="headerIndex"
:style="{
width: headerItem.width + 'px',
}"
>
<span>
{{ dataItem[headerItem.key] ? convertData(headerItem, dataItem) : '--' }}
</span>
</td>
<!-- 操作 -->
<td
id="dataTd_button"
v-if="option.dataStyle.buttonDivShow"
:style="{
width: option.dataStyle.buttonDivWidth + 'px',
}"
>
<div class="buttonDiv">
<n-button
v-if="option.dataStyle.buttonDivShow1"
size="small"
quaternary
@click="pitchLocation(dataItem, dataIndex)"
>
<Button1 :dataStyle="option.dataStyle" />
</n-button>
<span
v-if="option.dataStyle.buttonDivShow1 && option.dataStyle.buttonDivShow2"
class="buttonDivspan"
>
|
</span>
<n-button
v-if="option.dataStyle.buttonDivShow2"
size="small"
quaternary
@click="moveLocation(dataItem, dataIndex)"
>
<Button2 :dataStyle="option.dataStyle" />
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, toRefs, watch, reactive, ref } from 'vue';
import { CreateComponentType } from '@/packages/index.d';
import { icon } from '@/plugins';
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
import { useChartDataFetch } from '@/hooks';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash-es';
import Button1 from './svg/button1.vue';
import Button2 from './svg/button2.vue';
const props = defineProps({
chartConfig: {
type: Object as PropType<CreateComponentType>,
required: true,
},
});
const { w, h } = toRefs(props.chartConfig.attr);
const option = reactive({
header: props.chartConfig.option.header,
dataset: props.chartConfig.option.dataset,
filterset: props.chartConfig.option.filterset,
dataStyle: props.chartConfig.option.dataStyle,
});
//
const headHeight: any = computed(() => {
if (option.dataStyle.tableHeaderShow) {
return document.querySelector('.theadDiv')?.offsetHeight + 10;
} else {
return 0;
}
});
//
const headWidths = computed(() => {
let widths: any = [];
if (option.dataStyle.tableHeaderShow) {
setTimeout(() => {
if (option.dataStyle.orderBumberShow) {
widths.push(document.getElementById('dataTd_order')?.offsetWidth);
}
option.header.forEach((item, index) => {
let td = document.getElementById('dataTd_' + index);
widths.push(td?.offsetWidth);
});
if (option.dataStyle.buttonDivShow) {
widths.push(document.getElementById('dataTd_button')?.offsetWidth);
}
console.log(widths);
}, 500);
}
return widths;
});
//
const searchFlag = ref(false);
const searchAllFlag = ref(false);
function search() {
searchFlag.value = true;
}
function searchAll() {
searchAllFlag.value = true;
}
//
const filterData = computed(() => {
let data = cloneDeep(option.dataset);
option.filterset.forEach((filtersetItem) => {
// console.log(filtersetItem);
if (
(filtersetItem.field && filtersetItem.value) ||
(!filtersetItem.field && filtersetItem.showType == 'inputSearchAll')
) {
switch (filtersetItem.compareType) {
//
case 'greater':
if (['timePicker'].includes(filtersetItem.showType)) {
data = data.filter(
(d) =>
dayjs(d[filtersetItem.field], 'HH:mm:ss').diff(
dayjs(filtersetItem.value, 'HH:mm:ss'),
) > 0,
);
} else {
data = data.filter(
(d) => Number(d[filtersetItem.field]) > Number(filtersetItem.value),
);
}
break;
//
case 'less':
if (['timePicker'].includes(filtersetItem.showType)) {
data = data.filter(
(d) =>
dayjs(d[filtersetItem.field], 'HH:mm:ss').diff(
dayjs(filtersetItem.value, 'HH:mm:ss'),
) < 0,
);
} else {
data = data.filter(
(d) => Number(d[filtersetItem.field]) < Number(filtersetItem.value),
);
}
break;
//
case 'greaterAndEqual':
if (['timePicker'].includes(filtersetItem.showType)) {
data = data.filter(
(d) =>
dayjs(d[filtersetItem.field], 'HH:mm:ss').diff(
dayjs(filtersetItem.value, 'HH:mm:ss'),
) >= 0,
);
} else {
data = data.filter(
(d) => Number(d[filtersetItem.field]) >= Number(filtersetItem.value),
);
}
break;
//
case 'lessAndEqual':
if (['timePicker'].includes(filtersetItem.showType)) {
data = data.filter(
(d) =>
dayjs(d[filtersetItem.field], 'HH:mm:ss').diff(
dayjs(filtersetItem.value, 'HH:mm:ss'),
) <= 0,
);
} else {
data = data.filter(
(d) => Number(d[filtersetItem.field]) <= Number(filtersetItem.value),
);
}
break;
//
case 'equal':
if (['checkbox'].includes(filtersetItem.showType)) {
//
data = data.filter((d) =>
filtersetItem.value.some((li) => d[filtersetItem.field].includes(li)),
);
} else if (['selectGroup', 'buttonGroupList'].includes(filtersetItem.showType)) {
//
if (filtersetItem.value.length > 0) {
data = data.filter((d) =>
filtersetItem.value.some((li) => d[filtersetItem.field].includes(li)),
);
}
} else {
//
data = data.filter((d) => d[filtersetItem.field] == filtersetItem.value);
}
break;
//
case 'notequal':
if (['checkbox'].includes(filtersetItem.showType)) {
//
data = data.filter((d) =>
filtersetItem.value.some((li) => !d[filtersetItem.field].includes(li)),
);
} else if (['selectGroup', 'buttonGroupList'].includes(filtersetItem.showType)) {
//
if (filtersetItem.value.length > 0) {
data = data.filter((d) =>
filtersetItem.value.some((li) => !d[filtersetItem.field].includes(li)),
);
}
} else {
//
data = data.filter((d) => d[filtersetItem.field] !== filtersetItem.value);
}
break;
//
case 'contain':
if (['checkbox'].includes(filtersetItem.showType)) {
//
data = data.filter((d) =>
filtersetItem.value.some((li) => d[filtersetItem.field].includes(li)),
);
} else if (['selectGroup', 'buttonGroupList'].includes(filtersetItem.showType)) {
//
if (filtersetItem.value.length > 0) {
data = data.filter((d) =>
filtersetItem.value.some((li) => d[filtersetItem.field].includes(li)),
);
}
} else if (['inputSearch'].includes(filtersetItem.showType)) {
//
if (searchFlag.value) {
data = data.filter(
(d) => d[filtersetItem.field].indexOf(filtersetItem.value) !== -1,
);
searchFlag.value = false;
}
} else if (['inputSearchAll'].includes(filtersetItem.showType)) {
//
if (searchAllFlag.value) {
data = data.filter((d) => {
let flag = false;
option.header.some((headerLi) => {
if (d[headerLi.key]) {
flag = flag ? true : d[headerLi.key].includes(filtersetItem.value);
}
});
return flag;
});
searchAllFlag.value = false;
}
} else {
//
data = data.filter((d) => d[filtersetItem.field].indexOf(filtersetItem.value) !== -1);
}
break;
//
case 'notcontain':
if (['checkbox'].includes(filtersetItem.showType)) {
//
data = data.filter((d) =>
filtersetItem.value.some((li) => !d[filtersetItem.field].includes(li)),
);
} else if (['selectGroup', 'buttonGroupList'].includes(filtersetItem.showType)) {
//
if (filtersetItem.value.length > 0) {
data = data.filter((d) =>
filtersetItem.value.some((li) => !d[filtersetItem.field].includes(li)),
);
}
} else {
//
data = data.filter((d) => d[filtersetItem.field].indexOf(filtersetItem.value) == -1);
}
break;
//
case 'between':
if (['timeRange'].includes(filtersetItem.showType)) {
data = data.filter((d) => {
const time = dayjs(d[filtersetItem.field], 'HH:mm:ss');
const startTime = dayjs(filtersetItem.value, 'HH:mm:ss');
const endTime = dayjs(filtersetItem.value2, 'HH:mm:ss');
return time.diff(startTime) >= 0 && time.diff(endTime) <= 0;
});
}
if (['dateRange'].includes(filtersetItem.showType)) {
data = data.filter((d) => {
const time = dayjs(d[filtersetItem.field], 'YYYY-MM-DD HH:mm:ss');
const startTime = dayjs(filtersetItem.value[0], 'YYYY-MM-DD HH:mm:ss');
const endTime = dayjs(filtersetItem.value[1], 'YYYY-MM-DD HH:mm:ss');
return time.diff(startTime) >= 0 && time.diff(endTime) <= 0;
});
}
break;
}
}
});
return data;
});
// -
function clickFilterButton(item, buttonItem) {
if (item.value == buttonItem.value) {
item.value = '';
} else {
item.value = buttonItem.value;
}
}
// -
function clickFilterButtonList(item, buttonItem) {
if (!item.value) {
item.value = [];
}
if (item.value.includes(buttonItem.value)) {
item.value.splice(item.value.indexOf(buttonItem.value), 1);
} else {
item.value.push(buttonItem.value);
}
}
//
function convertData(header, data) {
let prefix = header.prefix ? header.prefix : '';
let suffix = header.suffix ? header.suffix : '';
let convertData = data[header.key];
//
if (convertData && header.convert == '0' && isValidNumber(convertData)) {
convertData =
typeof convertData === 'string'
? String(parseFloat(convertData).toFixed(2))
: String(convertData.toFixed(2));
}
// 000
if (convertData && header.convert == '1' && isValidTime(convertData)) {
convertData =
dayjs(convertData, 'HH:mm:ss').hour() +
'小时' +
dayjs(convertData, 'HH:mm:ss').minute() +
'分钟' +
dayjs(convertData, 'HH:mm:ss').second() +
'秒';
}
// YYYY-MM-DD HH:mm:ss
let format = 'YYYY-MM-DD HH:mm:ss';
if (header.convert == '2') {
format = 'YYYY-MM-DD';
}
if (header.convert == '3') {
format = 'YYYY/MM/DD';
}
if (
convertData &&
['2', '3', '4'].includes(header.convert) &&
isValidDate(convertData, format)
) {
convertData = dayjs(convertData).format(format);
}
return prefix + convertData + suffix;
}
//
function isValidNumber(str) {
const regex = /^-?\d+(\.\d+)?$/;
return regex.test(String(str).trim());
}
//
function isValidTime(str) {
const parsedDate = dayjs(str, 'HH:mm:ss');
if (parsedDate.isValid()) {
return true;
} else {
return false;
}
}
//
function isValidDate(str, format) {
const parsedDate = dayjs(str, format);
if (parsedDate.isValid()) {
return true;
} else {
return false;
}
}
//
function pitchLocation(dataItem, dataIndex) {}
function moveLocation(dataItem, dataIndex) {}
// setdata
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
props.chartConfig.option.dataset = newData;
});
</script>
<style lang="scss" scoped>
.TableFilter {
width: v-bind('`${w}px`');
height: v-bind('`${h}px`');
//
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none;
}
.filterDiv {
width: 100%;
padding-top: v-bind('`${option.dataStyle.filterMarginTop}px`');
padding-left: v-bind('`${option.dataStyle.filterMarginLeft}px`');
padding-right: v-bind('`${option.dataStyle.filterMarginLeft}px`');
}
//
.filterTitle {
text-align: v-bind('`${option.dataStyle.filterTitleAlign}`');
font-size: v-bind('`${option.dataStyle.filterTitleFontSize}px`');
color: v-bind('`${option.dataStyle.filterTitleFontColor}`');
font-family:
PingFangSC,
PingFang SC;
font-weight: 500;
line-height: v-bind('`${option.dataStyle.filterTitleFontSize}px`');
height: v-bind('`${option.dataStyle.filterItemHeight}px`');
font-style: normal;
}
.filterContentDiv {
width: 100%;
height: v-bind('`${option.dataStyle.filterItemHeight}px`');
justify-content: v-bind('`${option.dataStyle.filterContentAlign}`');
text-align: v-bind('`${option.dataStyle.filterContentAlign}`');
align-items: v-bind('`${option.dataStyle.filterContentAlign}`');
}
//
.filterContentFont {
font-size: v-bind('`${option.dataStyle.filterChooseFontSize}px`');
color: v-bind('`${option.dataStyle.filterChooseFontColor}`');
}
::v-deep .n-checkbox-box {
width: v-bind('`${option.dataStyle.filterChooseFontSize}px`') !important;
height: v-bind('`${option.dataStyle.filterChooseFontSize}px`') !important;
}
::v-deep .n-radio__dot {
width: v-bind('`${option.dataStyle.filterChooseFontSize}px`') !important;
height: v-bind('`${option.dataStyle.filterChooseFontSize}px`') !important;
}
.filterContentFont_nochoose {
background: #1c211f;
color: #18d09d;
border: 1px solid #18d09d;
font-size: v-bind('`${option.dataStyle.filterChooseFontSize}px`');
width: v-bind('`${option.dataStyle.filterChooseFontSize + 62}px`');
height: v-bind('`${option.dataStyle.filterChooseFontSize + 12}px`');
border-radius: v-bind('`${option.dataStyle.filterChooseFontSize + 12}px`');
text-align: center;
line-height: v-bind('`${option.dataStyle.filterChooseFontSize + 12}px`');
}
.filterContentFont_choose {
background: #26d1ad;
color: #0d2822;
font-size: v-bind('`${option.dataStyle.filterChooseFontSize}px`');
width: v-bind('`${option.dataStyle.filterChooseFontSize + 62}px`');
height: v-bind('`${option.dataStyle.filterChooseFontSize + 12}px`');
border-radius: v-bind('`${option.dataStyle.filterChooseFontSize + 12}px`');
text-align: center;
line-height: v-bind('`${option.dataStyle.filterChooseFontSize + 12}px`');
}
//
.theadDiv {
margin-top: v-bind('`${option.dataStyle.tableMarginTop}px`');
padding-left: v-bind('`${option.dataStyle.tableMarginLeft}px`');
padding-right: v-bind('`${option.dataStyle.tableMarginLeft}px`');
}
//
.tbodyDiv {
overflow-y: auto;
height: v-bind(
'`${h - headHeight - option.dataStyle.filterMarginTop - option.dataStyle.filterItemHeight * option.filterset.length - option.dataStyle.tableMarginTop}px`'
);
padding-left: v-bind('`${option.dataStyle.tableMarginLeft}px`');
padding-right: v-bind('`${option.dataStyle.tableMarginLeft}px`');
}
.tbodyDiv::-webkit-scrollbar {
display: none;
}
.tbodyDiv {
scrollbar-width: none;
-ms-overflow-style: none;
}
.th {
background: v-bind('`${option.dataStyle.tableHeaderBackgroud}`');
text-align: v-bind('`${option.dataStyle.tableAlign}`');
color: v-bind('`${option.dataStyle.tableHeaderFontColor}`');
font-size: v-bind('`${option.dataStyle.tableHeaderFontSize}px`');
}
td {
text-align: v-bind('`${option.dataStyle.tableAlign}`');
background: #ffffff00;
color: v-bind('`${option.dataStyle.tableDataFontColor}`');
font-size: v-bind('`${option.dataStyle.tableDataFontSize}px`');
border-bottom: 0px;
}
.td_background {
background: v-bind('`${option.dataStyle.tableDataBackgroud}`');
}
.td_image1 {
background-image: url('@/assets/images/chart/zhichu/component/ModalTable_td1.png');
background-size: 100% 100%;
}
.td_image2 {
background-image: url('@/assets/images/chart/zhichu/component/ModalTable_td2.png');
background-size: 100% 100%;
}
.buttonDiv {
display: flex;
align-items: v-bind('`${option.dataStyle.tableAlign}`');
justify-content: center;
width: v-bind('`${option.dataStyle.buttonDivWidth}px`');
}
.buttonDivspan {
color: #00611a;
}
</style>

View File

@ -8,7 +8,6 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<title>编组 16</title>
<defs>
<linearGradient
x1="50%"

View File

@ -0,0 +1,69 @@
<template>
<div>
<svg
:width="props.dataStyle.buttonWidth"
:height="props.dataStyle.buttonHeight"
viewBox="0 0 22 22"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-TableFilter-2-1">
<stop stop-color="#099C48" offset="0%"></stop>
<stop stop-color="#ADFFBF" offset="100%"></stop>
</linearGradient>
<path
d="M21.2019328,9.14688177 L20.0206813,6.19375288 C19.8155181,5.68221122 19.3188567,5.34775913 18.7677109,5.35000178 L15.8500195,5.35000178 L15.8500195,4.67500089 C15.8500195,4.30220819 15.5478113,4 15.1750186,4 L2.35000178,4 C1.60441638,4 1,4.60441638 1,5.35000178 L1,14.8000142 C1,15.5455996 1.60441638,16.150016 2.35000178,16.150016 L3.78437866,16.150016 C4.09026867,17.3447101 5.1667745,18.1803128 6.4000071,18.1803128 C7.6332397,18.1803128 8.70974553,17.3447101 9.01563554,16.150016 L13.2343911,16.150016 C13.5402811,17.3447101 14.6167869,18.1803128 15.8500195,18.1803128 C17.0832521,18.1803128 18.159758,17.3447101 18.465648,16.150016 L19.9000249,16.150016 C20.6456102,16.150016 21.2500396,15.5455996 21.2500396,14.8000142 L21.2500396,9.4000071 C21.2505672,9.31331105 21.2342323,9.22733821 21.2019328,9.14688177 L21.2019328,9.14688177 Z M6.4000071,16.8250169 C5.65442171,16.8250169 5.05000533,16.2206005 5.05000533,15.4750151 C5.05000533,14.7294297 5.65442171,14.1250133 6.4000071,14.1250133 C7.14559249,14.1250133 7.75000888,14.7294297 7.75000888,15.4750151 C7.75000888,16.2206005 7.14559249,16.8250169 6.4000071,16.8250169 Z M15.8500195,16.8250169 C15.1044341,16.8250169 14.5000178,16.2206005 14.5000178,15.4750151 C14.5000178,14.7294297 15.1044341,14.1250133 15.8500195,14.1250133 C16.5956049,14.1250133 17.2000213,14.7294297 17.2000213,15.4750151 C17.2000213,16.2206005 16.5956049,16.8250169 15.8500195,16.8250169 Z M15.8500195,8.72500621 L15.8500195,6.70000355 L18.7677109,6.70000355 L19.5777119,8.72500621 L15.8500195,8.72500621 Z"
id="path-TableFilter-2-2"
></path>
<filter
x="-44.4%"
y="-63.5%"
width="188.9%"
height="226.9%"
filterUnits="objectBoundingBox"
id="filter-TableFilter-2-3"
>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur
stdDeviation="3"
in="shadowOffsetOuter1"
result="shadowBlurOuter1"
></feGaussianBlur>
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0.71372549 0 0 0 0 0 0 0 0 0.5 0"
type="matrix"
in="shadowBlurOuter1"
></feColorMatrix>
</filter>
</defs>
<g id="监测平台" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="林业防火-智处-在线人员弹窗" transform="translate(-1412, -543)" fill-rule="nonzero">
<g id="在线人员弹窗----" transform="translate(418.2674, 225.0542)">
<g id="编组-14" transform="translate(54.7326, 64)">
<g id="列表" transform="translate(0, 242.9458)">
<g id="形状" transform="translate(939, 11)">
<use
fill="black"
fill-opacity="1"
filter="url(#filter-TableFilter-2-3)"
xlink:href="#path-TableFilter-2-2"
></use>
<use
fill="url(#linearGradient-TableFilter-2-1)"
xlink:href="#path-TableFilter-2-2"
></use>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
</template>
<script setup lang="ts">
const props = defineProps(['dataStyle']);
</script>

View File

@ -5,57 +5,99 @@ import { chartInitConfig } from '@/settings/designSetting'
import { ZhiChu_ModalTableConfig } from './index'
import dataJson from './data.json'
const {header, source, filterset} = dataJson;
const {header, source} = dataJson;
export const option = {
header: header,
dataset: source,
filterset: filterset,
dataStyle: {
colLeft: 4,
mapListValue: ['water', 'goods', 'barrack'],
buttonValue: '0',
distanceradio: '1',
// 筛选项
filterMarginTop: 20,
filterMarginLeft: 50,
filterItemHeight: 45,
filterTitleAlign: 'left',
filterTitleFontSize: 24,
filterTitleFontColor: '#ADFFC0',
filterContentAlign: 'left',
filterChooseFontSize: 18,
filterChooseFontColor: '#FFFFFF',
// 水源-表格
tableMarginTop: 20,
tableMarginLeft: 20,
tableBordered: false,
tableSigleColumn: true,
tableSingleLine: true,
tableStriped: false,
tableSize: 'small',
tableAlign: 'center',
// 表头
tableHeaderShow: false,
// 序号
orderBumberShow: false,
tableOrderBumberShow: false,
// 操作
buttonDivShow: true,
buttonDivShow1: true,
buttonDivShow2: true,
buttonWidth: 22,
buttonHeight: 22,
tableButtonDivShow: true,
tableButtonWidth: 22,
tableButtonHeight: 22,
// 宽度
orderDivWidth: 50,
buttonDivWidth: 100,
tableOrderDivWidth: 50,
tableButtonDivWidth: 100,
// 字体
tableHeaderBackgroud: '#FFFFFF',
tableHeaderFontSize: 20,
tableHeaderFontColor: '#000000',
tableDataBackgroudShow: false,
tableDataBackgroud: '#FFFFFF',
tableDataFontSize: 20,
tableDataFontColor: '#FFFFFF',
// 物资-队列
goodsMarginTop: 20,
goodsMarginLeft: 20,
goodsDivMarginTop: 20,
goodsDivMarginLeft: 20,
// 标题
goodsTitleFontSize1: 18,
goodsTitleFontColor1: '#FFFFFF',
goodsTitleFontSize2: 14,
goodsTitleFontColor2: '#FFFFFF',
goodsTitleFontSize3: 16,
goodsTitleFontColor3: '#FFFFFF',
// 按钮
goodsButtonBackgroud: '#409EFF',
goodsButtonFontSize: 16,
goodsButtonFontColor: '#FFFFFF',
// 线
goodsLineStartColor: '#4DFFB5',
goodsLineEndColor: '#0B4D2C',
// text
goodsTextFontSize: 16,
goodsTextFontColor: '#FFFFFF',
// 营房-队列
barrackMarginTop: 20,
barrackMarginLeft: 20,
barrackDivMarginTop: 20,
barrackDivMarginLeft: 20,
// 标题
barrackTitleFontSize1: 18,
barrackTitleFontColor1: '#FFFFFF',
barrackTitleFontSize2: 14,
barrackTitleFontColor2: '#FFFFFF',
barrackTitleFontSize3: 16,
barrackTitleFontColor3: '#FFFFFF',
// 按钮
barrackButtonBackgroud: '#409EFF',
barrackButtonFontSize: 16,
barrackButtonFontColor: '#FFFFFF',
// 线
barrackLineStartColor: '#4DFFB5',
barrackLineEndColor: '#0B4D2C',
// text
barrackTextFontSize: 16,
barrackTextFontColor: '#FFFFFF',
},
}

View File

@ -1,261 +1,5 @@
<template>
<!-- 筛选项数据显示设置 -->
<CollapseItem name="筛选项数据显示设置" :expanded="true">
<span style="font-size: 12px; margin-left: 8px"> 筛选项字段与类型 </span>
<div style="overflow-x: auto">
<n-table
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th>标题名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in props.optionData.filterset" :key="index">
<td>
<n-input v-model:value="item.title" type="text" size="small" />
</td>
<td>
<div style="width: 70px">
<n-button
class="go-ml-2"
type="primary"
size="small"
ghost
@click="
props.optionData.filterset.splice(index + 1, 0, {
title: null,
field: null,
compareType: null,
showType: null,
value: [],
options: [{ label: null, value: null }],
})
"
>
+
</n-button>
<n-button
class="go-ml-2"
type="warning"
size="small"
ghost
:disabled="props.optionData.filterset.length === 1"
@click="
if (props.optionData.filterset.length !== 1) {
props.optionData.filterset.splice(index, 1);
}
"
>
-
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
</CollapseItem>
<CollapseItem
:expanded="false"
v-for="(filtersetItem, filtersetIndex) in optionData.filterset"
:key="filtersetIndex"
:alone="false"
:name="'筛选项【' + filtersetItem.title + '】设置'"
>
<SettingItem name="筛选类型">
<n-select
v-model:value="filtersetItem.showType"
:options="[
{ label: '多选框', value: 'checkbox' },
{ label: '单选框', value: 'radio' },
{ label: '下拉框', value: 'select' },
{ label: '下拉框(多选)', value: 'selectGroup' },
{ label: '按钮组', value: 'buttonGroup' },
{ label: '按钮组(多选)', value: 'buttonGroupList' },
{ label: '列搜索框', value: 'inputSearch' },
{ label: '全数据搜索框', value: 'inputSearchAll' },
{ label: '时间选择器', value: 'timePicker' },
{ label: '时间范围', value: 'timeRange' },
{ label: '日期范围', value: 'dateRange' },
]"
clearable
size="small"
placeholder="筛选类型"
@change="
filtersetItem.compareType = null;
filtersetItem.value = null;
"
/>
</SettingItem>
<SettingItem v-if="!['inputSearchAll'].includes(filtersetItem.showType)" name="对应字段">
<n-select
v-model:value="filtersetItem.field"
:options="columnsOptions"
size="small"
clearable
placeholder="对应字段"
/>
</SettingItem>
<SettingItem name="筛选方式">
<n-select
v-model:value="filtersetItem.compareType"
:options="getOptionsByShowType(filtersetItem)"
clearable
size="small"
placeholder="筛选方式"
/>
</SettingItem>
<!-- 默认值搜索框无默认值 -->
<SettingItem
v-if="!['inputSearch', 'inputSearchAll'].includes(filtersetItem.showType)"
name="默认值"
>
<!-- 多选框下拉框多选按钮组 多选-->
<n-select
v-if="['checkbox', 'selectGroup', 'buttonGroupList'].includes(filtersetItem.showType)"
v-model:value="filtersetItem.value"
:options="filtersetItem.options"
multiple
clearable
size="small"
placeholder="默认值"
/>
<!-- 时间选择器 -->
<n-time-picker
v-else-if="['timePicker'].includes(filtersetItem.showType)"
v-model:formatted-value="filtersetItem.value"
value-format="HH:mm:ss"
format="HH:mm:ss"
size="small"
clearable
placeholder="默认时间"
/>
<!-- 时间范围 -->
<div v-else-if="['timeRange'].includes(filtersetItem.showType)">
<n-time-picker
v-model:formatted-value="filtersetItem.value"
value-format="HH:mm:ss"
format="HH:mm:ss"
size="small"
clearable
placeholder="开始时间"
/>
<n-time-picker
v-model:formatted-value="filtersetItem.value2"
value-format="HH:mm:ss"
format="HH:mm:ss"
size="small"
clearable
placeholder="结束时间"
/>
</div>
<!-- 日期范围 -->
<n-date-picker
v-else-if="['dateRange'].includes(filtersetItem.showType)"
v-model:formatted-value="filtersetItem.value"
value-format="yyyy-MM-dd HH:mm:ss"
format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
size="small"
clearable
/>
<!-- 其他 -->
<n-select
v-else
v-model:value="filtersetItem.value"
:options="filtersetItem.options"
clearable
size="small"
placeholder="默认值"
/>
</SettingItem>
<!-- 搜索框时间日期框无选项 -->
<div style="overflow-x: auto">
<n-table
v-if="
!['inputSearch', 'inputSearchAll', 'timePicker', 'timeRange', 'dateRange'].includes(
filtersetItem.showType,
)
"
class="go-request-header-table-box"
style="margin-top: 12px"
:single-line="false"
size="small"
>
<thead>
<tr>
<th>名称</th>
<th></th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(optionsItem, optionsIndex) in filtersetItem.options" :key="optionsIndex">
<td>
<n-input
v-model:value="optionsItem.label"
type="text"
size="small"
placeholder="名称"
/>
</td>
<td>
<n-input
v-model:value="optionsItem.value"
type="text"
size="small"
placeholder="值"
/>
</td>
<td>
<div style="width: 70px">
<n-button
class="go-ml-2"
type="primary"
size="small"
ghost
@click="
filtersetItem.options.splice(optionsIndex + 1, 0, {
label: null,
value: null,
})
"
>
+
</n-button>
<n-button
class="go-ml-2"
type="warning"
size="small"
ghost
:disabled="optionsItem.length === 1"
@click="
if (filtersetItem.options.length !== 1) {
filtersetItem.options.splice(optionsIndex, 1);
}
"
>
-
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
</CollapseItem>
<br />
<!-- 筛选项样式设置 -->
<CollapseItem name="筛选项样式设置" :expanded="true">
<CollapseItem name="筛选显示设置" :expanded="true">
<SettingItemBox :alone="false" name="整体设置">
<SettingItem :alone="false" name="上方距离">
<n-input-number
@ -273,6 +17,14 @@
placeholder="左右距离"
/>
</SettingItem>
<SettingItem :alone="false" name="标题占比">
<n-space vertical>
<n-slider v-model:value="optionData.dataStyle.colLeft" :step="1" :min="0" :max="12" />
<n-input-number v-model:value="optionData.dataStyle.colLeft" size="small">
<template #suffix> / 24 </template>
</n-input-number>
</n-space>
</SettingItem>
<SettingItem :alone="false" name="单个筛选项高度">
<n-input-number
v-model:value="optionData.dataStyle.filterItemHeight"
@ -281,6 +33,9 @@
placeholder="单个筛选项高度"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="筛选标题设置">
<SettingItem name="标题对齐方式">
<n-select
v-model:value="optionData.dataStyle.filterTitleAlign"
@ -292,30 +47,6 @@
size="small"
/>
</SettingItem>
<SettingItem name="内容对齐方式">
<n-select
v-model:value="optionData.dataStyle.filterContentAlign"
:options="[
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' },
]"
size="small"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="标题样式">
<SettingItem :alone="false" name="标题占比">
<n-space vertical>
<n-slider v-model:value="optionData.dataStyle.colLeft" :step="1" :min="0" :max="24" />
<n-input-number v-model:value="optionData.dataStyle.colLeft" size="small">
<template #suffix> / 24 </template>
</n-input-number>
</n-space>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="字体设置">
<SettingItem name="筛选标题字体颜色">
<n-color-picker
size="small"
@ -331,6 +62,20 @@
placeholder="筛选标题字体大小"
></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="筛选内容设置">
<SettingItem name="筛选内容对齐方式">
<n-select
v-model:value="optionData.dataStyle.filterContentAlign"
:options="[
{ label: '靠左', value: 'left' },
{ label: '居中', value: 'center' },
{ label: '靠右', value: 'right' },
]"
size="small"
/>
</SettingItem>
<SettingItem name="筛选内容字体颜色">
<n-color-picker
size="small"
@ -347,13 +92,48 @@
></n-input-number>
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="true" name="默认值设置">
<SettingItem :alone="false" name="地图显示">
<n-select
v-model:value="optionData.dataStyle.mapListValue"
:options="[
{ label: '水源', value: 'water' },
{ label: '物资', value: 'goods' },
{ label: '营房', value: 'barrack' },
{ label: '防火通道', value: 'fanghuotongdao' },
{ label: '三轮通道', value: 'sanluntongdao' },
{ label: '步行道', value: 'buxingdao' },
]"
placeholder="请选择选项(可多选)"
clearable
multiple
/>
</SettingItem>
<SettingItem :alone="false" name="资源列表">
<n-radio-group v-model:value="optionData.dataStyle.buttonValue" name="radiogroup">
<n-radio value="0"> 水源 </n-radio>
<n-radio value="1"> 物资 </n-radio>
<n-radio value="2"> 营房 </n-radio>
</n-radio-group>
</SettingItem>
<SettingItem :alone="false" name="距离">
<n-radio-group v-model:value="optionData.dataStyle.distanceradio" name="radiogroup">
<n-radio value="1"> 1km以内 </n-radio>
<n-radio value="2"> 5km以内 </n-radio>
<n-radio value="3"> 10km以内 </n-radio>
<n-radio value="4"> 20km以内 </n-radio>
<n-radio value="5"> 全部 </n-radio>
</n-radio-group>
</SettingItem>
</SettingItemBox>
</CollapseItem>
<CollapseItem name="表格数据显示设置" :expanded="true">
<span style="font-size: 12px; margin-left: 8px">
表格数据请先确定数据部分再确定表格数据
</span>
<CollapseItem name="水源-表格数据显示设置" :expanded="true">
<div style="overflow-x: auto">
<span style="font-size: 12px; margin-left: 8px">
表格数据请先确定数据部分再确定表格数据
</span>
<n-table
class="go-request-header-table-box"
style="margin-top: 12px"
@ -407,7 +187,7 @@
</SettingItem>
<SettingItem :alone="false" name="序列宽度">
<n-input-number
v-model:value="optionData.dataStyle.orderDivWidth"
v-model:value="optionData.dataStyle.tableOrderDivWidth"
:min="0"
size="small"
placeholder="序列宽度"
@ -415,7 +195,7 @@
</SettingItem>
<SettingItem :alone="false" name="操作列宽度">
<n-input-number
v-model:value="optionData.dataStyle.buttonDivWidth"
v-model:value="optionData.dataStyle.tableButtonDivWidth"
:min="0"
size="small"
placeholder="操作列宽度"
@ -500,11 +280,6 @@
<n-switch v-model:value="optionData.dataStyle.tableBordered" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否使用斑马条纹">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableStriped" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否不设定行的分割线">
<n-space>
<n-switch v-model:value="optionData.dataStyle.tableSigleColumn" size="small"></n-switch>
@ -523,28 +298,21 @@
</SettingItem>
<SettingItem name="是否显示序号列">
<n-space>
<n-switch v-model:value="optionData.dataStyle.orderBumberShow" size="small"></n-switch>
<n-switch
v-model:value="optionData.dataStyle.tableOrderBumberShow"
size="small"
></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示操作列">
<n-space>
<n-switch v-model:value="optionData.dataStyle.buttonDivShow" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示第一按钮">
<n-space>
<n-switch v-model:value="optionData.dataStyle.buttonDivShow1" size="small"></n-switch>
</n-space>
</SettingItem>
<SettingItem name="是否显示第二按钮">
<n-space>
<n-switch v-model:value="optionData.dataStyle.buttonDivShow2" size="small"></n-switch>
<n-switch v-model:value="optionData.dataStyle.tableButtonDivShow" size="small"></n-switch>
</n-space>
</SettingItem>
</SettingItemBox>
</CollapseItem>
<CollapseItem name="表格样式设置" :expanded="true">
<CollapseItem name="水源-表格样式设置" :expanded="true">
<SettingItemBox name="整体设置">
<SettingItem name="上方距离">
<n-input-number
@ -611,21 +379,6 @@
</SettingItem>
</SettingItemBox>
<SettingItemBox :alone="false" name="表格设置">
<SettingItem name="表格背景颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.tableDataBackgroud"
></n-color-picker>
</SettingItem>
<SettingItem name="是否使用表格背景颜色">
<n-space>
<n-switch
v-model:value="optionData.dataStyle.tableDataBackgroudShow"
size="small"
></n-switch>
</n-space>
</SettingItem>
<SettingItem name="表格字体颜色">
<n-color-picker
size="small"
@ -643,6 +396,314 @@
</SettingItem>
</SettingItemBox>
</CollapseItem>
<CollapseItem name="物资-数据显示设置" :expanded="true">
<SettingItemBox name="整体设置">
<SettingItem name="上方距离">
<n-input-number
v-model:value="optionData.dataStyle.goodsMarginTop"
:min="0"
size="small"
placeholder="上方距离"
/>
</SettingItem>
<SettingItem name="左右距离">
<n-input-number
v-model:value="optionData.dataStyle.goodsMarginLeft"
:min="0"
size="small"
placeholder="左右距离"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="单个内里样式设置">
<SettingItem name="上方距离">
<n-input-number
v-model:value="optionData.dataStyle.goodsDivMarginTop"
:min="0"
size="small"
placeholder="上方距离"
/>
</SettingItem>
<SettingItem name="左右距离">
<n-input-number
v-model:value="optionData.dataStyle.goodsDivMarginLeft"
:min="0"
size="small"
placeholder="左右距离"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="标题设置">
<SettingItem name="名称字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsTitleFontColor1"
></n-color-picker>
</SettingItem>
<SettingItem name="名称字体大小">
<n-input-number
v-model:value="optionData.dataStyle.goodsTitleFontSize1"
:min="0"
size="small"
placeholder="名称字体大小"
/>
</SettingItem>
<SettingItem name="距离字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsTitleFontColor2"
></n-color-picker>
</SettingItem>
<SettingItem name="距离字体大小">
<n-input-number
v-model:value="optionData.dataStyle.goodsTitleFontSize2"
:min="0"
size="small"
placeholder="距离字体大小"
/>
</SettingItem>
<SettingItem name="预计时间字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsTitleFontColor3"
></n-color-picker>
</SettingItem>
<SettingItem name="预计时间字体大小">
<n-input-number
v-model:value="optionData.dataStyle.goodsTitleFontSize3"
:min="0"
size="small"
placeholder="预计时间字体大小"
/>
</SettingItem>
<SettingItem name="按钮背景颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsButtonBackgroud"
></n-color-picker>
</SettingItem>
<SettingItem name="按钮字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsButtonFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="按钮字体大小">
<n-input-number
v-model:value="optionData.dataStyle.goodsButtonFontSize"
:min="0"
size="small"
placeholder="按钮字体大小"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="间隔线设置">
<SettingItem name="线起始颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsLineStartColor"
></n-color-picker>
</SettingItem>
<SettingItem>
<n-button size="small" @click="optionData.dataStyle.goodsLineStartColor = '#4DFFB5'">
恢复默认
</n-button>
</SettingItem>
<SettingItem name="线终结颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsLineEndColor"
></n-color-picker>
</SettingItem>
<SettingItem>
<n-button size="small" @click="optionData.dataStyle.goodsLineEndColor = '#0B4D2C'">
恢复默认
</n-button>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="文本设置">
<SettingItem name="文本字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.goodsTextFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="文本字体大小">
<n-input-number
v-model:value="optionData.dataStyle.goodsTextFontSize"
:min="0"
size="small"
placeholder="文本字体大小"
/>
</SettingItem>
</SettingItemBox>
</CollapseItem>
<CollapseItem name="营房-数据显示设置" :expanded="true">
<SettingItemBox name="整体设置">
<SettingItem name="上方距离">
<n-input-number
v-model:value="optionData.dataStyle.barrackMarginTop"
:min="0"
size="small"
placeholder="上方距离"
/>
</SettingItem>
<SettingItem name="左右距离">
<n-input-number
v-model:value="optionData.dataStyle.barrackMarginLeft"
:min="0"
size="small"
placeholder="左右距离"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="单个内里样式设置">
<SettingItem name="上方距离">
<n-input-number
v-model:value="optionData.dataStyle.barrackDivMarginTop"
:min="0"
size="small"
placeholder="上方距离"
/>
</SettingItem>
<SettingItem name="左右距离">
<n-input-number
v-model:value="optionData.dataStyle.barrackDivMarginLeft"
:min="0"
size="small"
placeholder="左右距离"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="标题设置">
<SettingItem name="名称字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackTitleFontColor1"
></n-color-picker>
</SettingItem>
<SettingItem name="名称字体大小">
<n-input-number
v-model:value="optionData.dataStyle.barrackTitleFontSize1"
:min="0"
size="small"
placeholder="名称字体大小"
/>
</SettingItem>
<SettingItem name="距离字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackTitleFontColor2"
></n-color-picker>
</SettingItem>
<SettingItem name="距离字体大小">
<n-input-number
v-model:value="optionData.dataStyle.barrackTitleFontSize2"
:min="0"
size="small"
placeholder="距离字体大小"
/>
</SettingItem>
<SettingItem name="预计时间字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackTitleFontColor3"
></n-color-picker>
</SettingItem>
<SettingItem name="预计时间字体大小">
<n-input-number
v-model:value="optionData.dataStyle.barrackTitleFontSize3"
:min="0"
size="small"
placeholder="预计时间字体大小"
/>
</SettingItem>
<SettingItem name="按钮背景颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackButtonBackgroud"
></n-color-picker>
</SettingItem>
<SettingItem name="按钮字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackButtonFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="按钮字体大小">
<n-input-number
v-model:value="optionData.dataStyle.barrackButtonFontSize"
:min="0"
size="small"
placeholder="按钮字体大小"
/>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="间隔线设置">
<SettingItem name="线起始颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackLineStartColor"
></n-color-picker>
</SettingItem>
<SettingItem>
<n-button size="small" @click="optionData.dataStyle.barrackLineStartColor = '#4DFFB5'">
恢复默认
</n-button>
</SettingItem>
<SettingItem name="线终结颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackLineEndColor"
></n-color-picker>
</SettingItem>
<SettingItem>
<n-button size="small" @click="optionData.dataStyle.barrackLineEndColor = '#0B4D2C'">
恢复默认
</n-button>
</SettingItem>
</SettingItemBox>
<SettingItemBox name="文本设置">
<SettingItem name="文本字体颜色">
<n-color-picker
size="small"
:modes="['hex']"
v-model:value="optionData.dataStyle.barrackTextFontColor"
></n-color-picker>
</SettingItem>
<SettingItem name="文本字体大小">
<n-input-number
v-model:value="optionData.dataStyle.barrackTextFontSize"
:min="0"
size="small"
placeholder="文本字体大小"
/>
</SettingItem>
</SettingItemBox>
</CollapseItem>
</template>
<script setup lang="ts">
@ -733,94 +794,4 @@
immediate: true,
},
);
//
function getOptionsByShowType(filtersetItem) {
let result: any = [];
if (filtersetItem.showType) {
switch (filtersetItem.showType) {
case 'checkbox':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '含有', value: 'contain' },
];
break;
case 'radio':
//
result = [
{ label: '大于', value: 'greater' },
{ label: '小于', value: 'less' },
{ label: '大于等于', value: 'greaterAndEqual' },
{ label: '小于等于', value: 'lessAndEqual' },
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
{ label: '含有', value: 'contain' },
{ label: '不含有', value: 'notcontain' },
];
break;
case 'select':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '含有', value: 'contain' },
];
break;
case 'selectGroup':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '含有', value: 'contain' },
];
break;
case 'buttonGroup':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
{ label: '含有', value: 'contain' },
{ label: '不含有', value: 'notcontain' },
];
break;
case 'buttonGroupList':
//
result = [
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
{ label: '含有', value: 'contain' },
{ label: '不含有', value: 'notcontain' },
];
break;
case 'inputSearch':
//
result = [{ label: '含有', value: 'contain' }];
break;
case 'inputSearchAll':
//
result = [{ label: '含有', value: 'contain' }];
break;
case 'timePicker':
//
result = [
{ label: '大于', value: 'greater' },
{ label: '小于', value: 'less' },
{ label: '大于等于', value: 'greaterAndEqual' },
{ label: '小于等于', value: 'lessAndEqual' },
{ label: '等于', value: 'equal' },
{ label: '不等于', value: 'notequal' },
];
break;
case 'timeRange':
//
result = [{ label: '在两者之间', value: 'between' }];
break;
case 'dateRange':
//
result = [{ label: '在两者之间', value: 'between' }];
break;
}
}
return result;
}
</script>

View File

@ -46,141 +46,141 @@
"convert": "1"
}
],
"filterset": [
{
"title": "地图显示",
"field": "type",
"compareType": "contain",
"showType": "checkbox",
"value": [ "水源", "物资", "营房", "防火通道", "三轮通道", "步行道" ],
"options": [
{ "label": "水源", "value": "水源" },
{ "label": "物资", "value": "物资" },
{ "label": "营房", "value": "营房" },
{ "label": "防火通道", "value": "防火通道" },
{ "label": "三轮通道", "value": "三轮通道" },
{ "label": "步行道", "value": "步行道" }
]
},
{
"title": "资源列表",
"field": "type",
"compareType": "equal",
"showType": "buttonGroup",
"value": null,
"options": [
{ "label": "水源", "value": "水源" },
{ "label": "物资", "value": "物资" },
{ "label": "营房", "value": "营房" }
]
},
{
"title": "重点资源",
"field": null,
"compareType": null,
"showType": null,
"value": null,
"options": [
{ "label": null, "value": null }
]
},
{
"title": "距离",
"field": "distance",
"compareType": "lessAndEqual",
"showType": "radio",
"value": null,
"options": [
{ "label": "1km以内", "value": "1" },
{ "label": "5km以内", "value": "5" },
{ "label": "10km以内", "value": "10" },
{ "label": "20km以内", "value": "20" },
{ "label": "全部", "value": null }
]
}
],
"source": [
{
"id": "1",
"name": "坑塘",
"type": "水源",
"type": "0",
"area": null,
"time": null,
"distance": "21.14",
"estimatedTime": "00:05:45",
"date": "2025-03-01 00:05:45"
},
{
"name": "东蒙布袋口检查",
"type": "物资",
"area": "300",
"time": "12.50",
"distance": "14.14",
"estimatedTime": "00:05:45",
"date": "2025-03-11 00:05:45"
},
{
"name": "北刘家庄蓄水池",
"type": "营房",
"area": "300",
"time": "12.50",
"distance": "0.14",
"estimatedTime": "00:05:45",
"date": "2025/03/01 00:05:45"
"date": "2025-03-01 00:05:45",
"goodsDetail": null,
"situation": null,
"teamLocation": null,
"lng": "118.16247495",
"lat": "35.36599084"
},
{
"name": "防火通道",
"type": "防火通道",
"area": null,
"time": null,
"distance": "2.14",
"estimatedTime": "00:05:45",
"date": "2025-12-01 00:05:45"
},
{
"name": "步行道",
"type": "步行道",
"id": "2",
"name": "东蒙布袋口检查",
"type": "0",
"area": "300",
"time": "5.50",
"time": "12.50",
"distance": "1.14",
"estimatedTime": "00:05:45",
"date": "2025-08-01 00:05:45"
"date": "2025-03-11 00:05:45",
"goodsDetail": null,
"situation": null,
"teamLocation": null,
"lng": "118.153507",
"lat": "35.369431"
},
{
"name": "三轮通道",
"type": "三轮通道",
"id": "3",
"name": "北刘家庄蓄水池",
"type": "0",
"area": "300",
"time": "9.50",
"time": "12.50",
"distance": "5.14",
"estimatedTime": "00:05:45",
"date": "2025-10-21 00:05:45"
"date": "2025/03/01 00:05:45",
"goodsDetail": null,
"situation": null,
"teamLocation": null,
"lng": "118.16339875",
"lat": "35.35693363"
},
{
"name": "坑塘",
"type": "水源",
"id": "4",
"name": "塔山林场物资储备库",
"type": "1",
"area": null,
"time": null,
"distance": "10.14",
"distance": "3.14",
"estimatedTime": "00:05:45",
"date": "2025-06-01 00:05:45"
"date": "2025/03/01 00:05:45",
"goodsDetail": "1、打草机 10个 2、干粉灭火弹 69箱 3、水剂灭火弹 45箱 4、高压细水雾 4台 5、斯蒂尔灭火机 13台 6、华盛泰山灭火机 2台 7、华盛泰山制草机 1台 8、打孔机 2台 9、肩抗灭火器 4个 10、森林灭火弹 6箱 11、扫把,20把 12、铁耙子 30把, 13、斯蒂尔制草机, 6个 14、高枝油锯 6个 15、水泵 2台 16、水枪 17个",
"situation": null,
"teamLocation": null,
"lng": "118.11856589",
"lat": "35.37110306"
},
{
"id": "5",
"name": "东蒙中队驻防地",
"type": "2",
"area": null,
"time": null,
"distance": "0.60",
"estimatedTime": "00:05:45",
"date": "2025/03/01 00:05:45",
"goodsDetail": null,
"situation": "2辆皮卡、2辆运兵车、5台水泵、管带:2760米(92节)、22台风力灭火机、二号工具20个",
"teamLocation": "东蒙中队驻地",
"lng": "118.03173443854365",
"lat": "35.43087222278798"
},
{
"id": "6",
"name": "东蒙布袋口检查",
"type": "物资",
"type": "0",
"area": "300",
"time": "12.50",
"distance": "10.14",
"distance": "1.14",
"estimatedTime": "00:05:45",
"date": "2025-06-12 00:05:45"
"date": "2025-03-11 00:05:45",
"goodsDetail": null,
"situation": null,
"teamLocation": null,
"lng": "118.19313469",
"lat": "35.35926978"
},
{
"id": "7",
"name": "北刘家庄蓄水池",
"type": "营房",
"type": "0",
"area": "300",
"time": "12.50",
"distance": "19.14",
"distance": "5.14",
"estimatedTime": "00:05:45",
"date": "2025-05-01 00:05:45"
"date": "2025/03/01 00:05:45",
"goodsDetail": null,
"situation": null,
"teamLocation": null,
"lng": "118.16693284",
"lat": "35.39109119"
},
{
"id": "8",
"name": "塔山林场物资储备库",
"type": "1",
"area": null,
"time": null,
"distance": "3.14",
"estimatedTime": "00:05:45",
"date": "2025/03/01 00:05:45",
"goodsDetail": "1、打草机 10个 2、干粉灭火弹 69箱 3、水剂灭火弹 45箱 4、高压细水雾 4台 5、斯蒂尔灭火机 13台 6、华盛泰山灭火机 2台 7、华盛泰山制草机 1台 8、打孔机 2台 9、肩抗灭火器 4个 10、森林灭火弹 6箱 11、扫把,20把 12、铁耙子 30把, 13、斯蒂尔制草机, 6个 14、高枝油锯 6个 15、水泵 2台 16、水枪 17个",
"situation": null,
"teamLocation": null,
"lng": "118.16142131",
"lat": "35.36308522"
},
{
"id": "9",
"name": "薛庄镇9号蓄水池",
"type": "0",
"area": null,
"time": null,
"distance": "0.60",
"estimatedTime": "00:05:45",
"date": "2025/03/01 00:05:45",
"goodsDetail": null,
"situation": "2辆皮卡、2辆运兵车、5台水泵、管带:2760米(92节)、22台风力灭火机、二号工具20个",
"teamLocation": "东蒙中队驻地",
"lng": "118.16303303",
"lat": "35.36772074"
}
]
}

View File

@ -0,0 +1,158 @@
<template>
<div class="barrack">
<div
class="item"
v-for="(dataItem, dataIndex) in filterData"
:key="dataIndex"
:class="dataIndex % 2 == 0 ? 'div_image1' : 'div_image2'"
>
<div class="text1">
<div class="titleDiv">
<span class="titleSpan1">{{ dataItem.name }}</span>
<span class="titleSpan2">{{ dataItem.distance }}km </span>
<span class="titleSpan3">预计到达时间{{ dataItem.estimatedTime }}</span>
</div>
<div class="buttonDiv">
<n-button class="button" size="small" @click="moveLocation(dataItem)">
<Button1 :dataStyle="props.dataStyle" /> <span class="buttonSpan">定位</span>
</n-button>
<n-button class="button" size="small" @click="roamLocation(dataItem)">
<Button2 :dataStyle="props.dataStyle" /> <span class="buttonSpan">漫游</span>
</n-button>
</div>
</div>
<div
class="rate"
:style="{
width: '100%',
height: '3px',
marginTop: '10px',
marginBottom: '10px',
background: `linear-gradient(90deg,
${props.dataStyle.barrackLineStartColor} 0%,
${props.dataStyle.barrackLineEndColor} 100%)`,
}"
/>
<div class="text2"> 装备情况 {{ dataItem.situation }} </div>
<div
class="rate"
:style="{
width: '100%',
height: '3px',
marginTop: '10px',
marginBottom: '10px',
background: `linear-gradient(90deg,
${props.dataStyle.barrackLineStartColor} 0%,
${props.dataStyle.barrackLineEndColor} 100%)`,
}"
/>
<div class="text2"> 队伍驻地 {{ dataItem.teamLocation }} </div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, toRefs, watch, reactive, ref } from 'vue';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash-es';
import Button1 from './button1.vue';
import Button2 from './button2.vue';
const props = defineProps(['dataset', 'dataStyle', 'header', 'h', 'filterDivHeight']);
const emit = defineEmits(['moveLocation', 'roamLocation']);
function moveLocation(dataItem) {
emit('moveLocation', [dataItem.lng, dataItem.lat]);
}
function roamLocation(dataItem) {
emit('roamLocation', [dataItem.lng, dataItem.lat]);
}
//
const filterData = computed(() => {
return props.dataset.filter((item) => item.type == 2);
});
</script>
<style lang="scss" scoped>
.barrack {
overflow-y: auto;
height: v-bind(
'`${props.h -props.filterDivHeight - props.dataStyle.filterMarginTop - props.dataStyle.barrackMarginTop}px`'
);
margin-top: v-bind('`${props.dataStyle.barrackMarginTop}px`');
padding-left: v-bind('`${props.dataStyle.barrackMarginLeft}px`');
padding-right: v-bind('`${props.dataStyle.barrackMarginLeft}px`');
}
.barrack::-webkit-scrollbar {
display: none;
}
.barrack {
scrollbar-width: none;
-ms-overflow-style: none;
}
.div_image1 {
background: #165f58;
}
.div_image2 {
background: #147c73;
}
.item {
padding-top: v-bind('`${props.dataStyle.barrackDivMarginTop}px`');
padding-bottom: v-bind('`${props.dataStyle.barrackDivMarginTop}px`');
padding-left: v-bind('`${props.dataStyle.barrackDivMarginLeft}px`');
padding-right: v-bind('`${props.dataStyle.barrackDivMarginLeft}px`');
}
.text1 {
width: 100%;
position: relative;
display: inline-flex;
.titleDiv {
.titleSpan1 {
font-size: v-bind('`${props.dataStyle.barrackTitleFontSize1}px`');
font-weight: 800;
color: v-bind('`${props.dataStyle.barrackTitleFontColor1}`');
margin-right: 20px;
}
.titleSpan2 {
font-size: v-bind('`${props.dataStyle.barrackTitleFontSize2}px`');
font-weight: 400;
color: v-bind('`${props.dataStyle.barrackTitleFontColor2}`');
margin-right: 30px;
}
.titleSpan3 {
font-size: v-bind('`${props.dataStyle.barrackTitleFontSize3}px`');
font-weight: 400;
color: v-bind('`${props.dataStyle.barrackTitleFontColor3}`');
margin-right: 30px;
}
}
.buttonDiv {
display: flex;
justify-content: right;
text-align: right;
align-items: right;
gap: 10px;
}
.button {
display: inline-flex;
background: v-bind('`${props.dataStyle.barrackButtonBackgroud}`');
font-size: v-bind('`${props.dataStyle.barrackButtonFontSize}px`');
color: v-bind('`${props.dataStyle.barrackButtonFontColor}`');
span {
margin-left: 5px;
}
}
}
.text2 {
font-size: v-bind('`${props.dataStyle.barrackTextFontSize}px`');
color: v-bind('`${props.dataStyle.barrackTextFontColor}`');
}
</style>

View File

@ -0,0 +1,96 @@
<template>
<div>
<svg
:width="props.dataStyle.tableButtonWidth"
:height="props.dataStyle.tableButtonHeight"
viewBox="0 0 22 22"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<title>编组 16</title>
<defs>
<linearGradient
x1="50%"
y1="0%"
x2="50%"
y2="100%"
id="linearGradient-ZhiChu_ModalTable-1-1"
>
<stop stop-color="#009742" offset="0%"></stop>
<stop stop-color="#A9FFBE" offset="56.1194042%"></stop>
<stop stop-color="#AEFFC0" offset="100%"></stop>
</linearGradient>
<path
d="M6.5,0 C3.46386169,0.00319830091 1.00344429,2.28789449 1,5.10719402 C1,9.47732708 6,12.7778512 6.213125,12.9159776 C6.38534815,13.0280075 6.61465185,13.0280075 6.786875,12.9159776 C7,12.7778512 12,9.47732708 12,5.10719402 C11.9965557,2.28789449 9.53613831,0.00319830091 6.5,0 L6.5,0 Z M6.5,3.25003256 C7.6045695,3.25003256 8.5,4.08151206 8.5,5.10719402 C8.5,6.13287597 7.6045695,6.96435548 6.5,6.96435548 C5.3954305,6.96435548 4.5,6.13287597 4.5,5.10719402 C4.5,4.08151206 5.3954305,3.25003256 6.5,3.25003256 Z"
id="path-ZhiChu_ModalTable-1-2"
></path>
<filter
x="-54.5%"
y="-46.2%"
width="209.1%"
height="190.4%"
filterUnits="objectBoundingBox"
id="filter-ZhiChu_ModalTable-1-3"
>
<feMorphology
radius="0.5"
operator="dilate"
in="SourceAlpha"
result="shadowSpreadOuter1"
></feMorphology>
<feOffset dx="0" dy="0" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur
stdDeviation="1.5"
in="shadowOffsetOuter1"
result="shadowBlurOuter1"
></feGaussianBlur>
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0.714933276 0 0 0 0 0 0 0 0 0.5 0"
type="matrix"
in="shadowBlurOuter1"
></feColorMatrix>
</filter>
</defs>
<g id="监测平台" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="林业防火-智处-在线人员弹窗" transform="translate(-1373, -543)">
<g id="在线人员弹窗----" transform="translate(418.2674, 225.0542)">
<g id="编组-14" transform="translate(54.7326, 64)">
<g id="列表" transform="translate(0, 242.9458)">
<g id="map-pin-fill" transform="translate(900, 11)">
<g id="编组-16" transform="translate(5, 4)">
<g id="形状" fill-rule="nonzero">
<use
fill="black"
fill-opacity="1"
filter="url(#filter-ZhiChu_ModalTable-1-3)"
xlink:href="#path-ZhiChu_ModalTable-1-2"
></use>
<use
fill="url(#linearGradient-ZhiChu_ModalTable-1-1)"
xlink:href="#path-ZhiChu_ModalTable-1-2"
></use>
</g>
<ellipse
id="椭圆形"
fill="#AEFFC0"
opacity="0.527510143"
cx="6.5"
cy="13"
rx="6.5"
ry="1"
></ellipse>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
</div>
</template>
<script setup lang="ts">
const props = defineProps(['dataStyle']);
</script>

View File

@ -1,8 +1,8 @@
<template>
<div>
<svg
:width="props.dataStyle.buttonWidth"
:height="props.dataStyle.buttonHeight"
:width="props.dataStyle.tableButtonWidth"
:height="props.dataStyle.tableButtonHeight"
viewBox="0 0 22 22"
version="1.1"
xmlns="http://www.w3.org/2000/svg"

View File

@ -0,0 +1,156 @@
<template>
<div class="goods">
<div
class="item"
v-for="(dataItem, dataIndex) in filterData"
:key="dataIndex"
:class="dataIndex % 2 == 0 ? 'div_image1' : 'div_image2'"
>
<div class="text1">
<div class="titleDiv">
<span class="titleSpan1">{{ dataItem.name }}</span>
<span class="titleSpan2">{{ dataItem.distance }}km </span>
<span class="titleSpan3">预计到达时间{{ dataItem.estimatedTime }}</span>
</div>
<div class="buttonDiv">
<n-button class="button" size="small" @click="moveLocation(dataItem)">
<Button1 :dataStyle="props.dataStyle" /> <span class="buttonSpan">定位</span>
</n-button>
<n-button class="button" size="small" @click="roamLocation(dataItem)">
<Button2 :dataStyle="props.dataStyle" /> <span class="buttonSpan">漫游</span>
</n-button>
</div>
</div>
<div
class="rate"
:style="{
width: '100%',
height: '3px',
marginTop: '10px',
marginBottom: '10px',
background: `linear-gradient(90deg,
${props.dataStyle.goodsLineStartColor} 0%,
${props.dataStyle.goodsLineEndColor} 100%)`,
}"
/>
<div class="text2"> 物资明细 {{ dataItem.goodsDetail }} </div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, toRefs, watch, reactive, ref } from 'vue';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash-es';
import Button1 from './button1.vue';
import Button2 from './button2.vue';
const props = defineProps([
'dataset',
'dataStyle',
'header',
'h',
'filterDivHeight',
'searchValue',
]);
const emit = defineEmits(['moveLocation', 'roamLocation']);
function moveLocation(dataItem) {
emit('moveLocation', [dataItem.lng, dataItem.lat]);
}
function roamLocation(dataItem) {
emit('roamLocation', [dataItem.lng, dataItem.lat]);
}
//
const filterData = computed(() => {
let result = props.dataset.filter((item) => item.type == 1);
if (props.searchValue) {
result = props.dataset.filter((item) => item.name.includes(props.searchValue));
}
return result;
});
</script>
<style lang="scss" scoped>
.goods {
overflow-y: auto;
height: v-bind(
'`${props.h -props.filterDivHeight - props.dataStyle.filterMarginTop - props.dataStyle.goodsMarginTop}px`'
);
margin-top: v-bind('`${props.dataStyle.goodsMarginTop}px`');
padding-left: v-bind('`${props.dataStyle.goodsMarginLeft}px`');
padding-right: v-bind('`${props.dataStyle.goodsMarginLeft}px`');
}
.goods::-webkit-scrollbar {
display: none;
}
.goods {
scrollbar-width: none;
-ms-overflow-style: none;
}
.div_image1 {
background: #165f58;
}
.div_image2 {
background: #147c73;
}
.item {
padding-top: v-bind('`${props.dataStyle.goodsDivMarginTop}px`');
padding-bottom: v-bind('`${props.dataStyle.goodsDivMarginTop}px`');
padding-left: v-bind('`${props.dataStyle.goodsDivMarginLeft}px`');
padding-right: v-bind('`${props.dataStyle.goodsDivMarginLeft}px`');
}
.text1 {
width: 100%;
position: relative;
display: inline-flex;
.titleDiv {
.titleSpan1 {
font-size: v-bind('`${props.dataStyle.goodsTitleFontSize1}px`');
font-weight: 800;
color: v-bind('`${props.dataStyle.goodsTitleFontColor1}`');
margin-right: 20px;
}
.titleSpan2 {
font-size: v-bind('`${props.dataStyle.goodsTitleFontSize2}px`');
font-weight: 400;
color: v-bind('`${props.dataStyle.goodsTitleFontColor2}`');
margin-right: 30px;
}
.titleSpan3 {
font-size: v-bind('`${props.dataStyle.goodsTitleFontSize3}px`');
font-weight: 400;
color: v-bind('`${props.dataStyle.goodsTitleFontColor3}`');
margin-right: 30px;
}
}
.buttonDiv {
display: flex;
justify-content: right;
text-align: right;
align-items: right;
gap: 10px;
}
.button {
display: inline-flex;
background: v-bind('`${props.dataStyle.goodsButtonBackgroud}`');
font-size: v-bind('`${props.dataStyle.goodsButtonFontSize}px`');
color: v-bind('`${props.dataStyle.goodsButtonFontColor}`');
span {
margin-left: 5px;
}
}
}
.text2 {
font-size: v-bind('`${props.dataStyle.goodsTextFontSize}px`');
color: v-bind('`${props.dataStyle.goodsTextFontColor}`');
}
</style>

View File

@ -0,0 +1,282 @@
<template>
<div class="water">
<!-- 表格数据 -->
<!-- 表头 -->
<div class="theadDiv">
<n-table
:bordered="props.dataStyle.tableBordered"
:single-column="props.dataStyle.tableSigleColumn"
:single-line="props.dataStyle.tableSingleLine"
:size="props.dataStyle.tableSize"
>
<thead v-if="props.dataStyle.tableHeaderShow">
<tr
:style="{
backgroud: props.dataStyle.tableHeaderBackgroud,
}"
>
<th
class="th"
v-if="props.dataStyle.tableOrderBumberShow"
:style="{
width: headWidths[0] + 'px',
}"
>
</th>
<th
class="th"
v-for="(item, index) in props.header"
:key="index"
:style="{
width: headWidths[props.dataStyle.tableOrderBumberShow ? index + 1 : index] + 'px',
}"
>
{{ item.title }}
</th>
<th
class="th"
v-if="props.dataStyle.tableButtonDivShow"
:style="{
width: headWidths[headWidths.length - 1] + 'px',
}"
>
操作
</th>
</tr>
</thead>
</n-table>
</div>
<!-- 表格数据 -->
<div class="tbodyDiv">
<n-table
:bordered="props.dataStyle.tableBordered"
:single-column="props.dataStyle.tableSigleColumn"
:single-line="props.dataStyle.tableSingleLine"
:size="props.dataStyle.tableSize"
>
<tbody>
<tr
v-for="(dataItem, dataIndex) in filterData"
:key="dataIndex"
:class="dataIndex % 2 == 0 ? 'td_image1' : 'td_image2'"
>
<!-- 序号 -->
<td
id="dataTd_order"
v-if="props.dataStyle.tableOrderBumberShow"
:style="{
width: props.dataStyle.tableOrderDivWidth + 'px',
}"
>
{{ dataIndex + 1 }}
</td>
<!-- 数据 -->
<td
:id="'dataTd_' + headerIndex"
v-for="(headerItem, headerIndex) in props.header"
:key="headerIndex"
:style="{
width: headerItem.width + 'px',
}"
>
<span>
{{ dataItem[headerItem.key] ? convertData(headerItem, dataItem) : '--' }}
</span>
</td>
<!-- 操作 -->
<td
id="dataTd_button"
v-if="props.dataStyle.tableButtonDivShow"
:style="{
width: props.dataStyle.tableButtonDivWidth + 'px',
}"
>
<div class="buttonDiv">
<n-button size="small" quaternary @click="moveLocation(dataItem)">
<Button1 :dataStyle="props.dataStyle" />
</n-button>
<span class="buttonDivspan"> | </span>
<n-button size="small" quaternary @click="roamLocation(dataItem)">
<Button2 :dataStyle="props.dataStyle" />
</n-button>
</div>
</td>
</tr>
</tbody>
</n-table>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, PropType, toRefs, watch, reactive, ref } from 'vue';
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash-es';
import Button1 from './button1.vue';
import Button2 from './button2.vue';
const props = defineProps(['dataset', 'dataStyle', 'header', 'h', 'filterDivHeight']);
const emit = defineEmits(['moveLocation', 'roamLocation']);
function moveLocation(dataItem) {
emit('moveLocation', [dataItem.lng, dataItem.lat]);
}
function roamLocation(dataItem) {
emit('roamLocation', [dataItem.lng, dataItem.lat]);
}
//
const headHeight: any = computed(() => {
if (props.dataStyle.tableHeaderShow) {
return document.querySelector('.theadDiv')?.offsetHeight + 10;
} else {
return 0;
}
});
//
const headWidths = computed(() => {
let widths: any = [];
if (props.dataStyle.tableHeaderShow) {
setTimeout(() => {
if (props.dataStyle.tableOrderBumberShow) {
widths.push(document.getElementById('dataTd_order')?.offsetWidth);
}
props.header.forEach((item, index) => {
let td = document.getElementById('dataTd_' + index);
widths.push(td?.offsetWidth);
});
if (props.dataStyle.tableButtonDivShow) {
widths.push(document.getElementById('dataTd_button')?.offsetWidth);
}
console.log(widths);
}, 500);
}
return widths;
});
//
const filterData = computed(() => {
return props.dataset.filter((item) => item.type == 0);
});
//
function convertData(header, data) {
let prefix = header.prefix ? header.prefix : '';
let suffix = header.suffix ? header.suffix : '';
let convertData = data[header.key];
//
if (convertData && header.convert == '0' && isValidNumber(convertData)) {
convertData =
typeof convertData === 'string'
? String(parseFloat(convertData).toFixed(2))
: String(convertData.toFixed(2));
}
// 000
if (convertData && header.convert == '1' && isValidTime(convertData)) {
convertData =
dayjs(convertData, 'HH:mm:ss').hour() +
'小时' +
dayjs(convertData, 'HH:mm:ss').minute() +
'分钟' +
dayjs(convertData, 'HH:mm:ss').second() +
'秒';
}
// YYYY-MM-DD HH:mm:ss
let format = 'YYYY-MM-DD HH:mm:ss';
if (header.convert == '2') {
format = 'YYYY-MM-DD';
}
if (header.convert == '3') {
format = 'YYYY/MM/DD';
}
if (
convertData &&
['2', '3', '4'].includes(header.convert) &&
isValidDate(convertData, format)
) {
convertData = dayjs(convertData).format(format);
}
return prefix + convertData + suffix;
}
//
function isValidNumber(str) {
const regex = /^-?\d+(\.\d+)?$/;
return regex.test(String(str).trim());
}
//
function isValidTime(str) {
const parsedDate = dayjs(str, 'HH:mm:ss');
if (parsedDate.isValid()) {
return true;
} else {
return false;
}
}
//
function isValidDate(str, format) {
const parsedDate = dayjs(str, format);
if (parsedDate.isValid()) {
return true;
} else {
return false;
}
}
</script>
<style lang="scss" scoped>
//
.theadDiv {
margin-top: v-bind('`${props.dataStyle.tableMarginTop}px`');
padding-left: v-bind('`${props.dataStyle.tableMarginLeft}px`');
padding-right: v-bind('`${props.dataStyle.tableMarginLeft}px`');
}
//
.tbodyDiv {
overflow-y: auto;
height: v-bind(
'`${props.h -props.filterDivHeight - props.dataStyle.filterMarginTop - props.dataStyle.tableMarginTop}px`'
);
padding-left: v-bind('`${props.dataStyle.tableMarginLeft}px`');
padding-right: v-bind('`${props.dataStyle.tableMarginLeft}px`');
}
.tbodyDiv::-webkit-scrollbar {
display: none;
}
.tbodyDiv {
scrollbar-width: none;
-ms-overflow-style: none;
}
.th {
background: v-bind('`${props.dataStyle.tableHeaderBackgroud}`');
text-align: v-bind('`${props.dataStyle.tableAlign}`');
color: v-bind('`${props.dataStyle.tableHeaderFontColor}`');
font-size: v-bind('`${props.dataStyle.tableHeaderFontSize}px`');
}
td {
text-align: v-bind('`${props.dataStyle.tableAlign}`');
background: #ffffff00;
color: v-bind('`${props.dataStyle.tableDataFontColor}`');
font-size: v-bind('`${props.dataStyle.tableDataFontSize}px`');
border-bottom: 0px;
}
.td_image1 {
background-image: url('@/assets/images/chart/zhichu/component/ModalTable_td1.png');
background-size: 100% 100%;
}
.td_image2 {
background-image: url('@/assets/images/chart/zhichu/component/ModalTable_td2.png');
background-size: 100% 100%;
}
.buttonDiv {
display: flex;
align-items: v-bind('`${props.dataStyle.tableAlign}`');
justify-content: center;
}
.buttonDivspan {
color: #00611a;
}
</style>

View File

@ -0,0 +1,226 @@
var numberRegexp = /[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/;
// Matches sequences like '100 100' or '100 100 100'.
var tuples = new RegExp('^' + numberRegexp.source + '(\\s' + numberRegexp.source + '){1,}');
/*
* Parse WKT and return GeoJSON.
*
* @param {string} _ A WKT geometry
* @return {?Object} A GeoJSON geometry object
*/
function parse(input) {
if (!input) {
return {
"coordinates": [],
"type": "LineString"
}
}
var parts = input.split(';');
var _ = parts.pop();
var srid = (parts.shift() || '').split('=').pop();
var i = 0;
function $(re) {
var match = _.substring(i).match(re);
if (!match) return null;
else {
i += match[0].length;
return match[0];
}
}
function crs(obj) {
if (obj && srid.match(/\d+/)) {
obj.crs = {
type: 'name',
properties: {
name: 'urn:ogc:def:crs:EPSG::' + srid
}
};
}
return obj;
}
function white() { $(/^\s*/); }
function multicoords() {
white();
var depth = 0;
var rings = [];
var stack = [rings];
var pointer = rings;
var elem;
while (elem =
$(/^(\()/) ||
$(/^(\))/) ||
$(/^(,)/) ||
$(tuples)) {
if (elem === '(') {
stack.push(pointer);
pointer = [];
stack[stack.length - 1].push(pointer);
depth++;
} else if (elem === ')') {
// For the case: Polygon(), ...
if (pointer.length === 0) return null;
pointer = stack.pop();
// the stack was empty, input was malformed
if (!pointer) return null;
depth--;
if (depth === 0) break;
} else if (elem === ',') {
pointer = [];
stack[stack.length - 1].push(pointer);
} else if (!elem.split(/\s/g).some(isNaN)) {
Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat));
} else {
return null;
}
white();
}
if (depth !== 0) return null;
return rings;
}
function coords() {
var list = [];
var item;
var pt;
while (pt =
$(tuples) ||
$(/^(,)/)) {
if (pt === ',') {
list.push(item);
item = [];
} else if (!pt.split(/\s/g).some(isNaN)) {
if (!item) item = [];
Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat));
}
white();
}
if (item) list.push(item);
else return null;
return list.length ? list : null;
}
function point() {
if (!$(/^(point(\sz)?)/i)) return null;
white();
if (!$(/^(\()/)) return null;
var c = coords();
if (!c) return null;
white();
if (!$(/^(\))/)) return null;
return {
type: 'Point',
coordinates: c[0]
};
}
function multipoint() {
if (!$(/^(multipoint)/i)) return null;
white();
var newCoordsFormat = _
.substring(_.indexOf('(') + 1, _.length - 1)
.replace(/\(/g, '')
.replace(/\)/g, '');
_ = 'MULTIPOINT (' + newCoordsFormat + ')';
var c = multicoords();
if (!c) return null;
white();
return {
type: 'MultiPoint',
coordinates: c
};
}
function multilinestring() {
if (!$(/^(multilinestring)/i)) return null;
white();
var c = multicoords();
if (!c) return null;
white();
return {
type: 'MultiLineString',
coordinates: c
};
}
function linestring() {
if (!$(/^(linestring(\sz)?)/i)) return null;
white();
if (!$(/^(\()/)) return null;
var c = coords();
if (!c) return null;
if (!$(/^(\))/)) return null;
return {
type: 'LineString',
coordinates: c
};
}
function polygon() {
if (!$(/^(polygon(\sz)?)/i)) return null;
white();
var c = multicoords();
if (!c) return null;
return {
type: 'Polygon',
coordinates: c
};
}
function multipolygon() {
if (!$(/^(multipolygon)/i)) return null;
white();
var c = multicoords();
if (!c) return null;
return {
type: 'MultiPolygon',
coordinates: c
};
}
function geometrycollection() {
var geometries = [];
var geometry;
if (!$(/^(geometrycollection)/i)) return null;
white();
if (!$(/^(\()/)) return null;
while (geometry = root()) {
geometries.push(geometry);
white();
$(/^(,)/);
white();
}
if (!$(/^(\))/)) return null;
return {
type: 'GeometryCollection',
geometries: geometries
};
}
function root() {
return point() ||
linestring() ||
polygon() ||
multipoint() ||
multilinestring() ||
multipolygon() ||
geometrycollection();
}
return crs(root());
}
export { parse }

View File

@ -0,0 +1,742 @@
import axios from "axios";
import { gcj02towgs84, wgs84togcj02 } from 'coordtransform'
import * as turf from '@turf/turf'
import { Url } from "./urlFormat";
import { getAppEnvConfig } from '@/utils/env';
import * as mars3d from 'mars3d';
import { parse } from "./handleGeojson";
import { cloneDeep } from 'lodash-es';
const urls = new Url()
const GD_URL = "https://restapi.amap.com/v5/direction/driving"
const GD_KEY = "6af6a87038f44c8c793aa70331f2b7ca"
const { VITE_GLOB_API_URL } = getAppEnvConfig();
//路线的图层
let pathGraphicLayers: any = null
let pathOneGraphicLayers: any = null
let pathWaterGraphicLayers: any = null
let pathRoadGraphicLayers: any = null
let pathRoadGraphicLayers1: any = null
let pathRoadGraphicLayers2: any = null
//导航寻路
export const getRouterFunc = (params, method = 'all') => {
/**
* method
* postgis使postgis
* all : + postgis
* gaode:
*/
let { startlng, startlat, endlng, endlat } = params
if (method == 'postgis') {
//使用gpostgis进行导航
return new Promise((resolve, reject) => {
let postgisParams = {
startlng: startlng,
startlat: startlat,
endlng: endlng,
endlat: endlat,
areaname: 'feixian',
}
getRouterByPostGis(postgisParams).then(geojson => {
//postGisCoordinates:postgis返回的geojson取出坐标数组
let postGisCoordinates = getOneLineCoordinatesFromGeometry(geojson)
let startRouterLngLat = postGisCoordinates[0]
let endRouterLngLat = postGisCoordinates.at(-1)
let resObject = {
allCoordinates: postGisCoordinates, //全部线路的合集
postGisRoute: postGisCoordinates, // postgis线路
gdRoute: [],
startLngLat: [startlng, startlat], // 起点
endLngLat: [endlng, endlat], //终点
startRouterLngLat: startRouterLngLat, // 路线查询结果的起点
endRouterLngLat: endRouterLngLat, //路线查询结果的终点
}
let simpleRoute = getMinimumRoute(resObject)
resolve(simpleRoute)
})
}).catch(err => {
})
}
if (method == 'all') {
// 先用高德进行导航
return new Promise((resolve, reject) => {
getRouterByGD(params).then(solution => {
// solution 为多条线路的数组,现在先用第一条线路 solution.path[0]
let gdRoute = solution.path[0]
let postgisParams = {
startlng: gdRoute.endCoordinates[0],
startlat: gdRoute.endCoordinates[1],
endlng: endlng,
endlat: endlat,
areaname: 'feixian',
}
//使用gpostgis求出剩下的路线
getRouterByPostGis(postgisParams).then(geojson => {
//postGisCoordinates:postgis返回的geojson取出坐标数组
let postGisCoordinates = getOneLineCoordinatesFromGeometry(geojson)
// 高德返回的第一条线路的坐标数组
let path_gd = gdRoute.path_polyline
//合并高德和postgis的路线
let allCoordinates = path_gd.concat(postGisCoordinates)
//导航线路的起点和终点
let startRouterLngLat = allCoordinates[0]
let endRouterLngLat = allCoordinates.at(-1)
let resObject = {
allCoordinates: allCoordinates, //全部线路
gdRoute: path_gd, //高德的线路
postGisRoute: postGisCoordinates, // postgis的线路
startLngLat: [startlng, startlat], // 起点
endLngLat: [endlng, endlat], //终点
startRouterLngLat: startRouterLngLat, // 路线查询结果的起点
endRouterLngLat: endRouterLngLat, //路线查询结果的终点
}
let simpleRoute = getMinimumRoute(resObject)
resolve(simpleRoute)
})
})
})
}
}
//高德路线导航
export const getRouterByGD = (params) => {
/**
* type:Object
*/
let { startlng, startlat, endlng, endlat } = params
//wgs84转火星坐标系
var gcj02StartLngLat = wgs84togcj02(startlng, startlat);
var gcj02EndLngLat = wgs84togcj02(endlng, endlat);
let gd_params = {
origin: `${gcj02StartLngLat[0]},${gcj02StartLngLat[1]}`,
destination: `${gcj02EndLngLat[0]},${gcj02EndLngLat[1]}`,
show_fields: 'polyline',
key: GD_KEY,
strategy:2
}
let new_url = urls.getUrl(GD_URL, gd_params)
return new Promise((resolve, reject) => {
axios({
method: "get",
url: new_url,
}).then((res) => {
if (res.status === 200) {
let solution = []
//处理数据
res.data.route.paths.map(path => {
let route_len = path.distance
let path_polyline = path.steps.map(step => {
return step.polyline
})
let router_path_str = [] //暂时存放 ['117.927498,35.263264']
path_polyline.forEach(polyline => {
let step = polyline.split(';')
router_path_str = router_path_str.concat(step)
});
// 去掉重复点
let unique_router_path_str = [...new Set(router_path_str)]
// 坐标转数组
let unique_router_path = unique_router_path_str.map(path_str => {
let lng_lat_list = path_str.split(',')
//高德坐标系转wgs84坐标系
var wgs84Coordinate = gcj02towgs84(...lng_lat_list);
return wgs84Coordinate
})
//高德导航的起点和终点
let startCoordinates = unique_router_path[0]
let endCoordinates = unique_router_path.at(-1)
// 高德地图返回的结果:方案一。长度,线
solution.push(
{
route_len: route_len,
path_polyline: unique_router_path,
startCoordinates: startCoordinates,
endCoordinates: endCoordinates
}
)
})
let result = {
routerCount: parseInt(res.data.count),
path: solution
}
resolve(result)
} else {
reject(res)
}
}).catch(err => {
reject(err)
})
})
}
//使用postgres + postgis寻路
export const getRouterByPostGis = (params) => {
return new Promise((resolve, reject) => {
axios.get("http://221.2.83.254:9001/api/FirePrevention/LoadRoad"
+ "?startlng=" + params.startlng
+ "&startlat=" + params.startlat
+ "&endlng=" + params.endlng
+ "&endlat=" + params.endlat
+ "&areaname=" + params.areaname
).then((res) => {
if (res.data.length > 0) {
console.log('res.data::: ', res.data);
let LineString = res.data[0].route;
if (LineString == null || LineString == "null") {
//没有找到路线,返回空
console.log('PostGIS未找到合适了路线')
resolve(parse(null))
} else {
resolve(parse(LineString))
}
} else {
console.log('PostGIS未找到合适了路线')
resolve(parse(null))
}
}).catch(err => {
console.log('PostGIS寻路算法服务端错误')
resolve(parse(null))
})
})
}
// 绘制线路
export const drawRouterFunc = (pathObject) => {
let { allCoordinates, startLngLat, endLngLat, startRouterLngLat, endRouterLngLat, gdRoute, postGisRoute } = pathObject
// 添加graphic
if (pathGraphicLayers == null) {
pathGraphicLayers = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathGraphicLayers);
} else {
// pathGraphicLayers.clear();
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (allCoordinates.length == 2) {
//只绘制开头到结尾的路线的路段
let endPathGraphic = drawPolylineDashEntity([startLngLat, endLngLat], 'YELLOW')
pathGraphicLayers.addGraphic(endPathGraphic);
} else {
// 导航数据路段
// let gdPathGraphic = drawLineFlowEntity(gdRoute, 'YELLOW',10)
// let postgisPathGraphic = drawLineFlowEntity(postGisRoute, 'RED',10)
let pathGraphic = drawLineFlowEntity(allCoordinates, '#E6472B')
//开始的路段
let startPathGraphic = drawPolylineDashEntity([startLngLat, startRouterLngLat], 'YELLOW')
//结尾的路段
let endPathGraphic = drawPolylineDashEntity([endRouterLngLat, endLngLat], 'YELLOW')
// pathGraphicLayers.addGraphic(gdPathGraphic);
// pathGraphicLayers.addGraphic(postgisPathGraphic);
pathGraphicLayers.addGraphic(pathGraphic);
pathGraphicLayers.addGraphic(startPathGraphic);
pathGraphicLayers.addGraphic(endPathGraphic);
}
}
// 绘制线路
export const drawRoutersFunc = (pathObject) => {
let { allCoordinates, startLngLat, endLngLat, startRouterLngLat, endRouterLngLat, gdRoute, postGisRoute } = pathObject
// 添加graphic
if (pathGraphicLayers == null) {
pathGraphicLayers = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathGraphicLayers);
} else {
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (allCoordinates.length == 2) {
//只绘制开头到结尾的路线的路段
// let endPathGraphic = drawPolylineDashEntity([startLngLat, endLngLat], 'YELLOW')
// pathGraphicLayers.addGraphic(endPathGraphic);
} else {
// 导航数据路段
// let gdPathGraphic = drawLineFlowEntity(gdRoute, 'YELLOW',10)
// let postgisPathGraphic = drawLineFlowEntity(postGisRoute, 'RED',10)
let pathGraphic = drawLineFlowEntityColor(allCoordinates, '#E6472B')
//开始的路段
// let startPathGraphic = drawPolylineDashEntity([startLngLat, startRouterLngLat], 'YELLOW')
//结尾的路段
// let endPathGraphic = drawPolylineDashEntity([endRouterLngLat, endLngLat], 'YELLOW')
let endPathGraphic = drawPolylineEntity([endRouterLngLat, endLngLat], 'YELLOW')
// pathGraphicLayers.addGraphic(gdPathGraphic);
// pathGraphicLayers.addGraphic(postgisPathGraphic);
pathGraphicLayers.addGraphic(pathGraphic);
// pathGraphicLayers.addGraphic(startPathGraphic);
pathGraphicLayers.addGraphic(endPathGraphic);
}
}
export const drawRoutersFuncWaterGrid = (pathObject) => {
let { pointArr } = pathObject
// 添加graphic
if (pathWaterGraphicLayers == null) {
pathWaterGraphicLayers = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathWaterGraphicLayers);
} else {
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (pointArr.length == 2) {
} else {
// 导航数据路段
let pathGraphic
pathGraphic = drawLineFlowEntityColor2(pointArr, '#56C3F9')
pathWaterGraphicLayers.addGraphic(pathGraphic);
}
}
export const drawRoutersFuncGrid = (pathObject) => {
let { pointArr, Type } = pathObject
// 添加graphic
if (pathRoadGraphicLayers == null) {
pathRoadGraphicLayers = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathRoadGraphicLayers);
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (pointArr.length == 2) {
} else {
// 导航数据路段
let pathGraphic;
pathGraphic = drawLineFlowEntityColor1(pointArr, '#E6472B')
pathRoadGraphicLayers.addGraphic(pathGraphic);
}
}
export const drawRoutersFuncGrid1 = (pathObject) => {
let { pointArr, Type } = pathObject
// 添加graphic
if (pathRoadGraphicLayers1 == null) {
pathRoadGraphicLayers1 = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathRoadGraphicLayers1);
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (pointArr.length == 2) {
} else {
// 导航数据路段
let pathGraphic1;
pathGraphic1 = drawLineFlowEntityColor2(pointArr, '#1F5FDE')
pathRoadGraphicLayers1.addGraphic(pathGraphic1);
}
}
export const drawRoutersFuncGrid2 = (pathObject) => {
let { pointArr, Type } = pathObject
// 添加graphic
if (pathRoadGraphicLayers2 == null) {
pathRoadGraphicLayers2 = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathRoadGraphicLayers2);
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (pointArr.length == 2) {
} else {
// 导航数据路段
let pathGraphic2;
pathGraphic2 = drawLineFlowEntityColor2(pointArr, '#9C9C9C')
pathRoadGraphicLayers2.addGraphic(pathGraphic2);
}
}
// 高亮路线
export const drawOneRouterFunc = (pathObject) => {
let { allCoordinates, startLngLat, endLngLat, startRouterLngLat, endRouterLngLat, gdRoute, postGisRoute } = pathObject
// 添加graphic
if (pathOneGraphicLayers == null) {
pathOneGraphicLayers = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(pathOneGraphicLayers);
} else {
pathOneGraphicLayers.clear();
}
//当只有两组数据时,说明没有导航的路线,直接使用虚线连接
if (allCoordinates.length == 2) {
//只绘制开头到结尾的路线的路段
let endPathGraphic = drawPolylineDashEntity([startLngLat, endLngLat], 'YELLOW')
pathOneGraphicLayers.addGraphic(endPathGraphic);
} else {
// 导航数据路段
// let gdPathGraphic = drawLineFlowEntity(gdRoute, 'YELLOW',10)
// let postgisPathGraphic = drawLineFlowEntity(postGisRoute, 'RED',10)
let pathGraphic = drawLineFlowEntity(allCoordinates, 'CYAN')
//开始的路段
// let startPathGraphic = drawPolylineDashEntity([startLngLat, startRouterLngLat], 'YELLOW')
//结尾的路段
// let endPathGraphic = drawPolylineDashEntity([endRouterLngLat, endLngLat], 'YELLOW')
// pathGraphicLayers.addGraphic(gdPathGraphic);
// pathGraphicLayers.addGraphic(postgisPathGraphic);
pathOneGraphicLayers.addGraphic(pathGraphic);
// pathGraphicLayers.addGraphic(startPathGraphic);
// pathGraphicLayers.addGraphic(endPathGraphic);
}
}
// 生成虚线实体
const drawPolylineDashEntity = (positions, cesiumColor,width=5) => {
return new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: width,
clampToGround: true,
materialType: mars3d.MaterialType.PolylineDash,
materialOptions: {
color: '#FFFF00',
dashLength: 8.0,
},
},
});
}
const drawPolylineEntity = (positions, cesiumColor,width=5) => {
return new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: width,
clampToGround: true,
// materialType: mars3d.MaterialType.PolylineDash,
materialOptions: {
color: '#FF0000',
// dashLength: 8.0,
},
},
});
}
// 生成动态线实体
const drawLineFlowEntity = (positions, cesiumColor,width=5) => {
return new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: width,
clampToGround: true,
materialType: mars3d.MaterialType.LineFlowColor,
materialOptions: {
color: cesiumColor,
speed: 1000,
percent: 1,
alpha: 1
},
},
});
}
const drawLineFlowEntityColor = (positions, cesiumColor,width=5) => {
return new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: width,
clampToGround: true,
materialType: mars3d.MaterialType.LineFlowColor,
materialOptions: {
color: cesiumColor,
speed: 1000,
percent: 1,
alpha: 1
},
},
});
}
const drawLineFlowEntityColor1 = (positions, cesiumColor,width=5) => {
return new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: width,
clampToGround: true,
materialType: mars3d.MaterialType.LineFlowColor,
materialOptions: {
color: cesiumColor,
speed: 1000,
percent: 1,
alpha: 1
},
},
});
}
const drawLineFlowEntityColor2 = (positions, cesiumColor,width=5) => {
return new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
width: width,
clampToGround: true,
materialType: mars3d.MaterialType.LineFlowColor,
materialOptions: {
color: cesiumColor,
speed: 1000,
percent: 1,
alpha: 1
},
},
});
}
//删除路线
export const clearRouterFunc = () => {
if (pathGraphicLayers == null) {
return false
} else {
pathGraphicLayers.clear();
}
if (pathOneGraphicLayers == null) {
return false
} else {
pathOneGraphicLayers.clear();
}
}
//删除水网路线
export const clearWaterRouterFunc = () => {
if (pathWaterGraphicLayers == null) {
return false
} else {
pathWaterGraphicLayers.clear();
}
}
//删除路网路线
export const clearRoadRouterFunc = () => {
if (pathRoadGraphicLayers == null) {
return false
} else {
pathRoadGraphicLayers.clear();
}
}
export const clearRoadRouterFunc1 = () => {
if (pathRoadGraphicLayers1 == null) {
return false
} else {
pathRoadGraphicLayers1.clear();
}
}
export const clearRoadRouterFunc2 = () => {
if (pathRoadGraphicLayers2 == null) {
return false
} else {
pathRoadGraphicLayers2.clear();
}
}
//计算最近路线
const getMinimumRoute = (pathObject) => {
//备份Object
let pathObjectClone = cloneDeep(pathObject)
let { allCoordinates, startLngLat, endLngLat, startRouterLngLat, endRouterLngLat, gdRoute, postGisRoute } = pathObjectClone
// 当只有一个点时终点说明高德地图和postgis都未查询到线路直接返回两点
if (allCoordinates.length <= 1) {
pathObjectClone.allCoordinates = [startLngLat, endLngLat]
return pathObjectClone
}
//当postgis寻路时计算两条线路的重叠之处
if (postGisRoute.length && gdRoute.length) {
//实例化turf标准格式
let gdRouteLine = turf.lineString(gdRoute);
let postGisRouteLine = turf.lineString(postGisRoute);
//获取postgis和高德寻路的所有交点
let intersectsGeojson = turf.lineIntersect(gdRouteLine, postGisRouteLine);
let intersectsCoordinates = getMultPointCoordinatesFromGeoJson(intersectsGeojson)
//如果相交点大于1说明路线有重复部分
if (intersectsCoordinates.length > 1) {
let lastIntersectsCoordinates = intersectsCoordinates[0]
let [slicedGdCoordinates, slicedPostGisCoordinates] = sliceByPoint(startRouterLngLat, gdRouteLine, endRouterLngLat, postGisRoute, lastIntersectsCoordinates)
allCoordinates = slicedGdCoordinates.concat(slicedPostGisCoordinates)
//处理后的结果赋值给pathObjectClone
pathObjectClone.gdRoute = slicedGdCoordinates
pathObjectClone.postGisRoute = slicedPostGisCoordinates
gdRouteLine = turf.lineString(slicedGdCoordinates);
postGisRouteLine = turf.lineString(slicedPostGisCoordinates);
}
// 阈值计算重复路线,去除重复线路
let overlapping = turf.lineOverlap(gdRouteLine, postGisRouteLine, { tolerance: 0.1 });
if (overlapping.features.length) {
let lastOverlapPoint = overlapping.features.at(-1).geometry.coordinates[0]
let [overlapGdCoordinates, overlapPostGisCoordinates] = sliceByPoint(startRouterLngLat, gdRouteLine, endRouterLngLat, postGisRoute, lastOverlapPoint)
allCoordinates = overlapGdCoordinates.concat(overlapPostGisCoordinates)
// 连接路段平滑过渡
}
}
// 转成turf标准线格式
let allRouteLine = turf.lineString(allCoordinates);
// 转成turf标准点格式
let startLngLatPoint = turf.point(startLngLat);
let startRouterLngLatPoint = turf.point(startRouterLngLat);
let endLngLatPoint = turf.point(endLngLat);
let endRouterLngLatPoint = turf.point(endRouterLngLat);
//获取终点到导航线最近的点
let snappedGeojson = turf.nearestPointOnLine(allRouteLine, endLngLatPoint, { units: 'miles' });
let snappedCoordinates = getOnePointCoordinatesFromGeoJson(snappedGeojson)
// 根据最近的点截取路线,取前半部分
let slicedGeojson = turf.lineSlice(startRouterLngLat, turf.point(snappedCoordinates), allRouteLine);
let slicedCoordinates = getOnePointCoordinatesFromGeoJson(slicedGeojson)
//把截取后的路线赋值给pathObjectClone
pathObjectClone.allCoordinates = slicedCoordinates
//计算出发地到目的地的图上距离(直线)
let distanceStartToEnd = turf.distance(startLngLatPoint, endLngLatPoint)
//计算出发点到出发导航路线出发点的步行距离
let distanceStartToStartRoute = turf.distance(startLngLatPoint, startRouterLngLatPoint)
//计算终点到出发导航路线终点的步行距离
let distanceEndToEndRoute = turf.distance(endLngLatPoint, endRouterLngLatPoint)
//如果出发点与目的地的实际距离小于步行的距离,直接使用出发点到目的地的距离,导航此时不适用
if (distanceStartToEnd < (distanceStartToStartRoute + distanceEndToEndRoute)) {
pathObjectClone.allCoordinates = [startLngLat, endLngLat]
}
//把终点到导航终点改为距离线路的最近的的点
pathObjectClone.endRouterLngLat = snappedCoordinates
return pathObjectClone
}
const sliceByPoint = (line1Start, line1, line2End, line2, point) => {
/**
* pointline1line2
* line1Start:line1 [lng.lat]
* line2End:line2 [lng,lat]
*/
//拷贝line2的坐标数组进行倒序排列
let line2Copy = [...line2]
line2Copy.reverse()
let line2CopyReverseLineString = turf.lineString(line2Copy);
// 根据point截取路线line1取前半部分
// 根据point截取路线line2取后半部分
//然后将两部分拼接让line1路线从第一个交点处转向line2路段
let slicedLine1Geojson = turf.lineSlice(line1Start, turf.point(point), line1);
let slicedLine1Coordinates = getOnePointCoordinatesFromGeoJson(slicedLine1Geojson)
let slicedLine2Geojson = turf.lineSlice(line2End, turf.point(point), line2CopyReverseLineString);
let slicedLine2Coordinates = getOnePointCoordinatesFromGeoJson(slicedLine2Geojson)
slicedLine2Coordinates.reverse()
return [slicedLine1Coordinates, slicedLine2Coordinates]
}
// 坐标转geoJson
const comLineStringGeoJson = (coordinates) => {
return {
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": coordinates,
"type": "LineString"
}
}
}
// 从一条线的geometry中获取坐标
const getOneLineCoordinatesFromGeometry = (geometry) => {
let coordinates = geometry.coordinates
// console.log('geometry::: ', geometry);
let list = []
if (geometry.type == "MultiLineString") {
coordinates.map(coord => {
list = list.concat(coord)
})
} else if (geometry.type == 'LineString') {
list = list.concat(geometry.coordinates)
} else {
list = []
}
return list
}
//从一个点的geojson中返回坐标点
const getOnePointCoordinatesFromGeoJson = (geojson) => {
return geojson.geometry.coordinates
}
//从多个点的geojson中返回坐标点
const getMultPointCoordinatesFromGeoJson = (geojson) => {
// console.log('geojson::: ', geojson);
return geojson.features.map(feature => {
return feature.geometry.coordinates
})
}
// 全局漫游
let carGraphicEntity: any = null;
let carGraphicLayer: any = null;
export const handlerStartRoaming = (coordinates,roaming=true)=>{
if(carGraphicLayer == null){
carGraphicLayer = new mars3d.layer.GraphicLayer();
window.globalMap.addLayer(carGraphicLayer);
}else{
carGraphicLayer.clear();
}
carGraphicEntity = new mars3d.graphic.FixedRoute({
name: "步行路线",
frameRate: 1,
speed: 500,
autoStop: false, // 到达终点自动停止
clockLoop: true, // 循环播放
positions: coordinates,
pauseTime: 0,
camera: {
type: "gs",
radius: 2500
},
model: {
// url: "//data.mars3d.cn/gltf/mars/car/bus3.gltf",
url:"http://221.2.83.254:9010/cartoon_fire_truck/xiaofangche.gltf",
scale: 5,
minimumPixelSize: 50,
clampToGround: true
},
circle: {
radius: 10,
materialType: mars3d.MaterialType.CircleWave,
materialOptions: {
color: "#ffff00",
opacity: 0.3,
speed: 10,
count: 3,
gradient: 0.1
},
clampToGround: true
}
})
carGraphicLayer.addGraphic(carGraphicEntity)
// 开始漫游
carGraphicEntity.enabled = roaming;
carGraphicEntity.start()
}
export const handlerStopRoaming = ()=>{
if(carGraphicEntity){
carGraphicLayer.removeGraphic(carGraphicEntity);
carGraphicEntity = null;
}
}

View File

@ -0,0 +1,28 @@
export class Url {
/**
* url
* @param {Object} data {a:1}
* @returns {string}
*/
getParam(data) {
let url = '';
for (let k in data) {
let value = data[k] !== undefined ? data[k] : '';
if (value !== undefined && value !== "" && value !== null) {
url += `&${k}=${encodeURIComponent(value)}`
}
}
return url ? url.substring(1) : ''
}
/**
* url
* @param {string} url url
* @param {Json} data json
* @returns {string}
*/
getUrl(url, data) {
//看原始url地址中开头是否带?,然后拼接处理好的参数
return url += (url.indexOf('?') < 0 ? '?' : '') + this.getParam(data)
}
}

View File

@ -33,7 +33,7 @@ export const option = {
lineBackgroud: '#000000',
lineBorderWidth: 1,
lineBorderColor: '#ffffff',
lineBorderColor: '#4D6647',
lineHeight: 15,
lineStartColor: '#0B4D2C',
lineEndColor: '#4DFFB5',

View File

@ -135,7 +135,7 @@
align-items: center;
justify-content: v-bind('`${option.dataStyle.labelAlign}`');
width: v-bind('`${option.dataStyle.labelWidth}px`');
border-right: 1px solid #ffffff;
border-right: 1px solid v-bind('`${option.dataStyle.lineBorderColor}`');
color: v-bind('`${option.dataStyle.labelFontColor}`');
font-size: v-bind('`${option.dataStyle.labelFontSize}px`');