Commit b3bfc3571cd30a20c811ad50f8b01c68a36079c7

Authored by ww
1 parent e06ff2e7

feat: 新增动态接口功能, Icon组件

Showing 50 changed files with 2633 additions and 42 deletions
1 1 # Proxy
2   -VITE_GLOB_PROXY = [["/api", "http://222.180.200.114:48080/api"]]
  2 +# VITE_GLOB_PROXY = [["/api", "http://222.180.200.114:48080/api"]]
  3 +VITE_GLOB_PROXY = [["/api", "http://192.168.10.118:8080/api"]]
3 4
4 5 # Api prefix
5 6 VITE_GLOB_API_URL = /api
... ...
... ... @@ -2,7 +2,9 @@ module.exports = {
2 2 root: true,
3 3 parser: 'vue-eslint-parser',
4 4 globals: {
5   - postMessage: true
  5 + postMessage: true,
  6 + Recordable: 'readonly',
  7 + Nullable: 'readonly',
6 8 },
7 9 parserOptions: {
8 10 parser: '@typescript-eslint/parser',
... ... @@ -31,8 +33,5 @@ module.exports = {
31 33 'vue/multi-word-component-names': 'off',
32 34 'vue/valid-template-root': 'off',
33 35 'vue/no-mutating-props': 'off',
34   - },
35   - global: {
36   - Recordable: 'readonly'
37 36 }
38 37 }
... ...
  1 +import { defHttp } from "@/utils/external/http/axios";
  2 +import { DictItem } from "./model";
  3 +
  4 +enum Api {
  5 + GET_DICT = '/dict_item'
  6 +}
  7 +
  8 +export const getDictItemByCode = (value: string) => {
  9 + return defHttp.post<DictItem[]>({
  10 + url: `${Api.GET_DICT}/find`,
  11 + params: {
  12 + dictCode: value
  13 + }
  14 + })
  15 +}
... ...
  1 +export interface DictItem {
  2 + id: string;
  3 + itemText: string;
  4 + itemValue: string;
  5 + description: string;
  6 + sort: number;
  7 + status: number;
  8 + createTime: string;
  9 + updateTime: string;
  10 +}
... ...
  1 +import { RequestBodyEnum } from "@/enums/httpEnum"
  2 +import { RequestConfigType, RequestGlobalConfigType } from "@/store/modules/chartEditStore/chartEditStore.d"
  3 +import { defHttp } from "@/utils/external/http/axios"
  4 +
  5 +export const isFullUrl = (url = '') => {
  6 + try {
  7 + new URL(url)
  8 + return true
  9 + } catch (error) {
  10 + return false
  11 + }
  12 +}
  13 +
  14 +export const getUrl = (url = '') => {
  15 + const isFullUrlFlag = isFullUrl(url)
  16 + const { origin } = window.location
  17 + return isFullUrlFlag ? new URL(url) : { pathname: url, origin }
  18 +}
  19 +
  20 +export const customRequest = async (request: RequestConfigType) => {
  21 + const { requestHttpType, requestParams, requestParamsBodyType, requestUrl } = request as RequestGlobalConfigType & RequestConfigType
  22 + const { Params, Header, Body } = requestParams
  23 +
  24 + const { origin, pathname } = getUrl(requestUrl!)
  25 +
  26 + const body = Body[requestParamsBodyType as Exclude<'NONE', keyof typeof RequestBodyEnum>]
  27 +
  28 + return defHttp.request<any>({
  29 + url: pathname,
  30 + baseURL: origin,
  31 + method: requestHttpType,
  32 + params: Params,
  33 + data: body,
  34 + headers: Header
  35 + }, {
  36 + joinPrefix: false,
  37 + apiUrl: ''
  38 + })
  39 +}
... ...
  1 + import { defHttp } from '@/utils/external/http/axios';
  2 +import { DeviceAttributesRecord, GetDeviceListParams, PublicInterfaceRecord } from './model';
  3 +import { PaginationResult } from '/#/external/axios';
  4 +
  5 +enum Api {
  6 + PUBLIC_API = '/data_view_interface',
  7 + ORG_LISt = '/organization/me/list',
  8 + DEVICE_PROFILE_LIST = '/device_profile/me/list',
  9 + DEVICE_LIST = '/device/list',
  10 + DEVICE_ATTR_LIST = '/device/attributes',
  11 + GET_PUBLIC_INTERFACE_ALL = '/data_view_interface/find_all_interface',
  12 + GET_PUBLIC_INTERFACE_DETAIL = '/data_view_interface/get_interface_details'
  13 +}
  14 +
  15 +export const getPublicInterface = async (params: Record<'page' | 'pageSize', number>) => {
  16 + return defHttp.get<PaginationResult<PublicInterfaceRecord>>({
  17 + url: Api.PUBLIC_API,
  18 + params
  19 + })
  20 +}
  21 +
  22 +export const getOrgList = async () => {
  23 + return defHttp.get({
  24 + url: Api.ORG_LISt
  25 + })
  26 +}
  27 +
  28 +export const getDeviceProfileList = async (params?: Record<'deviceType', string>) => {
  29 + return defHttp.get({
  30 + url: Api.DEVICE_PROFILE_LIST,
  31 + params
  32 + })
  33 +}
  34 +
  35 +export const getDeviceList = async (params?: GetDeviceListParams) => {
  36 + return defHttp.get({
  37 + url: Api.DEVICE_LIST,
  38 + params
  39 + })
  40 +}
  41 +
  42 +export const getDeviceAttrList = async (deviceProfileId: string) => {
  43 + return defHttp.get<DeviceAttributesRecord[]>({
  44 + url: `${Api.DEVICE_ATTR_LIST}/${deviceProfileId}`
  45 + })
  46 +}
  47 +
  48 +
  49 +export const getDeviceInterfaceDetail = async (interfaces: string[]) => {
  50 + return defHttp.get({
  51 + url: Api.GET_PUBLIC_INTERFACE_DETAIL,
  52 + params: interfaces
  53 + })
  54 +}
  55 +
  56 +export const getAllPublicInterface = async () => {
  57 + return defHttp.get<PublicInterfaceRecord[]>({
  58 + url: Api.GET_PUBLIC_INTERFACE_ALL
  59 + })
  60 +}
... ...
  1 +import { RequestParams as OriginRequestParams } from "@/enums/httpEnum"
  2 +
  3 +export interface RequestParams {
  4 + Body: Recordable
  5 + Header: Recordable
  6 + Params: {
  7 + key: string
  8 + required: boolean
  9 + value: string
  10 + }[]
  11 +}
  12 +
  13 +export interface PublicInterfaceRecord {
  14 + id: string,
  15 + creator: string,
  16 + createTime: string,
  17 + updater: string,
  18 + updateTime: string,
  19 + enabled: boolean,
  20 + tenantId: string,
  21 + interfaceName: string,
  22 + requestContentType: number,
  23 + requestOriginUrl: string,
  24 + requestHttpType: string,
  25 + requestParamsBodyType: string,
  26 + requestUrl: string,
  27 + requestParams: string | OriginRequestParams,
  28 +}
  29 +
  30 +
  31 +export interface GetDeviceListParams {
  32 + organizationId?: string
  33 + deviceProfileId?: string
  34 + deviceType?: string
  35 +}
  36 +
  37 +export interface DeviceAttributesDetail {
  38 + dataType: {
  39 + specs: Recordable
  40 + type: string
  41 + }
  42 +}
  43 +
  44 +export interface DeviceAttributesRecord {
  45 + name: string,
  46 + identifier: string
  47 + detail: DeviceAttributesDetail
  48 +}
... ...
  1 +<script lang="ts" setup>
  2 +import { NForm, NGrid } from 'naive-ui';
  3 +import { computed, reactive, ref, unref, useAttrs } from 'vue';
  4 +import { BasicFormProps, FormSchema } from './types/form';
  5 +import { basicProps } from './props';
  6 +import { useDesign } from '@/hooks/external/useDesign';
  7 +import { dateItemType } from './helper';
  8 +import { dateUtil } from '@/utils/external/dateUtil';
  9 +import { Dayjs } from 'dayjs';
  10 +import FormItem from './components/FormItem.vue';
  11 +import { deepMerge } from '@/utils/external';
  12 +
  13 +const props = defineProps(basicProps)
  14 +
  15 +const attrs = useAttrs()
  16 +
  17 +const fromModel = reactive<Recordable>({})
  18 +
  19 +const defaultValueRef = ref<Recordable>({})
  20 +
  21 +const isInitialDefaultRef = ref(false)
  22 +
  23 +const propsRef = ref<Partial<BasicFormProps>>({})
  24 +
  25 +const schemaRef = ref<Nullable<FormSchema[]>>(null)
  26 +
  27 +const formElRef = ref<Nullable<Recordable>>(null)
  28 +
  29 +const { prefixCls } = useDesign('basic-form')
  30 +
  31 +const getProps = computed(() => {
  32 + return { ...props, ...unref(propsRef) } as BasicFormProps
  33 +})
  34 +
  35 +
  36 +const getBindValue = computed(() => {
  37 + return { ...attrs, ...unref(getProps) } as Recordable
  38 +})
  39 +
  40 +
  41 +const getSchema = computed(() => {
  42 + const schemas = unref(schemaRef) || unref(getProps).schemas
  43 +
  44 + for (const schema of schemas) {
  45 + const { defaultValue, component } = schema
  46 +
  47 + if (defaultValue && dateItemType.includes(component)) {
  48 + if (!Array.isArray(defaultValue)) {
  49 + schema.defaultValue = dateUtil(defaultValue)
  50 + } else {
  51 + const def: Dayjs[] = []
  52 + defaultValue.forEach(item => def.push(dateUtil(item)))
  53 + schema.defaultValue = def
  54 + }
  55 + }
  56 + }
  57 +
  58 + return schemas
  59 +})
  60 +
  61 +async function setProps(params: Partial<BasicFormProps>) {
  62 + propsRef.value = deepMerge(unref(propsRef) || {},)
  63 +}
  64 +
  65 +const setFormModel = (key: string, value: any) => {
  66 + fromModel[key] = value
  67 +
  68 +}
  69 +
  70 +const handleEnterPress = () => {
  71 +
  72 +}
  73 +
  74 +
  75 +</script>
  76 +
  77 +<template>
  78 + <NForm ref="formElRef" :model="fromModel" @keypress.enter="handleEnterPress">
  79 + <NGrid>
  80 + <slot name="formHeader" />
  81 + <template v-for="schema in getSchema" :key="schema.field">
  82 + <FormItem :schema="schema" :formProps="getProps" :allDefaultValues="defaultValueRef" :formModel="fromModel"
  83 + :setFormModel="setFormModel">
  84 + <template #[item]="data" v-for="item in Object.keys($slots)">
  85 + <slot :name="item" v-bind="data" />
  86 + </template>
  87 + </FormItem>
  88 + </template>
  89 + <slot name="formFooter" />
  90 + </NGrid>
  91 + </NForm>
  92 +</template>
  93 +
  94 +<style scoped lang="scss">
  95 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +import { NFormItemGi } from 'naive-ui';
  3 +
  4 +
  5 +</script>
  6 +
  7 +<template>
  8 + <NFormItemGi>
  9 +
  10 + </NFormItemGi>
  11 +</template>
  12 +
  13 +<style lang="scss" scoped>
  14 +
  15 +</style>
... ...
  1 +import { FormComponentTypeEnum } from './types'
  2 +
  3 +const DATE_TYPE = [FormComponentTypeEnum.DATE_PICKER]
  4 +
  5 +
  6 +export const dateItemType = DATE_TYPE
... ...
  1 +import { FormProps } from 'naive-ui'
  2 +import { FormSchema } from './types/form'
  3 +
  4 +const DEFAULT_FORM_PROPS: FormProps = {
  5 + size: 'small',
  6 +
  7 +}
  8 +
  9 +export const basicProps = {
  10 + formProps: {
  11 + type: Object as PropType<FormProps>,
  12 + default: () => DEFAULT_FORM_PROPS
  13 + },
  14 +
  15 + /**
  16 + * @description 表单字段映射成时间
  17 + */
  18 + fieldMapToTime: {
  19 + type: Array as PropType<any[]>,
  20 + default: () => []
  21 + },
  22 +
  23 + /**
  24 + * @description 表单配置
  25 + */
  26 + schemas: {
  27 + type: Array as PropType<FormSchema[]>,
  28 + default: () => []
  29 + },
  30 +
  31 +
  32 +}
... ...
  1 +import { ExtractPropTypes } from "vue";
  2 +import { FormComponentTypeEnum } from ".";
  3 +import { basicProps } from "../props";
  4 +
  5 +export type BasicFormProps = ExtractPropTypes<typeof basicProps>
  6 +
  7 +
  8 +export interface FormSchema {
  9 + field: string
  10 +
  11 + changeEvent?: string
  12 +
  13 + valueField?: string
  14 +
  15 + label: string
  16 +
  17 + component: FormComponentTypeEnum
  18 +
  19 + defaultValue?: any
  20 +}
... ...
  1 +export enum FormComponentTypeEnum {
  2 + DATE_PICKER = 'DatePicker',
  3 +}
... ...
1 1 <script lang="ts" setup>
2   -import { NInput, NPopover, NScrollbar } from 'naive-ui';
  2 +import { NInput, NInputGroup, NInputGroupLabel, NPopover, NScrollbar } from 'naive-ui';
3 3 import '@/assets/external/iconfont/iconfont.js'
4 4 import iconfont from '@/assets/external/iconfont/iconfont.json'
5 5 import SvgIcon from './SvgIcon.vue';
6 6 import { computed, ref, unref } from 'vue';
  7 +import { useDesignStore } from '@/store/modules/designStore/designStore';
7 8
8 9 const props = defineProps({
9 10 value: {
... ... @@ -21,7 +22,11 @@ const getIconList = computed(() => {
21 22 return icons.filter(icon => icon.includes(text))
22 23 })
23 24
  25 +const designStore = useDesignStore()
24 26
  27 +const themeColor = computed(() => {
  28 + return designStore.getAppTheme
  29 +})
25 30
26 31 const handleChange = (icon: string) => {
27 32 emit('update:value', icon)
... ... @@ -30,28 +35,38 @@ const handleChange = (icon: string) => {
30 35 </script>
31 36
32 37 <template>
33   - <NInput :value="value" placeholder="请选择图标" disabled>
34   - <template #suffix>
35   - <NPopover trigger="click">
36   - <NInput v-model:value="searchValue" size="small" />
37   - <NScrollbar style="max-height: 120px; max-width: 210px;">
38   - <ul class="icon-list">
39   - <li class="icon-item" v-for="icon in getIconList" :key="icon" @click="handleChange(icon)"
40   - :style="{ border: icon === props.value ? '1px solid #51d6a9' : '' }">
41   - <SvgIcon :name="icon" prefix="iconfont" />
42   - </li>
43   - </ul>
44   - </NScrollbar>
45   - <template #trigger>
  38 + <NInputGroup class="icon-picker">
  39 + <NInput :value="value" placeholder="请选择图标" disabled />
  40 + <NPopover trigger="click">
  41 + <NInput v-model:value="searchValue" size="small" style="text-overflow: ellipsis;" />
  42 + <NScrollbar style="max-height: 120px; max-width: 210px; margin-top: 5px;">
  43 + <ul class="icon-list">
  44 + <li class="icon-item" v-for="icon in getIconList" :key="icon" @click="handleChange(icon)"
  45 + :style="{ border: icon === props.value ? `1px solid ${themeColor}` : '' }">
  46 + <SvgIcon :name="icon" prefix="iconfont" />
  47 + </li>
  48 + </ul>
  49 + </NScrollbar>
  50 + <template #trigger>
  51 + <NInputGroupLabel style="cursor: pointer;">
46 52 <SvgIcon :name="value || 'grid-one'" prefix="iconfont" style="cursor: pointer;" />
47   - </template>
48   - </NPopover>
49   - </template>
50   - </NInput>
  53 + </NInputGroupLabel>
  54 + </template>
  55 + </NPopover>
  56 + </NInputGroup>
51 57 </template>
52 58
53   -<style lang="scss">
  59 +<style lang="scss" scoped>
54 60 .icon {
  61 + &-picker {
  62 + @include deep() {
  63 + .n-input__input-el {
  64 + text-overflow: ellipsis;
  65 + }
  66 + }
  67 + }
  68 +
  69 +
55 70 &-list {
56 71 display: flex;
57 72 list-style: none;
... ... @@ -70,11 +85,13 @@ const handleChange = (icon: string) => {
70 85 display: flex;
71 86 justify-content: center;
72 87 align-items: center;
  88 + transition: transform .5s linear;
73 89
74 90 &:hover {
75   - border: 1px solid #51d6a9;
  91 + border: 1px solid v-bind("themeColor");
76 92 }
77 93 }
78 94
  95 +
79 96 }
80 97 </style>
... ...
  1 +export enum DictEnum {
  2 + BUILTIN_PARAMS = 'dataview_builtin_params'
  3 +}
... ...
  1 +export { RequestDataTypeEnum } from '../httpEnum'
  2 +
1 3 /**
2 4 * @description: Request result set
3 5 */
... ... @@ -29,3 +31,33 @@ export enum ContentTypeEnum {
29 31 // form-data upload
30 32 FORM_DATA = 'multipart/form-data;charset=UTF-8',
31 33 }
  34 +
  35 +
  36 +// 请求主体类型
  37 +export enum RequestContentTypeEnum {
  38 + // 普通请求
  39 + DEFAULT = 0,
  40 + // SQL请求
  41 + SQL = 1,
  42 + // websocket请求
  43 + WEB_SOCKET = 2
  44 +}
  45 +
  46 +export enum RequestContentTypeNameEnum {
  47 + // 普通请求
  48 + DEFAULT = '普通请求',
  49 + // SQL请求
  50 + SQL = 'SQL请求',
  51 + // websocket请求
  52 + WEB_SOCKET = 'WebSocket请求'
  53 +}
  54 +
  55 +
  56 +export enum RequestDataTypeNameEnum {
  57 + // 静态数据
  58 + STATIC = '静态数据',
  59 + // 请求数据
  60 + AJAX = '自定义请求',
  61 + // 数据池
  62 + Pond = '公共接口'
  63 +}
... ...
  1 +import { ref, toRefs, toRaw } from 'vue'
  2 +import type VChart from 'vue-echarts'
  3 +import { customizeHttp } from '@/api/http'
  4 +import { useChartDataPondFetch } from '@/hooks/'
  5 +import { CreateComponentType, ChartFrameEnum } from '@/packages/index.d'
  6 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  7 +import { RequestDataTypeEnum } from '@/enums/httpEnum'
  8 +import { isPreview, newFunctionHandle, intervalUnitHandle } from '@/utils'
  9 +import { setOption } from '@/packages/public/chart'
  10 +
  11 +// 获取类型
  12 +type ChartEditStoreType = typeof useChartEditStore
  13 +
  14 +/**
  15 + * setdata 数据监听与更改
  16 + * @param targetComponent
  17 + * @param useChartEditStore 若直接引会报错,只能动态传递
  18 + * @param updateCallback 自定义更新函数
  19 + */
  20 +export const useChartDataFetch = (
  21 + targetComponent: CreateComponentType,
  22 + useChartEditStore: ChartEditStoreType,
  23 + updateCallback?: (...args: any) => any
  24 +) => {
  25 + const vChartRef = ref<typeof VChart | null>(null)
  26 + let fetchInterval: any = 0
  27 +
  28 + // 数据池
  29 + const { addGlobalDataInterface } = useChartDataPondFetch()
  30 +
  31 + // 组件类型
  32 + const { chartFrame } = targetComponent.chartConfig
  33 +
  34 + // eCharts 组件配合 vChart 库更新方式
  35 + const echartsUpdateHandle = (dataset: any) => {
  36 + if (chartFrame === ChartFrameEnum.ECHARTS) {
  37 + if (vChartRef.value) {
  38 + setOption(vChartRef.value, { dataset: dataset })
  39 + }
  40 + }
  41 + }
  42 +
  43 + const requestIntervalFn = () => {
  44 + const chartEditStore = useChartEditStore()
  45 +
  46 + // 全局数据
  47 + const {
  48 + requestOriginUrl,
  49 + requestIntervalUnit: globalUnit,
  50 + requestInterval: globalRequestInterval
  51 + } = toRefs(chartEditStore.getRequestGlobalConfig)
  52 +
  53 + // 目标组件
  54 + const {
  55 + requestDataType,
  56 + requestUrl,
  57 + requestIntervalUnit: targetUnit,
  58 + requestInterval: targetInterval
  59 + } = toRefs(targetComponent.request)
  60 +
  61 + // 非请求类型
  62 + if (requestDataType.value !== RequestDataTypeEnum.AJAX) return
  63 +
  64 + try {
  65 + // 处理地址
  66 + if (requestUrl?.value) {
  67 + // requestOriginUrl 允许为空
  68 + const completePath = requestOriginUrl && requestOriginUrl.value + requestUrl.value
  69 + if (!completePath) return
  70 +
  71 + clearInterval(fetchInterval)
  72 +
  73 + const fetchFn = async () => {
  74 + const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.getRequestGlobalConfig))
  75 + if (res) {
  76 + try {
  77 + const filter = targetComponent.filter
  78 + echartsUpdateHandle(newFunctionHandle(res?.data, res, filter))
  79 + // 更新回调函数
  80 + if (updateCallback) {
  81 + updateCallback(newFunctionHandle(res?.data, res, filter))
  82 + }
  83 + } catch (error) {
  84 + console.error(error)
  85 + }
  86 + }
  87 + }
  88 +
  89 + // 立即调用
  90 + fetchFn()
  91 + // 定时时间
  92 + const time = targetInterval && targetInterval.value ? targetInterval.value : globalRequestInterval.value
  93 + // 单位
  94 + const unit = targetInterval && targetInterval.value ? targetUnit.value : globalUnit.value
  95 + // 开启轮询
  96 + if (time) fetchInterval = setInterval(fetchFn, intervalUnitHandle(time, unit))
  97 + }
  98 + // eslint-disable-next-line no-empty
  99 + } catch (error) {
  100 + console.log(error)
  101 + }
  102 + }
  103 +
  104 + if (isPreview()) {
  105 + // 判断是否是数据池类型
  106 + targetComponent.request.requestDataType === RequestDataTypeEnum.Pond
  107 + ? addGlobalDataInterface(targetComponent, useChartEditStore, updateCallback || echartsUpdateHandle)
  108 + : requestIntervalFn()
  109 + }
  110 + return { vChartRef }
  111 +}
... ...
... ... @@ -55,3 +55,5 @@ export enum IconDynamicEffectNameEnum {
55 55 JUMP = '跳动',
56 56 SPIN = '旋转'
57 57 }
  58 +
  59 +
\ No newline at end of file
... ...
... ... @@ -4,9 +4,10 @@ import { ConfigType, PackagesCategoryEnum } from '@/packages/index.d'
4 4 import { ClockConfig } from '@/packages/components/external/Decorates/Mores/Icon'
5 5
6 6 export function useInjectLib(packagesList: EPackagesType) {
  7 +
7 8 packagesList[EPackagesCategoryEnum.COMPOSES] = ComposesList
8 9
9   - // addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.DECORATES, ClockConfig)
  10 + addWidgetToCategoryByCategoryName(packagesList, PackagesCategoryEnum.DECORATES, ClockConfig)
10 11 }
11 12
12 13 /**
... ...
  1 +import { RequestBodyEnum, RequestContentTypeEnum, RequestDataTypeEnum, RequestHttpEnum } from "@/enums/httpEnum";
  2 +import { CreateComponentType } from "@/packages/index.d";
  3 +
  4 +
  5 +export interface DynamicRequest {
  6 + // 所选全局数据池的对应 id
  7 + requestDataPondId?: string
  8 + // 组件定制轮询时间
  9 + requestInterval?: number
  10 + // 获取数据的方式
  11 + requestDataType: RequestDataTypeEnum
  12 + // 请求方式 get/post/del/put/patch
  13 + requestHttpType: RequestHttpEnum
  14 + // 源后续的 url
  15 + requestUrl?: string
  16 + // 请求内容主体方式 普通/sql
  17 + requestContentType: RequestContentTypeEnum
  18 + // 请求体类型
  19 + requestParamsBodyType: RequestBodyEnum
  20 + // SQL 请求对象
  21 + requestSQLContent: {
  22 + sql: string
  23 + }
  24 +}
  25 +
  26 +export interface ECreateComponentType extends CreateComponentType {
  27 + test: 123
  28 +}
... ...
  1 +export enum DynamicRequestStoreEnum {
  2 + // 组件id
  3 + ID = 'id',
  4 + // 请求源地址
  5 + REQUEST_ORIGIN_URL = 'requestOriginUrl',
  6 + // 请求间隔时间
  7 + REQUEST_INTERVAL = 'requestInterval',
  8 + // 请求单位
  9 + REQUEST_INTERVAL_UNIT = 'requestIntervalUnit',
  10 + // 请求头
  11 + REQUEST_HEADER = 'requestHeader'
  12 +}
  13 +
  14 +export interface DynamicRequestStoreType {
  15 +
  16 +}
... ...
  1 + import { defineStore } from "pinia";
  2 +
  3 +export const useDynamicRequest = defineStore({
  4 + id: 'useDynamicRequest',
  5 + state: () => ({
  6 +
  7 + }),
  8 + getters: {
  9 +
  10 + },
  11 + actions: {
  12 +
  13 + }
  14 +})
... ...
  1 +import dayjs, { ConfigType } from 'dayjs';
  2 +
  3 +export const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
  4 +export const DATE_FORMAT = 'YYYY-MM-DD ';
  5 +
  6 +export function formatToDateTime(
  7 + date: ConfigType = undefined,
  8 + format = DATE_TIME_FORMAT
  9 +): string {
  10 + return dayjs(date).format(format);
  11 +}
  12 +
  13 +export function formatToDate(date: ConfigType = undefined, format = DATE_FORMAT): string {
  14 + return dayjs(date).format(format);
  15 +}
  16 +
  17 +export const dateUtil = dayjs;
... ...
... ... @@ -45,7 +45,7 @@ const transform: AxiosTransform = {
45 45 if (apiUrl && isString(apiUrl)) {
46 46 config.url = `${apiUrl}${config.url}`;
47 47 }
48   -
  48 +
49 49 const params = config.params || {};
50 50 const data = config.data || false;
51 51 formatDate && data && !isString(data) && formatRequestDate(data);
... ... @@ -143,7 +143,7 @@ const transform: AxiosTransform = {
143 143 }
144 144 } catch (error: any) {
145 145 throw new Error(error);
146   - }
  146 + }
147 147 checkStatus(error?.response?.status, msg, errorMessageMode);
148 148 return Promise.reject(response?.data);
149 149 },
... ... @@ -197,9 +197,3 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
197 197 }
198 198 export const defHttp = createAxios();
199 199
200   -// other api url
201   -export const otherHttp = createAxios({
202   - requestOptions: {
203   - apiUrl: 'xxx',
204   - },
205   -});
... ...
... ... @@ -89,3 +89,5 @@ export const withInstall = <T extends Component>(component: T, alias?: string) =
89 89 };
90 90 return component as T & Plugin;
91 91 };
  92 +
  93 +
\ No newline at end of file
... ...
  1 +import { inject, InjectionKey, provide, reactive, readonly as defineReadonly } from 'vue'
  2 +
  3 +export interface CreateContextOptions {
  4 + readonly?: boolean
  5 + createProvider?: boolean
  6 + native?: boolean
  7 +}
  8 +
  9 +export function createContext<T>(context: any, key: InjectionKey<T> = Symbol(), options: CreateContextOptions = {}) {
  10 + const { readonly, createProvider, native } = options
  11 +
  12 + const state = reactive(context)
  13 +
  14 + const provideData = readonly ? defineReadonly(state) : state
  15 +
  16 + !createProvider && provide(key, native ? context : provideData)
  17 +
  18 + return {
  19 + state
  20 + }
  21 +}
  22 +
  23 +export function useContext<T>(key: InjectionKey<T> = Symbol(), defaultValue?: any) {
  24 + return inject(key, defaultValue || {})
  25 +}
... ...
  1 +import ChartDataMatchingAndShow from './index.vue'
  2 +
  3 +export { ChartDataMatchingAndShow }
... ...
  1 +<template>
  2 + <NTimeline class="go-chart-configurations-timeline">
  3 + <NTimelineItem v-show="isCharts && dimensionsAndSource" type="info" :title="TimelineTitleEnum.MAPPING">
  4 + <n-table striped>
  5 + <thead>
  6 + <tr>
  7 + <th v-for="item in tableTitle" :key="item">{{ item }}</th>
  8 + </tr>
  9 + </thead>
  10 + <tbody>
  11 + <tr v-for="(item, index) in dimensionsAndSource" :key="index">
  12 + <td>{{ item.field }}</td>
  13 + <td>{{ item.mapping }}</td>
  14 + <td>
  15 + <NSpace v-if="item.result === 0">
  16 + <NBadge dot type="success"></NBadge>
  17 + <NText>无</NText>
  18 + </NSpace>
  19 + <NSpace v-else>
  20 + <NBadge dot :type="item.result === 1 ? 'success' : 'error'"></NBadge>
  21 + <NText>匹配{{ item.result === 1 ? '成功' : '失败' }}</NText>
  22 + </NSpace>
  23 + </td>
  24 + </tr>
  25 + </tbody>
  26 + </n-table>
  27 + </NTimelineItem>
  28 + <NTimelineItem v-show="filterShow" color="#97846c" :title="TimelineTitleEnum.FILTER">
  29 + <NSpace :size="18" vertical>
  30 + <NText depth="3">过滤器默认处理接口返回值的「data」字段</NText>
  31 + <ChartDataMonacoEditor></ChartDataMonacoEditor>
  32 + </NSpace>
  33 + </NTimelineItem>
  34 + <NTimelineItem type="success" :title="TimelineTitleEnum.CONTENT">
  35 + <NSpace vertical>
  36 + <NSpace class="source-btn-box">
  37 + <NUpload v-model:file-list="uploadFileListRef" :show-file-list="false" :customRequest="customRequest"
  38 + @before-upload="beforeUpload">
  39 + <NSpace>
  40 + <NButton v-if="!ajax" class="sourceBtn-item" :disabled="noData">
  41 + <template #icon>
  42 + <NIcon>
  43 + <document-add-icon />
  44 + </NIcon>
  45 + </template>
  46 + 导入(json / txt)
  47 + </NButton>
  48 + </NSpace>
  49 + </NUpload>
  50 + <div>
  51 + <NButton class="sourceBtn-item" :disabled="noData" @click="download">
  52 + <template #icon>
  53 + <NIcon>
  54 + <document-download-icon />
  55 + </NIcon>
  56 + </template>
  57 + 下载
  58 + </NButton>
  59 + <n-tooltip trigger="hover">
  60 + <template #trigger>
  61 + <NIcon class="go-ml-1" size="21" :depth="3">
  62 + <help-outline-icon></help-outline-icon>
  63 + </NIcon>
  64 + </template>
  65 + <NText depth="3">点击【下载】查看完整数据</NText>
  66 + </n-tooltip>
  67 + </div>
  68 + </NSpace>
  69 + <NCard size="small">
  70 + <NScrollbar style="max-height: 400px;">
  71 + <NCode :code="toString(source)" language="json"></NCode>
  72 + </NScrollbar>
  73 + </NCard>
  74 + </NSpace>
  75 + </NTimelineItem>
  76 + </NTimeline>
  77 +</template>
  78 +
  79 +<script setup lang="ts">
  80 +import { ref, computed, watch } from 'vue'
  81 +import { ChartFrameEnum } from '@/packages/index.d'
  82 +import { RequestDataTypeEnum } from '@/enums/httpEnum'
  83 +import { icon } from '@/plugins'
  84 +import isObject from 'lodash/isObject'
  85 +import { toString, isArray } from '@/utils'
  86 +import { useTargetData } from '../../../../hooks/useTargetData.hook'
  87 +import { DataResultEnum, TimelineTitleEnum } from '../../../index.d'
  88 +import { useFile } from '../../../hooks/useFile.hooks'
  89 +import { ChartDataMonacoEditor } from '../ChartDataMonacoEditor'
  90 +import { NBadge, NButton, NCard, NCode, NIcon, NScrollbar, NSpace, NText, NTimeline, NTimelineItem, NUpload } from 'naive-ui'
  91 +
  92 +const { targetData } = useTargetData()
  93 +const props = defineProps({
  94 + show: {
  95 + type: Boolean,
  96 + required: false
  97 + },
  98 + ajax: {
  99 + type: Boolean,
  100 + required: true
  101 + }
  102 +})
  103 +
  104 +// 表格标题
  105 +const tableTitle = ['字段', '映射', '状态']
  106 +
  107 +const { HelpOutlineIcon } = icon.ionicons5
  108 +const { DocumentAddIcon, DocumentDownloadIcon } = icon.carbon
  109 +
  110 +const source = ref()
  111 +const dimensions = ref()
  112 +const dimensionsAndSource = ref()
  113 +const noData = ref(false)
  114 +
  115 +const { uploadFileListRef, customRequest, beforeUpload, download } = useFile(targetData)
  116 +
  117 +// 是否展示过滤器
  118 +const filterShow = computed(() => {
  119 + return targetData.value.request.requestDataType !== RequestDataTypeEnum.STATIC
  120 +})
  121 +
  122 +// 是支持 dataset 的图表类型
  123 +const isCharts = computed(() => {
  124 + return targetData.value.chartConfig.chartFrame === ChartFrameEnum.ECHARTS
  125 +})
  126 +
  127 +// 处理映射列表状态结果
  128 +const matchingHandle = (mapping: string) => {
  129 + let res = DataResultEnum.SUCCESS
  130 + for (let i = 0; i < source.value.length; i++) {
  131 + if (source.value[i][mapping] === undefined) {
  132 + res = DataResultEnum.FAILURE
  133 + return res
  134 + }
  135 + }
  136 + return DataResultEnum.SUCCESS
  137 +}
  138 +
  139 +// 处理映射列表
  140 +const dimensionsAndSourceHandle = () => {
  141 + try {
  142 + // 去除首项数据轴标识
  143 + return dimensions.value.map((dimensionsItem: string, index: number) => {
  144 + return index === 0
  145 + ? {
  146 + // 字段
  147 + field: '通用标识',
  148 + // 映射
  149 + mapping: dimensionsItem,
  150 + // 结果
  151 + result: DataResultEnum.NULL
  152 + }
  153 + : {
  154 + field: `数据项-${index}`,
  155 + mapping: dimensionsItem,
  156 + result: matchingHandle(dimensionsItem)
  157 + }
  158 + })
  159 + } catch (error) {
  160 + return []
  161 + }
  162 +}
  163 +
  164 +watch(
  165 + () => targetData.value?.option?.dataset,
  166 + (
  167 + newData?: {
  168 + source: any
  169 + dimensions: any
  170 + } | null
  171 + ) => {
  172 + if (newData && targetData?.value?.chartConfig?.chartFrame === ChartFrameEnum.ECHARTS) {
  173 + // 只有 DataSet 数据才有对应的格式
  174 + source.value = newData
  175 + if (isCharts.value) {
  176 + dimensions.value = newData.dimensions
  177 + dimensionsAndSource.value = dimensionsAndSourceHandle()
  178 + }
  179 + } else if (newData !== undefined && newData !== null) {
  180 + dimensionsAndSource.value = null
  181 + source.value = newData
  182 + } else {
  183 + noData.value = true
  184 + source.value = '此组件无数据源'
  185 + }
  186 + if (isArray(newData)) {
  187 + dimensionsAndSource.value = null
  188 + }
  189 + },
  190 + {
  191 + immediate: true
  192 + }
  193 +)
  194 +</script>
  195 +
  196 +<style lang="scss" scoped>
  197 +@include go('chart-configurations-timeline') {
  198 + @include deep() {
  199 + pre {
  200 + white-space: pre-wrap;
  201 + word-wrap: break-word;
  202 + }
  203 + }
  204 +
  205 + .source-btn-box {
  206 + margin-top: 10px !important;
  207 + }
  208 +}
  209 +</style>
... ...
  1 +import ChartDataMonacoEditor from './index.vue'
  2 +
  3 +export { ChartDataMonacoEditor }
... ...
  1 +<template>
  2 + <template v-if="targetData.filter">
  3 + <NCard>
  4 + <p><span class="func-keyword">function</span>&nbsp;&nbsp;filter(res)&nbsp;&nbsp;{</p>
  5 + <!-- 函数体 -->
  6 + <div class="go-ml-4">
  7 + <NCode :code="targetData.filter" language="typescript"></NCode>
  8 + </div>
  9 + <p>}</p>
  10 + <template #footer>
  11 + <NSpace justify="end">
  12 + <NButton type="primary" tertiary size="small" @click="addFilter">
  13 + <template #icon>
  14 + <NIcon>
  15 + <filter-edit-icon />
  16 + </NIcon>
  17 + </template>
  18 + 编辑
  19 + </NButton>
  20 + <NButton tertiary size="small" @click="delFilter"> 删除 </NButton>
  21 + </NSpace>
  22 + </template>
  23 + </NCard>
  24 + </template>
  25 + <template v-else>
  26 + <NButton class="go-ml-3" @click="addFilter">
  27 + <template #icon>
  28 + <NIcon>
  29 + <filter-icon />
  30 + </NIcon>
  31 + </template>
  32 + 新增过滤器
  33 + </NButton>
  34 + </template>
  35 +
  36 + <!-- 弹窗 -->
  37 + <NModal class="go-chart-data-monaco-editor" v-model:show="showModal" :mask-closable="false" :closeOnEsc="false">
  38 + <NCard :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 600px">
  39 + <template #header>
  40 + <NSpace>
  41 + <NText>过滤器函数编辑器</NText>
  42 + </NSpace>
  43 + </template>
  44 + <template #header-extra> </template>
  45 + <NSpace size="small" vertical>
  46 + <NSpace justify="space-between">
  47 + <div>
  48 + <NSpace vertical>
  49 + <NTag type="info">
  50 + <span class="func-keyword">function</span>&nbsp;&nbsp;filter(res)&nbsp;&nbsp;{
  51 + </NTag>
  52 + <monaco-editor v-model:modelValue="filter" width="460px" height="380px" language="javascript" />
  53 + <NTag type="info">}</NTag>
  54 + </NSpace>
  55 + </div>
  56 + <NDivider vertical style="height: 480px" />
  57 + <NScrollbar style="max-height: 480px">
  58 + <NSpace :size="15" vertical>
  59 + <!-- <div class="editor-data-show"> -->
  60 + <!-- <NSpace>
  61 + <NText depth="3">默认过滤数据(data):</NText>
  62 + <NCode :code="toString(sourceData?.data) || '暂无'" language="json" :word-wrap="true"></NCode>
  63 + </NSpace> -->
  64 + <!-- </div> -->
  65 + <div class="editor-data-show">
  66 + <NSpace>
  67 + <NText depth="3">接口返回数据(res):</NText>
  68 + <NScrollbar style="max-height: 300px;">
  69 + <NCode :code="toString(sourceData) || '暂无'" language="json" :word-wrap="true"></NCode>
  70 + </NScrollbar>
  71 + </NSpace>
  72 + </div>
  73 + <div class="editor-data-show">
  74 + <NSpace>
  75 + <NText depth="3">过滤器结果:</NText>
  76 + <NCode :code="filterRes || '暂无'" language="json" :word-wrap="true"></NCode>
  77 + </NSpace>
  78 + </div>
  79 + </NSpace>
  80 + </NScrollbar>
  81 + </NSpace>
  82 + </NSpace>
  83 + <template #action>
  84 + <NSpace justify="space-between">
  85 + <div class="go-flex-items-center">
  86 + <NTag :bordered="false" type="primary">
  87 + <template #icon>
  88 + <NIcon :component="DocumentTextIcon" />
  89 + </template>
  90 + 规则
  91 + </NTag>
  92 + <NText class="go-ml-2" depth="2">过滤器默认处理接口返回值的「data」字段</NText>
  93 + </div>
  94 +
  95 + <NSpace>
  96 + <NButton size="medium" @click="closeFilter">取消</NButton>
  97 + <NButton size="medium" type="primary" @click="saveFilter">保存</NButton>
  98 + </NSpace>
  99 + </NSpace>
  100 + </template>
  101 + </NCard>
  102 + </NModal>
  103 +</template>
  104 +
  105 +<script lang="ts" setup>
  106 +import { ref, computed, watch, toRaw } from 'vue'
  107 +import { MonacoEditor } from '@/components/Pages/MonacoEditor'
  108 +import { icon } from '@/plugins'
  109 +import { goDialog, toString } from '@/utils'
  110 +import cloneDeep from 'lodash/cloneDeep'
  111 +import { useTargetData } from '../../../../hooks/useTargetData.hook'
  112 +import { customRequest } from '@/api/external/customRequest'
  113 +import { NButton, NCard, NCode, NDivider, NIcon, NModal, NScrollbar, NSpace, NTag, NText } from 'naive-ui'
  114 +
  115 +const { DocumentTextIcon } = icon.ionicons5
  116 +const { FilterIcon, FilterEditIcon } = icon.carbon
  117 +const { targetData } = useTargetData()
  118 +
  119 +// 受控弹窗
  120 +const showModal = ref(false)
  121 +// filter 函数模板
  122 +const filter = ref(targetData.value.filter || `return res`)
  123 +// 过滤错误标识
  124 +const errorFlag = ref(false)
  125 +// 目标静态/接口数据
  126 +const sourceData = ref<any>('')
  127 +
  128 +// 动态获取数据
  129 +const fetchTargetData = async () => {
  130 + try {
  131 + const res = await customRequest(toRaw(targetData.value.request))
  132 + if (res) {
  133 + sourceData.value = res
  134 + return
  135 + }
  136 + window['$message'].warning('没有拿到返回值,请检查接口!')
  137 + } catch (error) {
  138 + console.error(error);
  139 + window['$message'].warning('数据异常,请检查参数!')
  140 + }
  141 +}
  142 +
  143 +// 过滤结果
  144 +const filterRes = computed(() => {
  145 + try {
  146 + const fn = new Function('data', 'res', filter.value)
  147 + const response = cloneDeep(sourceData.value)
  148 + const res = fn(response?.data, response)
  149 + // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  150 + errorFlag.value = false
  151 + return toString(res)
  152 + } catch (error) {
  153 + // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  154 + errorFlag.value = true
  155 + return `过滤函数错误,日志:${error}`
  156 + }
  157 +})
  158 +
  159 +// 新增过滤器
  160 +const addFilter = () => {
  161 + showModal.value = true
  162 +}
  163 +
  164 +// 删除过滤器
  165 +const delFilter = () => {
  166 + goDialog({
  167 + message: '是否删除过滤器',
  168 + onPositiveCallback: () => {
  169 + targetData.value.filter = undefined
  170 + }
  171 + })
  172 +}
  173 +
  174 +// 关闭过滤器
  175 +const closeFilter = () => {
  176 + showModal.value = false
  177 +}
  178 +
  179 +// 新增过滤器
  180 +const saveFilter = () => {
  181 + if (errorFlag.value) {
  182 + window['$message'].error('过滤函数错误,无法进行保存')
  183 + return
  184 + }
  185 + targetData.value.filter = filter.value
  186 + closeFilter()
  187 +}
  188 +
  189 +watch(
  190 + () => showModal.value,
  191 + (newData: boolean) => {
  192 + if (newData) {
  193 + fetchTargetData()
  194 + filter.value = targetData.value.filter || `return res`
  195 + }
  196 + }
  197 +)
  198 +</script>
  199 +
  200 +<style lang="scss" scoped>
  201 +.func-keyword {
  202 + color: #b478cf;
  203 +}
  204 +
  205 +@include go('chart-data-monaco-editor') {
  206 +
  207 + &.NCard.NModal,
  208 + .NCard {
  209 + @extend .go-background-filter;
  210 + }
  211 +
  212 + .editor-data-show {
  213 + @include fetch-bg-color('filter-color');
  214 + width: 420px;
  215 + padding: 20px;
  216 + border-radius: 5px;
  217 + }
  218 +}
  219 +</style>
... ...
  1 +<script setup lang="ts">
  2 +import { ref, computed, onBeforeUnmount, watchEffect, toRaw, unref } from 'vue'
  3 +import { icon } from '@/plugins'
  4 +import { useDesignStore } from '@/store/modules/designStore/designStore'
  5 +import { SettingItemBox } from '@/components/Pages/ChartItemSetting'
  6 +import { RequestDataTypeEnum } from '@/enums/external/httpEnum'
  7 +import { ChartDataMatchingAndShow } from '../../../external/components/ChartDataMatchingAndShow'
  8 +import { newFunctionHandle } from '@/utils'
  9 +import { useTargetData } from '../../../../hooks/useTargetData.hook'
  10 +import { NButton, NSelect, NTooltip, NIcon, SelectOption } from 'naive-ui'
  11 +import { RequestInfoPanel } from '../RequestInfoPanel'
  12 +import { RequestModal } from '../RequestModal'
  13 +import { customRequest } from '@/api/external/customRequest'
  14 +
  15 +const { HelpOutlineIcon, FlashIcon } = icon.ionicons5
  16 +const { targetData } = useTargetData()
  17 +
  18 +const requestModal = ref<InstanceType<typeof RequestModal>>()
  19 +
  20 +const designStore = useDesignStore()
  21 +
  22 +
  23 +const selectedRequestType = ref(targetData.value.request.requestDataType || RequestDataTypeEnum.AJAX)
  24 +
  25 +const getApiRequestType: SelectOption[] = [
  26 + { label: '自定义请求', value: RequestDataTypeEnum.AJAX },
  27 + { label: '公共接口', value: RequestDataTypeEnum.Pond }
  28 +]
  29 +
  30 +// 是否展示数据分析
  31 +const loading = ref(false)
  32 +const showMatching = ref(false)
  33 +
  34 +let firstFocus = 0
  35 +let lastFilter: any = undefined
  36 +
  37 +// 发送请求
  38 +const sendHandle = async () => {
  39 + if (!targetData.value?.request) return
  40 +
  41 + loading.value = true
  42 + try {
  43 + console.log('enter')
  44 + const res = await customRequest(toRaw(targetData.value.request))
  45 + loading.value = false
  46 + if (res) {
  47 + targetData.value.option.dataset = newFunctionHandle(res, res, targetData.value.filter)
  48 + showMatching.value = true
  49 + return
  50 + }
  51 + window['$message'].warning('数据异常,请检查参数!')
  52 + } catch (error) {
  53 + loading.value = false
  54 + window['$message'].warning('数据异常,请检查参数!')
  55 + }
  56 +}
  57 +
  58 +// 颜色
  59 +const themeColor = computed(() => {
  60 + return designStore.getAppTheme
  61 +})
  62 +
  63 +const handleClickPanel = () => {
  64 + unref(requestModal)?.openModal(true, unref(selectedRequestType))
  65 +}
  66 +
  67 +watchEffect(() => {
  68 + const filter = targetData.value?.filter
  69 + if (lastFilter !== filter && firstFocus) {
  70 + lastFilter = filter
  71 + sendHandle()
  72 + }
  73 + firstFocus++
  74 +})
  75 +
  76 +onBeforeUnmount(() => {
  77 + lastFilter = null
  78 +})
  79 +
  80 +
  81 +</script>
  82 +
  83 +<template>
  84 + <div class="things-kit-chart-configurations-data-ajax">
  85 + <SettingItemBox name="接口方式" :alone="true">
  86 + <NSelect v-model:value="selectedRequestType" :options="getApiRequestType" />
  87 + </SettingItemBox>
  88 +
  89 + <RequestInfoPanel @clickPanel="handleClickPanel" />
  90 +
  91 + <SettingItemBox :alone="true">
  92 + <template #name>
  93 + 测试
  94 + <NTooltip trigger="hover">
  95 + <template #trigger>
  96 + <NIcon size="21" :depth="3">
  97 + <HelpOutlineIcon />
  98 + </NIcon>
  99 + </template>
  100 + 默认赋值给 dataset 字段
  101 + </NTooltip>
  102 + </template>
  103 + <NButton type="primary" ghost @click="sendHandle">
  104 + <template #icon>
  105 + <NIcon>
  106 + <FlashIcon />
  107 + </NIcon>
  108 + </template>
  109 + 发送请求
  110 + </NButton>
  111 + </SettingItemBox>
  112 +
  113 + <!-- 底部数据展示 -->
  114 + <ChartDataMatchingAndShow :show="showMatching && !loading" :ajax="true"></ChartDataMatchingAndShow>
  115 +
  116 + <!-- 骨架图 -->
  117 + <go-skeleton :load="loading" :repeat="3"></go-skeleton>
  118 +
  119 + <RequestModal ref="requestModal" />
  120 + </div>
  121 +</template>
  122 +
  123 +
  124 +
  125 +<style lang="scss" scoped>
  126 +@include thingsKit('chart-configurations-data-ajax') {
  127 + .n-card-shallow {
  128 + &.n-card {
  129 + @extend .go-background-filter;
  130 +
  131 + @include deep() {
  132 + .n-card__content {
  133 + padding: 10px;
  134 + }
  135 + }
  136 + }
  137 +
  138 + .edit-text {
  139 + position: absolute;
  140 + top: 0px;
  141 + left: 0px;
  142 + width: 325px;
  143 + height: 270px;
  144 + cursor: pointer;
  145 + opacity: 0;
  146 + transition: all 0.3s;
  147 + @extend .go-background-filter;
  148 + backdrop-filter: blur(2px) !important;
  149 + }
  150 +
  151 + &:hover {
  152 + border-color: v-bind('themeColor');
  153 +
  154 + .edit-text {
  155 + opacity: 1;
  156 + }
  157 + }
  158 + }
  159 +}
  160 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +import { getAllPublicInterface, getPublicInterface } from '@/api/external/dynamicRequest'
  3 +import { PublicInterfaceRecord } from '@/api/external/dynamicRequest/model';
  4 +import { SettingItem, SettingItemBox } from '@/components/Pages/ChartItemSetting';
  5 +import { RequestContentTypeEnum, RequestContentTypeNameEnum } from '@/enums/external/httpEnum';
  6 +import { RequestBodyEnum, RequestHttpEnum, RequestHttpIntervalEnum, RequestParams, RequestParamsTypeEnum } from '@/enums/httpEnum';
  7 +import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d';
  8 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
  9 +import { NForm, NCard, NDatePicker, NEmpty, NFormItem, NInput, NInputGroup, NInputNumber, NScrollbar, NSelect, NSpin, NTabPane, NTabs, NTreeSelect, SelectOption, FormInst, NButton, FormItemInst } from 'naive-ui';
  10 +import { ref, reactive, onMounted, computed, unref } from 'vue'
  11 +import { selectTimeOptions, selectTypeOptions } from '../../../index.d';
  12 +import ParamsTable from '../RequestModal/ParamsTable.vue';
  13 +import RequestBody from '../RequestModal/RequestBody.vue';
  14 +import { ComponentType, useDynamicPublicForm } from './useDynamicPublicForm';
  15 +import { transferData } from './utils';
  16 +
  17 +const componentMap: { [key in ComponentType]?: any } = {
  18 + [ComponentType.SELECT_TREE]: NTreeSelect,
  19 + [ComponentType.SELECT]: NSelect,
  20 + [ComponentType.INPUT]: NInput,
  21 + [ComponentType.DATE_PICKER]: NDatePicker
  22 +}
  23 +const publicInterfaceList = ref<PublicInterfaceRecord[]>([])
  24 +
  25 +const selectedPublicInterface = ref<string>()
  26 +
  27 +const toJSON = (string: string) => {
  28 + try {
  29 + return JSON.parse(string)
  30 + } catch (error) {
  31 + return {}
  32 + }
  33 +}
  34 +
  35 +const getSelectedInterface = computed(() => {
  36 + const record = unref(publicInterfaceList).find(item => item.id === unref(selectedPublicInterface))! || {}
  37 + const _record = JSON.parse(JSON.stringify(record))
  38 + _record.requestParams = toJSON(_record.requestParams)
  39 + return _record
  40 +})
  41 +
  42 +const getPublicInterfaceList = async () => {
  43 + if (unref(publicInterfaceList).length) return
  44 + const result = await getAllPublicInterface()
  45 + publicInterfaceList.value = result
  46 +}
  47 +
  48 +const handleFilter = (query: string, option: SelectOption) => {
  49 + return ((option as Recordable).interfaceName).includes(query)
  50 +}
  51 +
  52 +const { getDynamicFormSchemas, params, createForm, validFlag, setDynamicFormValue } = useDynamicPublicForm(getSelectedInterface)
  53 +
  54 +const getConfigurationData = () => {
  55 + const value = transferData(unref(getSelectedInterface), unref(getSelectedInterface).requestParams as unknown as RequestParams, params)
  56 + return value
  57 +}
  58 +
  59 +const requestHttpTypeRef = ref<RequestHttpEnum>()
  60 +
  61 +const requestIntervalValueRef = ref<number>(30)
  62 +
  63 +const requestIntervalUnitRef = ref<RequestHttpIntervalEnum>(RequestHttpIntervalEnum.SECOND)
  64 +
  65 +const requestContentTypeRef = ref<RequestContentTypeEnum>()
  66 +
  67 +const requestParamsTypeRef = ref<RequestParamsTypeEnum>(RequestParamsTypeEnum.PARAMS)
  68 +
  69 +const requestParams = ref<RequestParams>({ Body: { [RequestBodyEnum.FORM_DATA]: {}, [RequestBodyEnum.JSON]: '', [RequestBodyEnum.XML]: '', [RequestBodyEnum.X_WWW_FORM_URLENCODED]: {} }, Header: {}, Params: {} })
  70 +
  71 +const requestParamsBodyTypeRef = ref<RequestBodyEnum>(RequestBodyEnum.X_WWW_FORM_URLENCODED)
  72 +
  73 +const headerRef = ref()
  74 +
  75 +const dynamicFormItemEl = ref<FormItemInst[]>()
  76 +
  77 +const handleSelectedInterfaceChange = (value: string, option: PublicInterfaceRecord) => {
  78 + createForm()
  79 + requestContentTypeRef.value = option.requestContentType as RequestContentTypeEnum
  80 + requestHttpTypeRef.value = option.requestHttpType as RequestHttpEnum
  81 +}
  82 +
  83 +const getGetRequestTypeName = (key: RequestContentTypeEnum) => {
  84 + return RequestContentTypeNameEnum[RequestContentTypeEnum[key] as keyof typeof RequestContentTypeEnum]
  85 +}
  86 +
  87 +const setConfigurationData = async (request: RequestConfigType) => {
  88 + await getPublicInterfaceList()
  89 + const { requestDataPondId, requestParams: params, requestParamsBodyType, requestContentType, requestHttpType, } = request
  90 + const { Header } = params
  91 + selectedPublicInterface.value = requestDataPondId
  92 + requestContentTypeRef.value = requestContentType
  93 + requestHttpTypeRef.value = requestHttpType
  94 + headerRef.value = Header
  95 + requestParamsBodyTypeRef.value = requestParamsBodyType
  96 + requestParams.value = params
  97 + setDynamicFormValue(params)
  98 +}
  99 +
  100 +
  101 +const validate = async () => {
  102 + for (const item of unref(dynamicFormItemEl) || []) {
  103 + await item.validate()
  104 + }
  105 + return unref(validFlag)
  106 +}
  107 +
  108 +
  109 +defineExpose({
  110 + getConfigurationData,
  111 + setConfigurationData,
  112 + validate
  113 +})
  114 +
  115 +onMounted(() => {
  116 + getPublicInterfaceList()
  117 +})
  118 +
  119 +</script>
  120 +
  121 +<template>
  122 + <SettingItemBox name="公共接口" :alone="true" :item-right-style="{ gridTemplateColumns: '5fr 2fr 1fr' }">
  123 + <SettingItem name="请求方式 & URL地址">
  124 + <NInputGroup>
  125 + <NSelect v-model:value="selectedPublicInterface" label-field="interfaceName" value-field="id"
  126 + :options="publicInterfaceList" :filter="handleFilter" filterable :reset-menu-on-options-change="false"
  127 + @update-value="handleSelectedInterfaceChange" size="small">
  128 + </NSelect>
  129 + <NSelect v-if="requestContentTypeRef !== RequestContentTypeEnum.WEB_SOCKET" v-model:value="requestHttpTypeRef"
  130 + style="width: 150px;" size="small" :options="(selectTypeOptions as SelectOption[])" :disabled="true" />
  131 + <NSelect v-if="requestContentTypeRef === RequestContentTypeEnum.WEB_SOCKET" :disabled="true" style="width: 150px;"
  132 + :default-value="RequestContentTypeEnum.WEB_SOCKET" size="small"
  133 + :options="[{ label: 'WebSocket', value: RequestContentTypeEnum.WEB_SOCKET, type: 'ignored' }]">
  134 + </NSelect>
  135 + </NInputGroup>
  136 + </SettingItem>
  137 + <SettingItem name="更新间隔,为 0 只会初始化">
  138 + <NInputGroup>
  139 + <NInputNumber v-model:value="requestIntervalValueRef" class="select-time-number" size="small" min="0"
  140 + :show-button="false" placeholder="请输入数字" />
  141 + <!-- 单位 -->
  142 + <NSelect v-model:value="requestIntervalUnitRef" class="select-time-options" size="small"
  143 + :options="(selectTimeOptions as SelectOption[])" />
  144 + </NInputGroup>
  145 + </SettingItem>
  146 + </SettingItemBox>
  147 + <SettingItemBox name="请求方式" :alone="true" :item-right-style="{ gridTemplateColumns: '1fr 1fr' }">
  148 + <NTabs v-model:value="requestContentTypeRef" type="segment" size="small">
  149 + <NTabPane :disabled="requestContentTypeRef !== RequestContentTypeEnum.DEFAULT"
  150 + :name="RequestContentTypeEnum.DEFAULT" :tab="getGetRequestTypeName(RequestContentTypeEnum.DEFAULT)" />
  151 + <NTabPane :disabled="requestContentTypeRef !== RequestContentTypeEnum.SQL" :name="RequestContentTypeEnum.SQL"
  152 + :tab="getGetRequestTypeName(RequestContentTypeEnum.SQL)" />
  153 + <NTabPane :disabled="requestContentTypeRef !== RequestContentTypeEnum.WEB_SOCKET"
  154 + :name="RequestContentTypeEnum.WEB_SOCKET" :tab="getGetRequestTypeName(RequestContentTypeEnum.WEB_SOCKET)" />
  155 + </NTabs>
  156 + </SettingItemBox>
  157 + <SettingItemBox>
  158 + <NTabs v-model:value="requestParamsTypeRef" :default-value="RequestParamsTypeEnum.PARAMS">
  159 + <NTabPane v-for="item in RequestParamsTypeEnum" :name="item" :key="item"></NTabPane>
  160 + </NTabs>
  161 + </SettingItemBox>
  162 +
  163 + <SettingItemBox :item-right-style="{ gridTemplateColumns: '3fr 2fr' }">
  164 + <NCard v-if="requestParamsTypeRef === RequestParamsTypeEnum.PARAMS" class="dynamic-form">
  165 + <NScrollbar style="max-height: 400px; padding: 0 10px; box-sizing: border-box;;">
  166 + <NForm>
  167 + <template v-for="item in getDynamicFormSchemas" :key="item.key">
  168 + <NFormItem ref="dynamicFormItemEl" :required="item.required" :label="item.name" :rule="item.rules">
  169 + <component :is="componentMap[item.component]" v-bind="item.props" clearable />
  170 + </NFormItem>
  171 + </template>
  172 + </NForm>
  173 + <NEmpty v-if="!selectedPublicInterface || !getDynamicFormSchemas.length" description="请选择公共接口" />
  174 + </NScrollbar>
  175 + </NCard>
  176 +
  177 +
  178 + <RequestBody v-if="requestParamsTypeRef === RequestParamsTypeEnum.BODY"
  179 + v-model:request-params-body-type="requestParamsBodyTypeRef" v-model:value="requestParams" />
  180 +
  181 + <ParamsTable v-if="requestParamsTypeRef === RequestParamsTypeEnum.HEADER" v-model:value="headerRef" />
  182 +
  183 + </SettingItemBox>
  184 +</template>
  185 +
  186 +<style scoped lang="scss">
  187 +.loading-container {
  188 + display: flex;
  189 + align-items: center;
  190 + justify-content: center;
  191 +}
  192 +
  193 +.dynamic-form {
  194 + @include deep() {
  195 + .n-date-picker {
  196 + width: 100%;
  197 + }
  198 + }
  199 +}
  200 +</style>
... ...
  1 +import { getDictItemByCode } from "@/api/external/common"
  2 +import { DictItem } from "@/api/external/common/model"
  3 +import { getDeviceAttrList, getDeviceList, getDeviceProfileList, getOrgList } from "@/api/external/dynamicRequest"
  4 +import { PublicInterfaceRecord, RequestParams } from "@/api/external/dynamicRequest/model"
  5 +import { RequestConfigType } from "@/store/modules/chartEditStore/chartEditStore.d"
  6 +import { DatePickerProps, FormItemRule, InputProps, SelectProps, TreeSelectProps } from "naive-ui"
  7 +import { computed, onMounted, reactive, Ref, ref, unref } from "vue"
  8 +import { DictEnum } from '@/enums/external/dictEnum'
  9 +
  10 +const GROUP_SEPARATOR = ','
  11 +
  12 +export enum BuiltInVariable {
  13 + DATE = 'date',
  14 + KEYS = 'keys',
  15 + ENTITY_ID = 'entityId',
  16 + ORGANIZATION_ID = 'organizationId',
  17 + DEVICE_PROFILE_ID = 'deviceProfileId',
  18 +}
  19 +
  20 +export enum ComponentType {
  21 + SELECT = 'select',
  22 + SELECT_TREE = 'selectTree',
  23 + DATE_PICKER = 'datePicker',
  24 + INPUT = 'input'
  25 +}
  26 +
  27 +export interface DynamicFormSchema {
  28 + key: string
  29 + name?: string
  30 + component: ComponentType,
  31 + required?: boolean
  32 + rules?: FormItemRule
  33 + props: Recordable
  34 +}
  35 +
  36 +export type BuiltInVariableRecord = { [key in BuiltInVariable]: DictItem }
  37 +
  38 +export const useDynamicPublicForm = (publicInterfaceRef: Ref<PublicInterfaceRecord>) => {
  39 +
  40 + const getParams = computed(() => {
  41 + let params = (publicInterfaceRef.value.requestParams as unknown as RequestParams).Params || []
  42 + if (!Array.isArray(params)) params = []
  43 + return params
  44 + })
  45 +
  46 + const getUsedBuiltInVariable = computed(() => {
  47 + const hasUsed = unref(getParams).reduce((prev, next) => {
  48 + const groupList = next.key.split(GROUP_SEPARATOR)
  49 + return [...prev, ...(groupList.length > 1 ? groupList : [next.key])]
  50 + }, [] as string[])
  51 + return hasUsed
  52 + })
  53 +
  54 + const builtInVariable = ref<BuiltInVariableRecord>()
  55 +
  56 + const params = reactive<Recordable & { [key in BuiltInVariable]?: any }>({})
  57 +
  58 + const optionsSet = reactive<Recordable>({})
  59 +
  60 + const basicPreset = (filterKey: string) => {
  61 + return {
  62 + size: 'small',
  63 + filterable: true,
  64 + filter(query, option: Recordable) {
  65 + return option[filterKey].includes(query)
  66 + }
  67 + } as SelectProps
  68 + }
  69 +
  70 + const validIsExist = (keys: BuiltInVariable) => {
  71 + return unref(getUsedBuiltInVariable).includes(keys)
  72 + }
  73 +
  74 + const handleSelectOrgEffect = () => {
  75 + params[BuiltInVariable.ENTITY_ID] = null
  76 + getDeviceOption()
  77 + }
  78 +
  79 + const handleSelectDeviceProfileEffect = () => {
  80 + params[BuiltInVariable.ENTITY_ID] = null
  81 + params[BuiltInVariable.KEYS] = null
  82 + getDeviceOption()
  83 + getDeviceAttrOption()
  84 + }
  85 +
  86 + const getBuiltInVariable = async () => {
  87 + if (Object.keys(unref(builtInVariable) || {}).length) return
  88 + const result = await getDictItemByCode(DictEnum.BUILTIN_PARAMS)
  89 + builtInVariable.value = result.reduce((prev, next) => {
  90 + return { ...prev, [next.itemValue]: next }
  91 + }, {} as BuiltInVariableRecord)
  92 + }
  93 +
  94 + const getOrgOption = async () => {
  95 + if (!validIsExist(BuiltInVariable.ORGANIZATION_ID)) return
  96 + optionsSet[BuiltInVariable.ORGANIZATION_ID] = await getOrgList()
  97 + }
  98 +
  99 + const getDeviceProfileOption = async () => {
  100 + if (!validIsExist(BuiltInVariable.DEVICE_PROFILE_ID)) return
  101 + optionsSet[BuiltInVariable.DEVICE_PROFILE_ID] = await getDeviceProfileList()
  102 + }
  103 +
  104 + const getDeviceOption = async () => {
  105 + if (!validIsExist(BuiltInVariable.DEVICE_PROFILE_ID) || !validIsExist(BuiltInVariable.ORGANIZATION_ID) || !params[BuiltInVariable.ORGANIZATION_ID]) return
  106 + optionsSet[BuiltInVariable.ENTITY_ID] = await getDeviceList({ [BuiltInVariable.ORGANIZATION_ID]: params[BuiltInVariable.ORGANIZATION_ID], [BuiltInVariable.DEVICE_PROFILE_ID]: params[BuiltInVariable.DEVICE_PROFILE_ID] })
  107 + }
  108 +
  109 + const getDeviceAttrOption = async () => {
  110 + if (!validIsExist(BuiltInVariable.KEYS)) return
  111 + if (!params[BuiltInVariable.DEVICE_PROFILE_ID]) return
  112 + const result = await getDeviceAttrList(params[BuiltInVariable.DEVICE_PROFILE_ID])
  113 + if (Array.isArray(result)) optionsSet[BuiltInVariable.KEYS] = result
  114 + }
  115 +
  116 + const getSelectOrgTree = computed<TreeSelectProps>(() => {
  117 + return {
  118 + ...basicPreset('name'),
  119 + value: params[BuiltInVariable.ORGANIZATION_ID],
  120 + options: unref(optionsSet[BuiltInVariable.ORGANIZATION_ID]),
  121 + labelField: 'name',
  122 + keyField: 'id',
  123 + childrenField: 'children',
  124 + onUpdateValue(value) {
  125 + params[BuiltInVariable.ORGANIZATION_ID] = value
  126 + handleSelectOrgEffect()
  127 + }
  128 + } as TreeSelectProps
  129 + })
  130 +
  131 +
  132 + const getSelectDeviceProfile = computed<SelectProps>(() => {
  133 + return {
  134 + ...basicPreset('name'),
  135 + value: params[BuiltInVariable.DEVICE_PROFILE_ID],
  136 + options: unref(optionsSet[BuiltInVariable.DEVICE_PROFILE_ID]),
  137 + labelField: 'name',
  138 + valueField: 'id',
  139 + onUpdateValue(value) {
  140 + params[BuiltInVariable.DEVICE_PROFILE_ID] = value
  141 + handleSelectDeviceProfileEffect()
  142 + }
  143 + }
  144 + })
  145 +
  146 + const getSelectDevice = computed<SelectProps>(() => {
  147 + return {
  148 + ...basicPreset('name'),
  149 + value: params[BuiltInVariable.ENTITY_ID],
  150 + options: unref(optionsSet[BuiltInVariable.ENTITY_ID]),
  151 + labelField: 'name',
  152 + valueField: 'id',
  153 + onUpdateValue(value) {
  154 + params[BuiltInVariable.ENTITY_ID] = value
  155 + }
  156 + }
  157 + })
  158 +
  159 + const getSelectDeviceAttr = computed<SelectProps>(() => {
  160 + return {
  161 + ...basicPreset('name'),
  162 + value: params[BuiltInVariable.KEYS],
  163 + options: unref(optionsSet[BuiltInVariable.KEYS]),
  164 + labelField: 'name',
  165 + valueField: 'identifier',
  166 + onUpdateValue(value) {
  167 + params[BuiltInVariable.KEYS] = value
  168 + }
  169 + }
  170 + })
  171 +
  172 + const getSelectDate = computed(() => {
  173 + return {
  174 + size: 'small',
  175 + value: params[BuiltInVariable.DATE],
  176 + type: 'datetimerange',
  177 + clearable: true,
  178 + defaultTime: ['00:00:00', '00:00:00'],
  179 + onUpdateValue(value) {
  180 + params[BuiltInVariable.DATE] = value
  181 + }
  182 + } as DatePickerProps
  183 + })
  184 +
  185 + const getBasicInput = (key: string) => {
  186 + return {
  187 + size: 'small',
  188 + value: params[key],
  189 + onUpdateValue(value) {
  190 + params[key] = value
  191 + }
  192 + } as InputProps
  193 + }
  194 +
  195 + const builtInVariableConfiguration: { [key in BuiltInVariable]?: DynamicFormSchema } = {
  196 + [BuiltInVariable.ORGANIZATION_ID]: {
  197 + component: ComponentType.SELECT_TREE,
  198 + key: BuiltInVariable.ORGANIZATION_ID,
  199 + props: getSelectOrgTree
  200 + },
  201 + [BuiltInVariable.DEVICE_PROFILE_ID]: {
  202 + component: ComponentType.SELECT,
  203 + key: BuiltInVariable.DEVICE_PROFILE_ID,
  204 + props: getSelectDeviceProfile
  205 + },
  206 + [BuiltInVariable.ENTITY_ID]: {
  207 + component: ComponentType.SELECT,
  208 + key: BuiltInVariable.ENTITY_ID,
  209 + props: getSelectDevice
  210 + },
  211 + [BuiltInVariable.KEYS]: {
  212 + component: ComponentType.SELECT,
  213 + key: BuiltInVariable.KEYS,
  214 + props: getSelectDeviceAttr
  215 + },
  216 + [BuiltInVariable.DATE]: {
  217 + component: ComponentType.DATE_PICKER,
  218 + key: BuiltInVariable.DATE,
  219 + props: getSelectDate
  220 + }
  221 + }
  222 +
  223 + const validFlag = ref(true)
  224 +
  225 + const createRules = (required: boolean, key: string, message: string) => {
  226 + return [{
  227 + trigger: ['blur', 'change'],
  228 + validator() {
  229 + if (required && !params[key]) {
  230 + validFlag.value = false
  231 + return new Error(`${message}是必填项`)
  232 + }
  233 + validFlag.value = true
  234 + }
  235 + }] as FormItemRule
  236 + }
  237 +
  238 + const toFormSchemas = (builtInVariableKey: string, required: boolean, value: any) => {
  239 + const groupList = builtInVariableKey.split(GROUP_SEPARATOR)
  240 + return groupList.reduce((prev, next) => {
  241 + const result = builtInVariableConfiguration[next as BuiltInVariable]
  242 + const props = unref(result?.props)
  243 + const name = (unref(builtInVariable) || {})[next as BuiltInVariable]?.itemText
  244 + let schema: DynamicFormSchema
  245 + if (builtInVariableConfiguration[next as BuiltInVariable]) {
  246 + schema = {
  247 + ...result,
  248 + props,
  249 + name,
  250 + required,
  251 + rules: createRules(required, next, name || next)
  252 + } as DynamicFormSchema
  253 + } else {
  254 + schema = {
  255 + key: value,
  256 + name: value,
  257 + component: ComponentType.INPUT,
  258 + props: getBasicInput(value),
  259 + required,
  260 + rules: createRules(required, value, value)
  261 + } as DynamicFormSchema
  262 + }
  263 +
  264 + return [...prev, schema]
  265 +
  266 + }, [] as DynamicFormSchema[])
  267 +
  268 + }
  269 +
  270 + const getDynamicFormSchemas = computed(() => {
  271 + return unref(getParams).reduce((prev, { key, value, required }) => {
  272 + const schemas = toFormSchemas(key, required, value)
  273 + return [...prev, ...schemas]
  274 + }, [] as DynamicFormSchema[])
  275 + })
  276 +
  277 + const createForm = async () => {
  278 + await getBuiltInVariable()
  279 + await getOrgOption()
  280 + getDeviceProfileOption()
  281 + getDeviceOption()
  282 + }
  283 +
  284 + const setParams = (Params: Recordable) => {
  285 + unref(getParams).forEach(({ key, value }) => {
  286 + if (unref(getUsedBuiltInVariable).includes(key)) {
  287 + params[key] = Params[key]
  288 + }
  289 + params[value] = Params[value]
  290 + })
  291 + }
  292 +
  293 + const setDynamicFormValue = (requestParams: RequestConfigType['requestParams']) => {
  294 + const { Params } = requestParams
  295 + setParams(Params)
  296 + getOrgOption()
  297 + getDeviceProfileOption()
  298 + getDeviceOption()
  299 + getDeviceAttrOption()
  300 + }
  301 +
  302 + onMounted(() => {
  303 + getBuiltInVariable()
  304 + })
  305 +
  306 + return {
  307 + validFlag,
  308 + params,
  309 + getDynamicFormSchemas,
  310 + setDynamicFormValue,
  311 + createForm
  312 + }
  313 +}
  314 +
... ...
  1 +import { PublicInterfaceRecord } from "@/api/external/dynamicRequest/model"
  2 +import { RequestDataTypeEnum, RequestParams } from "@/enums/httpEnum"
  3 +import { RequestConfigType } from "@/store/modules/chartEditStore/chartEditStore.d"
  4 +
  5 +export const transferData = (publicInterfaceRecord: PublicInterfaceRecord, requestParams: RequestParams, fillParams: Recordable) => {
  6 + const { requestContentType, requestHttpType, id, requestUrl, requestParamsBodyType, requestOriginUrl } = publicInterfaceRecord
  7 + const { Header, Body } = requestParams
  8 + return {
  9 + requestDataPondId: id,
  10 + // requestInterval
  11 + // 获取数据的方式
  12 + requestDataType: RequestDataTypeEnum.Pond,
  13 + // 请求方式 get/post/del/put/patch
  14 + requestHttpType,
  15 + // 源后续的 url
  16 + requestUrl: `${requestOriginUrl}${requestUrl}`,
  17 + // 请求内容主体方式 普通/sql
  18 + requestContentType,
  19 + // 请求体类型
  20 + requestParamsBodyType,
  21 + // SQL 请求对象
  22 + // requestSQLContent: {
  23 + // sql: ''
  24 + // }
  25 + // requestIntervalUnit
  26 + requestParams: {
  27 + Header,
  28 + Body,
  29 + Params: fillParams
  30 + }
  31 +
  32 + } as RequestConfigType
  33 +}
... ...
  1 +<script setup lang="ts">
  2 +import { toRefs, computed } from 'vue'
  3 +import { icon } from '@/plugins'
  4 +import { useDesignStore } from '@/store/modules/designStore/designStore'
  5 +import { SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
  6 +import { SelectHttpTimeNameObj, RequestContentTypeEnum } from '@/enums/httpEnum'
  7 +import { useTargetData } from '@/views/chart/ContentConfigurations/components/hooks/useTargetData.hook'
  8 +import { NButton, NCard, NTag, NInput, NIcon } from 'naive-ui'
  9 +
  10 +const emit = defineEmits(['clickPanel'])
  11 +
  12 +const { FlashIcon, PulseIcon } = icon.ionicons5
  13 +const { targetData, chartEditStore } = useTargetData()
  14 +
  15 +const {
  16 + requestOriginUrl,
  17 + requestInterval: GlobalRequestInterval,
  18 + requestIntervalUnit: GlobalRequestIntervalUnit
  19 +} = toRefs(chartEditStore.getRequestGlobalConfig)
  20 +
  21 +const designStore = useDesignStore()
  22 +
  23 +// 请求配置 model
  24 +const requestModelHandle = () => {
  25 + emit('clickPanel')
  26 +}
  27 +
  28 +// 颜色
  29 +const themeColor = computed(() => {
  30 + return designStore.getAppTheme
  31 +})
  32 +
  33 +</script>
  34 +
  35 +<template>
  36 + <NCard class="n-card-shallow">
  37 + <SettingItemBox name="请求配置">
  38 + <SettingItem name="类型">
  39 + <NTag :bordered="false" type="primary" style="border-radius: 5px">
  40 + {{ targetData.request.requestContentType === RequestContentTypeEnum.DEFAULT ? '普通请求' : 'SQL请求' }}
  41 + </NTag>
  42 + </SettingItem>
  43 +
  44 + <SettingItem name="方式">
  45 + <NInput size="small" :placeholder="targetData.request.requestHttpType || '暂无'" :disabled="true"></NInput>
  46 + </SettingItem>
  47 +
  48 + <SettingItem name="组件间隔">
  49 + <NInput size="small" :placeholder="`${targetData.request.requestInterval || '暂无'}`" :disabled="true">
  50 + <template #suffix> {{ SelectHttpTimeNameObj[targetData.request.requestIntervalUnit] }} </template>
  51 + </NInput>
  52 + </SettingItem>
  53 +
  54 + <SettingItem name="全局间隔(默认)">
  55 + <NInput size="small" :placeholder="`${GlobalRequestInterval || '暂无'} `" :disabled="true">
  56 + <template #suffix> {{ SelectHttpTimeNameObj[GlobalRequestIntervalUnit] }} </template>
  57 + </NInput>
  58 + </SettingItem>
  59 + </SettingItemBox>
  60 +
  61 + <SettingItemBox name="源地址" :alone="true">
  62 + <NInput size="small" :placeholder="requestOriginUrl || '暂无'" :disabled="true">
  63 + <template #prefix>
  64 + <NIcon :component="PulseIcon" />
  65 + </template>
  66 + </NInput>
  67 + </SettingItemBox>
  68 +
  69 + <SettingItemBox name="组件地址" :alone="true">
  70 + <NInput size="small" :placeholder="targetData.request.requestUrl || '暂无'" :disabled="true">
  71 + <template #prefix>
  72 + <NIcon :component="FlashIcon" />
  73 + </template>
  74 + </NInput>
  75 + </SettingItemBox>
  76 +
  77 + <div class="edit-text" @click="requestModelHandle">
  78 + <div class="go-absolute-center">
  79 + <NButton type="primary" secondary>编辑配置</NButton>
  80 + </div>
  81 + </div>
  82 + </NCard>
  83 +</template>
  84 +
  85 +<style lang="scss" scoped>
  86 +.n-card-shallow {
  87 + &.n-card {
  88 + @extend .go-background-filter;
  89 +
  90 + @include deep() {
  91 + .n-card__content {
  92 + padding: 10px;
  93 + }
  94 + }
  95 + }
  96 +
  97 + .edit-text {
  98 + position: absolute;
  99 + top: 0px;
  100 + left: 0px;
  101 + width: 325px;
  102 + height: 270px;
  103 + cursor: pointer;
  104 + opacity: 0;
  105 + transition: all 0.3s;
  106 + @extend .go-background-filter;
  107 + backdrop-filter: blur(2px) !important;
  108 + }
  109 +
  110 + &:hover {
  111 + border-color: v-bind('themeColor');
  112 +
  113 + .edit-text {
  114 + opacity: 1;
  115 + }
  116 + }
  117 +}
  118 +</style>
... ...
  1 +<script setup lang="ts">
  2 +import { NInput, NInputNumber, NInputGroup, NButton, NSelect, SelectOption, NTabs, NTabPane, NSpace } from 'naive-ui'
  3 +import { SettingItem, SettingItemBox } from '@/components/Pages/ChartItemSetting';
  4 +import { selectTimeOptions, selectTypeOptions } from '../../../index.d';
  5 +import { RequestBodyEnum, RequestContentTypeEnum, RequestDataTypeEnum, RequestHttpEnum, RequestHttpIntervalEnum, RequestParams } from '@/enums/httpEnum';
  6 +import { ref, unref } from 'vue';
  7 +import DefaultRequestContent from './DefaultRequestContent.vue';
  8 +import SqlConfiguration from './SqlConfiguration.vue';
  9 +import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d';
  10 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
  11 +import { getUrl } from '@/api/external/customRequest'
  12 +
  13 +const chartEditStore = useChartEditStore()
  14 +
  15 +const requestContentTypeRef = ref(RequestContentTypeEnum.DEFAULT)
  16 +
  17 +const requestHttpTypeRef = ref(RequestHttpEnum.GET)
  18 +
  19 +const requestIntervalRef = ref<number | undefined>(20)
  20 +
  21 +const requestIntervalUnitRef = ref(RequestHttpIntervalEnum.SECOND)
  22 +
  23 +const requestSQLContentRef = ref('select * from where')
  24 +
  25 +const requestParamsRef = ref({ Header: {}, Params: {}, Body: { 'form-data': {}, 'json': '', 'x-www-form-urlencoded': {}, 'xml': '' } } as RequestParams)
  26 +
  27 +const requestParamsBodyTypeRef = ref(RequestBodyEnum.NONE)
  28 +
  29 +const requestUrlRef = ref<string>()
  30 +
  31 +const getConfigurationData = (): RequestConfigType => {
  32 + return {
  33 + requestContentType: unref(requestContentTypeRef),
  34 + requestHttpType: unref(requestHttpTypeRef),
  35 + requestInterval: unref(requestIntervalRef),
  36 + requestIntervalUnit: unref(requestIntervalUnitRef),
  37 + requestDataType: RequestDataTypeEnum.AJAX,
  38 + requestParamsBodyType: unref(requestParamsBodyTypeRef),
  39 + requestSQLContent: {
  40 + sql: unref(requestSQLContentRef)
  41 + },
  42 + requestParams: unref(requestParamsRef),
  43 + requestUrl: `${chartEditStore.requestGlobalConfig.requestOriginUrl}${unref(requestUrlRef)}`
  44 + }
  45 +}
  46 +
  47 +
  48 +const setConfigurationData = (request: RequestConfigType) => {
  49 + const { requestContentType, requestHttpType, requestInterval, requestIntervalUnit, requestParamsBodyType, requestSQLContent, requestParams, requestUrl } = request
  50 + requestContentTypeRef.value = requestContentType
  51 + requestHttpTypeRef.value = requestHttpType
  52 + requestIntervalRef.value = requestInterval
  53 + requestIntervalUnitRef.value = requestIntervalUnit
  54 + requestParamsBodyTypeRef.value = requestParamsBodyType
  55 + requestSQLContentRef.value = requestSQLContent.sql
  56 + requestParamsRef.value = requestParams
  57 + const { pathname } = getUrl(requestUrl!)
  58 + requestUrlRef.value = pathname
  59 +}
  60 +
  61 +defineExpose({
  62 + getConfigurationData,
  63 + setConfigurationData
  64 +})
  65 +
  66 +</script>
  67 +
  68 +<template>
  69 + <SettingItemBox name="地址" :itemRightStyle="{
  70 + gridTemplateColumns: '5fr 2fr 1fr'
  71 + }">
  72 + <SettingItem name="请求方式 & URL地址">
  73 + <NInputGroup>
  74 + <NSelect v-model:value="requestHttpTypeRef" style="width: 150px;" size="small"
  75 + :options="(selectTypeOptions as SelectOption[])" />
  76 + <NInput v-model:value="requestUrlRef" class="select-time-number" size="small" min="0" :show-button="false"
  77 + placeholder="请输入URL地址" />
  78 + </NInputGroup>
  79 + </SettingItem>
  80 + <SettingItem name="更新间隔,为 0 只会初始化">
  81 + <NInputGroup>
  82 + <NInputNumber v-model:value="requestIntervalRef" class="select-time-number" size="small" min="0"
  83 + :show-button="false" placeholder="请输入数字" />
  84 + <!-- 单位 -->
  85 + <NSelect v-model:value="requestIntervalUnitRef" class="select-time-options" size="small"
  86 + :options="(selectTimeOptions as SelectOption[])" />
  87 + </NInputGroup>
  88 + </SettingItem>
  89 + </SettingItemBox>
  90 + <SettingItemBox name="选择方式">
  91 + <NTabs v-model:value="requestContentTypeRef" type="segment" size="small">
  92 + <NTabPane :name="RequestContentTypeEnum.DEFAULT" tab="普通请求" />
  93 + <NTabPane :name="RequestContentTypeEnum.SQL" tab="SQL请求" />
  94 + </NTabs>
  95 + </SettingItemBox>
  96 + <SettingItemBox>
  97 + <DefaultRequestContent v-if="requestContentTypeRef === RequestContentTypeEnum.DEFAULT"
  98 + v-model:value="requestParamsRef" v-model:request-params-body-type="requestParamsBodyTypeRef" />
  99 + <SqlConfiguration v-model:value="requestSQLContentRef" v-if="requestContentTypeRef === RequestContentTypeEnum.SQL" />
  100 + </SettingItemBox>
  101 +</template>
... ...
  1 +<script lang="ts" setup>
  2 +import { RequestBodyEnum, RequestParams, RequestParamsTypeEnum } from '@/enums/httpEnum';
  3 +import { NTabPane, NTabs } from 'naive-ui';
  4 +import { ref } from 'vue';
  5 +import ParamsTable from './ParamsTable.vue';
  6 +import RequestBody from './RequestBody.vue';
  7 +
  8 +defineProps<{
  9 + value: RequestParams
  10 + requestParamsBodyType: RequestBodyEnum
  11 +}>()
  12 +
  13 +const emit = defineEmits(['update:requestParamsBodyType'])
  14 +
  15 +const tabValue = ref(RequestParamsTypeEnum.PARAMS)
  16 +
  17 +const handleSyncRequestParamsBodyType = (value: RequestBodyEnum) => {
  18 + emit('update:requestParamsBodyType', value)
  19 +}
  20 +
  21 +
  22 +</script>
  23 +
  24 +<template>
  25 + <section class="container">
  26 + <NTabs v-model:value="tabValue">
  27 + <NTabPane v-for="item in RequestParamsTypeEnum" :name="item" :key="item"></NTabPane>
  28 + </NTabs>
  29 + <section v-if="tabValue === RequestParamsTypeEnum.PARAMS">
  30 + <ParamsTable v-model:value="value[RequestParamsTypeEnum.PARAMS]" />
  31 + </section>
  32 +
  33 + <RequestBody v-model:value="value" @update:requestParamsBodyType="handleSyncRequestParamsBodyType"
  34 + v-model:requestParamsBodyType="requestParamsBodyType" v-if="tabValue === RequestParamsTypeEnum.BODY" />
  35 +
  36 + <ParamsTable v-if="tabValue === RequestParamsTypeEnum.HEADER" v-model:value="value.Header" />
  37 + </section>
  38 +</template>
  39 +
  40 +<style lang="scss" scoped>
  41 +.container {
  42 + width: 600px;
  43 +}
  44 +
  45 +.body-container {
  46 + margin-top: 20px;
  47 +}
  48 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +import { RequestParamsTypeEnum } from '@/enums/httpEnum';
  3 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
  4 +import { NTabPane, NTabs } from 'naive-ui';
  5 +import { ref } from 'vue';
  6 +import ParamsTable from './ParamsTable.vue';
  7 +
  8 +const chartEditStore = useChartEditStore()
  9 +
  10 +const tabs: RequestParamsTypeEnum[] = [RequestParamsTypeEnum.HEADER]
  11 +
  12 +const currenPanel = ref(RequestParamsTypeEnum.HEADER)
  13 +
  14 +
  15 +</script>
  16 +
  17 +<template>
  18 + <NTabs type="line" animated>
  19 + <NTabPane v-model:value="currenPanel" v-for="item in tabs" :name="item" :tab="item" :key="item" />
  20 + </NTabs>
  21 + <section>
  22 + <ParamsTable v-if="currenPanel === RequestParamsTypeEnum.HEADER" v-model:value="chartEditStore.requestGlobalConfig.requestParams.Header" />
  23 + </section>
  24 +</template>
... ...
  1 +<script lang="ts" setup>
  2 +import { SettingItem, SettingItemBox } from '@/components/Pages/ChartItemSetting';
  3 +import { icon } from '@/plugins';
  4 +import { NButton, NCard, NCollapseTransition, NIcon, NInput, NInputGroup, NInputNumber, NSelect, NSpace, NTag, NTooltip, SelectOption } from 'naive-ui';
  5 +import { computed, ref, unref } from 'vue';
  6 +import GlobalParamsConfiguration from './GlobalParamsConfiguration.vue'
  7 +import { ChevronDown, ChevronUp } from '@vicons/carbon'
  8 +import { useDesignStore } from '@/store/modules/designStore/designStore';
  9 +import { selectTimeOptions } from '../../../index.d';
  10 +import { RequestConfigType, RequestGlobalConfigType } from '@/store/modules/chartEditStore/chartEditStore.d'
  11 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
  12 +import { useTargetData } from '../../../../hooks/useTargetData.hook';
  13 +import { RequestHttpIntervalEnum } from '@/enums/httpEnum';
  14 +
  15 +const { PencilIcon } = icon.ionicons5
  16 +const designStore = useDesignStore()
  17 +const editDisabled = ref(false)
  18 +const collapseHeaderTable = ref(false)
  19 +
  20 +const { chartEditStore } = useTargetData()
  21 +
  22 +const handleCollapse = () => {
  23 + collapseHeaderTable.value = !unref(collapseHeaderTable)
  24 +}
  25 +
  26 +
  27 +// 颜色
  28 +const themeColor = computed(() => {
  29 + return designStore.getAppTheme
  30 +})
  31 +
  32 +
  33 +
  34 +</script>
  35 +
  36 +<template>
  37 + <NCard>
  38 + <template #header>
  39 + <NTag type="info">全局公共配置</NTag>
  40 + </template>
  41 + <NSpace vertical>
  42 + <SettingItemBox name="服务" :itemRightStyle="{
  43 + gridTemplateColumns: '5fr 2fr 1fr'
  44 + }">
  45 + <!-- 源地址 -->
  46 + <SettingItem name="前置 URL">
  47 + <NInput v-model:value="chartEditStore.requestGlobalConfig.requestOriginUrl" default-value="http://127.0.0.1" :disabled="editDisabled"
  48 + size="small" placeholder="例:http://127.0.0.1"></NInput>
  49 + </SettingItem>
  50 + <SettingItem name="更新间隔,为 0 只会初始化">
  51 + <NInputGroup>
  52 + <NInputNumber v-model:value="chartEditStore.requestGlobalConfig.requestInterval" class="select-time-number"
  53 + size="small" min="0" :show-button="false" :disabled="editDisabled" placeholder="请输入数字">
  54 + </NInputNumber>
  55 + <!-- 单位 -->
  56 + <NSelect v-model:value="chartEditStore.requestGlobalConfig.requestIntervalUnit" class="select-time-options"
  57 + size="small" :default-value="RequestHttpIntervalEnum.SECOND" :options="selectTimeOptions as SelectOption[]"
  58 + :disabled="editDisabled" />
  59 + </NInputGroup>
  60 + </SettingItem>
  61 + <NButton v-show="editDisabled" type="primary" ghost @click="editDisabled = false">
  62 + <template #icon>
  63 + <NIcon>
  64 + <PencilIcon />
  65 + </NIcon>
  66 + </template>
  67 + 编辑配置
  68 + </NButton>
  69 + </SettingItemBox>
  70 + <NCollapseTransition :show="collapseHeaderTable">
  71 + <GlobalParamsConfiguration />
  72 + </NCollapseTransition>
  73 + <section class="arrow">
  74 + <NTooltip>
  75 + <template #trigger>
  76 + <NIcon @click="handleCollapse" size="30">
  77 + <ChevronDown v-show="!collapseHeaderTable" />
  78 + <ChevronUp v-show="collapseHeaderTable" />
  79 + </NIcon>
  80 + </template>
  81 + <span>{{ collapseHeaderTable ? '收起' : '展开' }}</span>
  82 + </NTooltip>
  83 + </section>
  84 + </NSpace>
  85 + </NCard>
  86 +</template>
  87 +
  88 +
  89 +<style lang="scss" scoped>
  90 +.arrow {
  91 + display: flex;
  92 + justify-content: center;
  93 + align-items: center;
  94 + cursor: pointer;
  95 +
  96 + &:hover {
  97 + color: v-bind('themeColor');
  98 +
  99 + }
  100 +}
  101 +</style>
... ...
  1 +<script setup lang="ts" >
  2 +import { getUUID } from '@/utils';
  3 +import { ButtonProps, DataTableColumns, InputProps, NButton, NDataTable, NIcon, NInput, NSpace, NTag, NTooltip, TagProps } from 'naive-ui';
  4 +import { computed, h, ref, unref, watch } from 'vue';
  5 +import { Subtract, Add } from '@vicons/carbon'
  6 +import { isObject } from '@/utils/external/is';
  7 +
  8 +interface DataSource {
  9 + id: string
  10 + value?: string
  11 + keyName?: string
  12 + result?: boolean
  13 +}
  14 +
  15 +const columns: DataTableColumns<DataSource> = [
  16 + {
  17 + title: '序号',
  18 + key: 'index',
  19 + render: (_, index) => index + 1,
  20 + width: 50,
  21 + },
  22 + {
  23 + title: 'Key',
  24 + key: 'keyName',
  25 + render: (row, index) => {
  26 + return h(
  27 + NInput,
  28 + {
  29 + value: row.keyName,
  30 + onBlur: () => handleInputBlur(row),
  31 + onUpdateValue: (value: string) => unref(dataSource)[index].keyName = value,
  32 + size: 'small',
  33 + disabled: props.disabled
  34 + } as InputProps
  35 + )
  36 + }
  37 + },
  38 + {
  39 + title: 'Value',
  40 + key: 'value',
  41 + render: (row, index) => {
  42 + return h(
  43 + NInput,
  44 + {
  45 + value: row.value,
  46 + onBlur: () => handleInputBlur(row),
  47 + onUpdateValue: (value: string) => unref(dataSource)[index].value = value,
  48 + size: 'small',
  49 + disabled: props.disabled
  50 + } as InputProps
  51 + )
  52 + }
  53 + },
  54 + {
  55 + title: '操作',
  56 + key: 'actions',
  57 + render: (row) => {
  58 + return h(
  59 + NSpace,
  60 + () => [
  61 + h(
  62 + NTooltip,
  63 + null,
  64 + {
  65 + trigger: () => h(
  66 + NButton,
  67 + {
  68 + type: 'success',
  69 + size: 'small',
  70 + ghost: true,
  71 + onClick: () => handleAddRow(row),
  72 + disabled: props.disabled || !unref(canAddRow)
  73 + } as ButtonProps,
  74 + {
  75 + default: () => h(NIcon, () => h(Add))
  76 + }
  77 + ),
  78 + default: () => '插入行'
  79 + }
  80 + ),
  81 + h(
  82 + NTooltip,
  83 + null,
  84 + {
  85 + trigger: () => h(
  86 + NButton,
  87 + {
  88 + type: 'warning',
  89 + size: 'small',
  90 + ghost: true,
  91 + onClick: () => handleSubtractRow(row),
  92 + disabled: props.disabled || !unref(canDeleteRow)
  93 + } as ButtonProps,
  94 + {
  95 + default: () => h(NIcon, () => h(Subtract))
  96 + }
  97 + ),
  98 + default: () => '删除行'
  99 + }
  100 + )
  101 + ]
  102 + )
  103 + },
  104 + width: 100,
  105 + },
  106 + {
  107 + title: '结果',
  108 + key: 'result',
  109 + render: (row) => {
  110 + return h(NTag, { type: row.result ? 'success' : 'error' } as TagProps, () => `${row.result ? '' : '未'}通过`)
  111 + },
  112 + width: 80
  113 + }
  114 +]
  115 +
  116 +const props = withDefaults(
  117 + defineProps<{
  118 + value?: Recordable
  119 + disabled?: boolean
  120 + maxRow?: number
  121 + }>(),
  122 + {
  123 + disabled: false,
  124 + maxRow: 50,
  125 + }
  126 +)
  127 +
  128 +const emit = defineEmits(['update:value'])
  129 +
  130 +const createNewRow = () => {
  131 + return { id: getUUID(), result: true } as DataSource
  132 +}
  133 +
  134 +const dataSource = ref<DataSource[]>([
  135 + createNewRow()
  136 +])
  137 +
  138 +watch(
  139 + () => props.value,
  140 + (target) => {
  141 + if (target && isObject(target) && Object.keys(target).length) {
  142 + dataSource.value = Object.keys(props.value || {}).map(keyName => ({ ...createNewRow(), keyName, value: Reflect.get(props.value || {}, keyName) }))
  143 + } else {
  144 + dataSource.value = [createNewRow()]
  145 + }
  146 + },
  147 + {
  148 + immediate: true,
  149 + deep: true
  150 + }
  151 +)
  152 +
  153 +const canDeleteRow = computed(() => {
  154 + return unref(dataSource).length >= 2
  155 +})
  156 +
  157 +const canAddRow = computed(() => {
  158 + return unref(dataSource).length < props.maxRow
  159 +})
  160 +
  161 +const handleAddRow = (record: DataSource) => {
  162 + const index = unref(dataSource).findIndex(item => item.id === record.id)
  163 + unref(dataSource).splice(index + 1, 0, createNewRow())
  164 +}
  165 +
  166 +const handleSubtractRow = (record: DataSource) => {
  167 + const index = unref(dataSource).findIndex(item => item.id === record.id)
  168 + unref(dataSource).splice(index, 1)
  169 +}
  170 +
  171 +
  172 +const handleInputBlur = (record: DataSource) => {
  173 + const { keyName, value } = record
  174 + record.result = !!(keyName && value)
  175 + if (unref(dataSource).every(item => item.result)) {
  176 + emit('update:value', getHeaderConfiguration())
  177 + }
  178 +}
  179 +
  180 +const getHeaderConfiguration = () => {
  181 + return unref(dataSource).reduce((prev, next) => {
  182 + const { result, value, keyName } = next
  183 + const header = result && value && keyName ? { [keyName]: value } : {}
  184 + return { ...prev, ...header }
  185 + }, {} as Recordable)
  186 +}
  187 +
  188 +</script>
  189 +
  190 +<template>
  191 + <NDataTable size="small" :columns="columns" :data="dataSource" max-height="300" />
  192 +</template>
... ...
  1 +<script lang="ts" setup>
  2 +import { MonacoEditor } from '@/components/Pages/MonacoEditor';
  3 +import { RequestBodyEnum, RequestBodyEnumList, RequestParams } from '@/enums/httpEnum';
  4 +import { NCard } from 'naive-ui';
  5 +import ParamsTable from './ParamsTable.vue';
  6 +
  7 +defineProps<{
  8 + requestParamsBodyType: RequestBodyEnum
  9 + value: RequestParams
  10 +}>()
  11 +
  12 +const emit = defineEmits(['update:requestParamsBodyType'])
  13 +
  14 +const handleSyncRequestParamsBodyType = (value: RequestBodyEnum) => {
  15 + emit('update:requestParamsBodyType', value)
  16 +}
  17 +
  18 +</script>
  19 +
  20 +<template>
  21 + <section>
  22 + <NRadioGroup v-model:value="requestParamsBodyType" @update:value="handleSyncRequestParamsBodyType">
  23 + <NSpace>
  24 + <NRadio v-for="item in RequestBodyEnumList" :key="item" :value="item">
  25 + {{ item }}
  26 + </NRadio>
  27 + </NSpace>
  28 + </NRadioGroup>
  29 +
  30 + <section class="body-container">
  31 + <!-- 为 none 时 -->
  32 + <NCard v-if="requestParamsBodyType === RequestBodyEnum.NONE">
  33 + <NText depth="3">该接口没有 Body 体</NText>
  34 + </NCard>
  35 +
  36 + <ParamsTable v-if="requestParamsBodyType === RequestBodyEnum.FORM_DATA"
  37 + v-model:value="value.Body[RequestBodyEnum.FORM_DATA]" />
  38 +
  39 + <ParamsTable v-if="requestParamsBodyType === RequestBodyEnum.X_WWW_FORM_URLENCODED"
  40 + v-model:value="value.Body[RequestBodyEnum.X_WWW_FORM_URLENCODED]" />
  41 +
  42 + <MonacoEditor v-if="requestParamsBodyType === RequestBodyEnum.JSON"
  43 + v-model:modelValue="value.Body[RequestBodyEnum.JSON]" width="600px" height="200px" language="json" />
  44 +
  45 + <MonacoEditor v-if="requestParamsBodyType === RequestBodyEnum.XML"
  46 + v-model:modelValue="value.Body[RequestBodyEnum.XML]" width="600px" height="200px" language="xml" />
  47 + </section>
  48 + </section>
  49 +</template>
  50 +
  51 +<style lang="scss" scoped>
  52 + .body-container {
  53 + margin-top: 20px;
  54 + }
  55 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +import { SettingItemBox } from '@/components/Pages/ChartItemSetting';
  3 +import { MonacoEditor } from '@/components/Pages/MonacoEditor';
  4 +import { NTag } from 'naive-ui';
  5 +
  6 +defineProps<{
  7 + value: string
  8 +}>()
  9 +
  10 +const emits = defineEmits(['update:value'])
  11 +
  12 +const updateValue = (value: string) => {
  13 + emits('update:value', value)
  14 +}
  15 +
  16 +</script>
  17 +
  18 +<template>
  19 + <section>
  20 + <NTag type="warning">需要后台提供专门处理sql的借口</NTag>
  21 + <SettingItemBox name="键名">
  22 + <NTag type="success" style="width: fit-content;">sql</NTag>
  23 + </SettingItemBox>
  24 + <SettingItemBox name="键值">
  25 + <MonacoEditor v-model:modelValue="value" @update:modelValue="updateValue" width="600px" height="200px"
  26 + language="sql" />
  27 + </SettingItemBox>
  28 + </section>
  29 +</template>
... ...
  1 +<script lang="ts" setup>
  2 +import { RequestDataTypeEnum, RequestDataTypeNameEnum } from '@/enums/external/httpEnum';
  3 +import { useDesign } from '@/hooks/external/useDesign';
  4 +import { CreateComponentType } from '@/packages/index.d';
  5 +import { CreateComponentGroupType } from '@/packages/index.d';
  6 +import { RequestConfigType } from '@/store/modules/chartEditStore/chartEditStore.d';
  7 +import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore';
  8 +import { GlobalComponentConfig, NButton, NDivider, NModal, NSpace, NTag } from 'naive-ui';
  9 +import { ref, unref, computed, nextTick } from 'vue';
  10 +import { PublicInterfaceForm } from '../PublicInterfaceForm';
  11 +import ComponentConfiguration from './ComponentConfiguration.vue';
  12 +import GlobalPublicConfiguration from './GlobalPublicConfiguration.vue';
  13 +import { createRequestModalContext } from './useRequestModalContext';
  14 +
  15 +const requestDataType = ref<RequestDataTypeEnum>(RequestDataTypeEnum.AJAX)
  16 +
  17 +const showModal = ref(false)
  18 +
  19 +const chartEditStore = useChartEditStore()
  20 +
  21 +const globalConfigurationEl = ref<Nullable<InstanceType<typeof GlobalPublicConfiguration>>>()
  22 +
  23 +const componentConfigurationEl = ref<Nullable<InstanceType<typeof ComponentConfiguration>>>()
  24 +
  25 +const publicInterfaceFormEl = ref<Nullable<InstanceType<typeof PublicInterfaceForm>>>()
  26 +
  27 +const getRequestTypeName = computed(() => {
  28 + return RequestDataTypeNameEnum[RequestDataTypeEnum[unref(requestDataType)] as keyof typeof RequestDataTypeEnum]
  29 +})
  30 +
  31 +const { prefixCls } = useDesign('chart-data-request')
  32 +
  33 +const openModal = async (flag = true, type: RequestDataTypeEnum) => {
  34 + requestDataType.value = type
  35 + showModal.value = flag
  36 + await nextTick()
  37 + unref(componentConfigurationEl)?.setConfigurationData(unref(selectTarget)!.request || {})
  38 + unref(publicInterfaceFormEl)?.setConfigurationData(unref(selectTarget)!.request || {})
  39 +}
  40 +
  41 +const handleSaveAndRequest = () => {
  42 + handleSaveAction()
  43 +}
  44 +
  45 +const selectTarget = computed<CreateComponentType | CreateComponentGroupType | undefined>(() => {
  46 + const selectId = chartEditStore.getTargetChart.selectId
  47 + // 排除多个
  48 + if (selectId.length !== 1) return undefined
  49 + const target = chartEditStore.componentList[chartEditStore.fetchTargetIndex()]
  50 + return target as CreateComponentType | CreateComponentGroupType
  51 +})
  52 +
  53 +const validate = async () => {
  54 + if (unref(requestDataType) === RequestDataTypeEnum.Pond) {
  55 + return await unref(publicInterfaceFormEl)?.validate()
  56 + }
  57 + return true
  58 +}
  59 +
  60 +const getResult = () => {
  61 + if (unref(requestDataType) === RequestDataTypeEnum.AJAX) {
  62 + return unref(componentConfigurationEl)?.getConfigurationData()
  63 + } else if (unref(requestDataType) === RequestDataTypeEnum.Pond) {
  64 + return unref(publicInterfaceFormEl)?.getConfigurationData()
  65 + }
  66 +
  67 + return {} as unknown as RequestConfigType
  68 +}
  69 +
  70 +const handleSaveAction = async () => {
  71 + if (!(await validate())) return
  72 + const value = getResult()
  73 + console.log(value)
  74 + if (unref(selectTarget)) {
  75 + chartEditStore.updateComponentList(chartEditStore.fetchTargetIndex(), {
  76 + ...unref(selectTarget)!,
  77 + request: value!
  78 + })
  79 + }
  80 + showModal.value = false
  81 +}
  82 +
  83 +createRequestModalContext({
  84 + requestDataType
  85 +})
  86 +
  87 +defineExpose({
  88 + openModal,
  89 +})
  90 +
  91 +</script>
  92 +
  93 +<template>
  94 + <NModal :class="prefixCls" v-model:show="showModal" preset="card" style="width: 1000px;" title="请求配置">
  95 + <GlobalPublicConfiguration v-if="requestDataType === RequestDataTypeEnum.AJAX" ref="globalConfigurationEl" />
  96 + <NDivider v-if="requestDataType === RequestDataTypeEnum.AJAX" />
  97 + <ComponentConfiguration v-if="requestDataType === RequestDataTypeEnum.AJAX" ref="componentConfigurationEl" />
  98 + <PublicInterfaceForm v-if="requestDataType === RequestDataTypeEnum.Pond" ref="publicInterfaceFormEl" />
  99 + <template #action>
  100 + <NSpace justify="space-between">
  101 + <div style="display: flex; align-items: center; ">
  102 + <span>「 更多 」</span>
  103 + <span style="margin-right: 5px;">——</span>
  104 + <NTag type="info">{{ getRequestTypeName }}</NTag>
  105 + </div>
  106 + <NButton type="primary" @click="handleSaveAndRequest">保存 & 发送请求</NButton>
  107 + </NSpace>
  108 + </template>
  109 + </NModal>
  110 +</template>
  111 +
  112 +
  113 +<style lang="scss" >
  114 +@include thingsKit('chart-data-request') {
  115 +
  116 + &.n-card.n-modal,
  117 + .n-card {
  118 + @extend .go-background-filter;
  119 + }
  120 +
  121 + .n-card-shallow {
  122 + background-color: rgba(0, 0, 0, 0) !important;
  123 + }
  124 +
  125 + @include deep() {
  126 + &>.n-card__content {
  127 + padding-right: 0;
  128 + }
  129 +
  130 + .n-card__content {
  131 + padding-bottom: 5px;
  132 + }
  133 + }
  134 +}
  135 +</style>
... ...
  1 +import { RequestDataTypeEnum } from "@/enums/external/httpEnum"
  2 +import { createContext, useContext } from "@/utils/external/useAppContext"
  3 +import { InjectionKey, Ref } from "vue"
  4 +
  5 +export interface RequestContextProps {
  6 + requestDataType: Ref<RequestDataTypeEnum>
  7 +}
  8 +
  9 +const key: InjectionKey<RequestContextProps> = Symbol()
  10 +
  11 +export const useRequestModalContext = () => {
  12 + return useContext<RequestContextProps>(key)
  13 +}
  14 +
  15 +export const createRequestModalContext = (context: RequestContextProps) => {
  16 + return createContext(context, key)
  17 +}
... ...
1 1 <template>
2 2 <div class="go-chart-configurations-data" v-if="targetData">
3 3 <setting-item-box name="请求方式" :alone="true">
4   - <n-select v-model:value="targetData.request.requestDataType" :disabled="isNotData" :options="selectOptions" />
  4 + <!-- THINGS_KIT 请求方式选项修改-->
  5 + <n-select v-if="targetData.request.requestDataType !== RequestDataTypeEnum.Pond"
  6 + v-model:value="targetData.request.requestDataType" :disabled="isNotData" :options="selectOptions" />
  7 + <n-select v-if="targetData.request.requestDataType === RequestDataTypeEnum.Pond"
  8 + v-model:value="targetData.request.requestDataType" :disabled="isNotData" :options="[
  9 + {
  10 + label: SelectCreateDataEnum.STATIC,
  11 + value: RequestDataTypeEnum.STATIC
  12 + },
  13 + {
  14 + label: SelectCreateDataEnum.AJAX,
  15 + value: RequestDataTypeEnum.Pond
  16 + }
  17 + ]" />
5 18 </setting-item-box>
6 19 <!-- 静态 -->
7 20 <chart-data-static v-if="targetData.request.requestDataType === RequestDataTypeEnum.STATIC"></chart-data-static>
  21 + <!-- THINGS_KIT 重写动态请求面板 -->
  22 + <ChartDynamicRequest v-if="targetData.request.requestDataType !== RequestDataTypeEnum.STATIC" />
8 23 <!-- 动态 -->
9   - <chart-data-ajax v-if="targetData.request.requestDataType === RequestDataTypeEnum.AJAX"></chart-data-ajax>
  24 + <!-- <chart-data-ajax v-if="targetData.request.requestDataType === RequestDataTypeEnum.AJAX"></chart-data-ajax> -->
  25 +
  26 + <!-- THINGS_KIT 隐藏公共请求面板 -->
10 27 <!-- 数据池 -->
11   - <chart-data-pond v-if="targetData.request.requestDataType === RequestDataTypeEnum.Pond"></chart-data-pond>
  28 + <!-- <chart-data-pond v-if="targetData.request.requestDataType === RequestDataTypeEnum.Pond"></chart-data-pond> -->
12 29 </div>
13 30 </template>
14 31
... ... @@ -23,7 +40,10 @@ import { SelectCreateDataType, SelectCreateDataEnum } from './index.d'
23 40
24 41 const ChartDataStatic = loadAsyncComponent(() => import('./components/ChartDataStatic/index.vue'))
25 42 const ChartDataAjax = loadAsyncComponent(() => import('./components/ChartDataAjax/index.vue'))
26   -const ChartDataPond = loadAsyncComponent(() => import('./components/ChartDataPond/index.vue'))
  43 +
  44 +// THINGS_KIT 重写动态请求面板
  45 +// const ChartDataPond = loadAsyncComponent(() => import('./components/ChartDataPond/index.vue'))
  46 +const ChartDynamicRequest = loadAsyncComponent(() => import('./external/components/ChartDynamicRequest/index.vue'))
27 47
28 48 const { targetData } = useTargetData()
29 49
... ... @@ -37,10 +57,11 @@ const selectOptions: SelectCreateDataType[] = [
37 57 label: SelectCreateDataEnum.AJAX,
38 58 value: RequestDataTypeEnum.AJAX
39 59 },
40   - {
41   - label: SelectCreateDataEnum.Pond,
42   - value: RequestDataTypeEnum.Pond
43   - }
  60 + // THINGS_KIT 隐藏公共请求面板
  61 + // {
  62 + // label: SelectCreateDataEnum.Pond,
  63 + // value: RequestDataTypeEnum.Pond
  64 + // }
44 65 ]
45 66
46 67 // 无数据源
... ...