初始化
This commit is contained in:
271
frontend/packages/RemoteComponents/index.vue
Normal file
271
frontend/packages/RemoteComponents/index.vue
Normal file
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<div
|
||||
v-loading="loading"
|
||||
class="bs-remote-wrap"
|
||||
element-loading-text="远程组件加载中..."
|
||||
>
|
||||
<component
|
||||
:is="remoteComponent"
|
||||
:ref="'remoteComponent'+config.code"
|
||||
:config="config"
|
||||
@linkage="linkEvent"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import linkageMixins from 'data-room-ui/js/mixins/linkageMixins'
|
||||
import commonMixins from 'data-room-ui/js/mixins/commonMixins'
|
||||
import remoteVueLoader from 'remote-vue-loader'
|
||||
import * as g2Plot from '@antv/g2plot'
|
||||
import * as _echarts from 'echarts'
|
||||
import { mapMutations, mapState } from 'vuex'
|
||||
import innerRemoteComponents, { getRemoteComponents } from 'data-room-ui/RemoteComponents/remoteComponentsList'
|
||||
import { getBizComponentInfo } from 'data-room-ui/js/api/bigScreenApi'
|
||||
import { settingToTheme } from 'data-room-ui/js/utils/themeFormatting'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
export default {
|
||||
name: 'LcdpRemoteComponent',
|
||||
mixins: [linkageMixins, commonMixins],
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('bigScreen', {
|
||||
pageInfo: state => state.pageInfo,
|
||||
customTheme: state => state.pageInfo.pageConfig.customTheme
|
||||
}),
|
||||
isPreview () {
|
||||
return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
remoteComponent: null,
|
||||
componentInstance: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
created () {
|
||||
this.getRemoteComponent()
|
||||
},
|
||||
mounted () {
|
||||
this.chartInit()
|
||||
},
|
||||
methods: {
|
||||
changeData (config, filterList) {
|
||||
// 调用混入中的方法
|
||||
let promise = this.$options.mixins[1].methods.changeData.call(this, config, filterList)
|
||||
return promise.then(res => {
|
||||
config = res
|
||||
// 当前组件的方法调用
|
||||
let remote = this.$refs['remoteComponent'+config.code]
|
||||
if (remote && remote.changeData){
|
||||
remote.changeData(config, filterList)
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
},
|
||||
...mapMutations('bigScreen', ['changeChartConfig']),
|
||||
// 尝试渲染远程文件或远程字符串
|
||||
getRemoteComponent () {
|
||||
this.loading = true
|
||||
// 1. 系统组件 通过配置获取
|
||||
if (this.config.customize.vueSysComponentDirName) {
|
||||
const remoteComponentList = [...innerRemoteComponents, ...getRemoteComponents()]
|
||||
// 获得系统组件最新的配置, 同步
|
||||
const config = remoteComponentList?.find(item => item.customize.vueSysComponentDirName === this.config.customize.vueSysComponentDirName)
|
||||
const vueFile = config?.customize?.vueFile
|
||||
const option = config?.option
|
||||
const setting = config?.setting
|
||||
// 同步配置
|
||||
this.synchConfig(option, setting)
|
||||
this.remoteComponent = vueFile
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
// 2. 业务组件 通过请求获取
|
||||
if (this.config.customize.vueBizComponentCode) {
|
||||
getBizComponentInfo(this.config.customize.vueBizComponentCode).then(data => {
|
||||
const vueContent = data.vueContent
|
||||
const settingContent = data.settingContent
|
||||
if (!this.config?.option?.data) {
|
||||
this.resolveStrSetting(settingContent)
|
||||
this.config = this.dataFormatting(this.config, { success: false })
|
||||
}
|
||||
this.remoteComponent = remoteVueLoader('data:text/plain,' + encodeURIComponent(vueContent))
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
async chartInit () {
|
||||
let config = this.config
|
||||
config.g2Plots = g2Plot
|
||||
config.echarts = _echarts
|
||||
this.componentInstance = this.$refs['remoteComponent' + config.code]
|
||||
// key和code相等,说明是一进来刷新,调用list接口
|
||||
if (this.config.code === this.config.key || this.isPreview) {
|
||||
// 改变样式
|
||||
config = this.changeStyle(config) ? this.changeStyle(config) : config
|
||||
// 改变数据
|
||||
config = await this.changeDataByCode(config)
|
||||
} else {
|
||||
// 否则说明是更新,这里的更新只指更新数据(改变样式时是直接调取changeStyle方法),因为更新数据会改变key,调用chart接口
|
||||
// TODO 直接改变prop控制台会报错,待优化
|
||||
try {
|
||||
this.config = await this.changeData(config)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
},
|
||||
linkEvent (formData) {
|
||||
this.linkage(formData)
|
||||
},
|
||||
/**
|
||||
* 处理当前组件的字符串配置
|
||||
*/
|
||||
resolveStrSetting (settingContent) {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let option = {}
|
||||
// eslint-disable-next-line prefer-const
|
||||
let setting = []
|
||||
// eslint-disable-next-line prefer-const, no-unused-vars
|
||||
let title = []
|
||||
// eslint-disable-next-line prefer-const, no-unused-vars
|
||||
let data = []
|
||||
// eslint-disable-next-line prefer-const, no-unused-vars
|
||||
let optionHandler = ''
|
||||
// eslint-disable-next-line prefer-const
|
||||
settingContent = settingContent.replaceAll('const ', '')
|
||||
// 去掉 export default及后面代码
|
||||
settingContent = settingContent.replace(/export default[\s\S]*/, '')
|
||||
eval(settingContent)
|
||||
// this.config.option = {
|
||||
// ...this.config.option,
|
||||
// ...option
|
||||
// }
|
||||
this.config.option = {
|
||||
...option,
|
||||
...this.config.option
|
||||
}
|
||||
this.config.setting = setting
|
||||
// 样式改变时更新主题配置
|
||||
// this.config.theme = settingToTheme(cloneDeep(this.config), this.customTheme)
|
||||
// 获取到setting后将其转化为theme
|
||||
// this.config.theme = settingToTheme(this.config, this.customTheme)
|
||||
|
||||
return {
|
||||
option,
|
||||
setting,
|
||||
optionHandler
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件的配置
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
// 将config.setting的配置转化为option里的配置,这里之所以将转化的方法提出来,是因为在改变维度指标和样式的时候都需要转化
|
||||
transformSettingToOption (config, type) {
|
||||
let option = null
|
||||
config.setting.forEach(set => {
|
||||
if (set.optionField) {
|
||||
const optionField = set.optionField.split('.')
|
||||
option = config.option
|
||||
optionField.forEach((field, index) => {
|
||||
if (index === optionField.length - 1) {
|
||||
// 数据配置时,必须有值才更新
|
||||
if ((set.tabName === type && type === 'data' && set.value) || (set.tabName === type && type === 'custom')) {
|
||||
option[field] = set.value
|
||||
}
|
||||
} else {
|
||||
// 如果没有这个属性,则创建该属性,并赋值为空对值
|
||||
if (!option[field]) {
|
||||
option[field] = {}
|
||||
}
|
||||
option = option[field]
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return config
|
||||
},
|
||||
dataFormatting (config, data) {
|
||||
// 数据返回成功则赋值
|
||||
if (data.success) {
|
||||
data = data.data
|
||||
config = this.transformSettingToOption(config, 'data')
|
||||
// 获取到后端返回的数据,有则赋值
|
||||
const option = config.option
|
||||
const setting = config.setting
|
||||
if (config.dataHandler) {
|
||||
try {
|
||||
// 此处函数处理data
|
||||
eval(config.dataHandler)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
config.option.data = data
|
||||
} else {
|
||||
// 数据返回失败则赋前端的模拟数据
|
||||
config.option.data = this.plotList?.find(plot => plot.name === config.name)?.option?.data || config?.option?.data
|
||||
}
|
||||
return config
|
||||
},
|
||||
// 组件的样式改变,返回改变后的config
|
||||
changeStyle (config, isUpdateTheme) {
|
||||
config = { ...this.config, ...config }
|
||||
config = this.transformSettingToOption(config, 'custom')
|
||||
// 这里定义了option和setting是为了保证在执行eval时,optionHandler、dataHandler里面可能会用到,
|
||||
const option = config.option
|
||||
const setting = config.setting
|
||||
if (this.config.optionHandler) {
|
||||
try {
|
||||
// 此处函数处理config
|
||||
eval(this.config.optionHandler)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
// 只有样式改变时更新主题配置,切换主题时不需要保存
|
||||
if (!isUpdateTheme) {
|
||||
config.theme = settingToTheme(cloneDeep(config), this.customTheme)
|
||||
}
|
||||
if (this.chart) {
|
||||
this.chart.update(config.option)
|
||||
}
|
||||
this.changeChartConfig(config)
|
||||
if (config.code === this.activeCode) {
|
||||
this.changeActiveItemConfig(config)
|
||||
}
|
||||
// this.changeChartConfig(config)
|
||||
// 在this.$refs['remoteComponent' + config.code]这个实例里判断是否存在customStyle方法,如果存在则执行
|
||||
const componentInstance = this.$refs['remoteComponent' + config.code]
|
||||
if (componentInstance && componentInstance.$options.methods && componentInstance.$options.methods.customStyle) {
|
||||
// 调用 customStyle 方法
|
||||
this.$refs['remoteComponent' + config.code]?.customStyle(config)
|
||||
}
|
||||
return config
|
||||
},
|
||||
// 同步配置
|
||||
synchConfig (option, setting) {
|
||||
// 对比this.config.setting 和 setting,进行合并,数据以this.config.option对象的value为准
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bs-remote-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 343 KiB |
@@ -0,0 +1,285 @@
|
||||
const title = '高德地图'
|
||||
// 右侧配置项
|
||||
const setting = [
|
||||
{
|
||||
label: '语言类型',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'select',
|
||||
// 字段
|
||||
field: 'customize_lang',
|
||||
optionField: 'customize.lang', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
options: [
|
||||
{
|
||||
label: '中文简称',
|
||||
value: 'zh_cn'
|
||||
},
|
||||
{
|
||||
label: '英文',
|
||||
value: 'en'
|
||||
},
|
||||
{
|
||||
label: '中英文对照',
|
||||
value: 'zh_en'
|
||||
}
|
||||
],
|
||||
// 绑定的值
|
||||
value: 'zh_cn',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: 'Key',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'input',
|
||||
// 字段
|
||||
field: 'customize_mapKey',
|
||||
// 高德地图的key
|
||||
optionField: 'customize.mapKey', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '1b0a1423b70bbcbc20c9c87327e5e94e',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '主题',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'select',
|
||||
// 字段
|
||||
field: 'customize_mapStyle',
|
||||
optionField: 'customize.mapStyle', // 对应options中的字段
|
||||
options: [
|
||||
{
|
||||
label: '标准',
|
||||
value: 'normal'
|
||||
},
|
||||
{
|
||||
label: '幻影黑',
|
||||
value: 'dark'
|
||||
},
|
||||
{
|
||||
label: '月光银',
|
||||
value: 'light'
|
||||
},
|
||||
{
|
||||
label: '远山黛',
|
||||
value: 'whitesmoke'
|
||||
},
|
||||
{
|
||||
label: '草色青',
|
||||
value: 'fresh'
|
||||
},
|
||||
{
|
||||
label: '雅士灰',
|
||||
value: 'grey'
|
||||
},
|
||||
{
|
||||
label: '涂鸦',
|
||||
value: 'graffiti'
|
||||
},
|
||||
{
|
||||
label: '马卡龙',
|
||||
value: 'macaron'
|
||||
},
|
||||
{
|
||||
label: '靛青蓝',
|
||||
value: 'blue'
|
||||
},
|
||||
{
|
||||
label: '极夜黑',
|
||||
value: 'darkblue'
|
||||
},
|
||||
{
|
||||
label: '酱籽',
|
||||
value: 'wine'
|
||||
}
|
||||
],
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 'normal',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '内容',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'select',
|
||||
// 字段
|
||||
field: 'customize_features',
|
||||
optionField: 'customize.features', // 对应options中的字段
|
||||
options: [
|
||||
{
|
||||
label: '区域面',
|
||||
value: 'bg'
|
||||
},
|
||||
{
|
||||
label: '道路',
|
||||
value: 'road'
|
||||
},
|
||||
{
|
||||
label: '建筑物',
|
||||
value: 'building'
|
||||
},
|
||||
{
|
||||
label: '标注',
|
||||
value: 'point'
|
||||
}
|
||||
],
|
||||
// 是否多选
|
||||
multiple: true,
|
||||
// 绑定的值
|
||||
value: ['bg', 'road', 'building', 'point'],
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
min: 0,
|
||||
// 字段
|
||||
field: 'customize_lng',
|
||||
optionField: 'customize.lng', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 117.13872961838531,
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '维度',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
// 范围
|
||||
min: 0,
|
||||
// 字段
|
||||
field: 'customize_lat',
|
||||
optionField: 'customize.lat', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 31.826653302438004,
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '缩放',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
// 范围
|
||||
min: 3,
|
||||
max: 18,
|
||||
// 字段
|
||||
field: 'customize_zoom',
|
||||
optionField: 'customize.zoom', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 16,
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '模式',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: '2D',
|
||||
value: '2D'
|
||||
},
|
||||
{
|
||||
label: '3D',
|
||||
value: '3D'
|
||||
}
|
||||
],
|
||||
// 字段
|
||||
field: 'customize_viewMode',
|
||||
optionField: 'customize.viewMode', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '2D',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '标记内容',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'input',
|
||||
// 字段
|
||||
field: 'customize_markerSpan',
|
||||
optionField: 'customize.markerSpan', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '科大国创软件股份有限公司',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '标记点经度',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
min: 0,
|
||||
// 字段
|
||||
field: 'customize_markerLng',
|
||||
optionField: 'customize.markerLng', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 117.13872961838531,
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '标记点维度',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
// 字段
|
||||
field: 'customize_markerLat',
|
||||
optionField: 'customize.markerLat', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 31.826653302438004,
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
}
|
||||
]
|
||||
|
||||
// 模拟数据
|
||||
|
||||
const option = {
|
||||
// 自定义组件其他属性
|
||||
customize: {
|
||||
lang: 'zh_cn',
|
||||
mapKey: '1b0a1423b70bbcbc20c9c87327e5e94e',
|
||||
mapStyle: 'normal',
|
||||
features: ['bg', 'road', 'building', 'point'],
|
||||
lng: 117.13872961838531,
|
||||
lat: 31.826653302438004,
|
||||
zoom: 17,
|
||||
markerSpan: '科大国创软件股份有限公司',
|
||||
markerLng: 117.13872961838531,
|
||||
markerLat: 31.826653302438004
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
title,
|
||||
setting,
|
||||
option: {
|
||||
...option,
|
||||
displayOption: {
|
||||
dataAllocation: {
|
||||
enable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div
|
||||
v-loading="loading"
|
||||
element-loading-text="地图加载中..."
|
||||
class="map-box"
|
||||
:class="{ 'no-pointer': isDesign }"
|
||||
>
|
||||
<div
|
||||
:id="`map-${config.code}`"
|
||||
style="width: 100%; height: 100%;"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||
export default {
|
||||
name: 'RemoteMap',
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
map: null,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
option () {
|
||||
return this.config.option
|
||||
},
|
||||
optionData () {
|
||||
return this.option.data
|
||||
},
|
||||
customize () {
|
||||
return this.option.customize
|
||||
},
|
||||
isDesign () {
|
||||
return (window?.BS_CONFIG?.routers?.designUrl || '/big-screen/design') === this.$route.path
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.initMap(this.customize)
|
||||
},
|
||||
methods: {
|
||||
initMap (customize) {
|
||||
this.loading = true
|
||||
this.updateKey = 0
|
||||
AMapLoader.load({
|
||||
key: customize.mapKey || '1b0a1423b70bbcbc20c9c87327e5e94e', // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: '1.4.15', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [
|
||||
'AMap.ToolBar',
|
||||
'AMap.Scale',
|
||||
'AMap.HawkEye',
|
||||
'AMap.MapType',
|
||||
'AMap.Geolocation'
|
||||
]
|
||||
}).then(() => {
|
||||
// 创建地图
|
||||
// eslint-disable-next-line no-undef
|
||||
this.map = new AMap.Map(`map-${this.config.code}`, {
|
||||
resizeEnable: true, // 是否监控地图容器尺寸变化
|
||||
lang: customize.lang,
|
||||
mapStyle: `amap://styles/${customize.mapStyle}`,
|
||||
center: [customize.lng, customize.lat],
|
||||
features: customize.features,
|
||||
zoom: customize.zoom,
|
||||
viewMode: customize.viewMode,
|
||||
plugins: ['AMap.ToolBar', 'AMap.Scale', 'AMap.MapType', 'AMap.Geolocation']
|
||||
})
|
||||
this.loading = false
|
||||
// eslint-disable-next-line no-undef
|
||||
this.map.addControl(new AMap.ToolBar())
|
||||
// 在图面添加比例尺控件,展示地图在当前层级和纬度下的比例尺
|
||||
// eslint-disable-next-line no-undef
|
||||
this.map.addControl(new AMap.Scale())
|
||||
// 在图面添加类别切换控件,实现默认图层与卫星图、实施交通图层之间切换的控制
|
||||
// eslint-disable-next-line no-undef
|
||||
this.map.addControl(new AMap.MapType())
|
||||
// 在图面添加定位控件,用来获取和展示用户主机所在的经纬度位置
|
||||
// eslint-disable-next-line no-undef
|
||||
this.map.addControl(new AMap.Geolocation())
|
||||
let marker = null // 用于存储标记对象的变量
|
||||
if (customize.markerSpan) {
|
||||
// 创建自定义标记内容
|
||||
const markerContent = document.createElement('div')
|
||||
markerContent.style.position = 'absolute'
|
||||
markerContent.style.width = '25px'
|
||||
markerContent.style.height = '34px'
|
||||
|
||||
// 创建标记图标
|
||||
const markerImg = document.createElement('img')
|
||||
markerImg.src = '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png'
|
||||
markerImg.style.width = '25px'
|
||||
markerImg.style.height = '34px'
|
||||
markerContent.appendChild(markerImg)
|
||||
|
||||
// 创建标记文本
|
||||
const markerSpan = document.createElement('span')
|
||||
markerSpan.className = 'marker'
|
||||
markerSpan.innerHTML = customize.markerSpan
|
||||
markerContent.appendChild(markerSpan)
|
||||
// 删除之前的标记(如果存在)
|
||||
if (marker) {
|
||||
this.map.remove(marker)
|
||||
}
|
||||
// 创建自定义标记
|
||||
// eslint-disable-next-line no-undef
|
||||
marker = new AMap.Marker({
|
||||
position: [customize.markerLng, customize.markerLat],
|
||||
content: markerContent,
|
||||
// eslint-disable-next-line no-undef
|
||||
offset: new AMap.Pixel(0, 0) // 设置标记偏移,使其指向标记位置
|
||||
})
|
||||
|
||||
// 将标记添加到地图中
|
||||
this.map.add(marker)
|
||||
// 动态修改标记的 right 位置
|
||||
const markerElement = marker.getContent()
|
||||
const markerTextElement = markerElement.querySelector('.marker')
|
||||
markerTextElement.style.right = 0 // 设置初始的 right 值
|
||||
if (markerTextElement) {
|
||||
setTimeout(() => {
|
||||
markerTextElement.style.right = `-${markerTextElement.clientWidth}px` // 设置新的 right 值
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
customStyle (config) {
|
||||
if (config && config.option && config.option.customize) {
|
||||
this.initMap(config.option.customize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.no-pointer {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.map-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
|
||||
::v-deep .amap-marker-content img {
|
||||
width: 25px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
::v-deep .marker {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: -118px;
|
||||
color: #fff;
|
||||
padding: 4px 10px;
|
||||
box-shadow: 1px 1px 1px rgba(10, 10, 10, .2);
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
font-family: "";
|
||||
background-color: #25A5F7;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,128 @@
|
||||
const title = '边框组件'
|
||||
// 右侧配置项
|
||||
const setting = [
|
||||
{
|
||||
label: '对齐类型',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'select',
|
||||
// 字段
|
||||
field: 'customize_diagonalType',
|
||||
optionField: 'customize.diagonalType', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
options: [
|
||||
{
|
||||
label: '左上右下',
|
||||
value: 'leftTopRightBottom'
|
||||
},
|
||||
{
|
||||
label: '右上左下',
|
||||
value: 'rightTopLeftBottom'
|
||||
}
|
||||
],
|
||||
// 绑定的值
|
||||
value: 'leftTopRightBottom',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '背景颜色',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'colorPicker',
|
||||
// 字段
|
||||
field: 'customize_backgroundColor',
|
||||
optionField: 'customize.backgroundColor', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '#D3D3D32B',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '长度',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
// 字段
|
||||
field: 'customize_borderLength',
|
||||
optionField: 'customize.borderLength', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '30',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '粗细',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
// 字段
|
||||
field: 'customize_borderWidth',
|
||||
optionField: 'customize.borderWidth', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '4',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '边框颜色',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'colorPicker',
|
||||
// 字段
|
||||
field: 'customize_borderColor',
|
||||
optionField: 'customize.borderColor', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: '#409EFF',
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
},
|
||||
{
|
||||
label: '边框圆角',
|
||||
// 设置组件类型, select / input / colorPicker
|
||||
type: 'inputNumber',
|
||||
// 范围
|
||||
min: 0,
|
||||
max: 100,
|
||||
// 字段
|
||||
field: 'customize_borderRadius',
|
||||
optionField: 'customize.borderRadius', // 对应options中的字段
|
||||
// 是否多选
|
||||
multiple: false,
|
||||
// 绑定的值
|
||||
value: 4,
|
||||
// tab页。 data: 数据, custom: 自定义
|
||||
tabName: 'custom'
|
||||
}
|
||||
]
|
||||
|
||||
// 模拟数据
|
||||
|
||||
const option = {
|
||||
// 自定义组件其他属性
|
||||
customize: {
|
||||
diagonalType: 'leftTopRightBottom',
|
||||
borderLength: 30,
|
||||
borderWidth: 4,
|
||||
backgroundColor: '#D3D3D32B',
|
||||
borderRadius: 0,
|
||||
borderColor: '#409EFF'
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
title,
|
||||
setting,
|
||||
option: {
|
||||
...option,
|
||||
displayOption: {
|
||||
dataAllocation: {
|
||||
enable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div
|
||||
class="border-box"
|
||||
:style="{
|
||||
backgroundColor: customize.backgroundColor
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="customize.diagonalType === 'leftTopRightBottom'"
|
||||
class="left-diagonal"
|
||||
>
|
||||
<div
|
||||
class="top"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopRightRadius: customize.borderRadius + 'px',
|
||||
borderBottomRightRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="left"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopRightRadius: customize.borderRadius + 'px',
|
||||
borderBottomRightRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="right"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopLeftRadius: customize.borderRadius + 'px',
|
||||
borderBottomLeftRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="bottom"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopLeftRadius: customize.borderRadius + 'px',
|
||||
borderBottomLeftRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="customize.diagonalType === 'rightTopLeftBottom'"
|
||||
class="right-diagonal"
|
||||
>
|
||||
<div
|
||||
class="top"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopLeftRadius: customize.borderRadius + 'px',
|
||||
borderBottomLeftRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="right"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopLeftRadius: customize.borderRadius + 'px',
|
||||
borderBottomLeftRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="left"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopRightRadius: customize.borderRadius + 'px',
|
||||
borderBottomRightRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="bottom"
|
||||
:style="{
|
||||
width: customize.borderLength + 'px',
|
||||
height: customize.borderWidth + 'px',
|
||||
borderTopRightRadius: customize.borderRadius + 'px',
|
||||
borderBottomRightRadius: customize.borderRadius + 'px',
|
||||
backgroundColor: customize.borderColor
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'DiagonalBorder',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
option () {
|
||||
return this.config.option
|
||||
},
|
||||
optionData () {
|
||||
return this.option.data
|
||||
},
|
||||
customize () {
|
||||
return this.option.customize
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.customize.diagonalType === 'leftTopRightBottom') {
|
||||
const leftDiagonal = document.querySelector('.left-diagonal').querySelector('.left')
|
||||
const rightDiagonal = document.querySelector('.left-diagonal').querySelector('.right')
|
||||
leftDiagonal.style.left = `${this.customize.borderWidth / 2}px`
|
||||
leftDiagonal.style.top = `${this.customize.borderWidth / 2}px`
|
||||
rightDiagonal.style.right = `${this.customize.borderWidth / 2}px`
|
||||
rightDiagonal.style.bottom = `${this.customize.borderWidth / 2}px`
|
||||
} else {
|
||||
const leftDiagonal = document.querySelector('.right-diagonal').querySelector('.left')
|
||||
const rightDiagonal = document.querySelector('.right-diagonal').querySelector('.right')
|
||||
leftDiagonal.style.left = `${this.customize.borderWidth / 2}px`
|
||||
leftDiagonal.style.bottom = `${this.customize.borderWidth / 2}px`
|
||||
rightDiagonal.style.right = `${this.customize.borderWidth / 2}px`
|
||||
rightDiagonal.style.top = `${this.customize.borderWidth / 2}px`
|
||||
}
|
||||
},
|
||||
methods: { }
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.border-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.left-diagonal {
|
||||
.top {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
// 顺时针旋转90度
|
||||
.left {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: rotate(90deg);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transform: rotate(90deg);
|
||||
transform-origin: right;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transform-origin: right;
|
||||
}
|
||||
}
|
||||
|
||||
.right-diagonal {
|
||||
.top {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.right {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: right;
|
||||
}
|
||||
|
||||
.left {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform-origin: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
114
frontend/packages/RemoteComponents/remoteComponentsList.js
Normal file
114
frontend/packages/RemoteComponents/remoteComponentsList.js
Normal file
@@ -0,0 +1,114 @@
|
||||
// 得到用户自定义的远程组件列表
|
||||
import { dataConfig, settingConfig } from './settingConfig'
|
||||
// import _ from 'lodash'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
|
||||
const files = require.context('./innerComponents/', true, /index.vue$/)
|
||||
const innerRemoteComponents = []
|
||||
|
||||
files.keys().forEach(key => {
|
||||
const title = key.split('/')[1].replace('.vue', '')
|
||||
let img = null
|
||||
let config = {}
|
||||
try {
|
||||
img = require(`./innerComponents/${title}/component.png`)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
try {
|
||||
config = require(`./innerComponents/${title}/config.js`).default
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
innerRemoteComponents.push({
|
||||
title: config.title || title,
|
||||
vueSysComponentDirName: 'inner_' + title,
|
||||
vueFile: files(key).default,
|
||||
...config,
|
||||
img
|
||||
})
|
||||
})
|
||||
// 抛出内置系统组件
|
||||
export default getRemoteComponents(innerRemoteComponents)
|
||||
|
||||
// 抛出外部的用户系统组件
|
||||
export function getRemoteComponents (comList) {
|
||||
const customList = (comList || window.BS_CONFIG?.remoteComponents) || []
|
||||
|
||||
const list = []
|
||||
customList.forEach((config) => {
|
||||
list.push({
|
||||
name: config.name,
|
||||
title: config.title,
|
||||
icon: null,
|
||||
img: config.img,
|
||||
border: { type: '', titleHeight: 60, fontSize: 16, isTitle: true, padding: [16, 16, 16, 16] },
|
||||
className:
|
||||
'com.gccloud.dataroom.core.module.chart.components.RemoteComponentChart',
|
||||
w: 450,
|
||||
h: 320,
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotateX: config.rotateX || 0,
|
||||
rotateY: config.rotateY || 0,
|
||||
rotateZ: config.rotateZ || 0,
|
||||
perspective: config.perspective || 500,
|
||||
skewX: config.skewX || 0,
|
||||
skewY: config.skewY || 0,
|
||||
type: 'remoteComponent',
|
||||
option: {
|
||||
...cloneDeep(settingConfig),
|
||||
...config.option
|
||||
},
|
||||
// 右侧面板自定义配置
|
||||
setting: config.setting,
|
||||
...cloneDeep(dataConfig),
|
||||
// 自定义配置
|
||||
customize: {
|
||||
...config.customize,
|
||||
vueSysComponentDirName: config.vueSysComponentDirName,
|
||||
vueFile: config.vueFile
|
||||
}
|
||||
})
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
export function getRemoteComponentConfig (code, name) {
|
||||
const config = {
|
||||
name,
|
||||
title: name,
|
||||
icon: null,
|
||||
img: null,
|
||||
border: { type: '', titleHeight: 60, fontSize: 16, isTitle: true, padding: [16, 16, 16, 16] },
|
||||
className:
|
||||
'com.gccloud.dataroom.core.module.chart.components.RemoteComponentChart',
|
||||
w: 450,
|
||||
h: 320,
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotateX: 0,
|
||||
rotateY: 0,
|
||||
rotateZ: 0,
|
||||
perspective: 0,
|
||||
skewX: 0,
|
||||
skewY: 0,
|
||||
type: 'remoteComponent',
|
||||
option: {
|
||||
...cloneDeep(settingConfig)
|
||||
},
|
||||
// 右侧面板自定义配置
|
||||
setting: [],
|
||||
...cloneDeep(dataConfig),
|
||||
// 自定义配置
|
||||
customize: {
|
||||
// 文件路径
|
||||
vueSysComponentDirName: null,
|
||||
// 用户上传的vue文件编码,根据此编码获取文件内容
|
||||
vueBizComponentCode: code,
|
||||
// vue文本内容
|
||||
vueFileContent: null
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
21
frontend/packages/RemoteComponents/settingConfig.js
Normal file
21
frontend/packages/RemoteComponents/settingConfig.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { commonConfig, displayOption } from 'data-room-ui/js/config'
|
||||
export const settingConfig = {
|
||||
displayOption: { ...displayOption }
|
||||
}
|
||||
const customConfig = {
|
||||
root: {
|
||||
version: '2023071001',
|
||||
contribution: false
|
||||
},
|
||||
customize: {
|
||||
// 文件路径
|
||||
vueSysComponentDirName: null,
|
||||
// 用户上传的vue文件编码,根据此编码获取文件内容
|
||||
vueBizComponentCode: null,
|
||||
// vue文本内容
|
||||
vueFileContent: null
|
||||
}
|
||||
}
|
||||
export const dataConfig = {
|
||||
...commonConfig(customConfig)
|
||||
}
|
||||
Reference in New Issue
Block a user