@jetlinks-web/components 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { App } from 'vue'
2
+
3
+ import FullPage from './src/FullPage/index.vue'
4
+ import PermissionButton from './src/PermissionButton/index'
5
+ import BadgeStatus from './src/BadgeStatus'
6
+ import GeoComponent from './src/GeoComponent'
7
+ import ValueItem from './src/ValueItem'
8
+ import Echarts from './src/Echarts'
9
+
10
+ import { PageContainer, AIcon } from 'jetlinks-ui-components'
11
+ import { InitAMap } from './src/AMap'
12
+
13
+ export default {
14
+ install(app: App) {
15
+ app.component('PageContainer', PageContainer)
16
+ .component('AIcon', AIcon)
17
+ .component('PermissionButton', PermissionButton)
18
+ .component('FullPage', FullPage)
19
+ .component('BadgeStatus', BadgeStatus)
20
+ .component('InitAmap', InitAMap)
21
+ .component('GeoComponent', GeoComponent)
22
+ .component('ValueItem', ValueItem)
23
+ .component('Echarts', Echarts)
24
+ }
25
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@jetlinks-web/components",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.ts",
6
+ "module": "index.ts",
7
+ "keywords": [],
8
+ "files": [
9
+ "src",
10
+ "index.ts"
11
+ ],
12
+ "private": false,
13
+ "author": "",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "@vuemap/vue-amap": "^2.0.17",
17
+ "@vueuse/core": "^10.2.1",
18
+ "ant-design-vue": "^3.2.20",
19
+ "jetlinks-ui-components": "^1.0.27",
20
+ "lodash-es": "^4.17.21",
21
+ "pinia": "^2.1.6",
22
+ "vue": "^3.3.4",
23
+ "vue-router": "^4.2.4",
24
+ "@jetlinks/hooks": "npm:@jetlinks-web/hooks@^1.0.0",
25
+ "@jetlinks/stores": "npm:@jetlinks-web/stores@^1.0.0",
26
+ "@jetlinks/utils": "npm:@jetlinks-web/utils@^1.0.0"
27
+ },
28
+ "publishConfig": {
29
+ "registry": "https://registry.npmjs.org/",
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "clean": "pnpm rimraf node_modules && pnpm rimraf .turbo"
34
+ }
35
+ }
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <div
3
+ :style="props.style || { width: '100%', height: '100%' }"
4
+ :class="props.class"
5
+ >
6
+ <el-amap
7
+ v-if="amapKey"
8
+ v-bind="{
9
+ ...props,
10
+ ...$attrs
11
+ }"
12
+ :map-style="_mapStyle"
13
+ @init="initMap"
14
+ @click="(e) => emit('click', e)"
15
+ @dblclick="(e) => emit('dblclick', e)"
16
+ @movestart="(e) => emit('movestart', e)"
17
+ @moveend="(e) => emit('moveend', e)"
18
+ @rightclick="(e) => emit('rightclick', e)"
19
+ >
20
+ <template v-if="isOpenUi">
21
+ <template v-if="uiLoading">
22
+ <slot></slot>
23
+ </template>
24
+ </template>
25
+ <template v-else><slot></slot></template>
26
+ </el-amap>
27
+ <JEmpty v-else description="请配置高德地图key" style="padding: 20%" />
28
+ </div>
29
+ </template>
30
+
31
+ <script setup name="InitAMap" lang="ts">
32
+ import type { CSSProperties, PropType } from 'vue';
33
+ import { ref, computed } from 'vue'
34
+ import { initAMapApiLoader, ElAmap } from '@vuemap/vue-amap';
35
+ import '@vuemap/vue-amap/dist/style.css';
36
+ import { store } from '@jetlinks/stores'
37
+ import { getAMapUiPromise } from '@jetlinks/utils'
38
+ import { MapProps } from './util'
39
+ interface AMapProps {
40
+ style?: CSSProperties;
41
+ class?: string;
42
+ AMapUI?: string | boolean;
43
+ plugins?: string[]
44
+ }
45
+
46
+ const systemStore = store.SystemStore
47
+
48
+ const amapKey = systemStore.systemInfo.apiKey
49
+
50
+ const emit = defineEmits([
51
+ 'initMap',
52
+ 'click',
53
+ 'dblclick',
54
+ 'movestart',
55
+ 'moveend',
56
+ 'rightclick',
57
+ ])
58
+
59
+ const props = defineProps({
60
+ ...MapProps(),
61
+ style: Object as PropType<AMapProps['style']>,
62
+ class: String as PropType<AMapProps['class']>,
63
+ AMapUI: [String, Boolean],
64
+ center: Array,
65
+ plugin: Array,
66
+ zooms: {
67
+ type: Array,
68
+ default: [3, 20]
69
+ }
70
+ });
71
+
72
+ const _mapStyle = computed(() => {
73
+ return props.mapStyle ? `amap://styles/${props.mapStyle}` : undefined
74
+ })
75
+
76
+ initAMapApiLoader({
77
+ key: amapKey || '',
78
+ plugin: props.plugin
79
+ });
80
+
81
+ const uiLoading = ref<boolean>(false);
82
+
83
+ const map = ref<any>(null);
84
+
85
+ const isOpenUi = computed(() => {
86
+ return 'AMapUI' in props || props.AMapUI;
87
+ });
88
+ const getAMapUI = () => {
89
+ const version = typeof props.AMapUI === 'string' ? props.AMapUI : '1.1';
90
+ getAMapUiPromise(version).then(() => {
91
+ uiLoading.value = true;
92
+ });
93
+ };
94
+
95
+ const initMap = (e: any) => {
96
+ map.value = e;
97
+ emit('initMap', e)
98
+ if (isOpenUi.value) {
99
+ getAMapUI();
100
+ }
101
+ };
102
+
103
+ </script>
104
+
105
+ <style scoped>
106
+
107
+ </style>
@@ -0,0 +1 @@
1
+ export { default as InitAMap } from './Map.vue'
@@ -0,0 +1,56 @@
1
+ export const MapProps = () => ({
2
+ // 响应式
3
+ center: Array,
4
+ labelzIndex: Number,
5
+ lang: String,
6
+ mapStyle: String,
7
+
8
+ // 静态属性
9
+ vid: String,
10
+ amapManager: Object as any,
11
+ defaultCursor: String,
12
+ animateEnable: {
13
+ type: Boolean,
14
+ default: true
15
+ },
16
+ isHotspot: Boolean,
17
+ rotateEnable: {
18
+ type: Boolean,
19
+ default: true
20
+ },
21
+ resizeEnable: {
22
+ type: Boolean,
23
+ default: true
24
+ },
25
+ showIndoorMap: Boolean,
26
+ expandZoomRange: Boolean,
27
+ dragEnable: {
28
+ type: Boolean,
29
+ default: true
30
+ },
31
+ zoomEnable: {
32
+ type: Boolean,
33
+ default: true
34
+ },
35
+ doubleClickZoom: {
36
+ type: Boolean,
37
+ default: true
38
+ },
39
+ keyboardEnable: {
40
+ type: Boolean,
41
+ default: true
42
+ },
43
+ jogEnable: {
44
+ type: Boolean,
45
+ default: true
46
+ },
47
+ scrollWheel: {
48
+ type: Boolean,
49
+ default: true
50
+ },
51
+ touchZoom: {
52
+ type: Boolean,
53
+ default: true
54
+ },
55
+
56
+ })
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <j-badge
3
+ :color="_color"
4
+ :text="text"
5
+ ></j-badge>
6
+ </template>
7
+
8
+ <script setup lang="ts" name="BadgeStatus">
9
+ import { defineProps, computed } from 'vue'
10
+ import { getHexColor } from './color'
11
+ const props = defineProps({
12
+ text: {
13
+ type: String,
14
+ },
15
+ status: {
16
+ type: [String, Number],
17
+ default: 'default',
18
+ },
19
+ /**
20
+ * 自定义status值颜色
21
+ * @example {
22
+ * 1: 'success',
23
+ * 0: 'error'
24
+ * }
25
+ */
26
+ statusNames: {
27
+ type: Object,
28
+ default: () => ({
29
+ 'success': 'success',
30
+ 'warning': 'warning',
31
+ 'error': 'error',
32
+ 'default': 'default',
33
+ })
34
+ },
35
+ });
36
+
37
+ const _color = computed(() => {
38
+ return getHexColor(props.statusNames[props.status], 1)
39
+ })
40
+ </script>
@@ -0,0 +1,23 @@
1
+
2
+ const color = {
3
+ 'processing': '9, 46, 231',
4
+ 'error': '229, 0, 18',
5
+ 'success': '36, 178, 118',
6
+ 'warning': '255, 144, 0',
7
+ 'default': '102, 102, 102',
8
+ //告警颜色
9
+ 'level1': '229, 0, 18',
10
+ 'level2': '255, 148, 87',
11
+ 'level3': '250, 189, 71',
12
+ 'level4': '153, 153, 153',
13
+ 'level5': '196, 196, 196'
14
+ }
15
+ export const getHexColor = (code: string, pe: number = 0.1) => {
16
+ const _color = color[code] || color.default
17
+ if (code === 'default') {
18
+ pe = 0.1
19
+ }
20
+ return `rgba(${_color}, ${pe})`
21
+ }
22
+
23
+ export default color
@@ -0,0 +1,4 @@
1
+ import BadgeStatus from './Badge.vue'
2
+ export * from './color'
3
+
4
+ export default BadgeStatus
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div ref="echartsDom" class="echarts-warp" :style="style"></div>
3
+ </template>
4
+
5
+ <script lang="ts" name="Echarts" setup>
6
+ import { CSSProperties, nextTick, ref, watch, Ref } from 'vue'
7
+ import { useECharts } from '@jetlinks/hooks'
8
+
9
+ interface Props {
10
+ style?: CSSProperties
11
+ }
12
+
13
+ const props = defineProps({
14
+ options: {
15
+ type: Object,
16
+ default: undefined,
17
+ },
18
+ style: Object as PropType<Props['style']>,
19
+ })
20
+
21
+ const echartsDom = ref<Ref<HTMLDivElement> | HTMLDivElement>()
22
+
23
+ const { setOptions } = useECharts(echartsDom.value)
24
+
25
+ watch(
26
+ () => props.options,
27
+ () => {
28
+ if (props.options) {
29
+ nextTick(() => {
30
+ setOptions(props.options)
31
+ })
32
+ }
33
+ },
34
+ { immediate: true, deep: true },
35
+ )
36
+ </script>
37
+
38
+ <style scoped>
39
+ .echarts-warp {
40
+ width: 100%;
41
+ height: 100%;
42
+ }
43
+ </style>
@@ -0,0 +1,3 @@
1
+ import Echarts from './Echarts.vue'
2
+
3
+ export default Echarts
@@ -0,0 +1,27 @@
1
+ <template>
2
+ <div class='full-page-warp' ref='fullPage' :style='{ minHeight: `calc(100vh - ${y + 24}px)`}'>
3
+ <div class="full-page-warp-content">
4
+ <slot></slot>
5
+ </div>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup lang='ts' name='FullPage'>
10
+ import { ref } from 'vue'
11
+ import { useElementBounding } from '@vueuse/core'
12
+
13
+ const fullPage = ref(null)
14
+ const { y } = useElementBounding(fullPage)
15
+
16
+ </script>
17
+
18
+ <style scoped lang="less">
19
+ .full-page-warp {
20
+ background: #fff;
21
+ display: flex;
22
+ .full-page-warp-content {
23
+ width: 100%;
24
+ }
25
+ }
26
+
27
+ </style>
@@ -0,0 +1,139 @@
1
+ <!-- 坐标点拾取组件 -->
2
+ <template>
3
+ <div class="page-container">
4
+ <j-input allowClear v-bind="props" v-model:value="inputPoint">
5
+ <template #addonAfter>
6
+ <AIcon type="EnvironmentOutlined" @click="modalVisible = true" />
7
+ </template>
8
+ </j-input>
9
+ <j-modal
10
+ title="地理位置"
11
+ ok-text="确认"
12
+ cancel-text="取消"
13
+ v-model:visible="modalVisible"
14
+ width="700px"
15
+ @cancel="modalVisible = false"
16
+ @ok="handleModalSubmit"
17
+ destroyOnClose
18
+ >
19
+ <div style="width: 100%; height: 400px">
20
+ <div style="margin-bottom: 10px;">地理位置:{{ mapPoint }}</div>
21
+ <el-amap
22
+ :center="_center"
23
+ :zoom="_zoom"
24
+ @init="initMap"
25
+ @click="clickMap"
26
+ >
27
+ <el-amap-search-box visible @select="selectPosition" />
28
+ <el-amap-marker :position="position" />
29
+ </el-amap>
30
+ </div>
31
+ </j-modal>
32
+ </div>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { store } from '@jetlinks/stores'
37
+ import { initAMapApiLoader } from '@vuemap/vue-amap'
38
+ import '@vuemap/vue-amap/dist/style.css'
39
+ import { computed, CSSProperties, PropType, ref } from 'vue'
40
+
41
+ interface AMapProps {
42
+ style?: CSSProperties
43
+ class?: string
44
+ AMapUI?: string | boolean
45
+ plugins?: string[]
46
+ }
47
+
48
+ const systemStore = store.SystemStore
49
+
50
+ const amapKey = systemStore.systemInfo.apiKey
51
+
52
+ interface EmitProps {
53
+ (e: 'update:point', data: string): void
54
+ (e: 'change', data: string): void
55
+ }
56
+ const props = defineProps({
57
+ style: Object as PropType<AMapProps['style']>,
58
+ class: String as PropType<AMapProps['class']>,
59
+ AMapUI: [String, Boolean],
60
+ center: Array as PropType<number[]>,
61
+ plugin: Array,
62
+ zoom: {
63
+ type: Number,
64
+ default: 12,
65
+ },
66
+ securityJsCode: String,
67
+ point: { type: [Number, String], default: '' },
68
+ })
69
+ const emit = defineEmits<EmitProps>()
70
+
71
+
72
+ initAMapApiLoader({
73
+ key: amapKey,
74
+ securityJsCode: props.securityJsCode
75
+ })
76
+
77
+ // 手动输入的坐标点(经纬度字符串)
78
+ const inputPoint = computed({
79
+ get: () => {
80
+ return props.point
81
+ },
82
+ set: (val: any) => {
83
+ mapPoint.value = val
84
+ emit('update:point', val)
85
+ emit('change', val)
86
+ },
87
+ })
88
+
89
+ // 地图弹窗
90
+ const modalVisible = ref<boolean>(false)
91
+
92
+ const handleModalSubmit = () => {
93
+ inputPoint.value = mapPoint.value
94
+ modalVisible.value = false
95
+ }
96
+
97
+ // 地图拾取的坐标点(经纬度字符串)
98
+ const mapPoint = ref('')
99
+
100
+ const _zoom = ref<number>(props.zoom)
101
+ const _center = ref<number[]>(props?.center || [])
102
+ let map: any = null
103
+
104
+ // 地图经纬度
105
+ const position = ref<number[] | string[]>([])
106
+
107
+ /**
108
+ * 地图初始化
109
+ * @param e
110
+ */
111
+ const initMap = (e: any) => {
112
+ map = e
113
+
114
+ const pointStr = mapPoint.value as string
115
+ position.value = pointStr ? pointStr.split(',') : _center.value
116
+ }
117
+
118
+ /**
119
+ * 地图点击
120
+ * @param e
121
+ */
122
+ const clickMap = (e: any) => {
123
+ mapPoint.value = `${e.lnglat.lng},${e.lnglat.lat}`
124
+ position.value = [e.lnglat.lng, e.lnglat.lat]
125
+ }
126
+
127
+ /**
128
+ * 选择搜索结果
129
+ * @param e
130
+ */
131
+ const selectPosition = (e: any) => {
132
+ const selectPoint = [e.poi.location.lng, e.poi.location.lat]
133
+ map.setCenter(selectPoint)
134
+ mapPoint.value = selectPoint.join(',')
135
+ position.value = selectPoint
136
+ }
137
+ </script>
138
+
139
+ <style lang="less" scoped></style>
@@ -0,0 +1,3 @@
1
+ import GeoComponent from './GeoComponent.vue'
2
+
3
+ export default GeoComponent
@@ -0,0 +1,20 @@
1
+ import {watch, ref} from 'vue'
2
+ import type { Ref } from 'vue'
3
+ import { store } from '@jetlinks/stores'
4
+ import {isBoolean} from "lodash-es";
5
+
6
+ export const usePermission = (code?: string | string[] | boolean): {
7
+ hasPerm: Ref<boolean>
8
+ } => {
9
+ const hasPerm = ref(false)
10
+
11
+ watch(() => code, () => {
12
+ if (code) {
13
+ hasPerm.value = isBoolean(code) ? code : store.AuthStore.hasPermission(code)
14
+ }
15
+ }, { immediate: true })
16
+
17
+ return {
18
+ hasPerm
19
+ }
20
+ }
@@ -0,0 +1,107 @@
1
+ import {h, defineComponent, computed} from "vue";
2
+ import type { PropType, CSSProperties, ExtractPropTypes } from 'vue'
3
+ import { Button, Tooltip, Popconfirm } from 'jetlinks-ui-components'
4
+ import {PopconfirmProps, TooltipProps} from "ant-design-vue/es";
5
+ import {omit} from "lodash-es";
6
+ import {buttonProps} from "ant-design-vue/es/button/button";
7
+ import { usePermission } from './hooks'
8
+
9
+ type PermissionType = string | Array<string> | boolean
10
+
11
+ const definedProps = {
12
+ tooltip: {
13
+ type: Object as PropType<TooltipProps>,
14
+ },
15
+ popConfirm: {
16
+ type: Object as PropType<PopconfirmProps>,
17
+ },
18
+ hasPermission: {
19
+ type: [String, Array, Boolean] as PropType<PermissionType>,
20
+ default: undefined
21
+ },
22
+ style: {
23
+ type: Object as PropType<CSSProperties>
24
+ },
25
+ noPermissionTitle: {
26
+ type: String
27
+ },
28
+ ...omit(buttonProps(), 'icon')
29
+ }
30
+
31
+ type DefinedPropsType = Partial<ExtractPropTypes<typeof definedProps>>
32
+
33
+ const PermissionButton = defineComponent({
34
+ name: 'PermissionButton',
35
+ // @ts-ignore
36
+ slots: ['button', 'icon'],
37
+ props: definedProps,
38
+ setup(props, { slots }) {
39
+
40
+ const { popConfirm, tooltip } = props
41
+ const { hasPerm } = usePermission(props.hasPermission as PermissionType)
42
+
43
+ const permission = computed(() => {
44
+ if (!props.hasPermission || props.hasPermission === true) {
45
+ return true
46
+ }
47
+ return hasPerm.value
48
+ })
49
+
50
+ const isPermission = computed(() => {
51
+ if ('hasPermission' in props && permission.value) {
52
+ return 'disabled' in props ? props.disabled : false
53
+ }
54
+ return true
55
+ })
56
+
57
+ const hasPopConfirm = computed(() => !!popConfirm) // 是否包含确认弹窗
58
+ const hasTooltip = computed(() => !!tooltip) // 是否包含文字提示
59
+
60
+ return () => {
61
+ const { popConfirm, tooltip, hasPermission, noPermissionTitle, ...buttonProps } = props
62
+
63
+ const button = !slots.button ?
64
+ h(Button,
65
+ {
66
+ ...buttonProps,
67
+ disabled: isPermission.value,
68
+ },
69
+ {
70
+ default: () => slots?.default?.(),
71
+ icon: () => slots?.icon?.()
72
+ }) :
73
+ slots.button()
74
+
75
+ // 文字提示
76
+ const _tooltip = tooltip ? h(Tooltip, { ...tooltip, disabled: isPermission.value }, { default: () => button}) : undefined
77
+
78
+ // 无权限
79
+ const noPermissionButton = !permission.value ? h(Tooltip, { title: noPermissionTitle || '暂无权限,请联系管理员' }, { default: () => button}) : undefined
80
+
81
+ // 二次确认
82
+ const _popConfirm = popConfirm ?
83
+ h(Popconfirm,
84
+ {
85
+ ...popConfirm,
86
+ disabled: !permission.value || buttonProps.disabled,
87
+ overlayStyle: { width: '220px' }
88
+ }, { default: () => tooltip ? _tooltip : button })
89
+ : undefined
90
+
91
+ if (permission.value) {
92
+ if(hasPopConfirm.value) {
93
+ return _popConfirm
94
+ }
95
+
96
+ if (hasTooltip.value) {
97
+ return _tooltip
98
+ }
99
+
100
+ return button
101
+ }
102
+ return noPermissionButton
103
+ }
104
+ }
105
+ })
106
+
107
+ export default PermissionButton
@@ -0,0 +1,177 @@
1
+ <!-- 参数类型输入组件 -->
2
+ <template>
3
+ <div class="value-item-warp">
4
+ <j-select
5
+ v-if="typeMap.get(itemType) === 'select'"
6
+ v-model:value="myValue"
7
+ allowClear
8
+ @change="(_, options) => onChange(options)"
9
+ v-bind="props"
10
+ />
11
+ <j-date-picker
12
+ v-else-if="typeMap.get(itemType) === 'date'"
13
+ :valueFormat="valueFormat || 'YYYY-MM-DD HH:mm:ss'"
14
+ v-model:value="myValue"
15
+ allowClear
16
+ showTime
17
+ @change="onChange"
18
+ />
19
+ <j-input-number
20
+ v-else-if="typeMap.get(itemType) === 'inputNumber'"
21
+ v-bind="props"
22
+ v-model:value="myValue"
23
+ allowClear
24
+ @change="onChange"
25
+ />
26
+ <j-input
27
+ allowClear
28
+ v-bind="props"
29
+ v-else-if="typeMap.get(itemType) === 'object'"
30
+ v-model:value="myValue"
31
+ @change="onChange"
32
+ >
33
+ <template #addonAfter>
34
+ <AIcon type="FormOutlined" @click="modalVisible = true" />
35
+ </template>
36
+ </j-input>
37
+ <GeoComponent
38
+ v-else-if="typeMap.get(itemType) === 'geoPoint'"
39
+ v-model:point="myValue"
40
+ @change="onChange"
41
+ v-bind="props"
42
+ />
43
+ <j-input
44
+ v-else-if="typeMap.get(itemType) === 'file'"
45
+ v-model:value="myValue"
46
+ placeholder="请输入链接"
47
+ allowClear
48
+ @change="onChange"
49
+ >
50
+ <template #addonAfter>
51
+ <j-upload
52
+ name="file"
53
+ :action="action"
54
+ :headers="headers"
55
+ :showUploadList="false"
56
+ @change="handleFileChange"
57
+ >
58
+ <AIcon type="UploadOutlined" />
59
+ </j-upload>
60
+ </template>
61
+ </j-input>
62
+ <j-input-password
63
+ v-else-if="typeMap.get(itemType) === 'password'"
64
+ allowClear
65
+ v-bind="props"
66
+ type="password"
67
+ v-model:value="myValue"
68
+ @change="onChange"
69
+ />
70
+ <j-input
71
+ v-else
72
+ allowClear
73
+ v-bind="props"
74
+ type="text"
75
+ v-model:value="myValue"
76
+ @change="onChange"
77
+ />
78
+
79
+ <!-- 代码编辑器弹窗 -->
80
+ <j-modal
81
+ title="编辑"
82
+ ok-text="确认"
83
+ cancel-text="取消"
84
+ v-model:visible="modalVisible"
85
+ width="700px"
86
+ @cancel="modalVisible = false"
87
+ @ok="handleItemModalSubmit"
88
+ :zIndex="1100"
89
+ >
90
+ <div style="width: 100%; height: 300px">
91
+ <JMonacoEditor v-model:modelValue="objectValue" />
92
+ </div>
93
+ </j-modal>
94
+ </div>
95
+ </template>
96
+
97
+ <script setup lang="ts">
98
+ import { CSSProperties, PropType, ref, watch } from 'vue'
99
+ import { componentsType } from './util'
100
+
101
+ type Emits = {
102
+ (e: 'update:modelValue', data: string | number | boolean): void
103
+ (e: 'change', data: any, item?: any): void
104
+ }
105
+
106
+ interface ItemProps {
107
+ style?: CSSProperties
108
+ class?: string
109
+ }
110
+
111
+ const emit = defineEmits<Emits>()
112
+
113
+ const props = defineProps({
114
+ // 组件双向绑定的值
115
+ modelValue: {
116
+ type: [Number, String],
117
+ default: '',
118
+ },
119
+ // 组件类型
120
+ itemType: {
121
+ type: String,
122
+ default: () => 'string',
123
+ },
124
+ // 多选框
125
+ mode: {
126
+ type: String as PropType<'multiple' | 'tags' | 'combobox' | ''>,
127
+ default: '',
128
+ },
129
+ placeholder: String,
130
+ options: Array, // 下拉选择框下拉数据
131
+ style: Object as PropType<ItemProps['style']>,
132
+ class: String,
133
+ valueFormat: String,
134
+ action: [String, Promise],
135
+ headers: Object,
136
+ })
137
+
138
+ const typeMap = new Map(Object.entries(componentsType))
139
+
140
+ const myValue = ref<any>(undefined)
141
+ const modalVisible = ref<boolean>(false)
142
+ const objectValue = ref<string>('')
143
+
144
+ const handleItemModalSubmit = () => {
145
+ myValue.value = objectValue.value.replace(/[\r\n]\s*/g, '')
146
+ modalVisible.value = false
147
+ emit('update:modelValue', objectValue.value)
148
+ emit('change', objectValue.value)
149
+ }
150
+
151
+ const onChange = (e) => {
152
+ emit('update:modelValue', myValue.value)
153
+ emit('change', e)
154
+ }
155
+
156
+ const handleFileChange = (info: any) => {
157
+ if (info.file.status === 'done') {
158
+ const url = info.file.response?.result
159
+ myValue.value = url
160
+ emit('update:modelValue', url)
161
+ emit('change', url)
162
+ }
163
+ }
164
+
165
+ watch(
166
+ () => props.modelValue,
167
+ () => {
168
+ myValue.value = props.modelValue
169
+ if (props.itemType === 'object') {
170
+ objectValue.value = props.modelValue as string
171
+ }
172
+ },
173
+ { immediate: true },
174
+ )
175
+ </script>
176
+
177
+ <style lang="less" scoped></style>
@@ -0,0 +1,3 @@
1
+ import ValueItem from './ValueItem.vue'
2
+
3
+ export default ValueItem
@@ -0,0 +1,15 @@
1
+ export const componentsType = {
2
+ int: 'inputNumber',
3
+ long: 'inputNumber',
4
+ float: 'inputNumber',
5
+ double: 'inputNumber',
6
+ string: 'input',
7
+ array: 'input',
8
+ password: 'password',
9
+ enum: 'select',
10
+ boolean: 'select',
11
+ date: 'date',
12
+ object: 'object',
13
+ geoPoint: 'geoPoint',
14
+ file: 'file',
15
+ }