chore:平台容器框架升级,修复命令行环境丢失的问题
This commit is contained in:
		
							parent
							
								
									3e1a1b4a66
								
							
						
					
					
						commit
						9438489a11
					
				| @ -1,12 +1,12 @@ | ||||
| <template> | ||||
|   <el-config-provider :locale="zhCn"> | ||||
|     <Suspense> | ||||
|       <router-view></router-view> | ||||
|     </Suspense> | ||||
|   </el-config-provider> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { ElConfigProvider } from 'element-plus'; | ||||
| import zhCn from 'element-plus/es/locale/lang/zh-cn'; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <ElConfigProvider :locale="zhCn"> | ||||
|     <Suspense> | ||||
|       <router-view /> | ||||
|     </Suspense> | ||||
|   </ElConfigProvider> | ||||
| </template> | ||||
|  | ||||
							
								
								
									
										165
									
								
								apps/designer/src/auto-imports.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										165
									
								
								apps/designer/src/auto-imports.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -6,83 +6,98 @@ | ||||
| // biome-ignore lint: disable
 | ||||
| export {} | ||||
| declare global { | ||||
|   const EffectScope: typeof import('vue')['EffectScope'] | ||||
|   const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] | ||||
|   const computed: typeof import('vue')['computed'] | ||||
|   const createApp: typeof import('vue')['createApp'] | ||||
|   const createPinia: typeof import('pinia')['createPinia'] | ||||
|   const customRef: typeof import('vue')['customRef'] | ||||
|   const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] | ||||
|   const defineComponent: typeof import('vue')['defineComponent'] | ||||
|   const defineStore: typeof import('pinia')['defineStore'] | ||||
|   const effectScope: typeof import('vue')['effectScope'] | ||||
|   const getActivePinia: typeof import('pinia')['getActivePinia'] | ||||
|   const getCurrentInstance: typeof import('vue')['getCurrentInstance'] | ||||
|   const getCurrentScope: typeof import('vue')['getCurrentScope'] | ||||
|   const h: typeof import('vue')['h'] | ||||
|   const inject: typeof import('vue')['inject'] | ||||
|   const isProxy: typeof import('vue')['isProxy'] | ||||
|   const isReactive: typeof import('vue')['isReactive'] | ||||
|   const isReadonly: typeof import('vue')['isReadonly'] | ||||
|   const isRef: typeof import('vue')['isRef'] | ||||
|   const mapActions: typeof import('pinia')['mapActions'] | ||||
|   const mapGetters: typeof import('pinia')['mapGetters'] | ||||
|   const mapState: typeof import('pinia')['mapState'] | ||||
|   const mapStores: typeof import('pinia')['mapStores'] | ||||
|   const mapWritableState: typeof import('pinia')['mapWritableState'] | ||||
|   const markRaw: typeof import('vue')['markRaw'] | ||||
|   const nextTick: typeof import('vue')['nextTick'] | ||||
|   const onActivated: typeof import('vue')['onActivated'] | ||||
|   const onBeforeMount: typeof import('vue')['onBeforeMount'] | ||||
|   const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] | ||||
|   const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] | ||||
|   const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] | ||||
|   const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] | ||||
|   const onDeactivated: typeof import('vue')['onDeactivated'] | ||||
|   const onErrorCaptured: typeof import('vue')['onErrorCaptured'] | ||||
|   const onMounted: typeof import('vue')['onMounted'] | ||||
|   const onRenderTracked: typeof import('vue')['onRenderTracked'] | ||||
|   const onRenderTriggered: typeof import('vue')['onRenderTriggered'] | ||||
|   const onScopeDispose: typeof import('vue')['onScopeDispose'] | ||||
|   const onServerPrefetch: typeof import('vue')['onServerPrefetch'] | ||||
|   const onUnmounted: typeof import('vue')['onUnmounted'] | ||||
|   const onUpdated: typeof import('vue')['onUpdated'] | ||||
|   const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] | ||||
|   const provide: typeof import('vue')['provide'] | ||||
|   const reactive: typeof import('vue')['reactive'] | ||||
|   const readonly: typeof import('vue')['readonly'] | ||||
|   const ref: typeof import('vue')['ref'] | ||||
|   const resolveComponent: typeof import('vue')['resolveComponent'] | ||||
|   const setActivePinia: typeof import('pinia')['setActivePinia'] | ||||
|   const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'] | ||||
|   const shallowReactive: typeof import('vue')['shallowReactive'] | ||||
|   const shallowReadonly: typeof import('vue')['shallowReadonly'] | ||||
|   const shallowRef: typeof import('vue')['shallowRef'] | ||||
|   const storeToRefs: typeof import('pinia')['storeToRefs'] | ||||
|   const toRaw: typeof import('vue')['toRaw'] | ||||
|   const toRef: typeof import('vue')['toRef'] | ||||
|   const toRefs: typeof import('vue')['toRefs'] | ||||
|   const toValue: typeof import('vue')['toValue'] | ||||
|   const triggerRef: typeof import('vue')['triggerRef'] | ||||
|   const unref: typeof import('vue')['unref'] | ||||
|   const useAttrs: typeof import('vue')['useAttrs'] | ||||
|   const useCssModule: typeof import('vue')['useCssModule'] | ||||
|   const useCssVars: typeof import('vue')['useCssVars'] | ||||
|   const useId: typeof import('vue')['useId'] | ||||
|   const useLink: typeof import('vue-router')['useLink'] | ||||
|   const useModel: typeof import('vue')['useModel'] | ||||
|   const useRoute: typeof import('vue-router')['useRoute'] | ||||
|   const useRouter: typeof import('vue-router')['useRouter'] | ||||
|   const useSlots: typeof import('vue')['useSlots'] | ||||
|   const useTemplateRef: typeof import('vue')['useTemplateRef'] | ||||
|   const watch: typeof import('vue')['watch'] | ||||
|   const watchEffect: typeof import('vue')['watchEffect'] | ||||
|   const watchPostEffect: typeof import('vue')['watchPostEffect'] | ||||
|   const watchSyncEffect: typeof import('vue')['watchSyncEffect'] | ||||
|   const EffectScope: (typeof import('vue'))['EffectScope']; | ||||
|   const acceptHMRUpdate: (typeof import('pinia'))['acceptHMRUpdate']; | ||||
|   const computed: (typeof import('vue'))['computed']; | ||||
|   const createApp: (typeof import('vue'))['createApp']; | ||||
|   const createPinia: (typeof import('pinia'))['createPinia']; | ||||
|   const customRef: (typeof import('vue'))['customRef']; | ||||
|   const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent']; | ||||
|   const defineComponent: (typeof import('vue'))['defineComponent']; | ||||
|   const defineStore: (typeof import('pinia'))['defineStore']; | ||||
|   const effectScope: (typeof import('vue'))['effectScope']; | ||||
|   const getActivePinia: (typeof import('pinia'))['getActivePinia']; | ||||
|   const getCurrentInstance: (typeof import('vue'))['getCurrentInstance']; | ||||
|   const getCurrentScope: (typeof import('vue'))['getCurrentScope']; | ||||
|   const h: (typeof import('vue'))['h']; | ||||
|   const inject: (typeof import('vue'))['inject']; | ||||
|   const isProxy: (typeof import('vue'))['isProxy']; | ||||
|   const isReactive: (typeof import('vue'))['isReactive']; | ||||
|   const isReadonly: (typeof import('vue'))['isReadonly']; | ||||
|   const isRef: (typeof import('vue'))['isRef']; | ||||
|   const mapActions: (typeof import('pinia'))['mapActions']; | ||||
|   const mapGetters: (typeof import('pinia'))['mapGetters']; | ||||
|   const mapState: (typeof import('pinia'))['mapState']; | ||||
|   const mapStores: (typeof import('pinia'))['mapStores']; | ||||
|   const mapWritableState: (typeof import('pinia'))['mapWritableState']; | ||||
|   const markRaw: (typeof import('vue'))['markRaw']; | ||||
|   const nextTick: (typeof import('vue'))['nextTick']; | ||||
|   const onActivated: (typeof import('vue'))['onActivated']; | ||||
|   const onBeforeMount: (typeof import('vue'))['onBeforeMount']; | ||||
|   const onBeforeRouteLeave: (typeof import('vue-router'))['onBeforeRouteLeave']; | ||||
|   const onBeforeRouteUpdate: (typeof import('vue-router'))['onBeforeRouteUpdate']; | ||||
|   const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount']; | ||||
|   const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate']; | ||||
|   const onDeactivated: (typeof import('vue'))['onDeactivated']; | ||||
|   const onErrorCaptured: (typeof import('vue'))['onErrorCaptured']; | ||||
|   const onMounted: (typeof import('vue'))['onMounted']; | ||||
|   const onRenderTracked: (typeof import('vue'))['onRenderTracked']; | ||||
|   const onRenderTriggered: (typeof import('vue'))['onRenderTriggered']; | ||||
|   const onScopeDispose: (typeof import('vue'))['onScopeDispose']; | ||||
|   const onServerPrefetch: (typeof import('vue'))['onServerPrefetch']; | ||||
|   const onUnmounted: (typeof import('vue'))['onUnmounted']; | ||||
|   const onUpdated: (typeof import('vue'))['onUpdated']; | ||||
|   const onWatcherCleanup: (typeof import('vue'))['onWatcherCleanup']; | ||||
|   const provide: (typeof import('vue'))['provide']; | ||||
|   const reactive: (typeof import('vue'))['reactive']; | ||||
|   const readonly: (typeof import('vue'))['readonly']; | ||||
|   const ref: (typeof import('vue'))['ref']; | ||||
|   const resolveComponent: (typeof import('vue'))['resolveComponent']; | ||||
|   const setActivePinia: (typeof import('pinia'))['setActivePinia']; | ||||
|   const setMapStoreSuffix: (typeof import('pinia'))['setMapStoreSuffix']; | ||||
|   const shallowReactive: (typeof import('vue'))['shallowReactive']; | ||||
|   const shallowReadonly: (typeof import('vue'))['shallowReadonly']; | ||||
|   const shallowRef: (typeof import('vue'))['shallowRef']; | ||||
|   const storeToRefs: (typeof import('pinia'))['storeToRefs']; | ||||
|   const toRaw: (typeof import('vue'))['toRaw']; | ||||
|   const toRef: (typeof import('vue'))['toRef']; | ||||
|   const toRefs: (typeof import('vue'))['toRefs']; | ||||
|   const toValue: (typeof import('vue'))['toValue']; | ||||
|   const triggerRef: (typeof import('vue'))['triggerRef']; | ||||
|   const unref: (typeof import('vue'))['unref']; | ||||
|   const useAttrs: (typeof import('vue'))['useAttrs']; | ||||
|   const useCssModule: (typeof import('vue'))['useCssModule']; | ||||
|   const useCssVars: (typeof import('vue'))['useCssVars']; | ||||
|   const useId: (typeof import('vue'))['useId']; | ||||
|   const useLink: (typeof import('vue-router'))['useLink']; | ||||
|   const useModel: (typeof import('vue'))['useModel']; | ||||
|   const useRoute: (typeof import('vue-router'))['useRoute']; | ||||
|   const useRouter: (typeof import('vue-router'))['useRouter']; | ||||
|   const useSlots: (typeof import('vue'))['useSlots']; | ||||
|   const useTemplateRef: (typeof import('vue'))['useTemplateRef']; | ||||
|   const watch: (typeof import('vue'))['watch']; | ||||
|   const watchEffect: (typeof import('vue'))['watchEffect']; | ||||
|   const watchPostEffect: (typeof import('vue'))['watchPostEffect']; | ||||
|   const watchSyncEffect: (typeof import('vue'))['watchSyncEffect']; | ||||
| } | ||||
| // for type re-export
 | ||||
| declare global { | ||||
|   // @ts-ignore
 | ||||
|   export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' | ||||
|   import('vue') | ||||
|   export type { | ||||
|     Component, | ||||
|     ComponentPublicInstance, | ||||
|     ComputedRef, | ||||
|     DirectiveBinding, | ||||
|     ExtractDefaultPropTypes, | ||||
|     ExtractPropTypes, | ||||
|     ExtractPublicPropTypes, | ||||
|     InjectionKey, | ||||
|     PropType, | ||||
|     Ref, | ||||
|     MaybeRef, | ||||
|     MaybeRefOrGetter, | ||||
|     VNode, | ||||
|     WritableComputedRef | ||||
|   } from 'vue'; | ||||
|   import('vue'); | ||||
| } | ||||
|  | ||||
							
								
								
									
										5
									
								
								apps/designer/src/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								apps/designer/src/env.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1,7 +1,8 @@ | ||||
| /// <reference types="vite/client" />
 | ||||
| // / <reference types="vite/client" />
 | ||||
| 
 | ||||
| declare module '*.vue' { | ||||
|   import type { DefineComponent } from 'vue'; | ||||
| 
 | ||||
|   const component: DefineComponent<{}, {}, any>; | ||||
|   export default component; | ||||
| } | ||||
| @ -12,7 +13,7 @@ declare namespace NodeJS { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| declare module global { | ||||
| declare namespace global { | ||||
|   interface Window {} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,14 +1,14 @@ | ||||
| import { type BlockSchema } from '@vtj/core'; | ||||
| import type { BlockSchema } from '@vtj/core'; | ||||
| 
 | ||||
| import instance from './instance'; | ||||
| import { fi } from 'element-plus/es/locale/index.mjs'; | ||||
| 
 | ||||
| export type LowCodeFileSchema = { | ||||
|   project_id: number; | ||||
|   publish: boolean; | ||||
|   active: boolean; | ||||
|   dsl: BlockSchema; | ||||
|   file_path?: string; | ||||
|   file_id?: string; | ||||
|   file_path?: string; | ||||
|   project_id: number; | ||||
|   publish: boolean; | ||||
| }; | ||||
| 
 | ||||
| function transformFile(file: LowCodeFileSchema): LowCodeFileSchema { | ||||
|  | ||||
| @ -1,12 +1,13 @@ | ||||
| import type { HistorySchema } from '@vtj/core'; | ||||
| 
 | ||||
| import instance from './instance'; | ||||
| import { type HistorySchema } from '@vtj/core'; | ||||
| 
 | ||||
| export type LowCodeHistorySchema = { | ||||
|   project_id: number; | ||||
|   dsl?: HistorySchema; | ||||
|   file_id: string; | ||||
|   history_id: string; | ||||
|   id?: string; | ||||
|   dsl?: HistorySchema; | ||||
|   project_id: number; | ||||
| }; | ||||
| 
 | ||||
| function transformHistoryData(data: LowCodeHistorySchema) { | ||||
| @ -20,12 +21,12 @@ export type HistoriesResponse = { | ||||
|   code: number; | ||||
|   data: { | ||||
|     list: Array<{ | ||||
|       id: number; | ||||
|       project_id: number; | ||||
|       created_at: string; | ||||
|       dsl: Record<string, any>; | ||||
|       file_id: string; | ||||
|       history_id: string; | ||||
|       dsl: Record<string, any>; | ||||
|       created_at: string; | ||||
|       id: number; | ||||
|       project_id: number; | ||||
|       updated_at: string; | ||||
|     }>; | ||||
|     total: number; | ||||
| @ -34,10 +35,10 @@ export type HistoriesResponse = { | ||||
| }; | ||||
| 
 | ||||
| export type GetHistoriesParams = { | ||||
|   project_id: number; | ||||
|   file_id: string; | ||||
|   page?: number; | ||||
|   per_page?: number; | ||||
|   project_id: number; | ||||
| }; | ||||
| 
 | ||||
| export const getHistories = async (params: GetHistoriesParams) => { | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| export * from './api'; | ||||
| export * from './application'; | ||||
| export * from './block'; | ||||
| export * from './file'; | ||||
| export * from './history'; | ||||
| export * from './materials'; | ||||
| export * from './project'; | ||||
| export * from './application'; | ||||
| export * from './history'; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { type MaterialDescription } from '@vtj/core'; | ||||
| import type { MaterialDescription } from '@vtj/core'; | ||||
| 
 | ||||
| import instance from './instance'; | ||||
| 
 | ||||
| @ -52,13 +52,13 @@ export const getMaterials = async (id: number): Promise<MaterialResponse> => { | ||||
| }; | ||||
| 
 | ||||
| type MaterialData = { | ||||
|   project_id: number; | ||||
|   value: Record<string, MaterialDescription>; | ||||
|   created_at?: string; | ||||
|   // 从原interface合并的字段
 | ||||
|   id?: number; | ||||
|   name?: string; | ||||
|   created_at?: string; | ||||
|   project_id: number; | ||||
|   updated_at?: string; | ||||
|   value: Record<string, MaterialDescription>; | ||||
| }; | ||||
| 
 | ||||
| function transformMaterialData(data: MaterialData) { | ||||
|  | ||||
| @ -1,11 +1,13 @@ | ||||
| import { createApp } from 'vue'; | ||||
| 
 | ||||
| import { createPersistedState } from 'pinia-plugin-persistedstate'; | ||||
| 
 | ||||
| import router from './router'; | ||||
| import App from './App.vue'; | ||||
| import './style/index.scss'; | ||||
| import router from './router'; | ||||
| import { pinia } from './store'; | ||||
| 
 | ||||
| import './style/index.scss'; | ||||
| 
 | ||||
| // 添加持久化插件
 | ||||
| pinia.use( | ||||
|   createPersistedState({ | ||||
|  | ||||
| @ -1,55 +1,105 @@ | ||||
| // @ts-nocheck
 | ||||
| import { | ||||
|   type ProjectSchema, | ||||
|   type BlockSchema, | ||||
|   type HistorySchema, | ||||
|   type HistoryItem, | ||||
|   type MaterialDescription, | ||||
|   type ExtensionConfig, | ||||
|   type PageFile, | ||||
|   type BlockFile, | ||||
|   type NodeFromPlugin, | ||||
|   ProjectModel, | ||||
|   HistoryModel | ||||
| import type { | ||||
|   BlockFile, | ||||
|   BlockSchema, | ||||
|   ExtensionConfig, | ||||
|   HistoryItem, | ||||
|   HistorySchema, | ||||
|   MaterialDescription, | ||||
|   NodeFromPlugin, | ||||
|   PageFile, | ||||
|   ProjectSchema | ||||
| } from '@vtj/core'; | ||||
| import { mapToObject, Storage } from '@vtj/utils'; | ||||
| import { BaseService } from '@vtj/renderer'; | ||||
| import { isEmpty } from 'licia-es'; | ||||
| 
 | ||||
| import { | ||||
|   getProject, | ||||
|   updateProject, | ||||
|   createFile, | ||||
|   updateFile as updateLowCodeFile, | ||||
|   getFile as getLowCodeFile, | ||||
|   deleteFile as deleteLowCodeFile, | ||||
|   getHistory as getLowCodeHistory, | ||||
|   deleteHistory as deleteLowCodeHistory, | ||||
|   createHistory as createLowCodeHistory, | ||||
|   deleteFile as deleteLowCodeFile, | ||||
|   deleteHistory as deleteLowCodeHistory, | ||||
|   getFile as getLowCodeFile, | ||||
|   getHistories as getLowCodeHistories, | ||||
|   publishFile as publishLowCodeFile, | ||||
|   publishAllFile as publishLowCodeAllFile, | ||||
|   getHistory as getLowCodeHistory, | ||||
|   getMaterials as getLowCodeMaterials, | ||||
|   getProject, | ||||
|   postMaterials as postLowCodeMaterials, | ||||
|   publishAllFile as publishLowCodeAllFile, | ||||
|   publishFile as publishLowCodeFile, | ||||
|   updateFile as updateLowCodeFile, | ||||
|   updateMaterials as updateLowCodeMaterials, | ||||
|   deleteMaterials as deleteLowCodeMaterials | ||||
|   updateProject | ||||
| } from '@/io'; | ||||
| // @ts-nocheck
 | ||||
| import { HistoryModel, ProjectModel } from '@vtj/core'; | ||||
| import { BaseService } from '@vtj/renderer'; | ||||
| import { mapToObject, Storage } from '@vtj/utils'; | ||||
| import { isEmpty } from 'licia-es'; | ||||
| 
 | ||||
| const storage = new Storage({ | ||||
|   type: 'local', | ||||
|   expired: 0 | ||||
| }); | ||||
| 
 | ||||
| const stringifyFields = [ | ||||
|   'config', | ||||
|   'pages', | ||||
|   'dependencies', | ||||
|   'blocks', | ||||
| const stringifyFields = new Set([ | ||||
|   'apis', | ||||
|   'meta' | ||||
| ]; | ||||
|   'blocks', | ||||
|   'config', | ||||
|   'dependencies', | ||||
|   'meta', | ||||
|   'pages' | ||||
| ]); | ||||
| 
 | ||||
| let initProject: ProjectSchema; | ||||
| 
 | ||||
| export class LowCodeService extends BaseService { | ||||
|   public api = (type: string, data: any): Promise<any> => { | ||||
|     // console.log('api', type, data);
 | ||||
|     return Promise.resolve(true); | ||||
|   }; | ||||
| 
 | ||||
|   public getExtension(): Promise<ExtensionConfig | undefined> { | ||||
|     const extension = storage.get('extension'); | ||||
|     console.log('ExtensionConfig', extension); | ||||
|     return Promise.resolve(extension as ExtensionConfig | undefined); | ||||
|   } | ||||
| 
 | ||||
|   public async getFile(id: string): Promise<BlockSchema> { | ||||
|     return getLowCodeFile(id).then((lowCodeFile) => { | ||||
|       return lowCodeFile.dsl | ||||
|         ? Promise.resolve(lowCodeFile.dsl as BlockSchema) | ||||
|         : Promise.reject(null); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   public async getHistory(fileId: string): Promise<HistorySchema> { | ||||
|     const histories = await getLowCodeHistories({ | ||||
|       project_id: initProject.id, | ||||
|       file_id: fileId, | ||||
|       per_page: 50 | ||||
|     }); | ||||
|     const formatDsl = { | ||||
|       id: histories.list[0].file_id, | ||||
|       items: histories.list.map((item) => { | ||||
|         return { | ||||
|           ...item, | ||||
|           id: item.history_id, | ||||
|           label: item.created_at | ||||
|         }; | ||||
|       }) | ||||
|     }; | ||||
|     const history = new HistoryModel(formatDsl); | ||||
|     return history.toDsl(); | ||||
|   } | ||||
| 
 | ||||
|   public async getHistoryItem(fId: string, id: string): Promise<HistoryItem> { | ||||
|     const history = await getLowCodeHistory(id); | ||||
|     return history; | ||||
|   } | ||||
| 
 | ||||
|   public getPluginMaterial( | ||||
|     from: NodeFromPlugin | ||||
|   ): Promise<MaterialDescription | null> { | ||||
|     return Promise.resolve(null); | ||||
|   } | ||||
| 
 | ||||
|   public async init(project: ProjectSchema): Promise<ProjectSchema> { | ||||
|     console.log('init', project); | ||||
|     initProject = project; | ||||
| @ -64,34 +114,85 @@ export class LowCodeService extends BaseService { | ||||
|     const model = new ProjectModel(remoteProject); | ||||
|     console.log('model', model || { id: initProject.id }); | ||||
|     const dsl = model.toDsl(); | ||||
|     return Promise.resolve(dsl); | ||||
|     return dsl; | ||||
|   } | ||||
| 
 | ||||
|   public getExtension(): Promise<ExtensionConfig | undefined> { | ||||
|     const extension = storage.get('extension'); | ||||
|     console.log('ExtensionConfig', extension); | ||||
|     return Promise.resolve(extension as ExtensionConfig | undefined); | ||||
|   public publish(project: ProjectSchema): Promise<boolean> { | ||||
|     return publishLowCodeAllFile(Number(project.id)).then((res) => { | ||||
|       return true; | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   public async saveProject(project: ProjectSchema): Promise<boolean> { | ||||
|     const newProject = { | ||||
|       ...project, | ||||
|       ...Object.fromEntries( | ||||
|         Object.entries(project) | ||||
|           .filter(([key]) => stringifyFields.includes(key)) | ||||
|           .map(([key, value]) => [key, JSON.stringify(value)]) | ||||
|       ) | ||||
|     }; | ||||
|     // 剔除引擎自行添加的 id,避免接口更新冲突报错
 | ||||
|     Reflect.deleteProperty(newProject, 'id'); | ||||
|     await updateProject(initProject.id, newProject); | ||||
|   public publishFile( | ||||
|     project: ProjectSchema, | ||||
|     file: BlockFile | PageFile | ||||
|   ): Promise<boolean> { | ||||
|     return publishLowCodeFile(file.id).then((res) => { | ||||
|       return true; | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   public async removeFile(id: string): Promise<boolean> { | ||||
|     return deleteLowCodeFile(id).then(() => true); | ||||
|   } | ||||
| 
 | ||||
|   // TODO: 做成数据库存储后没啥用,保留就行
 | ||||
|   public removeHistory(id: string): Promise<boolean> { | ||||
|     // console.log('removeHistory', id);
 | ||||
|     return Promise.resolve(true); | ||||
|   } | ||||
|   public async removeHistoryItem(fId: string, ids: string[]): Promise<boolean> { | ||||
|     await Promise.all(ids.map((id) => deleteLowCodeHistory(id))); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   public getPluginMaterial( | ||||
|     from: NodeFromPlugin | ||||
|   ): Promise<MaterialDescription | null> { | ||||
|     return Promise.resolve(null); | ||||
|   public async saveFile(file: BlockSchema): Promise<boolean> { | ||||
|     console.log('saveFile', file); | ||||
|     if (file.id) { | ||||
|       const existFile = await getLowCodeFile(file.id); | ||||
|       return existFile.file_id | ||||
|         ? updateLowCodeFile(file.id, { | ||||
|             ...existFile, | ||||
|             dsl: file | ||||
|           }) | ||||
|             .then(() => { | ||||
|               return true; | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|               throw error; | ||||
|             }) | ||||
|         : createFile({ | ||||
|             project_id: initProject.id, | ||||
|             publish: false, | ||||
|             active: true, | ||||
|             dsl: file, | ||||
|             file_id: file.id | ||||
|           }) | ||||
|             .then(() => { | ||||
|               return true; | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|               throw error; | ||||
|             }); | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   public async saveHistory(history: HistorySchema): Promise<boolean> { | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   public async saveHistoryItem( | ||||
|     fileId: string, | ||||
|     historyItem: HistoryItem | ||||
|   ): Promise<boolean> { | ||||
|     await createLowCodeHistory({ | ||||
|       project_id: initProject.id, | ||||
|       file_id: fileId, | ||||
|       history_id: historyItem.id, | ||||
|       dsl: historyItem.dsl as HistorySchema | ||||
|     }); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   // TODO: 物料存储只有在发布为其他端 (比如 uinapp) 时才有用,当前版本时不需要的,暂且保留
 | ||||
| @ -120,130 +221,25 @@ export class LowCodeService extends BaseService { | ||||
| 
 | ||||
|     // @ts-ignore
 | ||||
|     // await deleteLowCodeMaterials(project.id);
 | ||||
|     return Promise.resolve(true); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   public async saveFile(file: BlockSchema): Promise<boolean> { | ||||
|     console.log('saveFile', file); | ||||
|     if (file.id) { | ||||
|       const existFile = await getLowCodeFile(file.id); | ||||
|       if (existFile.file_id) { | ||||
|         return updateLowCodeFile(file.id, { | ||||
|           ...existFile, | ||||
|           dsl: file | ||||
|         }) | ||||
|           .then(() => { | ||||
|             return Promise.resolve(true); | ||||
|           }) | ||||
|           .catch((err) => { | ||||
|             return Promise.reject(err); | ||||
|           }); | ||||
|       } else { | ||||
|         return createFile({ | ||||
|           project_id: initProject.id, | ||||
|           publish: false, | ||||
|           active: true, | ||||
|           dsl: file, | ||||
|           file_id: file.id | ||||
|         }) | ||||
|           .then(() => { | ||||
|             return Promise.resolve(true); | ||||
|           }) | ||||
|           .catch((err) => { | ||||
|             return Promise.reject(err); | ||||
|           }); | ||||
|       } | ||||
|     } | ||||
|     return Promise.resolve(false); | ||||
|   } | ||||
| 
 | ||||
|   public async getFile(id: string): Promise<BlockSchema> { | ||||
|     return getLowCodeFile(id).then((lowCodeFile) => { | ||||
|       if (lowCodeFile.dsl) { | ||||
|         return Promise.resolve(lowCodeFile.dsl as BlockSchema); | ||||
|       } else { | ||||
|         return Promise.reject(null); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   public async removeFile(id: string): Promise<boolean> { | ||||
|     return deleteLowCodeFile(id).then(() => Promise.resolve(true)); | ||||
|   } | ||||
| 
 | ||||
|   public async saveHistory(history: HistorySchema): Promise<boolean> { | ||||
|     return Promise.resolve(true); | ||||
|   } | ||||
| 
 | ||||
|   public api = (type: string, data: any): Promise<any> => { | ||||
|     // console.log('api', type, data);
 | ||||
|     return Promise.resolve(true); | ||||
|   public async saveProject(project: ProjectSchema): Promise<boolean> { | ||||
|     const newProject = { | ||||
|       ...project, | ||||
|       ...Object.fromEntries( | ||||
|         Object.entries(project) | ||||
|           .filter(([key]) => stringifyFields.has(key)) | ||||
|           .map(([key, value]) => [key, JSON.stringify(value)]) | ||||
|       ) | ||||
|     }; | ||||
|     // 剔除引擎自行添加的 id,避免接口更新冲突报错
 | ||||
|     Reflect.deleteProperty(newProject, 'id'); | ||||
|     await updateProject(initProject.id, newProject); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   protected uploader = (file: File, projectId: string): Promise<any> => { | ||||
|     // console.log('uploader', file, projectId);
 | ||||
|     return Promise.resolve(true); | ||||
|   }; | ||||
|   // TODO: 做成数据库存储后没啥用,保留就行
 | ||||
|   public removeHistory(id: string): Promise<boolean> { | ||||
|     // console.log('removeHistory', id);
 | ||||
|     return Promise.resolve(true); | ||||
|   } | ||||
| 
 | ||||
|   public async getHistory(fileId: string): Promise<HistorySchema> { | ||||
|     const histories = await getLowCodeHistories({ | ||||
|       project_id: initProject.id, | ||||
|       file_id: fileId, | ||||
|       per_page: 50 | ||||
|     }); | ||||
|     const formatDsl = { | ||||
|       id: histories.list[0].file_id, | ||||
|       items: histories.list.map((item) => { | ||||
|         return { | ||||
|           ...item, | ||||
|           id: item.history_id, | ||||
|           label: item.created_at | ||||
|         }; | ||||
|       }) | ||||
|     }; | ||||
|     const history = new HistoryModel(formatDsl); | ||||
|     return Promise.resolve(history.toDsl()); | ||||
|   } | ||||
| 
 | ||||
|   public async getHistoryItem(fId: string, id: string): Promise<HistoryItem> { | ||||
|     const history = await getLowCodeHistory(id); | ||||
|     return Promise.resolve(history); | ||||
|   } | ||||
| 
 | ||||
|   public async saveHistoryItem( | ||||
|     fileId: string, | ||||
|     historyItem: HistoryItem | ||||
|   ): Promise<boolean> { | ||||
|     await createLowCodeHistory({ | ||||
|       project_id: initProject.id, | ||||
|       file_id: fileId, | ||||
|       history_id: historyItem.id, | ||||
|       dsl: historyItem.dsl as HistorySchema | ||||
|     }); | ||||
|     return Promise.resolve(true); | ||||
|   } | ||||
| 
 | ||||
|   public async removeHistoryItem(fId: string, ids: string[]): Promise<boolean> { | ||||
|     await Promise.all(ids.map((id) => deleteLowCodeHistory(id))); | ||||
|     return Promise.resolve(true); | ||||
|   } | ||||
|   public publish(project: ProjectSchema): Promise<boolean> { | ||||
|     return publishLowCodeAllFile(Number(project.id)).then((res) => { | ||||
|       return Promise.resolve(true); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   public publishFile( | ||||
|     project: ProjectSchema, | ||||
|     file: PageFile | BlockFile | ||||
|   ): Promise<boolean> { | ||||
|     return publishLowCodeFile(file.id).then((res) => { | ||||
|       return Promise.resolve(true); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { ref, computed } from 'vue'; | ||||
| import axios from 'axios'; | ||||
| import { computed, ref } from 'vue'; | ||||
| 
 | ||||
| import { createPinia, defineStore } from 'pinia'; | ||||
| 
 | ||||
| // 创建 pinia 实例
 | ||||
| @ -38,7 +38,7 @@ export const useUserStore = defineStore('user', () => { | ||||
| 
 | ||||
| // 应用配置 store
 | ||||
| export const useAppStore = defineStore('app', () => { | ||||
|   const theme = ref<'light' | 'dark'>('light'); | ||||
|   const theme = ref<'dark' | 'light'>('light'); | ||||
|   const sidebarCollapsed = ref(false); | ||||
| 
 | ||||
|   // 持久化配置
 | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| @use '@vtj/web/src/index.scss'; | ||||
| @use '@vtj/web/src/index'; | ||||
| 
 | ||||
| html, | ||||
| body, | ||||
| #app { | ||||
|   height: 100%; | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
|   font-size: 14px; | ||||
|   height: 100%; | ||||
|   overflow: hidden; | ||||
|   font-size: 14px; | ||||
| } | ||||
| 
 | ||||
| #vtjLink { | ||||
|  | ||||
| @ -1,14 +1,16 @@ | ||||
| import * as Vue from 'vue'; | ||||
| import * as core from '@vtj/core'; | ||||
| import * as VtjUtils from '@vtj/utils'; | ||||
| import * as VtjUI from '@vtj/ui'; | ||||
| import * as designer from '@vtj/designer'; | ||||
| import * as renderer from '@vtj/renderer'; | ||||
| import * as VtjIcons from '@vtj/icons'; | ||||
| import * as ElementPlus from 'element-plus'; | ||||
| import type { ExtensionConfig } from '@vtj/core'; | ||||
| import type { EngineOptions } from '@vtj/designer'; | ||||
| 
 | ||||
| import * as Vue from 'vue'; | ||||
| 
 | ||||
| import * as core from '@vtj/core'; | ||||
| import * as designer from '@vtj/designer'; | ||||
| import * as VtjIcons from '@vtj/icons'; | ||||
| import * as renderer from '@vtj/renderer'; | ||||
| import * as VtjUI from '@vtj/ui'; | ||||
| import * as VtjUtils from '@vtj/utils'; | ||||
| import * as ElementPlus from 'element-plus'; | ||||
| 
 | ||||
| export type ExtensionOptions = ExtensionConfig; | ||||
| export type ExtensionFactory = ( | ||||
|   config: ExtensionConfig | ||||
| @ -20,11 +22,11 @@ export interface ExtensionOutput { | ||||
| } | ||||
| 
 | ||||
| export class Extension { | ||||
|   private urls: string[] = []; | ||||
|   private __adapters__: Record<string, any> = {}; | ||||
|   private __BASE_PATH__: string = '/'; | ||||
|   private library: string = ''; | ||||
|   private params: any[] = []; | ||||
|   private __BASE_PATH__: string = '/'; | ||||
|   private __adapters__: Record<string, any> = {}; | ||||
|   private urls: string[] = []; | ||||
|   constructor(private options: ExtensionOptions) { | ||||
|     const __VTJ_PRO__ = { | ||||
|       ...core, | ||||
| @ -62,15 +64,14 @@ export class Extension { | ||||
|         .filter((n) => renderer.isJSUrl(n)) | ||||
|         .map((n) => `${base}${n}`); | ||||
|       renderer.loadCssUrl(css); | ||||
|       if (scripts.length) { | ||||
|       if (scripts.length > 0) { | ||||
|         const output = await renderer | ||||
|           .loadScriptUrl(scripts, this.library) | ||||
|           .catch(() => null); | ||||
|         if (output && typeof output === 'function') { | ||||
|           options = output.call(output, this.options, this.params); | ||||
|         } else { | ||||
|           options = output || {}; | ||||
|         } | ||||
|         options = | ||||
|           output && typeof output === 'function' | ||||
|             ? output.call(output, this.options, this.params) | ||||
|             : output || {}; | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|  | ||||
| @ -4,7 +4,7 @@ import 'element-plus/theme-chalk/index.css'; | ||||
| import '@vtj/ui/dist/style.css'; | ||||
| import '@vtj/icons/dist/style.css'; | ||||
| 
 | ||||
| export * from './extension'; | ||||
| export * from '@vtj/core'; | ||||
| export * from '@vtj/designer'; | ||||
| export * from '@vtj/renderer'; | ||||
| export * from './extension'; | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| <script lang="ts" setup> | ||||
| import { ref } from 'vue'; | ||||
| import Postmate from 'postmate'; | ||||
| import { Engine, widgetManager } from '@vtj/pro'; | ||||
| import { request, jsonp } from '@vtj/utils'; | ||||
| import { useUserStore } from '@/store'; | ||||
| 
 | ||||
| import { LowCodeService } from '@/service'; | ||||
| import { useUserStore } from '@/store'; | ||||
| import { Engine, widgetManager } from '@vtj/pro'; | ||||
| import { jsonp, request } from '@vtj/utils'; | ||||
| import Postmate from 'postmate'; | ||||
| 
 | ||||
| const container = ref(); | ||||
| const service = new LowCodeService(); | ||||
| @ -68,7 +69,7 @@ onMounted(async () => { | ||||
| 
 | ||||
| <style scoped> | ||||
| .designer-container { | ||||
|   height: 100%; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| <script lang="ts" setup> | ||||
| import { XContainer } from '@vtj/web'; | ||||
| import { ElEmpty } from 'element-plus'; | ||||
| </script> | ||||
| <template> | ||||
|   <XContainer class="not-found" fit justify="center"> | ||||
|     <ElEmpty description="找不到页面【404】"></ElEmpty> | ||||
|     <ElEmpty description="找不到页面【404】" /> | ||||
|   </XContainer> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
|   import { XContainer } from '@vtj/web'; | ||||
|   import { ElEmpty } from 'element-plus'; | ||||
| </script> | ||||
| <style lang="scss" scoped></style> | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| <template> | ||||
|   <XContainer class="unauthorized" fit justify="center"> | ||||
|     <ElEmpty description="无权限访问该页面"></ElEmpty> | ||||
|   </XContainer> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { XContainer } from '@vtj/ui'; | ||||
| import { ElEmpty } from 'element-plus'; | ||||
| </script> | ||||
| <template> | ||||
|   <XContainer class="unauthorized" fit justify="center"> | ||||
|     <ElEmpty description="无权限访问该页面" /> | ||||
|   </XContainer> | ||||
| </template> | ||||
| <style lang="scss" scoped></style> | ||||
|  | ||||
| @ -1,9 +1,8 @@ | ||||
| import { defineConfig, loadEnv } from 'vite'; | ||||
| import { createViteConfig } from '@vtj/cli'; | ||||
| import { createDevTools } from '@vtj/local'; | ||||
| import mkcert from 'vite-plugin-mkcert'; | ||||
| import AutoImport from 'unplugin-auto-import/vite'; | ||||
| import path from 'path'; | ||||
| import { defineConfig, loadEnv } from 'vite'; | ||||
| import mkcert from 'vite-plugin-mkcert'; | ||||
| 
 | ||||
| const config = createViteConfig({ | ||||
|   // proxy,
 | ||||
|  | ||||
| @ -64,7 +64,6 @@ onMounted(() => { | ||||
| .responsive-iframe { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
| 
 | ||||
|   border: none; | ||||
| } | ||||
| </style> | ||||
|  | ||||
							
								
								
									
										4
									
								
								apps/renderer/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								apps/renderer/env.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1,8 +1,8 @@ | ||||
| /// <reference types="vite/client" />
 | ||||
| // / <reference types="vite/client" />
 | ||||
| 
 | ||||
| interface ImportMeta { | ||||
|   readonly env: { | ||||
|     NODE_ENV: "development" | "production"; | ||||
|     NODE_ENV: 'development' | 'production'; | ||||
|     VITE_API_BASE?: string; | ||||
|   }; | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,24 @@ | ||||
| <script setup lang="ts"> | ||||
| import { computed, watch, ref, getCurrentInstance } from 'vue' | ||||
| import { ElLoading } from 'element-plus' | ||||
| import Postmate from 'postmate' | ||||
| import { createProvider } from '@vtj/web' | ||||
| import { useQuery } from '@tanstack/vue-query' | ||||
| import { LowCodeService } from './service' | ||||
| import { getFile } from './io' | ||||
| import { request, jsonp } from '@vtj/utils' | ||||
| import { computed, watch, ref, getCurrentInstance } from 'vue'; | ||||
| import { ElLoading } from 'element-plus'; | ||||
| import Postmate from 'postmate'; | ||||
| import { createProvider } from '@vtj/web'; | ||||
| import { useQuery } from '@tanstack/vue-query'; | ||||
| import { LowCodeService } from './service'; | ||||
| import { getFile } from './io'; | ||||
| import { request, jsonp } from '@vtj/utils'; | ||||
| // import * as VtjUI from '@vtj/ui' | ||||
| 
 | ||||
| // 响应式状态 | ||||
| const renderer = ref() | ||||
| const lowCodeService = new LowCodeService() | ||||
| const renderer = ref(); | ||||
| const lowCodeService = new LowCodeService(); | ||||
| 
 | ||||
| // Postmate 握手协议 | ||||
| const postmate = new Postmate.Model({ | ||||
|   sayHi: (data: any) => { | ||||
|     console.log('sayHi',data) | ||||
|   } | ||||
| }) | ||||
|     console.log('sayHi', data); | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| // 数据模型 | ||||
| const model = { | ||||
| @ -27,54 +27,54 @@ const model = { | ||||
|   projectId: -1, | ||||
|   fileId: '', | ||||
|   url: '', | ||||
|   accessToken: '' | ||||
| } | ||||
|   accessToken: '', | ||||
| }; | ||||
| 
 | ||||
| // 数据查询 | ||||
| const { data: file, isFetching } = useQuery({ | ||||
|   queryKey: ['getFile'], | ||||
|   queryFn: async () => { | ||||
|     await postmate.then((parent) => { | ||||
|       parent.emit('some-event', 'y-code-renderer is ready') | ||||
|       Object.assign(model, parent.model) | ||||
|       localStorage.setItem('y-code-access-token', model.accessToken || '') | ||||
|     }) | ||||
|       parent.emit('some-event', 'y-code-renderer is ready'); | ||||
|       Object.assign(model, parent.model); | ||||
|       localStorage.setItem('y-code-access-token', model.accessToken || ''); | ||||
|     }); | ||||
| 
 | ||||
|     return getFile(model.fileId).then(() => { | ||||
|       request.useRequest((req) => { | ||||
|         req.headers.set('Authorization', `Bearer ${model.accessToken}`) | ||||
|         return req | ||||
|       }) | ||||
|         req.headers.set('Authorization', `Bearer ${model.accessToken}`); | ||||
|         return req; | ||||
|       }); | ||||
|       const { provider, onReady } = createProvider({ | ||||
|         nodeEnv: import.meta.env.NODE_ENV, | ||||
|         service: lowCodeService, | ||||
|         project: { id: model.projectId }, | ||||
|         adapter: { | ||||
|           request, | ||||
|           jsonp | ||||
|         } | ||||
|       }) | ||||
|           jsonp, | ||||
|         }, | ||||
|       }); | ||||
|       onReady(async () => { | ||||
|         const instance = getCurrentInstance() | ||||
|         instance?.appContext.app.use(provider) | ||||
|         renderer.value = await provider.getRenderComponent(model.fileId) | ||||
|       }) | ||||
|     }) | ||||
|   } | ||||
| }) | ||||
|         const instance = getCurrentInstance(); | ||||
|         instance?.appContext.app.use(provider); | ||||
|         renderer.value = await provider.getRenderComponent(model.fileId); | ||||
|       }); | ||||
|     }); | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| // 加载状态监控 | ||||
| watch(isFetching, (newVal) => { | ||||
|   if (newVal) { | ||||
|     ElLoading.service({ text: '低代码文件加载中...' }) | ||||
|     ElLoading.service({ text: '低代码文件加载中...' }); | ||||
|   } else { | ||||
|     ElLoading.service().close() | ||||
|     ElLoading.service().close(); | ||||
|   } | ||||
| }) | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div style="padding: 20px;"> | ||||
|   <div style="padding: 20px"> | ||||
|     <component :is="renderer" v-if="renderer" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| export * from "./env"; | ||||
| export * from './env'; | ||||
|  | ||||
| @ -1,13 +1,14 @@ | ||||
| import { BlockSchema } from "@vtj/core"; | ||||
| import instance from "./instance"; | ||||
| import { BlockSchema } from '@vtj/core'; | ||||
| 
 | ||||
| import instance from './instance'; | ||||
| 
 | ||||
| export type LowCodeFileSchema = { | ||||
|   project_id: number; | ||||
|   publish: boolean; | ||||
|   active: boolean; | ||||
|   dsl: BlockSchema; | ||||
|   file_path?: string; | ||||
|   file_id?: string; | ||||
|   file_path?: string; | ||||
|   project_id: number; | ||||
|   publish: boolean; | ||||
| }; | ||||
| 
 | ||||
| export const getFile = async (id: string): Promise<LowCodeFileSchema> => { | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| export * from "./file"; | ||||
| export * from "./project"; | ||||
| export * from './file'; | ||||
| export * from './project'; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import axios from "axios"; | ||||
| import axios from 'axios'; | ||||
| 
 | ||||
| // 创建独立实例
 | ||||
| const instance = axios.create({ | ||||
| @ -13,7 +13,7 @@ instance.interceptors.request.use( | ||||
|   }, | ||||
|   (error) => { | ||||
|     return Promise.reject(error); | ||||
|   } | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| instance.interceptors.response.use( | ||||
| @ -22,7 +22,7 @@ instance.interceptors.response.use( | ||||
|   }, | ||||
|   (error) => { | ||||
|     return Promise.reject(error); | ||||
|   } | ||||
|   }, | ||||
| ); | ||||
| // 导出实例
 | ||||
| export default instance; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import instance from "./instance"; | ||||
| import instance from './instance'; | ||||
| 
 | ||||
| export const getProject = async (id: string) => { | ||||
|   const response = await instance.get(`/api/v1/projects/${id}`); | ||||
|  | ||||
| @ -1,18 +1,29 @@ | ||||
| import { type ProjectSchema, type BlockSchema, ProjectModel } from "@vtj/core"; | ||||
| import { BaseService } from "@vtj/renderer"; | ||||
| import { getProject, getFile as getLowCodeFile } from "@/io"; | ||||
| import type { BlockSchema, ProjectSchema } from '@vtj/core'; | ||||
| 
 | ||||
| import { getFile as getLowCodeFile, getProject } from '@/io'; | ||||
| import { ProjectModel } from '@vtj/core'; | ||||
| import { BaseService } from '@vtj/renderer'; | ||||
| 
 | ||||
| let initProject: ProjectModel = {}; | ||||
| 
 | ||||
| export class LowCodeService extends BaseService { | ||||
|   public async getFile(id: string): Promise<BlockSchema> { | ||||
|     console.log('service getFile', id); | ||||
|     return getLowCodeFile(id).then((lowCodeFile) => { | ||||
|       return lowCodeFile.dsl | ||||
|         ? Promise.resolve(lowCodeFile.dsl as BlockSchema) | ||||
|         : Promise.reject(null); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   public async init(project: ProjectSchema) { | ||||
|     console.log("init", project); | ||||
|     console.log('init', project); | ||||
|     initProject = project; | ||||
|     const remoteProject = await getProject(project.id); | ||||
|     console.log("remoteProject", remoteProject); | ||||
|     console.log('remoteProject', remoteProject); | ||||
|     const model = new ProjectModel(remoteProject); | ||||
|     const dsl = model.toDsl(); | ||||
|     return Promise.resolve(dsl); | ||||
|     return dsl; | ||||
|   } | ||||
| 
 | ||||
|   public saveProject(project: ProjectSchema): Promise<boolean> { | ||||
| @ -21,7 +32,7 @@ export class LowCodeService extends BaseService { | ||||
|       ...Object.fromEntries( | ||||
|         Object.entries(project) | ||||
|           .filter(([key]) => stringifyFields.includes(key)) | ||||
|           .map(([key, value]) => [key, JSON.stringify(value)]) | ||||
|           .map(([key, value]) => [key, JSON.stringify(value)]), | ||||
|       ), | ||||
|     }; | ||||
|     updateProject(initProject.id, newProject); | ||||
| @ -29,15 +40,4 @@ export class LowCodeService extends BaseService { | ||||
|     // storage.save(`project_${model.id}`, model.toDsl());
 | ||||
|     return Promise.resolve(true); | ||||
|   } | ||||
| 
 | ||||
|   public async getFile(id: string): Promise<BlockSchema> { | ||||
|     console.log("service getFile", id); | ||||
|     return getLowCodeFile(id).then((lowCodeFile) => { | ||||
|       if (lowCodeFile.dsl) { | ||||
|         return Promise.resolve(lowCodeFile.dsl as BlockSchema); | ||||
|       } else { | ||||
|         return Promise.reject(null); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,15 @@ | ||||
| module.exports = { | ||||
|   branches: ["main", "master"], | ||||
|   branches: ['main', 'master'], | ||||
|   plugins: [ | ||||
|     "@semantic-release/commit-analyzer", | ||||
|     "@semantic-release/release-notes-generator", | ||||
|     "@semantic-release/changelog", | ||||
|     '@semantic-release/commit-analyzer', | ||||
|     '@semantic-release/release-notes-generator', | ||||
|     '@semantic-release/changelog', | ||||
|     // "@semantic-release/npm",
 | ||||
|     [ | ||||
|       "@semantic-release/git", | ||||
|       '@semantic-release/git', | ||||
|       { | ||||
|         assets: ["CHANGELOG.md", "package.json"], | ||||
|         message: "chore(release): ${nextRelease.version} [skip ci]", | ||||
|         assets: ['CHANGELOG.md', 'package.json'], | ||||
|         message: 'chore(release): ${nextRelease.version} [skip ci]', | ||||
|       }, | ||||
|     ], | ||||
|     // "@semantic-release/github",
 | ||||
|  | ||||
							
								
								
									
										68
									
								
								apps/y-code-v1/components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								apps/y-code-v1/components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -7,39 +7,39 @@ export {} | ||||
| 
 | ||||
| declare module 'vue' { | ||||
|   export interface GlobalComponents { | ||||
|     ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] | ||||
|     ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] | ||||
|     AButton: typeof import('ant-design-vue/es')['Button'] | ||||
|     ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] | ||||
|     ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup'] | ||||
|     ACol: typeof import('ant-design-vue/es')['Col'] | ||||
|     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider'] | ||||
|     ADropdown: typeof import('ant-design-vue/es')['Dropdown'] | ||||
|     AFloatButton: typeof import('ant-design-vue/es')['FloatButton'] | ||||
|     AForm: typeof import('ant-design-vue/es')['Form'] | ||||
|     AFormItem: typeof import('ant-design-vue/es')['FormItem'] | ||||
|     AImage: typeof import('ant-design-vue/es')['Image'] | ||||
|     AInput: typeof import('ant-design-vue/es')['Input'] | ||||
|     AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] | ||||
|     AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] | ||||
|     AMenu: typeof import('ant-design-vue/es')['Menu'] | ||||
|     AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] | ||||
|     AModal: typeof import('ant-design-vue/es')['Modal'] | ||||
|     APagination: typeof import('ant-design-vue/es')['Pagination'] | ||||
|     APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] | ||||
|     ARadio: typeof import('ant-design-vue/es')['Radio'] | ||||
|     ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] | ||||
|     ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] | ||||
|     ARangePicker: typeof import('ant-design-vue/es')['RangePicker'] | ||||
|     ARow: typeof import('ant-design-vue/es')['Row'] | ||||
|     ASelect: typeof import('ant-design-vue/es')['Select'] | ||||
|     ASpace: typeof import('ant-design-vue/es')['Space'] | ||||
|     ASpin: typeof import('ant-design-vue/es')['Spin'] | ||||
|     ASwitch: typeof import('ant-design-vue/es')['Switch'] | ||||
|     ATable: typeof import('ant-design-vue/es')['Table'] | ||||
|     RouterLink: typeof import('vue-router')['RouterLink'] | ||||
|     RouterView: typeof import('vue-router')['RouterView'] | ||||
|     YChart: typeof import('./src/components/common/y-chart.vue')['default'] | ||||
|     YTable: typeof import('./src/components/common/y-table.vue')['default'] | ||||
|     ABreadcrumb: (typeof import('ant-design-vue/es'))['Breadcrumb']; | ||||
|     ABreadcrumbItem: (typeof import('ant-design-vue/es'))['BreadcrumbItem']; | ||||
|     AButton: (typeof import('ant-design-vue/es'))['Button']; | ||||
|     ACheckbox: (typeof import('ant-design-vue/es'))['Checkbox']; | ||||
|     ACheckboxGroup: (typeof import('ant-design-vue/es'))['CheckboxGroup']; | ||||
|     ACol: (typeof import('ant-design-vue/es'))['Col']; | ||||
|     AConfigProvider: (typeof import('ant-design-vue/es'))['ConfigProvider']; | ||||
|     ADropdown: (typeof import('ant-design-vue/es'))['Dropdown']; | ||||
|     AFloatButton: (typeof import('ant-design-vue/es'))['FloatButton']; | ||||
|     AForm: (typeof import('ant-design-vue/es'))['Form']; | ||||
|     AFormItem: (typeof import('ant-design-vue/es'))['FormItem']; | ||||
|     AImage: (typeof import('ant-design-vue/es'))['Image']; | ||||
|     AInput: (typeof import('ant-design-vue/es'))['Input']; | ||||
|     AInputNumber: (typeof import('ant-design-vue/es'))['InputNumber']; | ||||
|     AInputPassword: (typeof import('ant-design-vue/es'))['InputPassword']; | ||||
|     AMenu: (typeof import('ant-design-vue/es'))['Menu']; | ||||
|     AMenuItem: (typeof import('ant-design-vue/es'))['MenuItem']; | ||||
|     AModal: (typeof import('ant-design-vue/es'))['Modal']; | ||||
|     APagination: (typeof import('ant-design-vue/es'))['Pagination']; | ||||
|     APopconfirm: (typeof import('ant-design-vue/es'))['Popconfirm']; | ||||
|     ARadio: (typeof import('ant-design-vue/es'))['Radio']; | ||||
|     ARadioButton: (typeof import('ant-design-vue/es'))['RadioButton']; | ||||
|     ARadioGroup: (typeof import('ant-design-vue/es'))['RadioGroup']; | ||||
|     ARangePicker: (typeof import('ant-design-vue/es'))['RangePicker']; | ||||
|     ARow: (typeof import('ant-design-vue/es'))['Row']; | ||||
|     ASelect: (typeof import('ant-design-vue/es'))['Select']; | ||||
|     ASpace: (typeof import('ant-design-vue/es'))['Space']; | ||||
|     ASpin: (typeof import('ant-design-vue/es'))['Spin']; | ||||
|     ASwitch: (typeof import('ant-design-vue/es'))['Switch']; | ||||
|     ATable: (typeof import('ant-design-vue/es'))['Table']; | ||||
|     RouterLink: (typeof import('vue-router'))['RouterLink']; | ||||
|     RouterView: (typeof import('vue-router'))['RouterView']; | ||||
|     YChart: (typeof import('./src/components/common/y-chart.vue'))['default']; | ||||
|     YTable: (typeof import('./src/components/common/y-table.vue'))['default']; | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										2
									
								
								apps/y-code-v1/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								apps/y-code-v1/env.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| /// <reference types="vite/client" />
 | ||||
| // / <reference types="vite/client" />
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <!DOCTYPE html> | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|  | ||||
| @ -1,23 +1,28 @@ | ||||
| <script setup> | ||||
| import { | ||||
|   ConfigProvider, | ||||
|   legacyLogicalPropertiesTransformer, | ||||
|   StyleProvider, | ||||
|   theme, | ||||
| } from 'ant-design-vue'; | ||||
| import zhCN from 'ant-design-vue/es/locale/zh_CN'; | ||||
| import { legacyLogicalPropertiesTransformer, StyleProvider, ConfigProvider, theme  } from 'ant-design-vue'; | ||||
| import dayjs from 'dayjs'; | ||||
| 
 | ||||
| import 'dayjs/locale/zh-cn'; | ||||
| 
 | ||||
| const { compactAlgorithm } = theme; | ||||
| 
 | ||||
| 
 | ||||
| dayjs.locale('zh-cn'); | ||||
| 
 | ||||
| ConfigProvider.config({ | ||||
|   prefixCls: 'ycode-ant', | ||||
| }) | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <a-config-provider | ||||
|     :theme="{ | ||||
|       algorithm: [ compactAlgorithm], | ||||
|       algorithm: [compactAlgorithm], | ||||
|     }" | ||||
|     :locale="zhCN" | ||||
|     :transformers="[legacyLogicalPropertiesTransformer]" | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { get } from "@/utils/request"; | ||||
| import { get } from '@/utils/request'; | ||||
| 
 | ||||
| export interface UserInfoType { | ||||
|   alias: string; | ||||
| @ -17,18 +17,18 @@ export interface UserInfoType { | ||||
| 
 | ||||
| interface DropListItem { | ||||
|   label: string; | ||||
|   value: string | number; | ||||
|   value: number | string; | ||||
|   mark: string; | ||||
| } | ||||
| 
 | ||||
| export const getUserInfo = () => | ||||
|   get<UserInfoType>({ | ||||
|     url: "/api/home/grade", | ||||
|     url: '/api/home/grade', | ||||
|   }); | ||||
| 
 | ||||
| export const logout = () => get({ url: "/api/common/logout" }); | ||||
| export const logout = () => get({ url: '/api/common/logout' }); | ||||
| 
 | ||||
| export const getProjectDrop = () => | ||||
|   get<DropListItem[]>({ | ||||
|     url: "/api/v1/project/get-project-drop", | ||||
|     url: '/api/v1/project/get-project-drop', | ||||
|   }); | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| import { post } from "@/utils/request"; | ||||
| import { post } from '@/utils/request'; | ||||
| 
 | ||||
| interface PreviewItemParams { | ||||
|   previewId: string | number; | ||||
|   filter?: string | []; | ||||
|   previewId: number | string; | ||||
|   filter?: [] | string; | ||||
|   page?: number; | ||||
|   perPage?: number; | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,78 @@ | ||||
| <script setup> | ||||
| import { computed, ref } from 'vue'; | ||||
| 
 | ||||
| import Column from '@/plugins/antv-g2plot/column.vue'; | ||||
| import Line from '@/plugins/antv-g2plot/line.vue'; | ||||
| import { cloneDeep } from 'lodash-es'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   title: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   chartCfg: { | ||||
|     type: Object, | ||||
|     default: () => ({}), | ||||
|   }, | ||||
|   filterConfig: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
| }); | ||||
| const emit = defineEmits(['toFilt']); | ||||
| 
 | ||||
| const chartType = ref('line'); | ||||
| const dateType = ref('day'); | ||||
| const filterData = ref({}); | ||||
| 
 | ||||
| const rangePicker = computed(() => { | ||||
|   switch (dateType.value) { | ||||
|     case 'month': { | ||||
|       return 'month'; | ||||
|     } | ||||
|     case 'week': { | ||||
|       return 'week'; | ||||
|     } | ||||
|     default: { | ||||
|       return 'date'; | ||||
|     } | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| const currentChart = computed(() => { | ||||
|   return props.chartCfg[chartType.value]; | ||||
| }); | ||||
| 
 | ||||
| const toFilt = () => { | ||||
|   const cloneFilter = cloneDeep(props.filterConfig); | ||||
|   const filter = cloneFilter | ||||
|     .filter((item) => { | ||||
|       return ( | ||||
|         filterData.value[item.name] !== undefined && | ||||
|         filterData.value[item.name] !== null | ||||
|       ); | ||||
|     }) | ||||
|     .map((item) => { | ||||
|       return item.type === 'time' | ||||
|         ? { | ||||
|             name: item.name, | ||||
|             type: item.type, | ||||
|             start_time: filterData.value[item.name][0].format('YYYY-MM-DD'), | ||||
|             end_time: filterData.value[item.name][1].format('YYYY-MM-DD'), | ||||
|             date_type: dateType.value, | ||||
|           } | ||||
|         : { | ||||
|             name: item.name, | ||||
|             type: item.type, | ||||
|             value: filterData.value[item.name], | ||||
|           }; | ||||
|     }); | ||||
|   emit('toFilt', { | ||||
|     filter, | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="chart-show-box"> | ||||
|     <div class="chart-name"> | ||||
| @ -38,72 +113,6 @@ | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { computed, ref } from "vue"; | ||||
| import Line from "@/plugins/antv-g2plot/line.vue"; | ||||
| import Column from "@/plugins/antv-g2plot/column.vue"; | ||||
| import { cloneDeep } from "lodash-es"; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   title: { | ||||
|     type: String, | ||||
|     default: "", | ||||
|   }, | ||||
|   chartCfg: { | ||||
|     type: Object, | ||||
|     default: () => ({}), | ||||
|   }, | ||||
|   filterConfig: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
| }); | ||||
| const emit = defineEmits(["toFilt"]); | ||||
| 
 | ||||
| const chartType = ref("line"); | ||||
| const dateType = ref("day"); | ||||
| const filterData = ref({}); | ||||
| 
 | ||||
| const rangePicker = computed(() => { | ||||
|   switch(dateType.value) { | ||||
|     case 'week': | ||||
|       return 'week'; | ||||
|     case 'month': | ||||
|       return 'month'; | ||||
|     default: | ||||
|       return 'date'; | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| const currentChart = computed(() => { | ||||
|   return props.chartCfg[chartType.value]; | ||||
| }) | ||||
| 
 | ||||
| const toFilt = () => { | ||||
|   const cloneFilter = cloneDeep(props.filterConfig); | ||||
|   const filter = cloneFilter | ||||
|     .filter((item) => { | ||||
|       return filterData.value[item.name] !== undefined && filterData.value[item.name] !== null; | ||||
|     }) | ||||
|     .map((item) => { | ||||
|       return item.type === 'time' ? { | ||||
|         name: item.name, | ||||
|         type: item.type, | ||||
|         start_time: filterData.value[item.name][0].format('YYYY-MM-DD'), | ||||
|         end_time: filterData.value[item.name][1].format('YYYY-MM-DD'), | ||||
|         date_type: dateType.value, | ||||
|       } : { | ||||
|         name: item.name, | ||||
|         type: item.type, | ||||
|         value: filterData.value[item.name], | ||||
|       } | ||||
|     }) | ||||
|   emit('toFilt', { | ||||
|     filter, | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| .chart-wrap { | ||||
|   padding: 20px; | ||||
|  | ||||
| @ -1,3 +1,139 @@ | ||||
| <script setup> | ||||
| import { reactive, ref, watch } from 'vue'; | ||||
| 
 | ||||
| import { CloudDownloadOutlined } from '@ant-design/icons-vue'; | ||||
| import { useDebounceFn } from '@vueuse/core'; | ||||
| import { cloneDeep } from 'lodash-es'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   previewId: { | ||||
|     type: Number, | ||||
|     default: null, | ||||
|   }, | ||||
|   filterConfig: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   columnConfig: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   dataList: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   total: { | ||||
|     type: Number, | ||||
|     default: 0, | ||||
|   }, | ||||
|   title: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   isExport: { | ||||
|     type: Number, | ||||
|     default: 0, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(['toFilt']); | ||||
| 
 | ||||
| const YCODE_BASEURL = import.meta.env.VITE_YCODE_BASEURL; | ||||
| 
 | ||||
| const filterData = ref({}); | ||||
| 
 | ||||
| const pageState = reactive({ | ||||
|   page: 1, | ||||
|   perPage: 20, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.filterConfig, | ||||
|   (newVal) => { | ||||
|     newVal.forEach((item) => { | ||||
|       // 给数值区间类型赋初始值,防止报错 | ||||
|       if (item.type === 'number_range' && !filterData.value[item.name]) { | ||||
|         filterData.value[item.name] = { | ||||
|           min: undefined, | ||||
|           max: undefined, | ||||
|         }; | ||||
|       } | ||||
|     }); | ||||
|   }, | ||||
|   { immediate: true }, | ||||
| ); | ||||
| 
 | ||||
| const filterOption = (input, option) => { | ||||
|   return option.label.toLowerCase().includes(input.toLowerCase()); | ||||
| }; | ||||
| 
 | ||||
| const getFilter = () => { | ||||
|   const cloneFilter = cloneDeep(props.filterConfig); | ||||
|   const filter = cloneFilter | ||||
|     .filter((item) => { | ||||
|       return ( | ||||
|         filterData.value[item.name] !== undefined && | ||||
|         filterData.value[item.name] !== null | ||||
|       ); | ||||
|     }) | ||||
|     .map((item) => { | ||||
|       if (item.type === 'time' && filterData.value[item.name]) { | ||||
|         // 日期类型的参数 | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           start_time: filterData.value[item.name][0].format('YYYY-MM-DD'), | ||||
|           end_time: filterData.value[item.name][1].format('YYYY-MM-DD'), | ||||
|         }; | ||||
|       } else if (item.type === 'date_time' && filterData.value[item.name]) { | ||||
|         // 带时分的日期类型参数 | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           start_time: `${filterData.value[item.name][0].format('YYYY-MM-DD HH:mm')}:00`, | ||||
|           end_time: `${filterData.value[item.name][1].format('YYYY-MM-DD HH:mm')}:59`, | ||||
|         }; | ||||
|       } else if (item.type === 'number_range') { | ||||
|         // 数值区间 | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           min: filterData.value[item.name].min | ||||
|             ? String(filterData.value[item.name].min) | ||||
|             : '', | ||||
|           max: filterData.value[item.name].max | ||||
|             ? String(filterData.value[item.name].max) | ||||
|             : '', | ||||
|         }; | ||||
|       } else { | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           value: filterData.value[item.name], | ||||
|         }; | ||||
|       } | ||||
|     }); | ||||
|   return filter; | ||||
| }; | ||||
| 
 | ||||
| const getData = () => { | ||||
|   emit('toFilt', { | ||||
|     filter: getFilter(), | ||||
|     page: pageState.page, | ||||
|     perPage: pageState.perPage, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const toFilt = useDebounceFn(() => { | ||||
|   pageState.page = 1; | ||||
|   getData(); | ||||
| }, 500); | ||||
| 
 | ||||
| const pageChange = () => { | ||||
|   getData(); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="y-table-container"> | ||||
|     <div class="y-table-name"> | ||||
| @ -21,7 +157,7 @@ | ||||
|           :filter-option="filterOption" | ||||
|           v-model:value="filterData[item.name]" | ||||
|           @change="toFilt" | ||||
|         ></a-select> | ||||
|         /> | ||||
|         <!-- 输入框 --> | ||||
|         <a-input | ||||
|           v-else-if="item.type === 'text'" | ||||
| @ -71,8 +207,7 @@ | ||||
|         <a | ||||
|           :href="`${YCODE_BASEURL}/api/v1/preview/export?preview_id=${previewId}&filter=${JSON.stringify(getFilter())}`" | ||||
|           target="_blank" | ||||
|           ><a-button type="primary"><CloudDownloadOutlined />导出</a-button></a | ||||
|         > | ||||
|           ><a-button type="primary"><CloudDownloadOutlined />导出</a-button></a> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="y-table-content"> | ||||
| @ -94,8 +229,7 @@ | ||||
|             v-else-if="column.show_type === 'link'" | ||||
|             target="_blank" | ||||
|             :href="record[column.dataIndex]" | ||||
|             >{{ record[column.dataIndex] }}</a | ||||
|           > | ||||
|             >{{ record[column.dataIndex] }}</a> | ||||
|           <div | ||||
|             v-else-if="column.show_type === 'richText'" | ||||
|             v-html="record[column.dataIndex]" | ||||
| @ -111,136 +245,13 @@ | ||||
|         :show-size-changer="false" | ||||
|         size="small" | ||||
|         class="pagination-box" | ||||
|         :show-total="total => `共 ${total} 条`" | ||||
|         :show-total="(total) => `共 ${total} 条`" | ||||
|         @change="pageChange" | ||||
|       /> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { reactive, ref, watch } from "vue"; | ||||
| import { useDebounceFn } from "@vueuse/core"; | ||||
| import { cloneDeep } from "lodash-es"; | ||||
| import { CloudDownloadOutlined } from "@ant-design/icons-vue"; | ||||
| 
 | ||||
| const YCODE_BASEURL = import.meta.env.VITE_YCODE_BASEURL | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   previewId: { | ||||
|     type: Number, | ||||
|     default: null, | ||||
|   }, | ||||
|   filterConfig: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   columnConfig: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   dataList: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   total: { | ||||
|     type: Number, | ||||
|     default: 0, | ||||
|   }, | ||||
|   title: { | ||||
|     type: String, | ||||
|     default: "", | ||||
|   }, | ||||
|   isExport: { | ||||
|     type: Number, | ||||
|     default: 0, | ||||
|   }, | ||||
| }); | ||||
| const emit = defineEmits(["toFilt"]); | ||||
| 
 | ||||
| const filterData = ref({}); | ||||
| 
 | ||||
| const pageState = reactive({ | ||||
|   page: 1, | ||||
|   perPage: 20, | ||||
| }); | ||||
| 
 | ||||
| watch(() => props.filterConfig, (newVal) => { | ||||
|   newVal.forEach((item) => { | ||||
|     // 给数值区间类型赋初始值,防止报错 | ||||
|     if (item.type === 'number_range' && !filterData.value[item.name]) { | ||||
|       filterData.value[item.name] = { | ||||
|         min: undefined, | ||||
|         max: undefined, | ||||
|       }; | ||||
|     } | ||||
|   }); | ||||
| }, { immediate: true }); | ||||
| 
 | ||||
| const filterOption = (input, option) => { | ||||
|   return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0; | ||||
| }; | ||||
| 
 | ||||
| const getFilter = () => { | ||||
|   const cloneFilter = cloneDeep(props.filterConfig); | ||||
|   const filter = cloneFilter | ||||
|     .filter((item) => { | ||||
|       return filterData.value[item.name] !== undefined && filterData.value[item.name] !== null; | ||||
|     }) | ||||
|     .map((item) => { | ||||
|       if (item.type === 'time' && filterData.value[item.name]) { | ||||
|         // 日期类型的参数 | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           start_time: filterData.value[item.name][0].format('YYYY-MM-DD'), | ||||
|           end_time: filterData.value[item.name][1].format('YYYY-MM-DD'), | ||||
|         }; | ||||
|       } else if (item.type === 'date_time' && filterData.value[item.name]) { | ||||
|         // 带时分的日期类型参数 | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           start_time: filterData.value[item.name][0].format('YYYY-MM-DD HH:mm') + ':00', | ||||
|           end_time: filterData.value[item.name][1].format('YYYY-MM-DD HH:mm') + ':59', | ||||
|         }; | ||||
|       } else if (item.type === 'number_range') { | ||||
|         // 数值区间 | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           min: filterData.value[item.name].min ? String(filterData.value[item.name].min) : '', | ||||
|           max: filterData.value[item.name].max ? String(filterData.value[item.name].max) : '', | ||||
|         }; | ||||
|       } else { | ||||
|         return { | ||||
|           name: item.name, | ||||
|           type: item.type, | ||||
|           value: filterData.value[item.name], | ||||
|         }; | ||||
|       } | ||||
|     }); | ||||
|   return filter | ||||
| }; | ||||
| 
 | ||||
| const getData = () => { | ||||
|   emit("toFilt", { | ||||
|     filter: getFilter(), | ||||
|     page: pageState.page, | ||||
|     perPage: pageState.perPage, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const toFilt = useDebounceFn(() => { | ||||
|   pageState.page = 1 | ||||
|   getData(); | ||||
| }, 500); | ||||
| 
 | ||||
| const pageChange = () => { | ||||
|   getData(); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| .y-table-name { | ||||
|   margin-bottom: 10px; | ||||
|  | ||||
| @ -1,13 +1,14 @@ | ||||
| // @primary-bg-color: #f8f8f8; | ||||
| @import "../src/assets/styles/variable.less"; | ||||
| @import '../src/assets/styles/variable.less'; | ||||
| 
 | ||||
| html, | ||||
| body { | ||||
|   background-color: @primary-bg-color; | ||||
|   height: 100%; | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
|   height: 100%; | ||||
|   background-color: @primary-bg-color; | ||||
| } | ||||
| 
 | ||||
| #app { | ||||
|   height: 100%; | ||||
| } | ||||
| @ -24,23 +25,25 @@ body { | ||||
| 
 | ||||
| /* 滚动条滑块 */ | ||||
| ::-webkit-scrollbar-thumb { | ||||
|   background-color: rgba(0, 0, 0, 0.15); | ||||
|   background-color: rgb(0 0 0 / 15%); | ||||
|   border-radius: 15px; | ||||
| } | ||||
| 
 | ||||
| /* 滚动条滑块悬停 */ | ||||
| ::-webkit-scrollbar-thumb:hover { | ||||
|   background-color: rgba(0, 0, 0, 0.25); | ||||
|   background-color: rgb(0 0 0 / 25%); | ||||
| } | ||||
| 
 | ||||
| .normal-container { | ||||
|   padding: 16px 24px; | ||||
|   border-radius: 6px; | ||||
|   background-color: #fff; | ||||
|   border-radius: 6px; | ||||
| } | ||||
| 
 | ||||
| .mt-8 { | ||||
|   margin-top: 8px; | ||||
| } | ||||
| 
 | ||||
| .mt-16 { | ||||
|   margin-top: 16px; | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,23 @@ | ||||
| <script setup lang="ts"> | ||||
| import { computed, onMounted, ref } from "vue"; | ||||
| import Header from "./components/Header.vue"; | ||||
| import Sider from "./components/Sider.vue"; | ||||
| import { computed, onMounted, ref } from 'vue'; | ||||
| 
 | ||||
| import { | ||||
|   FullscreenExitOutlined, | ||||
|   MenuFoldOutlined, | ||||
|   MenuUnfoldOutlined, | ||||
|   FullscreenExitOutlined, | ||||
| } from "@ant-design/icons-vue"; | ||||
| import { useEventListener } from "@vueuse/core"; | ||||
| import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper' | ||||
| } from '@ant-design/icons-vue'; | ||||
| import { useEventListener } from '@vueuse/core'; | ||||
| import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'; | ||||
| 
 | ||||
| import Header from './components/Header.vue'; | ||||
| import Sider from './components/Sider.vue'; | ||||
| 
 | ||||
| const __POWERED_BY_QIANKUN__ = computed(() => { | ||||
|   return qiankunWindow.__POWERED_BY_QIANKUN__ || window?.proxy?.__POWERED_BY_QIANKUN__ | ||||
| }) | ||||
|   return ( | ||||
|     qiankunWindow.__POWERED_BY_QIANKUN__ || | ||||
|     window?.proxy?.__POWERED_BY_QIANKUN__ | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| // const userInfoStore = useUserInfoStore(); | ||||
| const isCollapsed = ref(false); | ||||
| @ -23,7 +28,7 @@ onMounted(() => { | ||||
|   // userInfoStore.fetchUserInfo(); | ||||
| }); | ||||
| 
 | ||||
| useEventListener(window, "fullscreenchange", () => { | ||||
| useEventListener(window, 'fullscreenchange', () => { | ||||
|   isFullscreen.value = !!document.fullscreenElement; | ||||
| }); | ||||
| 
 | ||||
| @ -43,7 +48,7 @@ const handleExitFullscreen = () => { | ||||
|       class="left-aside" | ||||
|       :class="{ 'left-aside-collapsed': isCollapsed }" | ||||
|     > | ||||
|       <Sider :inlineCollapsed="isCollapsed" /> | ||||
|       <Sider :inline-collapsed="isCollapsed" /> | ||||
|       <div class="collapsed-icon"> | ||||
|         <component | ||||
|           :is="isCollapsed ? MenuUnfoldOutlined : MenuFoldOutlined" | ||||
| @ -60,7 +65,7 @@ const handleExitFullscreen = () => { | ||||
|       ref="container" | ||||
|     > | ||||
|       <header class="header"> | ||||
|         <Header @requestFullscreen="handleFullscreen" /> | ||||
|         <Header @request-fullscreen="handleFullscreen" /> | ||||
|       </header> | ||||
|       <div class="i-container"> | ||||
|         <router-view /> | ||||
|  | ||||
| @ -1,37 +1,40 @@ | ||||
| import { createApp } from "vue"; | ||||
| import { createPinia } from "pinia"; | ||||
| import App from "./App.vue"; | ||||
| import { createProjectRouter } from "./router"; | ||||
| import "./global.less"; | ||||
| import VueGridLayout from "vue-grid-layout"; // 引入layout
 | ||||
| import { createApp } from 'vue'; | ||||
| import VueGridLayout from 'vue-grid-layout'; // 引入layout
 | ||||
| 
 | ||||
| import { createPinia } from 'pinia'; | ||||
| import { | ||||
|   renderWithQiankun, | ||||
|   qiankunWindow, | ||||
| } from "vite-plugin-qiankun/dist/helper"; | ||||
|   renderWithQiankun, | ||||
| } from 'vite-plugin-qiankun/dist/helper'; | ||||
| 
 | ||||
| import App from './App.vue'; | ||||
| import { createProjectRouter } from './router'; | ||||
| 
 | ||||
| import './global.less'; | ||||
| 
 | ||||
| let app; | ||||
| function render(props: Object = {}) { | ||||
| function render(props: object = {}) { | ||||
|   app = createApp(App); | ||||
|   setStyleSheet(props.styles); | ||||
|   const router = createProjectRouter(props.base); | ||||
|   app.use(router); | ||||
|   app.use(VueGridLayout); | ||||
|   app.use(createPinia()); | ||||
|   app.mount("#y-code-app"); | ||||
|   app.mount('#y-code-app'); | ||||
| } | ||||
| 
 | ||||
| function setStyleSheet(styles: Object = {}) { | ||||
|   const styleEle = document.createElement("style"); | ||||
|   styleEle.type = "text/css"; | ||||
| function setStyleSheet(styles: object = {}) { | ||||
|   const styleEle = document.createElement('style'); | ||||
|   styleEle.type = 'text/css'; | ||||
|   styleEle.innerHTML = ` | ||||
|   :root { | ||||
|     --primary-color: ${styles.primaryColor || "#1677ff"}; | ||||
|     --primary-light-color: ${styles.primaryLightColor || "#4096ff"}; | ||||
|     --table-head-bg-color: ${styles.tableHeadBgColor || "#fafafa"}; | ||||
|     --table-head-font-color: ${styles.tableHeadFontColor || "#191919"}; | ||||
|     --primary-color: ${styles.primaryColor || '#1677ff'}; | ||||
|     --primary-light-color: ${styles.primaryLightColor || '#4096ff'}; | ||||
|     --table-head-bg-color: ${styles.tableHeadBgColor || '#fafafa'}; | ||||
|     --table-head-font-color: ${styles.tableHeadFontColor || '#191919'}; | ||||
|   } | ||||
|   `;
 | ||||
|   document.head.appendChild(styleEle); | ||||
|   document.head.append(styleEle); | ||||
| } | ||||
| 
 | ||||
| const __POWERED_BY_QIANKUN__ = | ||||
| @ -41,16 +44,16 @@ const __POWERED_BY_QIANKUN__ = | ||||
| if (__POWERED_BY_QIANKUN__) { | ||||
|   renderWithQiankun({ | ||||
|     bootstrap() { | ||||
|       console.log("bootstrap"); | ||||
|       console.log('bootstrap'); | ||||
|       return Promise.resolve(); | ||||
|     }, | ||||
|     mount(props) { | ||||
|       console.log("mount"); | ||||
|       console.log('mount'); | ||||
|       render(props); | ||||
|       return Promise.resolve(); | ||||
|     }, | ||||
|     unmount() { | ||||
|       console.log("unmount"); | ||||
|       console.log('unmount'); | ||||
|       if (app) { | ||||
|         app.unmount(); | ||||
|       } | ||||
|  | ||||
| @ -1,16 +1,13 @@ | ||||
| <template> | ||||
|   <div :class="className" :style="style" ref="container"></div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { Column } from "@antv/g2plot"; | ||||
| import { Column } from '@antv/g2plot'; | ||||
| 
 | ||||
| // hooks | ||||
| import useChart from "./useChart"; | ||||
| import useChart from './useChart'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   className: { | ||||
|     type: String, | ||||
|     default: "", | ||||
|     default: '', | ||||
|   }, | ||||
|   style: { | ||||
|     type: Object, | ||||
| @ -24,3 +21,7 @@ const props = defineProps({ | ||||
| 
 | ||||
| const { container } = useChart(Column, props); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div :class="className" :style="style" ref="container"></div> | ||||
| </template> | ||||
|  | ||||
| @ -1,17 +1,13 @@ | ||||
| <template> | ||||
|   <div :class="className" :style="style" ref="container"></div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { Line } from '@antv/g2plot'; | ||||
| 
 | ||||
| import { Line } from "@antv/g2plot"; | ||||
| // hooks | ||||
| import useChart from "./useChart"; | ||||
| import useChart from './useChart'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   className: { | ||||
|     type: String, | ||||
|     default: "", | ||||
|     default: '', | ||||
|   }, | ||||
|   style: { | ||||
|     type: Object, | ||||
| @ -25,3 +21,7 @@ const props = defineProps({ | ||||
| 
 | ||||
| const { container } = useChart(Line, props); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div :class="className" :style="style" ref="container"></div> | ||||
| </template> | ||||
|  | ||||
| @ -1,16 +1,13 @@ | ||||
| <template> | ||||
|   <div :class="className" :style="style" ref="container"></div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { Pie } from "@antv/g2plot"; | ||||
| import { Pie } from '@antv/g2plot'; | ||||
| 
 | ||||
| // hooks | ||||
| import useChart from "./useChart"; | ||||
| import useChart from './useChart'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   className: { | ||||
|     type: String, | ||||
|     default: "", | ||||
|     default: '', | ||||
|   }, | ||||
|   style: { | ||||
|     type: Object, | ||||
| @ -24,3 +21,7 @@ const props = defineProps({ | ||||
| 
 | ||||
| const { container } = useChart(Pie, props); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div :class="className" :style="style" ref="container"></div> | ||||
| </template> | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { onBeforeUnmount, onMounted, ref, watch } from "vue"; | ||||
| import { cloneDeep } from "lodash-es"; | ||||
| import { onBeforeUnmount, onMounted, ref, watch } from 'vue'; | ||||
| 
 | ||||
| import { cloneDeep } from 'lodash-es'; | ||||
| 
 | ||||
| export default function useChart(ChartClass, props) { | ||||
|   const chart = ref(null); // 表格实例
 | ||||
| @ -35,12 +36,12 @@ export default function useChart(ChartClass, props) { | ||||
|         onEvent(chartInstance, event); | ||||
|       } | ||||
|     }; | ||||
|     chartInstance.on("*", handler); | ||||
|     chartInstance.on('*', handler); | ||||
|   }); | ||||
| 
 | ||||
|   onBeforeUnmount(() => { | ||||
|     chart.value.destroy(); | ||||
|     chart.value.off("*", handler); | ||||
|     chart.value.off('*', handler); | ||||
|     chart.value = undefined; | ||||
|   }); | ||||
| 
 | ||||
| @ -54,32 +55,32 @@ export default function useChart(ChartClass, props) { | ||||
|     }, | ||||
|     { | ||||
|       deep: true, | ||||
|     } | ||||
|     }, | ||||
|   ); | ||||
| 
 | ||||
|   const toDataURL = (type = "image/png", encoderOptions) => { | ||||
|   const toDataURL = (type = 'image/png', encoderOptions) => { | ||||
|     return chart.value?.chart.canvas.cfg.el.toDataURL(type, encoderOptions); | ||||
|   }; | ||||
| 
 | ||||
|   const downloadImage = ( | ||||
|     name = "download", | ||||
|     type = "image/png", | ||||
|     encoderOptions | ||||
|     name = 'download', | ||||
|     type = 'image/png', | ||||
|     encoderOptions, | ||||
|   ) => { | ||||
|     let imageName = name; | ||||
|     if (name.indexOf(".") === -1) { | ||||
|       imageName = `${name}.${type.split("/")[1]}`; | ||||
|     if (!name.includes('.')) { | ||||
|       imageName = `${name}.${type.split('/')[1]}`; | ||||
|     } | ||||
|     const base64 = chart.value?.chart.canvas.cfg.el.toDataURL( | ||||
|       type, | ||||
|       encoderOptions | ||||
|       encoderOptions, | ||||
|     ); | ||||
|     let a = document.createElement("a"); | ||||
|     let a = document.createElement('a'); | ||||
|     a.href = base64; | ||||
|     a.download = imageName; | ||||
|     document.body.appendChild(a); | ||||
|     document.body.append(a); | ||||
|     a.click(); | ||||
|     document.body.removeChild(a); | ||||
|     a.remove(); | ||||
|     a = null; | ||||
|     return imageName; | ||||
|   }; | ||||
|  | ||||
| @ -1,16 +1,20 @@ | ||||
| import { createRouter, createWebHistory, type Router } from "vue-router"; | ||||
| import { titleGuard } from "./guards"; | ||||
| import routeList from "./routes"; | ||||
| import { qiankunWindow } from "vite-plugin-qiankun/dist/helper"; | ||||
| import type { Router } from 'vue-router'; | ||||
| 
 | ||||
| let router: Router | null = null; | ||||
| export const createProjectRouter = (base = "") => { | ||||
| import { createRouter, createWebHistory } from 'vue-router'; | ||||
| 
 | ||||
| import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'; | ||||
| 
 | ||||
| import { titleGuard } from './guards'; | ||||
| import routeList from './routes'; | ||||
| 
 | ||||
| let router: null | Router = null; | ||||
| export const createProjectRouter = (base = '') => { | ||||
|   const __POWERED_BY_QIANKUN__ = | ||||
|     qiankunWindow?.__POWERED_BY_QIANKUN__ || | ||||
|     window.proxy?.__POWERED_BY_QIANKUN__; | ||||
|   router = createRouter({ | ||||
|     history: createWebHistory( | ||||
|       base || (__POWERED_BY_QIANKUN__ ? "/y-code-app/" : "") | ||||
|       base || (__POWERED_BY_QIANKUN__ ? '/y-code-app/' : ''), | ||||
|     ), | ||||
|     routes: routeList, | ||||
|   }); | ||||
|  | ||||
| @ -1,11 +1,13 @@ | ||||
| import Layout from "@/layout/index.vue"; | ||||
| import type { RendererElement, RendererNode, VNode } from 'vue'; | ||||
| 
 | ||||
| import { h } from 'vue'; | ||||
| 
 | ||||
| import Layout from '@/layout/index.vue'; | ||||
| import { | ||||
|   HomeOutlined, | ||||
|   BarChartOutlined, | ||||
|   AppstoreOutlined, | ||||
| } from "@ant-design/icons-vue"; | ||||
| import { h } from "vue"; | ||||
| import type { VNode, RendererNode, RendererElement } from "vue"; | ||||
|   BarChartOutlined, | ||||
|   HomeOutlined, | ||||
| } from '@ant-design/icons-vue'; | ||||
| 
 | ||||
| export interface RouteType { | ||||
|   path: string; | ||||
| @ -26,85 +28,85 @@ export interface RouteType { | ||||
| 
 | ||||
| const routeList: RouteType[] = [ | ||||
|   { | ||||
|     path: "/", | ||||
|     name: "layout", | ||||
|     path: '/', | ||||
|     name: 'layout', | ||||
|     component: Layout, | ||||
|     meta: { title: "首页" }, | ||||
|     meta: { title: '首页' }, | ||||
|     children: [ | ||||
|       { | ||||
|         path: "", | ||||
|         name: "-", | ||||
|         path: '', | ||||
|         name: '-', | ||||
|         meta: {}, | ||||
|         children: [], | ||||
|         redirect: "/config-manage/project-cfg", | ||||
|         redirect: '/config-manage/project-cfg', | ||||
|       }, | ||||
|       { | ||||
|         path: "/config-manage", | ||||
|         name: "config-manage", | ||||
|         path: '/config-manage', | ||||
|         name: 'config-manage', | ||||
|         isMenu: true, | ||||
|         meta: { title: "配置管理" }, | ||||
|         meta: { title: '配置管理' }, | ||||
|         icon: () => h(HomeOutlined), | ||||
|         children: [ | ||||
|           { | ||||
|             path: "project-cfg", | ||||
|             name: "project-cfg", | ||||
|             path: 'project-cfg', | ||||
|             name: 'project-cfg', | ||||
|             component: () => | ||||
|               import("@/views/config-manage/project-cfg/index.vue"), | ||||
|             meta: { title: "项目配置" }, | ||||
|               import('@/views/config-manage/project-cfg/index.vue'), | ||||
|             meta: { title: '项目配置' }, | ||||
|             isMenu: true, | ||||
|             children: [], | ||||
|           }, | ||||
|           { | ||||
|             path: "module-cfg", | ||||
|             name: "module-cfg", | ||||
|             path: 'module-cfg', | ||||
|             name: 'module-cfg', | ||||
|             component: () => | ||||
|               import("@/views/config-manage/module-cfg/index.vue"), | ||||
|             meta: { title: "数据来源配置" }, | ||||
|               import('@/views/config-manage/module-cfg/index.vue'), | ||||
|             meta: { title: '数据来源配置' }, | ||||
|             isMenu: true, | ||||
|             children: [], | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|       { | ||||
|         path: "/view-all-manage", | ||||
|         name: "view-all-manage", | ||||
|         path: '/view-all-manage', | ||||
|         name: 'view-all-manage', | ||||
|         isMenu: true, | ||||
|         meta: { title: "视图管理" }, | ||||
|         meta: { title: '视图管理' }, | ||||
|         icon: () => h(BarChartOutlined), | ||||
|         children: [ | ||||
|           { | ||||
|             path: "view-list", | ||||
|             name: "view-list", | ||||
|             path: 'view-list', | ||||
|             name: 'view-list', | ||||
|             component: () => | ||||
|               import("@/views/view-all-manage/view-list/index.vue"), | ||||
|             meta: { title: "视图列表" }, | ||||
|               import('@/views/view-all-manage/view-list/index.vue'), | ||||
|             meta: { title: '视图列表' }, | ||||
|             isMenu: true, | ||||
|             children: [], | ||||
|           }, | ||||
|           { | ||||
|             path: "create-view", | ||||
|             name: "create-view", | ||||
|             path: 'create-view', | ||||
|             name: 'create-view', | ||||
|             component: () => | ||||
|               import("@/views/view-all-manage/create-view/index.vue"), | ||||
|             meta: { title: "创建视图" }, | ||||
|               import('@/views/view-all-manage/create-view/index.vue'), | ||||
|             meta: { title: '创建视图' }, | ||||
|             isMenu: true, | ||||
|             children: [], | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|       { | ||||
|         path: "/page-show-info", | ||||
|         name: "page-show-info", | ||||
|         path: '/page-show-info', | ||||
|         name: 'page-show-info', | ||||
|         isMenu: true, | ||||
|         meta: { title: "视图预览" }, | ||||
|         meta: { title: '视图预览' }, | ||||
|         icon: () => h(AppstoreOutlined), | ||||
|         children: [ | ||||
|           { | ||||
|             path: "page-info", | ||||
|             name: "page-info", | ||||
|             path: 'page-info', | ||||
|             name: 'page-info', | ||||
|             component: () => | ||||
|               import("@/views/page-show-info/page-info/index.vue"), | ||||
|             meta: { title: "项目报表" }, | ||||
|               import('@/views/page-show-info/page-info/index.vue'), | ||||
|             meta: { title: '项目报表' }, | ||||
|             isMenu: true, | ||||
|             children: [], | ||||
|           }, | ||||
|  | ||||
| @ -1,8 +1,10 @@ | ||||
| import { readonly, ref } from 'vue'; | ||||
| import { defineStore } from 'pinia'; | ||||
| import { getUserInfo } from '@/api/common'; | ||||
| import type { UserInfoType } from '@/api/common'; | ||||
| 
 | ||||
| import { readonly, ref } from 'vue'; | ||||
| 
 | ||||
| import { getUserInfo } from '@/api/common'; | ||||
| import { defineStore } from 'pinia'; | ||||
| 
 | ||||
| export const useUserInfoStore = defineStore('userInfoStore', () => { | ||||
|   const userInfo = ref<UserInfoType>(); | ||||
| 
 | ||||
|  | ||||
| @ -1,18 +1,19 @@ | ||||
| import axios, { AxiosError } from 'axios'; | ||||
| import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| import axios, { AxiosError } from 'axios'; | ||||
| 
 | ||||
| export interface ResopnseType<T> { | ||||
|   reason: string | ||||
|   message: string | ||||
|   data: T | ||||
|   ts: string | ||||
|   reason: string; | ||||
|   message: string; | ||||
|   data: T; | ||||
|   ts: string; | ||||
| } | ||||
| // export const YCODE_BASEURL: string = import.meta.env.VITE_YCODE_BASEURL;
 | ||||
| 
 | ||||
| // https://custom-chart-pre-api.shiyue.com
 | ||||
| 
 | ||||
| export const YCODE_BASEURL: string = 'https://custom-chart-pre-api.shiyue.com' | ||||
| export const YCODE_BASEURL: string = 'https://custom-chart-pre-api.shiyue.com'; | ||||
| 
 | ||||
| const requestType = { | ||||
|   base: YCODE_BASEURL, | ||||
| @ -20,7 +21,7 @@ const requestType  = { | ||||
| 
 | ||||
| const baseAxios: AxiosInstance = axios.create({ | ||||
|   baseURL: '', | ||||
|   timeout: 100000, | ||||
|   timeout: 100_000, | ||||
|   withCredentials: true, | ||||
| }); | ||||
| 
 | ||||
| @ -28,26 +29,30 @@ const errorHandle = (error: AxiosError) => { | ||||
|   if (error.response) { | ||||
|     const status = error.response?.status; | ||||
|     switch (status) { | ||||
|       case 401: | ||||
|       case 401: { | ||||
|         message.warning('请先登录'); | ||||
|         window.location.href = `${YCODE_BASEURL}/login?redirect=${encodeURIComponent(window.location.href)}`; | ||||
|         break; | ||||
|       case 403: | ||||
|       } | ||||
|       case 403: { | ||||
|         message.warning('权限不足'); | ||||
|         break; | ||||
|       case 500: | ||||
|       } | ||||
|       case 500: { | ||||
|         message.warning('服务器出错了……  (>_<)'); | ||||
|         break; | ||||
|       default: | ||||
|       } | ||||
|       default: { | ||||
|         message.warning('服务器出错了……  (>_<)'); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     return Promise.reject(error); | ||||
|   } | ||||
|   message.error(error.message); | ||||
|   return Promise.reject(error); | ||||
| }; | ||||
| //响应拦截器
 | ||||
| // 响应拦截器
 | ||||
| baseAxios.interceptors.response.use((response: AxiosResponse) => { | ||||
|   const { data, status } = response; | ||||
| 
 | ||||
| @ -64,7 +69,9 @@ baseAxios.interceptors.response.use((response: AxiosResponse) => { | ||||
|   } | ||||
| }, errorHandle); | ||||
| 
 | ||||
| type RequestConfig =  Omit<AxiosRequestConfig, 'baseURL'> & { baseURL?: keyof typeof requestType } | ||||
| type RequestConfig = Omit<AxiosRequestConfig, 'baseURL'> & { | ||||
|   baseURL?: keyof typeof requestType; | ||||
| }; | ||||
| const request = <T = any>(config: RequestConfig) => { | ||||
|   const host = requestType[config.baseURL || 'base']; | ||||
|   return new Promise<T>((resolve, reject) => { | ||||
| @ -73,8 +80,8 @@ const request = <T = any>(config: RequestConfig) => { | ||||
|       .then((res: T) => { | ||||
|         resolve(res); | ||||
|       }) | ||||
|       .catch((err: unknown) => { | ||||
|         reject(err); | ||||
|       .catch((error: unknown) => { | ||||
|         reject(error); | ||||
|       }); | ||||
|   }); | ||||
| }; | ||||
| @ -101,6 +108,7 @@ const put = <T = any>(config?: RequestConfig) => | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
| const del = <T = any>(config?: { url: string }) => request<ResopnseType<T>>({ ...config, method: 'DELETE' }); | ||||
| const del = <T = any>(config?: { url: string }) => | ||||
|   request<ResopnseType<T>>({ ...config, method: 'DELETE' }); | ||||
| 
 | ||||
| export { get, post, del, put, request }; | ||||
| export { del, get, post, put, request }; | ||||
|  | ||||
| @ -1,3 +1,121 @@ | ||||
| <script setup> | ||||
| import { ref, watch } from 'vue'; | ||||
| 
 | ||||
| import { getDbTableSelect } from '@/views/config-manage/module-cfg/service'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   open: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   type: { | ||||
|     type: String, | ||||
|     default: 'add', | ||||
|   }, | ||||
|   data: { | ||||
|     type: Object, | ||||
|     default: () => ({}), | ||||
|   }, | ||||
|   projectSelect: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(['ok']); | ||||
| const formRules = ref({ | ||||
|   modular_name: [ | ||||
|     { required: true, message: '请输入数据来源', trigger: 'submit' }, | ||||
|   ], | ||||
|   original_type: [{ required: true, message: '请选择', trigger: 'submit' }], | ||||
|   original_sql: [{ required: true, message: '请输入', trigger: 'submit' }], | ||||
|   table: [{ required: true, message: '请选择', trigger: 'submit' }], | ||||
| }); | ||||
| 
 | ||||
| const tableTypes = ref([]); | ||||
| const formRef = ref(); | ||||
| const formData = ref({ | ||||
|   project_id: undefined, | ||||
|   modular_name: undefined, | ||||
|   is_show: 0, | ||||
|   original_type: 1, // 1 - 自定义,2 - 指定表 | ||||
|   original_sql: undefined, | ||||
|   table: undefined, | ||||
|   is_other_database: 0, | ||||
|   drive_type: 1, | ||||
|   database_address: undefined, | ||||
|   database_port: undefined, | ||||
|   database_name: undefined, | ||||
|   database_username: undefined, | ||||
|   database_password: undefined, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.data, | ||||
|   (newVal) => { | ||||
|     if (props.type === 'add') { | ||||
|       resetFormData(); | ||||
|     } else { | ||||
|       formData.value = { | ||||
|         modular_id: newVal.modular_id, | ||||
|         project_id: newVal.project_id, | ||||
|         modular_name: newVal.modular_name, | ||||
|         is_show: newVal.is_show, | ||||
|         original_type: newVal.original_type, | ||||
|         original_sql: newVal.original_sql, | ||||
|         table: newVal.table, | ||||
|         drive_type: newVal.drive_type, | ||||
|         is_other_database: newVal.is_other_database, | ||||
|         database_address: newVal.database_address, | ||||
|         database_port: newVal.database_port, | ||||
|         database_name: newVal.database_name, | ||||
|         database_username: newVal.database_username, | ||||
|         database_password: newVal.database_password, | ||||
|       }; | ||||
|     } | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const toGetDbTable = () => { | ||||
|   getDbTableSelect({ projectId: formData.value.project_id }).then((res) => { | ||||
|     tableTypes.value = res.data; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const isOtherChange = (val) => { | ||||
|   formData.value.drive_type = val ? 1 : undefined; | ||||
|   formData.value.database_address = undefined; | ||||
|   formData.value.database_port = undefined; | ||||
|   formData.value.database_name = undefined; | ||||
|   formData.value.database_username = undefined; | ||||
|   formData.value.database_password = undefined; | ||||
| }; | ||||
| 
 | ||||
| const resetFormData = () => { | ||||
|   formData.value = { | ||||
|     project_id: undefined, | ||||
|     modular_name: undefined, | ||||
|     is_show: 0, | ||||
|     original_type: 1, // 1 - 自定义,2 - 指定表 | ||||
|     original_sql: undefined, | ||||
|     table: undefined, | ||||
|     is_other_database: 0, | ||||
|     drive_type: undefined, | ||||
|     database_address: undefined, | ||||
|     database_port: undefined, | ||||
|     database_name: undefined, | ||||
|     database_username: undefined, | ||||
|     database_password: undefined, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleOk = () => { | ||||
|   formRef.value.validate().then(() => { | ||||
|     emit('ok', formData.value); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <a-modal :open="open" @ok="handleOk"> | ||||
|     <a-form | ||||
| @ -24,8 +142,8 @@ | ||||
|       <a-form-item label="展示状态" name="is_show"> | ||||
|         <a-switch | ||||
|           v-model:checked="formData.is_show" | ||||
|           :checkedValue="1" | ||||
|           :unCheckedValue="0" | ||||
|           :checked-value="1" | ||||
|           :un-checked-value="0" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="数据源类型" name="original_type"> | ||||
| @ -58,8 +176,8 @@ | ||||
|       <a-form-item label="数据库特殊配置" name="is_other_database"> | ||||
|         <a-switch | ||||
|           v-model:checked="formData.is_other_database" | ||||
|           :checkedValue="1" | ||||
|           :unCheckedValue="0" | ||||
|           :checked-value="1" | ||||
|           :un-checked-value="0" | ||||
|           @change="isOtherChange" | ||||
|         /> | ||||
|       </a-form-item> | ||||
| @ -104,124 +222,6 @@ | ||||
|           </a-space> | ||||
|         </a-form-item> | ||||
|       </template> | ||||
|        | ||||
|     </a-form> | ||||
|   </a-modal> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { ref, watch } from "vue"; | ||||
| import { getDbTableSelect } from "@/views/config-manage/module-cfg/service"; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   open: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   type: { | ||||
|     type: String, | ||||
|     default: "add", | ||||
|   }, | ||||
|   data: { | ||||
|     type: Object, | ||||
|     default: () => ({}), | ||||
|   }, | ||||
|   projectSelect: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(["ok"]); | ||||
| const formRules = ref({ | ||||
|   modular_name: [ | ||||
|     { required: true, message: "请输入数据来源", trigger: "submit" }, | ||||
|   ], | ||||
|   original_type: [{ required: true, message: "请选择", trigger: "submit" }], | ||||
|   original_sql: [{ required: true, message: "请输入", trigger: "submit" }], | ||||
|   table: [{ required: true, message: "请选择", trigger: "submit" }], | ||||
| }); | ||||
| 
 | ||||
| const tableTypes = ref([]); | ||||
| const formRef = ref(); | ||||
| const formData = ref({ | ||||
|   project_id: undefined, | ||||
|   modular_name: undefined, | ||||
|   is_show: 0, | ||||
|   original_type: 1, // 1 - 自定义,2 - 指定表 | ||||
|   original_sql: undefined, | ||||
|   table: undefined, | ||||
|   is_other_database: 0, | ||||
|   drive_type: 1, | ||||
|   database_address: undefined, | ||||
|   database_port: undefined, | ||||
|   database_name: undefined, | ||||
|   database_username: undefined, | ||||
|   database_password: undefined, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.data, | ||||
|   (newVal) => { | ||||
|     if (props.type === "add") { | ||||
|       resetFormData(); | ||||
|     } else { | ||||
|       formData.value = { | ||||
|         modular_id: newVal.modular_id, | ||||
|         project_id: newVal.project_id, | ||||
|         modular_name: newVal.modular_name, | ||||
|         is_show: newVal.is_show, | ||||
|         original_type: newVal.original_type, | ||||
|         original_sql: newVal.original_sql, | ||||
|         table: newVal.table, | ||||
|         drive_type: newVal.drive_type, | ||||
|         is_other_database: newVal.is_other_database, | ||||
|         database_address: newVal.database_address, | ||||
|         database_port: newVal.database_port, | ||||
|         database_name: newVal.database_name, | ||||
|         database_username: newVal.database_username, | ||||
|         database_password: newVal.database_password, | ||||
|       }; | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| const toGetDbTable = () => { | ||||
|   getDbTableSelect({ projectId: formData.value.project_id }).then((res) => { | ||||
|     tableTypes.value = res.data; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const isOtherChange = (val) => { | ||||
|   formData.value.drive_type = val ? 1 : undefined | ||||
|   formData.value.database_address = undefined | ||||
|   formData.value.database_port = undefined | ||||
|   formData.value.database_name = undefined | ||||
|   formData.value.database_username = undefined | ||||
|   formData.value.database_password = undefined | ||||
| } | ||||
| 
 | ||||
| const resetFormData = () => { | ||||
|   formData.value = { | ||||
|     project_id: undefined, | ||||
|     modular_name: undefined, | ||||
|     is_show: 0, | ||||
|     original_type: 1, // 1 - 自定义,2 - 指定表 | ||||
|     original_sql: undefined, | ||||
|     table: undefined, | ||||
|     is_other_database: 0, | ||||
|     drive_type: undefined, | ||||
|     database_address: undefined, | ||||
|     database_port: undefined, | ||||
|     database_name: undefined, | ||||
|     database_username: undefined, | ||||
|     database_password: undefined, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleOk = () => { | ||||
|   formRef.value.validate().then(() => { | ||||
|     emit("ok", formData.value); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| @ -1,3 +1,223 @@ | ||||
| <script setup> | ||||
| import { onMounted, reactive, ref, watch } from 'vue'; | ||||
| 
 | ||||
| import { | ||||
|   originalTypes, | ||||
|   showTypeOpts, | ||||
|   viewCfgCols, | ||||
| } from '@/views/config-manage/module-cfg/config'; | ||||
| import { | ||||
|   deleteField, | ||||
|   getFieldList, | ||||
|   getFieldNumSelect, | ||||
|   getFieldTypeSelect, | ||||
|   saveField, | ||||
|   // getFieldDetail, | ||||
| } from '@/views/config-manage/module-cfg/service'; | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   open: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   modularId: { | ||||
|     type: Number, | ||||
|     default: 0, | ||||
|   }, | ||||
| }); | ||||
| const listLoading = ref(false); | ||||
| const fieldName = ref(''); | ||||
| const dataList = ref([]); | ||||
| const fieldTypeSel = ref([]); | ||||
| const fieldNumTypeSel = ref([]); | ||||
| const specialVisible = ref(false); | ||||
| const specialModalId = ref(); | ||||
| const pageState = reactive({ | ||||
|   page: 1, | ||||
|   perPage: 20, | ||||
|   total: 0, | ||||
| }); | ||||
| const editableData = reactive({}); | ||||
| const specialModalData = reactive({ | ||||
|   drive_type: undefined, | ||||
|   database_address: undefined, | ||||
|   database_port: undefined, | ||||
|   database_name: undefined, | ||||
|   database_username: undefined, | ||||
|   database_password: undefined, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.open, | ||||
|   (newVal) => { | ||||
|     if (newVal) { | ||||
|       toGetList(); | ||||
|     } | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   toGetFieldTypes(); | ||||
|   toGetFieldNumSelect(); | ||||
| }); | ||||
| 
 | ||||
| // 字段搜索类型下拉 | ||||
| const toGetFieldTypes = () => { | ||||
|   getFieldTypeSelect().then((res) => { | ||||
|     fieldTypeSel.value = res.data; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // 字段类型下拉 | ||||
| const toGetFieldNumSelect = () => { | ||||
|   getFieldNumSelect().then((res) => { | ||||
|     fieldNumTypeSel.value = res.data; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // 字段列表 | ||||
| const toGetList = () => { | ||||
|   listLoading.value = true; | ||||
|   getFieldList({ | ||||
|     fieldName: fieldName.value, | ||||
|     modularId: props.modularId, | ||||
|     page: pageState.page, | ||||
|     perPage: pageState.perPage, | ||||
|   }).then((res) => { | ||||
|     dataList.value = res.data.list; | ||||
|     pageState.total = res.data.total; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const pageChange = () => { | ||||
|   toGetList(); | ||||
| }; | ||||
| 
 | ||||
| const search = () => { | ||||
|   pageState.page = 1; | ||||
|   toGetList(); | ||||
| }; | ||||
| 
 | ||||
| const addField = () => { | ||||
|   const item = { | ||||
|     field_id: `${Date.now()}`, | ||||
|     field_title: undefined, | ||||
|     field_name: undefined, | ||||
|     field_numerical_type_id: undefined, | ||||
|     is_search: 0, | ||||
|     field_type_id: undefined, | ||||
|     belong_to_table: undefined, | ||||
|     is_other_database: 0, | ||||
|     original_type: undefined, | ||||
|     original_sql: undefined, | ||||
|     sort: 0, | ||||
|     drive_type: undefined, | ||||
|     database_address: undefined, | ||||
|     database_port: undefined, | ||||
|     database_name: undefined, | ||||
|     database_username: undefined, | ||||
|     database_password: undefined, | ||||
|   }; | ||||
|   dataList.value.unshift(item); | ||||
|   editableData[item.field_id] = { | ||||
|     ...item, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleEdit = (record) => { | ||||
|   editableData[record.field_id] = { | ||||
|     ...record, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleDelete = (record) => { | ||||
|   deleteField({ | ||||
|     field_id: record.field_id, | ||||
|   }).then(() => { | ||||
|     message.success('删除成功'); | ||||
|     toGetList(); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const handleCancel = (record) => { | ||||
|   if (typeof record.field_id === 'string') { | ||||
|     dataList.value.shift(); | ||||
|   } else { | ||||
|     delete editableData[record.field_id]; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const openSpecialModal = (record) => { | ||||
|   specialVisible.value = true; | ||||
|   specialModalId.value = record.field_id; | ||||
|   if (!specialModalData[record.field_id]) { | ||||
|     specialModalData[record.field_id] = { | ||||
|       drive_type: undefined, | ||||
|       database_address: undefined, | ||||
|       database_port: undefined, | ||||
|       database_name: undefined, | ||||
|       database_username: undefined, | ||||
|       database_password: undefined, | ||||
|     }; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const handleSave = (record) => { | ||||
|   const params = { | ||||
|     is_search: record.is_search, | ||||
|     field_type_id: record.field_type_id, | ||||
|     modular_id: props.modularId, | ||||
|     original_type: record.original_type, | ||||
|     original_sql: record.original_sql, | ||||
|     is_other_database: record.is_other_database, | ||||
|   }; | ||||
|   // 如果数据库特配弹框有数据 | ||||
|   if (specialModalData[record.field_id]) { | ||||
|     params.drive_type = specialModalData[record.field_id].drive_type; | ||||
|     params.database_address = | ||||
|       specialModalData[record.field_id].database_address; | ||||
|     params.database_port = specialModalData[record.field_id].database_port; | ||||
|     params.database_name = specialModalData[record.field_id].database_name; | ||||
|     params.database_username = | ||||
|       specialModalData[record.field_id].database_username; | ||||
|     params.database_password = | ||||
|       specialModalData[record.field_id].database_password; | ||||
|   } | ||||
|   // 检验必填参数 | ||||
|   const validateFields = [ | ||||
|     { field: 'field_title', msg: '请填写字段标题' }, | ||||
|     { field: 'field_name', msg: '请填写字段名称' }, | ||||
|     { field: 'field_numerical_type_id', msg: '请选择字段类型' }, | ||||
|     { field: 'belong_to_table', msg: '请填写关联表' }, | ||||
|     { field: 'show_type', msg: '请选择展示类型' }, | ||||
|   ]; | ||||
|   for (const curr of validateFields) { | ||||
|     if (record[curr.field]) { | ||||
|       params[curr.field] = record[curr.field]; | ||||
|     } else { | ||||
|       message.error(curr.msg); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|   if (record.is_search && !record.field_type_id) { | ||||
|     message.error('请选择搜索类型'); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // 如果是编辑操作 | ||||
|   if (typeof record.field_id === 'number') { | ||||
|     params.field_id = record.field_id; | ||||
|   } | ||||
| 
 | ||||
|   saveField(params).then(() => { | ||||
|     delete editableData[record.field_id]; | ||||
|     message.success('保存成功'); | ||||
|     toGetList(); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <a-modal :open="open" title="字段管理" style="top: 30px" :footer="null"> | ||||
|     <div class="field-manager"> | ||||
| @ -49,8 +269,7 @@ | ||||
|               placeholder="请选择" | ||||
|               allow-clear | ||||
|               style="width: 120px" | ||||
|             > | ||||
|             </a-select> | ||||
|             /> | ||||
|             <template v-else> | ||||
|               {{ record.field_numerical_name }} | ||||
|             </template> | ||||
| @ -63,8 +282,7 @@ | ||||
|               placeholder="请选择" | ||||
|               allow-clear | ||||
|               style="width: 120px" | ||||
|             > | ||||
|             </a-select> | ||||
|             /> | ||||
|             <template v-else> | ||||
|               {{ record.show_type }} | ||||
|             </template> | ||||
| @ -77,8 +295,7 @@ | ||||
|               placeholder="请选择" | ||||
|               allow-clear | ||||
|               style="width: 120px" | ||||
|             > | ||||
|             </a-select> | ||||
|             /> | ||||
|             <template v-else> | ||||
|               {{ record.field_type_name }} | ||||
|             </template> | ||||
| @ -87,29 +304,30 @@ | ||||
|             <a-switch | ||||
|               v-if="editableData[record.field_id]" | ||||
|               v-model:checked="record.is_search" | ||||
|               :checkedValue="1" | ||||
|               :unCheckedValue="0" | ||||
|               :checked-value="1" | ||||
|               :un-checked-value="0" | ||||
|             /> | ||||
|             <template v-else> | ||||
|               {{ record.is_search ? "是" : "否" }} | ||||
|               {{ record.is_search ? '是' : '否' }} | ||||
|             </template> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'is_other_database'"> | ||||
|             <template v-if="editableData[record.field_id]"> | ||||
|               <a-switch | ||||
|                 v-model:checked="record.is_other_database" | ||||
|                 :checkedValue="1" | ||||
|                 :unCheckedValue="0" | ||||
|                 :checked-value="1" | ||||
|                 :un-checked-value="0" | ||||
|               /> | ||||
|               <a-button | ||||
|                 v-if="record.is_other_database" | ||||
|                 type="link" | ||||
|                 @click="openSpecialModal(record)" | ||||
|                 >请填写</a-button | ||||
|               > | ||||
|                 请填写 | ||||
|               </a-button> | ||||
|             </template> | ||||
|             <template v-else> | ||||
|               {{ record.is_other_database ? "是" : "否" }} | ||||
|               {{ record.is_other_database ? '是' : '否' }} | ||||
|             </template> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'original_type'"> | ||||
| @ -119,20 +337,19 @@ | ||||
|               v-model:value="record.original_type" | ||||
|               :options="originalTypes" | ||||
|               allow-clear | ||||
|             > | ||||
|             </a-select> | ||||
|             /> | ||||
|             <template v-else> | ||||
|               {{ record.original_type_name }} | ||||
|             </template> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'action'"> | ||||
|             <a-space v-if="editableData[record.field_id]"> | ||||
|               <a-button type="primary" size="small" @click="handleSave(record)" | ||||
|                 >保存</a-button | ||||
|               > | ||||
|               <a-button size="small" @click="handleCancel(record)" | ||||
|                 >取消</a-button | ||||
|               > | ||||
|               <a-button type="primary" size="small" @click="handleSave(record)"> | ||||
|                 保存 | ||||
|               </a-button> | ||||
|               <a-button size="small" @click="handleCancel(record)"> | ||||
|                 取消 | ||||
|               </a-button> | ||||
|             </a-space> | ||||
|             <div v-else> | ||||
|               <a-button type="link" @click="handleEdit(record)">修改</a-button> | ||||
| @ -159,7 +376,7 @@ | ||||
|       :open="specialVisible" | ||||
|       :width="640" | ||||
|       title="数据库特配" | ||||
|       :bodyStyle="{ marginTop: '30px' }" | ||||
|       :body-style="{ marginTop: '30px' }" | ||||
|       @ok="specialVisible = false" | ||||
|       @cancel="specialVisible = false" | ||||
|     > | ||||
| @ -213,219 +430,6 @@ | ||||
|   </a-modal> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { onMounted, reactive, ref, watch } from "vue"; | ||||
| import { viewCfgCols, originalTypes, showTypeOpts } from "@/views/config-manage/module-cfg/config"; | ||||
| import { | ||||
|   getFieldTypeSelect, | ||||
|   getFieldNumSelect, | ||||
|   getFieldList, | ||||
|   deleteField, | ||||
|   saveField, | ||||
|   // getFieldDetail, | ||||
| } from "@/views/config-manage/module-cfg/service"; | ||||
| import { message } from "ant-design-vue"; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   open: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   modularId: { | ||||
|     type: Number, | ||||
|     default: 0, | ||||
|   }, | ||||
| }); | ||||
| const listLoading = ref(false); | ||||
| const fieldName = ref(""); | ||||
| const dataList = ref([]); | ||||
| const fieldTypeSel = ref([]); | ||||
| const fieldNumTypeSel = ref([]); | ||||
| const specialVisible = ref(false); | ||||
| const specialModalId = ref(); | ||||
| const pageState = reactive({ | ||||
|   page: 1, | ||||
|   perPage: 20, | ||||
|   total: 0, | ||||
| }); | ||||
| const editableData = reactive({}); | ||||
| const specialModalData = reactive({ | ||||
|   drive_type: undefined, | ||||
|   database_address: undefined, | ||||
|   database_port: undefined, | ||||
|   database_name: undefined, | ||||
|   database_username: undefined, | ||||
|   database_password: undefined, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.open, | ||||
|   (newVal) => { | ||||
|     if (newVal) { | ||||
|       toGetList(); | ||||
|     } | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   toGetFieldTypes(); | ||||
|   toGetFieldNumSelect(); | ||||
| }); | ||||
| 
 | ||||
| // 字段搜索类型下拉 | ||||
| const toGetFieldTypes = () => { | ||||
|   getFieldTypeSelect().then((res) => { | ||||
|     fieldTypeSel.value = res.data; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // 字段类型下拉 | ||||
| const toGetFieldNumSelect = () => { | ||||
|   getFieldNumSelect().then((res) => { | ||||
|     fieldNumTypeSel.value = res.data; | ||||
|   }) | ||||
| }; | ||||
| 
 | ||||
| // 字段列表 | ||||
| const toGetList = () => { | ||||
|   listLoading.value = true; | ||||
|   getFieldList({ | ||||
|     fieldName: fieldName.value, | ||||
|     modularId: props.modularId, | ||||
|     page: pageState.page, | ||||
|     perPage: pageState.perPage, | ||||
|   }).then((res) => { | ||||
|     dataList.value = res.data.list; | ||||
|     pageState.total = res.data.total; | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const pageChange = () => { | ||||
|   toGetList(); | ||||
| }; | ||||
| 
 | ||||
| const search = () => { | ||||
|   pageState.page = 1; | ||||
|   toGetList(); | ||||
| }; | ||||
| 
 | ||||
| const addField = () => { | ||||
|   const item = { | ||||
|     field_id: new Date().getTime() + "", | ||||
|     field_title: undefined, | ||||
|     field_name: undefined, | ||||
|     field_numerical_type_id: undefined, | ||||
|     is_search: 0, | ||||
|     field_type_id: undefined, | ||||
|     belong_to_table: undefined, | ||||
|     is_other_database: 0, | ||||
|     original_type: undefined, | ||||
|     original_sql: undefined, | ||||
|     sort: 0, | ||||
|     drive_type: undefined, | ||||
|     database_address: undefined, | ||||
|     database_port: undefined, | ||||
|     database_name: undefined, | ||||
|     database_username: undefined, | ||||
|     database_password: undefined, | ||||
|   }; | ||||
|   dataList.value.unshift(item); | ||||
|   editableData[item.field_id] = { | ||||
|     ...item, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleEdit = (record) => { | ||||
|   editableData[record.field_id] = { | ||||
|     ...record, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleDelete = (record) => { | ||||
|   deleteField({ | ||||
|     field_id: record.field_id, | ||||
|   }).then(() => { | ||||
|     message.success('删除成功') | ||||
|     toGetList(); | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| const handleCancel = (record) => { | ||||
|   if (typeof record.field_id === "string") { | ||||
|     dataList.value.shift(); | ||||
|   } else { | ||||
|     delete editableData[record.field_id]; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const openSpecialModal = (record) => { | ||||
|   specialVisible.value = true | ||||
|   specialModalId.value = record.field_id | ||||
|   if (!specialModalData[record.field_id]) { | ||||
|     specialModalData[record.field_id] = { | ||||
|       drive_type: undefined, | ||||
|       database_address: undefined, | ||||
|       database_port: undefined, | ||||
|       database_name: undefined, | ||||
|       database_username: undefined, | ||||
|       database_password: undefined, | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const handleSave = (record) => { | ||||
|   const params = { | ||||
|     is_search: record.is_search, | ||||
|     field_type_id: record.field_type_id, | ||||
|     modular_id: props.modularId, | ||||
|     original_type: record.original_type, | ||||
|     original_sql: record.original_sql, | ||||
|     is_other_database: record.is_other_database, | ||||
|   }; | ||||
|   // 如果数据库特配弹框有数据 | ||||
|   if (specialModalData[record.field_id]) { | ||||
|     params.drive_type = specialModalData[record.field_id].drive_type | ||||
|     params.database_address = specialModalData[record.field_id].database_address | ||||
|     params.database_port = specialModalData[record.field_id].database_port | ||||
|     params.database_name = specialModalData[record.field_id].database_name | ||||
|     params.database_username = specialModalData[record.field_id].database_username | ||||
|     params.database_password = specialModalData[record.field_id].database_password | ||||
|   } | ||||
|   // 检验必填参数 | ||||
|   const validateFields = [ | ||||
|     { field: 'field_title', msg: "请填写字段标题" }, | ||||
|     { field: 'field_name', msg: "请填写字段名称" }, | ||||
|     { field: 'field_numerical_type_id', msg: "请选择字段类型" }, | ||||
|     { field: 'belong_to_table', msg: "请填写关联表" }, | ||||
|     { field: 'show_type', msg: "请选择展示类型" }, | ||||
|   ] | ||||
|   for(let i = 0; i < validateFields.length; i++) { | ||||
|     const curr = validateFields[i]; | ||||
|     if (!record[curr.field]) { | ||||
|       message.error(curr.msg); | ||||
|       return; | ||||
|     } else { | ||||
|       params[curr.field] = record[curr.field]; | ||||
|     } | ||||
|   } | ||||
|   if (record.is_search && !record.field_type_id) { | ||||
|     message.error("请选择搜索类型"); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // 如果是编辑操作 | ||||
|   if (typeof record.field_id === "number") { | ||||
|     params.field_id = record.field_id; | ||||
|   } | ||||
| 
 | ||||
|   saveField(params).then(() => { | ||||
|     delete editableData[record.field_id]; | ||||
|     message.success("保存成功"); | ||||
|     toGetList(); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| .header-box { | ||||
|   margin-bottom: 10px; | ||||
|  | ||||
| @ -1,41 +1,41 @@ | ||||
| export const moduleCfgCols = [ | ||||
|   { dataIndex: "modular_id", title: "编号", align: "center" }, | ||||
|   { dataIndex: "modular_name", title: "数据来源名称", align: "center" }, | ||||
|   { dataIndex: "project_name", title: "项目名称", align: "center" }, | ||||
|   { dataIndex: "is_show", title: "展示状态", align: "center" }, | ||||
|   { dataIndex: "original_type_handle", title: "数据源类型", align: "center" }, | ||||
|   { dataIndex: 'modular_id', title: '编号', align: 'center' }, | ||||
|   { dataIndex: 'modular_name', title: '数据来源名称', align: 'center' }, | ||||
|   { dataIndex: 'project_name', title: '项目名称', align: 'center' }, | ||||
|   { dataIndex: 'is_show', title: '展示状态', align: 'center' }, | ||||
|   { dataIndex: 'original_type_handle', title: '数据源类型', align: 'center' }, | ||||
|   // { dataIndex: 'sort', title: '排序', align: 'center'},
 | ||||
|   { dataIndex: "action", title: "操作", align: "center" }, | ||||
|   { dataIndex: 'action', title: '操作', align: 'center' }, | ||||
| ]; | ||||
| 
 | ||||
| export const viewCfgCols = [ | ||||
|   { dataIndex: "field_name", title: "字段名称", align: "center" }, | ||||
|   { dataIndex: "field_title", title: "字段标题", align: "center" }, | ||||
|   { dataIndex: 'field_name', title: '字段名称', align: 'center' }, | ||||
|   { dataIndex: 'field_title', title: '字段标题', align: 'center' }, | ||||
|   { | ||||
|     dataIndex: "field_numerical_name", | ||||
|     title: "字段类型", | ||||
|     align: "center", | ||||
|     dataIndex: 'field_numerical_name', | ||||
|     title: '字段类型', | ||||
|     align: 'center', | ||||
|     width: 120, | ||||
|   }, | ||||
|   { dataIndex: "show_type", title: "展示类型", align: "center", width: 120 }, | ||||
|   { dataIndex: "field_type_name", title: "搜索类型", align: "center" }, | ||||
|   { dataIndex: "is_search", title: "是否可搜索", align: "center" }, | ||||
|   { dataIndex: "sort", title: "排序", align: "center" }, | ||||
|   { dataIndex: "belong_to_table", title: "所属表名称", align: "center" }, | ||||
|   { dataIndex: "is_other_database", title: "数据库特配", align: "center" }, | ||||
|   { dataIndex: "original_type", title: "数据源类型", align: "center" }, | ||||
|   { dataIndex: "original_sql", title: "数据源", align: "center", width: 400 }, | ||||
|   { dataIndex: "action", title: "操作", align: "center", width: 120 }, | ||||
|   { dataIndex: 'show_type', title: '展示类型', align: 'center', width: 120 }, | ||||
|   { dataIndex: 'field_type_name', title: '搜索类型', align: 'center' }, | ||||
|   { dataIndex: 'is_search', title: '是否可搜索', align: 'center' }, | ||||
|   { dataIndex: 'sort', title: '排序', align: 'center' }, | ||||
|   { dataIndex: 'belong_to_table', title: '所属表名称', align: 'center' }, | ||||
|   { dataIndex: 'is_other_database', title: '数据库特配', align: 'center' }, | ||||
|   { dataIndex: 'original_type', title: '数据源类型', align: 'center' }, | ||||
|   { dataIndex: 'original_sql', title: '数据源', align: 'center', width: 400 }, | ||||
|   { dataIndex: 'action', title: '操作', align: 'center', width: 120 }, | ||||
| ]; | ||||
| 
 | ||||
| export const originalTypes = [ | ||||
|   { label: "sql", value: 1 }, | ||||
|   { label: "json", value: 2 }, | ||||
|   { label: 'sql', value: 1 }, | ||||
|   { label: 'json', value: 2 }, | ||||
| ]; | ||||
| 
 | ||||
| export const showTypeOpts = [ | ||||
|   { label: "文本", value: "text" }, | ||||
|   { label: "图片", value: "img" }, | ||||
|   { label: "链接", value: "link" }, | ||||
|   { label: "富文本", value: "richText" }, | ||||
|   { label: '文本', value: 'text' }, | ||||
|   { label: '图片', value: 'img' }, | ||||
|   { label: '链接', value: 'link' }, | ||||
|   { label: '富文本', value: 'richText' }, | ||||
| ]; | ||||
|  | ||||
| @ -1,123 +1,36 @@ | ||||
| <template> | ||||
|   <div class="normal-container module-cfg-box"> | ||||
|     <div class="header-box"> | ||||
|       <a-space> | ||||
|         <a-input | ||||
|           v-model:value="modularName" | ||||
|           placeholder="请输入数据来源名称" | ||||
|           allow-clear | ||||
|           style="width: 200px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-select | ||||
|           placeholder="请选项目" | ||||
|           v-model:value="projectId" | ||||
|           :options="projectSel" | ||||
|           allow-clear | ||||
|           style="width: 200px" | ||||
|           @change="search" | ||||
|         ></a-select> | ||||
|         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||
|       </a-space> | ||||
|     </div> | ||||
|     <div class="content-box"> | ||||
|       <a-table | ||||
|         :columns="moduleCfgCols" | ||||
|         :data-source="dataList" | ||||
|         :pagination="false" | ||||
|         size="small" | ||||
|         bordered | ||||
|       > | ||||
|         <template #bodyCell="{ column, record }"> | ||||
|           <template v-if="column.dataIndex === 'is_show'"> | ||||
|             <a-switch | ||||
|               :checked="record.is_show" | ||||
|               :checkedValue="1" | ||||
|               :unCheckedValue="0" | ||||
|               @change="toChangeStatus(record.modular_id, $event)" | ||||
|             /> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'action'"> | ||||
|             <a-space> | ||||
|               <a-button type="link" size="small" @click="openFieldModal(record)" | ||||
|                 >字段管理</a-button | ||||
|               > | ||||
|               <a-button | ||||
|                 type="link" | ||||
|                 size="small" | ||||
|                 @click="toGetModularDetail(record.modular_id)" | ||||
|                 >编辑</a-button | ||||
|               > | ||||
|               <a-popconfirm | ||||
|                 title="确定删除吗?" | ||||
|                 @confirm="toDelete(record.modular_id)" | ||||
|               > | ||||
|                 <a-button type="link" size="small">删除</a-button> | ||||
|               </a-popconfirm> | ||||
|             </a-space> | ||||
|           </template> | ||||
|         </template> | ||||
|       </a-table> | ||||
|       <a-pagination | ||||
|         v-model:current="pageState.page" | ||||
|         :total="pageState.total" | ||||
|         :page-size="pageState.perPage" | ||||
|         :hide-on-single-page="false" | ||||
|         size="small" | ||||
|         class="pagination-box" | ||||
|         @change="toGetModularList" | ||||
|       /> | ||||
|     </div> | ||||
|     <CreateModal | ||||
|       :width="700" | ||||
|       :open="modalState.visible" | ||||
|       :title="modalState.title" | ||||
|       :type="modalState.type" | ||||
|       :data="modalState.data" | ||||
|       :projectSelect="projectSel" | ||||
|       @cancel="modalState.visible = false" | ||||
|       @ok="toSave" | ||||
|     /> | ||||
|     <FieldModal | ||||
|       title="字段管理" | ||||
|       width="90%" | ||||
|       :open="fieldModalState.visible" | ||||
|       :modularId="fieldModalState.modularId" | ||||
|       @cancel="fieldModalState.visible = false" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| <script setup> | ||||
| import { ref, reactive, onMounted } from "vue"; | ||||
| import { message } from "ant-design-vue"; | ||||
| import { moduleCfgCols } from "./config"; | ||||
| import { onMounted, reactive, ref } from 'vue'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| import CreateModal from './components/create-modal.vue'; | ||||
| import FieldModal from './components/field-modal.vue'; | ||||
| import { moduleCfgCols } from './config'; | ||||
| import { | ||||
|   getModularList, | ||||
|   deleteModular, | ||||
|   getModularDetail, | ||||
|   getModularList, | ||||
|   getProjectSelect, | ||||
|   saveModular, | ||||
|   updateStatus, | ||||
| } from "./service"; | ||||
| import CreateModal from "./components/create-modal.vue"; | ||||
| import FieldModal from "./components/field-modal.vue"; | ||||
| } from './service'; | ||||
| 
 | ||||
| const dataList = ref([]); | ||||
| const listLoading = ref(false); | ||||
| const saveLoading = ref(false); | ||||
| const detailLoading = ref(false); | ||||
| const modularName = ref(""); | ||||
| const modularName = ref(''); | ||||
| const projectId = ref(); | ||||
| const projectSel = ref([]); | ||||
| const modalState = reactive({ | ||||
|   title: "", | ||||
|   title: '', | ||||
|   visible: false, | ||||
|   type: "", // add - 新建,edit - 编辑 | ||||
|   type: '', // add - 新建,edit - 编辑 | ||||
|   data: {}, | ||||
| }); | ||||
| const fieldModalState = reactive({ | ||||
|   visible: false, | ||||
|   title: "", | ||||
|   title: '', | ||||
|   modularId: undefined, | ||||
| }); | ||||
| 
 | ||||
| @ -156,8 +69,8 @@ const toGetModularList = () => { | ||||
| const toGetModularDetail = (modularId) => { | ||||
|   detailLoading.value = true; | ||||
|   modalState.visible = true; | ||||
|   modalState.title = "编辑"; | ||||
|   modalState.type = "edit"; | ||||
|   modalState.title = '编辑'; | ||||
|   modalState.type = 'edit'; | ||||
|   getModularDetail({ modularId }) | ||||
|     .then((res) => { | ||||
|       modalState.data = res.data; | ||||
| @ -172,7 +85,7 @@ const toSave = (data) => { | ||||
|   saveLoading.value = true; | ||||
|   saveModular(data) | ||||
|     .then(() => { | ||||
|       message.success("保存成功"); | ||||
|       message.success('保存成功'); | ||||
|       modalState.visible = false; | ||||
|       toGetModularList(); | ||||
|     }) | ||||
| @ -184,7 +97,7 @@ const toSave = (data) => { | ||||
| // 删除 | ||||
| const toDelete = (id) => { | ||||
|   deleteModular({ modular_id: id }).then(() => { | ||||
|     message.success("删除成功"); | ||||
|     message.success('删除成功'); | ||||
|     search(); | ||||
|   }); | ||||
| }; | ||||
| @ -194,7 +107,7 @@ const toChangeStatus = (id, status) => { | ||||
|     modular_id: id, | ||||
|     status, | ||||
|   }).then(() => { | ||||
|     message.success("修改成功"); | ||||
|     message.success('修改成功'); | ||||
|     search(); | ||||
|   }); | ||||
| }; | ||||
| @ -202,8 +115,8 @@ const toChangeStatus = (id, status) => { | ||||
| // 点击新建 | ||||
| const openCreateModal = () => { | ||||
|   modalState.visible = true; | ||||
|   modalState.title = "新建"; | ||||
|   modalState.type = "add"; | ||||
|   modalState.title = '新建'; | ||||
|   modalState.type = 'add'; | ||||
|   modalState.data = {}; | ||||
| }; | ||||
| 
 | ||||
| @ -217,6 +130,100 @@ const openFieldModal = (record) => { | ||||
|   fieldModalState.modularId = record.modular_id; | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <div class="normal-container module-cfg-box"> | ||||
|     <div class="header-box"> | ||||
|       <a-space> | ||||
|         <a-input | ||||
|           v-model:value="modularName" | ||||
|           placeholder="请输入数据来源名称" | ||||
|           allow-clear | ||||
|           style="width: 200px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-select | ||||
|           placeholder="请选项目" | ||||
|           v-model:value="projectId" | ||||
|           :options="projectSel" | ||||
|           allow-clear | ||||
|           style="width: 200px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||
|       </a-space> | ||||
|     </div> | ||||
|     <div class="content-box"> | ||||
|       <a-table | ||||
|         :columns="moduleCfgCols" | ||||
|         :data-source="dataList" | ||||
|         :pagination="false" | ||||
|         size="small" | ||||
|         bordered | ||||
|       > | ||||
|         <template #bodyCell="{ column, record }"> | ||||
|           <template v-if="column.dataIndex === 'is_show'"> | ||||
|             <a-switch | ||||
|               :checked="record.is_show" | ||||
|               :checked-value="1" | ||||
|               :un-checked-value="0" | ||||
|               @change="toChangeStatus(record.modular_id, $event)" | ||||
|             /> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'action'"> | ||||
|             <a-space> | ||||
|               <a-button | ||||
|                 type="link" | ||||
|                 size="small" | ||||
|                 @click="openFieldModal(record)" | ||||
|               > | ||||
|                 字段管理 | ||||
|               </a-button> | ||||
|               <a-button | ||||
|                 type="link" | ||||
|                 size="small" | ||||
|                 @click="toGetModularDetail(record.modular_id)" | ||||
|               > | ||||
|                 编辑 | ||||
|               </a-button> | ||||
|               <a-popconfirm | ||||
|                 title="确定删除吗?" | ||||
|                 @confirm="toDelete(record.modular_id)" | ||||
|               > | ||||
|                 <a-button type="link" size="small">删除</a-button> | ||||
|               </a-popconfirm> | ||||
|             </a-space> | ||||
|           </template> | ||||
|         </template> | ||||
|       </a-table> | ||||
|       <a-pagination | ||||
|         v-model:current="pageState.page" | ||||
|         :total="pageState.total" | ||||
|         :page-size="pageState.perPage" | ||||
|         :hide-on-single-page="false" | ||||
|         size="small" | ||||
|         class="pagination-box" | ||||
|         @change="toGetModularList" | ||||
|       /> | ||||
|     </div> | ||||
|     <CreateModal | ||||
|       :width="700" | ||||
|       :open="modalState.visible" | ||||
|       :title="modalState.title" | ||||
|       :type="modalState.type" | ||||
|       :data="modalState.data" | ||||
|       :project-select="projectSel" | ||||
|       @cancel="modalState.visible = false" | ||||
|       @ok="toSave" | ||||
|     /> | ||||
|     <FieldModal | ||||
|       title="字段管理" | ||||
|       width="90%" | ||||
|       :open="fieldModalState.visible" | ||||
|       :modular-id="fieldModalState.modularId" | ||||
|       @cancel="fieldModalState.visible = false" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="less"> | ||||
| .header-box { | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import { get, post } from "@/utils/request"; | ||||
| import { get, post } from '@/utils/request'; | ||||
| 
 | ||||
| // 获取数据表配置列表
 | ||||
| export function getModularList({ page, perPage, modularName, projectId }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/modular/list", | ||||
|     url: '/api/v1/modular/list', | ||||
|     params: { | ||||
|       page, | ||||
|       per_page: perPage, | ||||
| @ -16,7 +16,7 @@ export function getModularList({ page, perPage, modularName, projectId }) { | ||||
| // 获取数据表配置详情
 | ||||
| export function getModularDetail({ modularId }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/modular/info", | ||||
|     url: '/api/v1/modular/info', | ||||
|     params: { | ||||
|       modular_id: modularId, | ||||
|     }, | ||||
| @ -26,7 +26,7 @@ export function getModularDetail({ modularId }) { | ||||
| // 保存数据库配置
 | ||||
| export function saveModular(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/modular/save", | ||||
|     url: '/api/v1/modular/save', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
| @ -34,7 +34,7 @@ export function saveModular(data) { | ||||
| // 删除数据库配置
 | ||||
| export function deleteModular(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/modular/del", | ||||
|     url: '/api/v1/modular/del', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
| @ -42,7 +42,7 @@ export function deleteModular(data) { | ||||
| // 修改数据表状态
 | ||||
| export function updateStatus(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/modular/change-status", | ||||
|     url: '/api/v1/modular/change-status', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
| @ -75,13 +75,13 @@ export function getFieldTypeSelect() { | ||||
| export function getFieldNumSelect() { | ||||
|   return get({ | ||||
|     url: `/api/v1/field/get-field-numerical-type-drop`, | ||||
|   }) | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // 获取字段列表
 | ||||
| export function getFieldList({ modularId, fieldName, page, perPage }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/field/list", | ||||
|     url: '/api/v1/field/list', | ||||
|     params: { | ||||
|       modular_id: modularId, | ||||
|       field_name: fieldName, | ||||
| @ -94,7 +94,7 @@ export function getFieldList({ modularId, fieldName, page, perPage }) { | ||||
| // 获取字段详情
 | ||||
| export function getFieldDetail({ fieldId }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/field/info", | ||||
|     url: '/api/v1/field/info', | ||||
|     params: { | ||||
|       field_id: fieldId, | ||||
|     }, | ||||
| @ -104,7 +104,7 @@ export function getFieldDetail({ fieldId }) { | ||||
| // 保存字段
 | ||||
| export function saveField(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/field/save", | ||||
|     url: '/api/v1/field/save', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
| @ -112,7 +112,7 @@ export function saveField(data) { | ||||
| // 删除字段
 | ||||
| export function deleteField(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/field/del", | ||||
|     url: '/api/v1/field/del', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,117 @@ | ||||
| <script setup> | ||||
| import { ref, watch } from 'vue'; | ||||
| 
 | ||||
| import { checkDbConnect } from '@/views/config-manage/project-cfg/service'; | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   open: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   type: { | ||||
|     type: String, | ||||
|     default: 'add', | ||||
|   }, | ||||
|   data: { | ||||
|     type: Object, | ||||
|     default: () => ({}), | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(['ok']); | ||||
| const formRules = { | ||||
|   project_name: [ | ||||
|     { required: true, message: '请输入项目名称', trigger: 'submit' }, | ||||
|   ], | ||||
|   mark: [{ required: true, message: '请输入项目标识', trigger: 'submit' }], | ||||
|   database_address: [ | ||||
|     { required: true, message: '请输入数据库地址', trigger: 'submit' }, | ||||
|   ], | ||||
|   database_port: [ | ||||
|     { required: true, message: '请输入数据库端口', trigger: 'submit' }, | ||||
|   ], | ||||
|   database_name: [ | ||||
|     { required: true, message: '请输入数据库名称', trigger: 'submit' }, | ||||
|   ], | ||||
| }; | ||||
| 
 | ||||
| const formRef = ref(); | ||||
| const formData = ref({ | ||||
|   project_name: undefined, | ||||
|   mark: undefined, | ||||
|   is_show: 0, | ||||
|   database_address: undefined, | ||||
|   database_port: undefined, | ||||
|   database_name: undefined, | ||||
|   database_username: undefined, | ||||
|   database_password: undefined, | ||||
|   drive_type: 1, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.data, | ||||
|   (newVal) => { | ||||
|     if (props.type === 'add') { | ||||
|       resetFormData(); | ||||
|     } else { | ||||
|       formData.value = newVal; | ||||
|     } | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| // 检查数据库连接 | ||||
| const toCheckDbConnect = () => { | ||||
|   if (validateConnect()) { | ||||
|     checkDbConnect({ | ||||
|       database_name: formData.value.database_name, | ||||
|       database_port: formData.value.database_port, | ||||
|       database_address: formData.value.database_address, | ||||
|       database_username: formData.value.database_username, | ||||
|       database_password: formData.value.database_password, | ||||
|       drive_type: formData.value.drive_type, | ||||
|     }).then((res) => { | ||||
|       message.success(res.message); | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const validateConnect = () => { | ||||
|   const fields = { | ||||
|     database_name: '请输入数据库名称', | ||||
|     database_port: '请输入数据库端口', | ||||
|     database_address: '请输入数据库地址', | ||||
|   }; | ||||
|   for (const key in fields) { | ||||
|     if (!formData.value[key]) { | ||||
|       message.error(fields[key]); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   return true; | ||||
| }; | ||||
| 
 | ||||
| const resetFormData = () => { | ||||
|   formData.value = { | ||||
|     project_name: undefined, | ||||
|     mark: undefined, | ||||
|     is_show: 0, | ||||
|     database_address: undefined, | ||||
|     database_port: undefined, | ||||
|     database_name: undefined, | ||||
|     database_username: undefined, | ||||
|     database_password: undefined, | ||||
|     drive_type: 1, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleOk = () => { | ||||
|   formRef.value.validate().then(() => { | ||||
|     emit('ok', formData.value); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <a-modal :open="open" @ok="handleOk"> | ||||
|     <a-form | ||||
| @ -14,13 +128,16 @@ | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="项目标识" name="mark"> | ||||
|         <a-input placeholder="请输入项目标识,例如:oa" v-model:value="formData.mark" /> | ||||
|         <a-input | ||||
|           placeholder="请输入项目标识,例如:oa" | ||||
|           v-model:value="formData.mark" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="展示状态" name="is_show"> | ||||
|         <a-switch | ||||
|           v-model:checked="formData.is_show" | ||||
|           :checkedValue="1" | ||||
|           :unCheckedValue="0" | ||||
|           :checked-value="1" | ||||
|           :un-checked-value="0" | ||||
|         /> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="数据库地址" name="database_address"> | ||||
| @ -54,9 +171,9 @@ | ||||
|             placeholder="请输入数据库密码" | ||||
|             v-model:value="formData.database_password" | ||||
|           /> | ||||
|           <a-button type="primary" @click="toCheckDbConnect" | ||||
|             >检测数据库连接</a-button | ||||
|           > | ||||
|           <a-button type="primary" @click="toCheckDbConnect"> | ||||
|             检测数据库连接 | ||||
|           </a-button> | ||||
|         </a-space> | ||||
|       </a-form-item> | ||||
|       <a-form-item label="数据库驱动" name="drive_type"> | ||||
| @ -68,116 +185,3 @@ | ||||
|     </a-form> | ||||
|   </a-modal> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { ref, watch } from "vue"; | ||||
| import { checkDbConnect } from "@/views/config-manage/project-cfg/service"; | ||||
| import { message } from "ant-design-vue"; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   open: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   type: { | ||||
|     type: String, | ||||
|     default: "add", | ||||
|   }, | ||||
|   data: { | ||||
|     type: Object, | ||||
|     default: () => ({}), | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(["ok"]); | ||||
| const formRules = { | ||||
|   project_name: [ | ||||
|     { required: true, message: "请输入项目名称", trigger: "submit" }, | ||||
|   ], | ||||
|   mark: [{ required: true, message: "请输入项目标识", trigger: "submit" }], | ||||
|   database_address: [ | ||||
|     { required: true, message: "请输入数据库地址", trigger: "submit" }, | ||||
|   ], | ||||
|   database_port: [ | ||||
|     { required: true, message: "请输入数据库端口", trigger: "submit" }, | ||||
|   ], | ||||
|   database_name: [ | ||||
|     { required: true, message: "请输入数据库名称", trigger: "submit" }, | ||||
|   ], | ||||
| }; | ||||
| 
 | ||||
| const formRef = ref(); | ||||
| const formData = ref({ | ||||
|   project_name: undefined, | ||||
|   mark: undefined, | ||||
|   is_show: 0, | ||||
|   database_address: undefined, | ||||
|   database_port: undefined, | ||||
|   database_name: undefined, | ||||
|   database_username: undefined, | ||||
|   database_password: undefined, | ||||
|   drive_type: 1, | ||||
| }); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.data, | ||||
|   (newVal) => { | ||||
|     if (props.type === "add") { | ||||
|       resetFormData(); | ||||
|     } else { | ||||
|       formData.value = newVal; | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| // 检查数据库连接 | ||||
| const toCheckDbConnect = () => { | ||||
|   if (validateConnect()) { | ||||
|     checkDbConnect({ | ||||
|       database_name: formData.value.database_name, | ||||
|       database_port: formData.value.database_port, | ||||
|       database_address: formData.value.database_address, | ||||
|       database_username: formData.value.database_username, | ||||
|       database_password: formData.value.database_password, | ||||
|       drive_type: formData.value.drive_type, | ||||
|     }).then((res) => { | ||||
|       message.success(res.message); | ||||
|     }); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const validateConnect = () => { | ||||
|   const fields = { | ||||
|     database_name: "请输入数据库名称", | ||||
|     database_port: "请输入数据库端口", | ||||
|     database_address: "请输入数据库地址", | ||||
|   }; | ||||
|   for (const key in fields) { | ||||
|     if (!formData.value[key]) { | ||||
|       message.error(fields[key]); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   return true; | ||||
| }; | ||||
| 
 | ||||
| const resetFormData = () => { | ||||
|   formData.value = { | ||||
|     project_name: undefined, | ||||
|     mark: undefined, | ||||
|     is_show: 0, | ||||
|     database_address: undefined, | ||||
|     database_port: undefined, | ||||
|     database_name: undefined, | ||||
|     database_username: undefined, | ||||
|     database_password: undefined, | ||||
|     drive_type: 1, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| const handleOk = () => { | ||||
|   formRef.value.validate().then(() => { | ||||
|     emit("ok", formData.value); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| export const projectCfgCols = [ | ||||
|   { dataIndex: 'project_id', title: '编号', align: 'center'}, | ||||
|   { dataIndex: 'project_name', title: '项目名称', align: 'center'}, | ||||
|   { dataIndex: 'mark', title: '项目标识', align: 'center'}, | ||||
|   { dataIndex: 'database_name', title: '数据库名', align: 'center'}, | ||||
|   { dataIndex: 'is_show', title: '展示状态', align: 'center'}, | ||||
|   { dataIndex: 'project_id', title: '编号', align: 'center' }, | ||||
|   { dataIndex: 'project_name', title: '项目名称', align: 'center' }, | ||||
|   { dataIndex: 'mark', title: '项目标识', align: 'center' }, | ||||
|   { dataIndex: 'database_name', title: '数据库名', align: 'center' }, | ||||
|   { dataIndex: 'is_show', title: '展示状态', align: 'center' }, | ||||
|   // { dataIndex: 'sort', title: '排序', align: 'center'},
 | ||||
|   { dataIndex: 'action', title: '操作', align: 'center'}, | ||||
|   { dataIndex: 'action', title: '操作', align: 'center' }, | ||||
| ]; | ||||
|  | ||||
| @ -1,99 +1,28 @@ | ||||
| <template> | ||||
|   <div class="normal-container project-cfg-box"> | ||||
|     <div class="header-box"> | ||||
|       <a-space> | ||||
|         <a-input | ||||
|           v-model:value="projectName" | ||||
|           placeholder="请输入项目名称" | ||||
|           allow-clear | ||||
|           style="width: 200px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||
|       </a-space> | ||||
|     </div> | ||||
|     <div class="content-box"> | ||||
|       <a-table | ||||
|         :columns="projectCfgCols" | ||||
|         :data-source="dataList" | ||||
|         :pagination="false" | ||||
|         size="small" | ||||
|         bordered | ||||
|       > | ||||
|         <template #bodyCell="{ column, record }"> | ||||
|           <template v-if="column.dataIndex === 'is_show'"> | ||||
|             <a-switch | ||||
|               v-model:checked="record.is_show" | ||||
|               :checkedValue="1" | ||||
|               :unCheckedValue="0" | ||||
|               @change="toChangeStatus(record.project_id, $event)" | ||||
|             /> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'action'"> | ||||
|             <a-space> | ||||
|               <a-button | ||||
|                 type="link" | ||||
|                 size="small" | ||||
|                 @click="toGetDetail(record.project_id)" | ||||
|               > | ||||
|                 编辑 | ||||
|               </a-button> | ||||
|               <a-popconfirm | ||||
|                 title="确定删除?" | ||||
|                 @confirm="toDelete(record.project_id)" | ||||
|               > | ||||
|                 <a-button type="link" size="small">删除</a-button> | ||||
|               </a-popconfirm> | ||||
|             </a-space> | ||||
|           </template> | ||||
|         </template> | ||||
|       </a-table> | ||||
|       <a-pagination | ||||
|         v-model:current="pageState.page" | ||||
|         :total="pageState.total" | ||||
|         :page-size="pageState.perPage" | ||||
|         :hide-on-single-page="false" | ||||
|         size="small" | ||||
|         class="pagination-box" | ||||
|         @change="toGetProjectList" | ||||
|       /> | ||||
|     </div> | ||||
|     <CreateModal | ||||
|       :width="700" | ||||
|       :open="modalState.visible" | ||||
|       :title="modalState.title" | ||||
|       :type="modalState.type" | ||||
|       :data="modalState.data" | ||||
|       :confirmLoading="saveLoading" | ||||
|       @cancel="modalState.visible = false" | ||||
|       @ok="toSave" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { onMounted, ref, reactive } from "vue"; | ||||
| import { message } from "ant-design-vue"; | ||||
| import { projectCfgCols } from "./config"; | ||||
| import { onMounted, reactive, ref } from 'vue'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| import CreateModal from './components/create-modal.vue'; | ||||
| import { projectCfgCols } from './config'; | ||||
| import { | ||||
|   getProjectList, | ||||
|   saveProject, | ||||
|   deleteProject, | ||||
|   getProjectDetail, | ||||
|   getProjectList, | ||||
|   saveProject, | ||||
|   updateStatus, | ||||
| } from "./service"; | ||||
| import CreateModal from "./components/create-modal.vue"; | ||||
| } from './service'; | ||||
| 
 | ||||
| const dataList = ref([]); | ||||
| const listLoading = ref(false); | ||||
| const saveLoading = ref(false); | ||||
| const detailLoading = ref(false); | ||||
| const projectName = ref(""); | ||||
| const projectName = ref(''); | ||||
| 
 | ||||
| const modalState = reactive({ | ||||
|   title: undefined, | ||||
|   visible: false, | ||||
|   type: "", // add - 新建,edit - 编辑 | ||||
|   type: '', // add - 新建,edit - 编辑 | ||||
|   data: { | ||||
|     project_name: undefined, | ||||
|     is_show: 0, | ||||
| @ -137,7 +66,7 @@ const toSave = (data) => { | ||||
|   saveLoading.value = true; | ||||
|   saveProject(data) | ||||
|     .then(() => { | ||||
|       message.success("保存成功"); | ||||
|       message.success('保存成功'); | ||||
|       modalState.visible = false; | ||||
|       toGetProjectList(); | ||||
|     }) | ||||
| @ -150,8 +79,8 @@ const toSave = (data) => { | ||||
| const toGetDetail = (projectId) => { | ||||
|   detailLoading.value = true; | ||||
|   modalState.visible = true; | ||||
|   modalState.title = "编辑"; | ||||
|   modalState.type = "edit"; | ||||
|   modalState.title = '编辑'; | ||||
|   modalState.type = 'edit'; | ||||
|   getProjectDetail({ projectId }).then((res) => { | ||||
|     modalState.data = res.data; | ||||
|   }); | ||||
| @ -160,14 +89,14 @@ const toGetDetail = (projectId) => { | ||||
| // 删除 | ||||
| const toDelete = (id) => { | ||||
|   deleteProject({ projectId: id }).then(() => { | ||||
|     message.success("删除成功"); | ||||
|     message.success('删除成功'); | ||||
|     search(); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const toChangeStatus = (id, status) => { | ||||
|   updateStatus({ project_id: id, status }).then(() => { | ||||
|     message.success("修改成功"); | ||||
|     message.success('修改成功'); | ||||
|     search(); | ||||
|   }); | ||||
| }; | ||||
| @ -180,12 +109,85 @@ const search = () => { | ||||
| // 点击新建 | ||||
| const openCreateModal = () => { | ||||
|   modalState.visible = true; | ||||
|   modalState.title = "新建"; | ||||
|   modalState.type = "add"; | ||||
|   modalState.title = '新建'; | ||||
|   modalState.type = 'add'; | ||||
|   modalState.data = {}; | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="normal-container project-cfg-box"> | ||||
|     <div class="header-box"> | ||||
|       <a-space> | ||||
|         <a-input | ||||
|           v-model:value="projectName" | ||||
|           placeholder="请输入项目名称" | ||||
|           allow-clear | ||||
|           style="width: 200px" | ||||
|           @change="search" | ||||
|         /> | ||||
|         <a-button type="primary" @click="openCreateModal">新建</a-button> | ||||
|       </a-space> | ||||
|     </div> | ||||
|     <div class="content-box"> | ||||
|       <a-table | ||||
|         :columns="projectCfgCols" | ||||
|         :data-source="dataList" | ||||
|         :pagination="false" | ||||
|         size="small" | ||||
|         bordered | ||||
|       > | ||||
|         <template #bodyCell="{ column, record }"> | ||||
|           <template v-if="column.dataIndex === 'is_show'"> | ||||
|             <a-switch | ||||
|               v-model:checked="record.is_show" | ||||
|               :checked-value="1" | ||||
|               :un-checked-value="0" | ||||
|               @change="toChangeStatus(record.project_id, $event)" | ||||
|             /> | ||||
|           </template> | ||||
|           <template v-if="column.dataIndex === 'action'"> | ||||
|             <a-space> | ||||
|               <a-button | ||||
|                 type="link" | ||||
|                 size="small" | ||||
|                 @click="toGetDetail(record.project_id)" | ||||
|               > | ||||
|                 编辑 | ||||
|               </a-button> | ||||
|               <a-popconfirm | ||||
|                 title="确定删除?" | ||||
|                 @confirm="toDelete(record.project_id)" | ||||
|               > | ||||
|                 <a-button type="link" size="small">删除</a-button> | ||||
|               </a-popconfirm> | ||||
|             </a-space> | ||||
|           </template> | ||||
|         </template> | ||||
|       </a-table> | ||||
|       <a-pagination | ||||
|         v-model:current="pageState.page" | ||||
|         :total="pageState.total" | ||||
|         :page-size="pageState.perPage" | ||||
|         :hide-on-single-page="false" | ||||
|         size="small" | ||||
|         class="pagination-box" | ||||
|         @change="toGetProjectList" | ||||
|       /> | ||||
|     </div> | ||||
|     <CreateModal | ||||
|       :width="700" | ||||
|       :open="modalState.visible" | ||||
|       :title="modalState.title" | ||||
|       :type="modalState.type" | ||||
|       :data="modalState.data" | ||||
|       :confirm-loading="saveLoading" | ||||
|       @cancel="modalState.visible = false" | ||||
|       @ok="toSave" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="less" scope> | ||||
| .header-box { | ||||
|   margin-bottom: 10px; | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { get, post } from "@/utils/request"; | ||||
| import { get, post } from '@/utils/request'; | ||||
| 
 | ||||
| // 获取项目列表
 | ||||
| export function getProjectList({ page, perPage, projectName }) { | ||||
|  | ||||
| @ -46,12 +46,12 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { ref, watch } from "vue"; | ||||
| import { ref, watch } from 'vue'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   type: { | ||||
|     type: String, | ||||
|     default: "add", | ||||
|     default: 'add', | ||||
|   }, | ||||
|   data: { | ||||
|     type: Object, | ||||
| @ -59,22 +59,22 @@ const props = defineProps({ | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(["ok"]); | ||||
| const emit = defineEmits(['ok']); | ||||
| const formRules = { | ||||
|   field_title: [ | ||||
|     { required: true, message: "请输入字段标题", trigger: "submit" }, | ||||
|     { required: true, message: '请输入字段标题', trigger: 'submit' }, | ||||
|   ], | ||||
|   field_name: [ | ||||
|     { required: true, message: "请输入字段名称", trigger: "submit" }, | ||||
|     { required: true, message: '请输入字段名称', trigger: 'submit' }, | ||||
|   ], | ||||
|   field_type_id: [ | ||||
|     { required: true, message: "请选择字段类型", trigger: "submit" }, | ||||
|     { required: true, message: '请选择字段类型', trigger: 'submit' }, | ||||
|   ], | ||||
|   belong_to_table: [ | ||||
|     { required: true, message: "请输入所属表", trigger: "submit" }, | ||||
|     { required: true, message: '请输入所属表', trigger: 'submit' }, | ||||
|   ], | ||||
|   original_sql: [ | ||||
|     { required: true, message: "请输入sql数据源", trigger: "submit" }, | ||||
|     { required: true, message: '请输入sql数据源', trigger: 'submit' }, | ||||
|   ], | ||||
| }; | ||||
| 
 | ||||
| @ -92,19 +92,19 @@ const formData = ref({ | ||||
| watch( | ||||
|   () => props.type, | ||||
|   (newVal) => { | ||||
|     if (newVal === "add") { | ||||
|     if (newVal === 'add') { | ||||
|       resetFormData(); | ||||
|     } else { | ||||
|       formData.value = props.data; | ||||
|     } | ||||
|   } | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| const resetFormData = () => {}; | ||||
| 
 | ||||
| const handleOk = () => { | ||||
|   formRef.value.validate().then(() => { | ||||
|     emit("ok", formData.value); | ||||
|     emit('ok', formData.value); | ||||
|   }); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| export const viewCfgCols = [ | ||||
|   { dataIndex: 'field_name', title: '字段名称', align: 'center'}, | ||||
|   { dataIndex: 'field_title', title: '字段标题', align: 'center'}, | ||||
|   { dataIndex: 'field_type_name', title: '字段类型', align: 'center'}, | ||||
|   { dataIndex: 'is_search', title: '是否可搜索', align: 'center'}, | ||||
|   { dataIndex: 'sort', title: '排序', align: 'center'}, | ||||
|   { dataIndex: 'belong_to_table', title: '所属表名称', align: 'center'}, | ||||
|   { dataIndex: 'original_sql', title: 'sql数据源', align: 'center'}, | ||||
|   { dataIndex: 'action', title: '操作', align: 'center'}, | ||||
|   { dataIndex: 'field_name', title: '字段名称', align: 'center' }, | ||||
|   { dataIndex: 'field_title', title: '字段标题', align: 'center' }, | ||||
|   { dataIndex: 'field_type_name', title: '字段类型', align: 'center' }, | ||||
|   { dataIndex: 'is_search', title: '是否可搜索', align: 'center' }, | ||||
|   { dataIndex: 'sort', title: '排序', align: 'center' }, | ||||
|   { dataIndex: 'belong_to_table', title: '所属表名称', align: 'center' }, | ||||
|   { dataIndex: 'original_sql', title: 'sql数据源', align: 'center' }, | ||||
|   { dataIndex: 'action', title: '操作', align: 'center' }, | ||||
| ]; | ||||
|  | ||||
| @ -69,26 +69,26 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { onMounted, ref, reactive } from "vue"; | ||||
| import { viewCfgCols } from "./config"; | ||||
| import { onMounted, ref, reactive } from 'vue'; | ||||
| import { viewCfgCols } from './config'; | ||||
| import { | ||||
|   getFieldList, | ||||
|   deleteField, | ||||
|   saveField, | ||||
|   getFieldDetail, | ||||
| } from "./service"; | ||||
| import CreateModal from "./components/create-modal.vue"; | ||||
| import { message } from "ant-design-vue"; | ||||
| } from './service'; | ||||
| import CreateModal from './components/create-modal.vue'; | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| const listLoading = ref(false); | ||||
| const saveLoading = ref(false); | ||||
| const dataList = ref([]); | ||||
| const fieldName = ref(""); | ||||
| const modularId = ref(""); | ||||
| const fieldName = ref(''); | ||||
| const modularId = ref(''); | ||||
| const modalState = reactive({ | ||||
|   title: undefined, | ||||
|   visible: false, | ||||
|   type: "", // add - 新建,edit - 编辑 | ||||
|   type: '', // add - 新建,edit - 编辑 | ||||
|   data: {}, | ||||
| }); | ||||
| const pageState = reactive({ | ||||
| @ -123,7 +123,7 @@ const toSave = (data) => { | ||||
|   saveLoading.value = true; | ||||
|   saveField(data) | ||||
|     .then(() => { | ||||
|       message.success("保存成功"); | ||||
|       message.success('保存成功'); | ||||
|     }) | ||||
|     .finally(() => { | ||||
|       saveLoading.value = false; | ||||
| @ -133,8 +133,8 @@ const toSave = (data) => { | ||||
| // 获取详情 | ||||
| const toGetDetail = (fieldId) => { | ||||
|   modalState.visible = true; | ||||
|   modalState.title = "编辑"; | ||||
|   modalState.type = "edit"; | ||||
|   modalState.title = '编辑'; | ||||
|   modalState.type = 'edit'; | ||||
|   getFieldDetail({ fieldId }).then((res) => { | ||||
|     modalState.data = res.data; | ||||
|   }); | ||||
| @ -143,7 +143,7 @@ const toGetDetail = (fieldId) => { | ||||
| // 删除 | ||||
| const toDelete = (fieldId) => { | ||||
|   deleteField({ field_id: fieldId }).then(() => { | ||||
|     message.success("删除成功"); | ||||
|     message.success('删除成功'); | ||||
|     search(); | ||||
|   }); | ||||
| }; | ||||
| @ -151,8 +151,8 @@ const toDelete = (fieldId) => { | ||||
| // 点击新建 | ||||
| const openCreateModal = () => { | ||||
|   modalState.visible = true; | ||||
|   modalState.title = "新建"; | ||||
|   modalState.type = "add"; | ||||
|   modalState.title = '新建'; | ||||
|   modalState.type = 'add'; | ||||
|   modalState.data = {}; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import { get, post } from "@/utils/request"; | ||||
| import { get, post } from '@/utils/request'; | ||||
| 
 | ||||
| // 获取列表
 | ||||
| export function getFieldList({ modularId, fieldName, page, perPage }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/field/list", | ||||
|     url: '/api/v1/field/list', | ||||
|     params: { | ||||
|       modularId, | ||||
|       fieldName, | ||||
| @ -16,7 +16,7 @@ export function getFieldList({ modularId, fieldName, page, perPage }) { | ||||
| // 获取详情
 | ||||
| export function getFieldDetail({ fieldId }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/field/info", | ||||
|     url: '/api/v1/field/info', | ||||
|     params: { | ||||
|       fieldId, | ||||
|     }, | ||||
| @ -26,7 +26,7 @@ export function getFieldDetail({ fieldId }) { | ||||
| // 保存字段
 | ||||
| export function saveField(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/field/save", | ||||
|     url: '/api/v1/field/save', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
| @ -34,7 +34,7 @@ export function saveField(data) { | ||||
| // 删除字段
 | ||||
| export function deleteField(data) { | ||||
|   return post({ | ||||
|     url: "/api/v1/field/del", | ||||
|     url: '/api/v1/field/del', | ||||
|     data, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| @ -47,7 +47,7 @@ | ||||
|                     :title="item.data.preview_name" | ||||
|                     :is-export="item.data.is_export" | ||||
|                     @toFilt=" | ||||
|                       (params?:object) => { | ||||
|                       (params?: object) => { | ||||
|                         handleSingle(ids[index], params); | ||||
|                       } | ||||
|                     " | ||||
| @ -58,7 +58,7 @@ | ||||
|                     :title="item.data.preview_name" | ||||
|                     :filter-config="item.data.filter" | ||||
|                     @toFilt=" | ||||
|                     (params?:object) => { | ||||
|                       (params?: object) => { | ||||
|                         handleSingle(ids[index], params); | ||||
|                       } | ||||
|                     " | ||||
| @ -87,8 +87,8 @@ | ||||
|                       :title="item.data.preview_name" | ||||
|                       :is-export="item.data.is_export" | ||||
|                       @toFilt=" | ||||
|                         (params?:object) => { | ||||
|                           handleSingle(ids[index], params,); | ||||
|                         (params?: object) => { | ||||
|                           handleSingle(ids[index], params); | ||||
|                         } | ||||
|                       " | ||||
|                     ></y-table> | ||||
| @ -98,7 +98,7 @@ | ||||
|                       :title="item.data.preview_name" | ||||
|                       :filter-config="item.data.filter" | ||||
|                       @toFilt=" | ||||
|                         (params?:object) => { | ||||
|                         (params?: object) => { | ||||
|                           handleSingle(ids[index], params); | ||||
|                         } | ||||
|                       " | ||||
| @ -114,17 +114,17 @@ | ||||
|   </div> | ||||
| </template> | ||||
| <script setup lang="ts"> | ||||
| import { ref, shallowRef, computed, onMounted, watch } from "vue"; | ||||
| import { useRoute, useRouter } from "vue-router"; | ||||
| import { BarsOutlined } from "@ant-design/icons-vue"; | ||||
| import { ref, shallowRef, computed, onMounted, watch } from 'vue'; | ||||
| import { useRoute, useRouter } from 'vue-router'; | ||||
| import { BarsOutlined } from '@ant-design/icons-vue'; | ||||
| // utils | ||||
| import PLimit from "p-limit"; | ||||
| import PLimit from 'p-limit'; | ||||
| // api | ||||
| import { searchInfo } from "@/api/preview/index"; | ||||
| import { getProjectDrop } from "@/api/common"; | ||||
| import { getPageInfo } from "./service"; | ||||
| import type { SelectProps } from "ant-design-vue"; | ||||
| import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper' | ||||
| import { searchInfo } from '@/api/preview/index'; | ||||
| import { getProjectDrop } from '@/api/common'; | ||||
| import { getPageInfo } from './service'; | ||||
| import type { SelectProps } from 'ant-design-vue'; | ||||
| import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'; | ||||
| 
 | ||||
| interface ItemDetail { | ||||
|   id: number | string; | ||||
| @ -143,13 +143,13 @@ interface Option extends SelectProps { | ||||
| } | ||||
| 
 | ||||
| const VIEW_TYPE = { | ||||
|   TABLE: "table", | ||||
|   CHART: "chart", | ||||
|   TABLE: 'table', | ||||
|   CHART: 'chart', | ||||
| }; | ||||
| 
 | ||||
| const SEARCH_TYPE = { | ||||
|   SEARCH: "search", | ||||
|   INIT: "init", | ||||
|   SEARCH: 'search', | ||||
|   INIT: 'init', | ||||
| }; | ||||
| 
 | ||||
| // hooks | ||||
| @ -162,8 +162,11 @@ const projectOptions = shallowRef<Option[]>(); | ||||
| const isDraggable = false; | ||||
| 
 | ||||
| const isInQiankun = computed(() => { | ||||
|   return qiankunWindow?.__POWERED_BY_QIANKUN__ || window?.proxy?.__POWERED_BY_QIANKUN__ | ||||
| }) | ||||
|   return ( | ||||
|     qiankunWindow?.__POWERED_BY_QIANKUN__ || | ||||
|     window?.proxy?.__POWERED_BY_QIANKUN__ | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| const layoutList = computed(() => { | ||||
|   return ids.value.map((item, index) => { | ||||
| @ -186,9 +189,12 @@ const ids = ref<Item[]>([]); | ||||
| 
 | ||||
| const pLimit = PLimit(2); | ||||
| 
 | ||||
| watch(() => route.query.viewId, () => { | ||||
|   getPageInfoData() | ||||
| }) | ||||
| watch( | ||||
|   () => route.query.viewId, | ||||
|   () => { | ||||
|     getPageInfoData(); | ||||
|   }, | ||||
| ); | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   getProjectList(); | ||||
| @ -254,19 +260,21 @@ const getSinglePreview = (data: { | ||||
| 
 | ||||
| // 获取页面信息所有的id | ||||
| const getPageInfoData = () => { | ||||
|   getPageInfo({ mark:projectTag.value, page_id: pageId.value ?? "-1" }) | ||||
|   getPageInfo({ mark: projectTag.value, page_id: pageId.value ?? '-1' }) | ||||
|     .then((res) => { | ||||
|       if (res.code === 200) { | ||||
|         if (route.query.viewId) { | ||||
|           ids.value = res.data?.filter((item: any) => { | ||||
|           ids.value = res.data | ||||
|             ?.filter((item: any) => { | ||||
|               return item.preview_id === Number(route.query.viewId); | ||||
|           }).map((item: any) => { | ||||
|             }) | ||||
|             .map((item: any) => { | ||||
|               return { | ||||
|                 id: item.preview_id, | ||||
|                 data: item, | ||||
|                 loading: false, | ||||
|             } | ||||
|           }) | ||||
|               }; | ||||
|             }); | ||||
|         } else { | ||||
|           ids.value = res.data?.map((item: any) => { | ||||
|             return { | ||||
| @ -295,14 +303,13 @@ const fetchFn = (delay: number, info: ItemDetail) => { | ||||
| const getAllCardsData = async () => { | ||||
|   let listDB = []; | ||||
|   for (let i in ids.value) { | ||||
|     listDB.push(pLimit(() => fetchFn(i === "0" ? 200 : 1000, ids.value[i]))); | ||||
|     listDB.push(pLimit(() => fetchFn(i === '0' ? 200 : 1000, ids.value[i]))); | ||||
|   } | ||||
|   await Promise.all(listDB); | ||||
|   //此处的listDB就是最后整合的数据 | ||||
| }; | ||||
| </script> | ||||
| <style lang="less" scoped> | ||||
| 
 | ||||
| .view-box { | ||||
|   height: 100%; | ||||
|   width: 100%; | ||||
| @ -312,7 +319,9 @@ const getAllCardsData = async () => { | ||||
|   border-radius: 4px; | ||||
|   transition: all 0.3s; | ||||
|   &:hover { | ||||
|     box-shadow: 0 0 20px 0 #0a103205, 0 14px 40px 0 #0a103208, | ||||
|     box-shadow: | ||||
|       0 0 20px 0 #0a103205, | ||||
|       0 14px 40px 0 #0a103208, | ||||
|       0 20px 60px 0 #0a10320d; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| import { get, post } from "@/utils/request"; | ||||
| import { get, post } from '@/utils/request'; | ||||
| interface PageInfoParams { | ||||
|   mark: string; | ||||
|   page_id: number | string; | ||||
| } | ||||
| export const getPageInfo = (data: PageInfoParams) => | ||||
|   get({ | ||||
|     url: "/api/v1/preview/get-preview-info", | ||||
|     url: '/api/v1/preview/get-preview-info', | ||||
|     params: data, | ||||
|   }); | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| @import "@/assets/styles/variable.less"; | ||||
| @import '@/assets/styles/variable.less'; | ||||
| 
 | ||||
| // 设置按钮 | ||||
| :deep(.ycode-ant-btn-primary) { | ||||
|   background-color: @primary-color !important; | ||||
| 
 | ||||
|   &:hover { | ||||
|     background-color: @primary-light-color !important; | ||||
|   } | ||||
| @ -13,7 +14,7 @@ | ||||
|   .ycode-ant-input-affix-wrapper:not( | ||||
|       .ycode-ant-input-affix-wrapper-disabled | ||||
|     ):hover | ||||
|   ) { | ||||
| ) { | ||||
|   border-color: @primary-light-color !important; | ||||
| } | ||||
| 
 | ||||
| @ -34,6 +35,7 @@ | ||||
|   &-focused { | ||||
|     border-color: @primary-light-color !important; | ||||
|   } | ||||
| 
 | ||||
|   &-active-bar { | ||||
|     background-color: @primary-light-color !important; | ||||
|   } | ||||
| @ -41,18 +43,20 @@ | ||||
| 
 | ||||
| // 设置表格 | ||||
| :deep(.ycode-ant-table-thead > tr > th) { | ||||
|   background-color: @table-head-bg-color !important; | ||||
|   color: @table-head-font-color !important; | ||||
|   background-color: @table-head-bg-color !important; | ||||
| } | ||||
| 
 | ||||
| // 设置分页器 | ||||
| :deep(.ycode-ant-pagination-item) { | ||||
|   &-active { | ||||
|     border-color: @primary-color !important; | ||||
| 
 | ||||
|     a { | ||||
|       color: @primary-color !important; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &-link-icon { | ||||
|     color: @primary-color !important; | ||||
|   } | ||||
|  | ||||
| @ -62,7 +62,11 @@ | ||||
|           <a-button | ||||
|             class="preview-btn" | ||||
|             :loading="previewLoading" | ||||
|             @click="() => {toPreview({})}" | ||||
|             @click=" | ||||
|               () => { | ||||
|                 toPreview({}); | ||||
|               } | ||||
|             " | ||||
|             >预览</a-button | ||||
|           > | ||||
|           <a-button type="primary" @click="addViewName">点击保存</a-button> | ||||
| @ -113,7 +117,10 @@ | ||||
|               /> | ||||
|               <!-- 数值区间 --> | ||||
|               <div | ||||
|                 v-else-if="item.type === 'number_range' && previewData.filterData[item.name]" | ||||
|                 v-else-if=" | ||||
|                   item.type === 'number_range' && | ||||
|                   previewData.filterData[item.name] | ||||
|                 " | ||||
|                 class="number_range_box" | ||||
|               > | ||||
|                 <a-input-number | ||||
| @ -170,7 +177,11 @@ | ||||
|               :hide-on-single-page="false" | ||||
|               size="small" | ||||
|               class="pagination-box" | ||||
|               @change="() => { toPreview({}) }" | ||||
|               @change=" | ||||
|                 () => { | ||||
|                   toPreview({}); | ||||
|                 } | ||||
|               " | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
| @ -178,7 +189,11 @@ | ||||
|           v-else-if="previewData.type === 'chart'" | ||||
|           :chart-cfg="previewData.chartCfg" | ||||
|           :filter-config="previewData.filter" | ||||
|           @toFilt="() => {toPreview({})}" | ||||
|           @toFilt=" | ||||
|             () => { | ||||
|               toPreview({}); | ||||
|             } | ||||
|           " | ||||
|         ></y-chart> | ||||
|         <div class="preview-area" v-else> | ||||
|           <div><BarChartOutlined /></div> | ||||
| @ -203,21 +218,21 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { onMounted, reactive, ref } from "vue"; | ||||
| import { onMounted, reactive, ref } from 'vue'; | ||||
| import { | ||||
|   getProModularField, | ||||
|   preview, | ||||
|   saveView, | ||||
|   getShowTypeSelect, | ||||
|   getFieldOpts, | ||||
| } from "./service"; | ||||
| import { message } from "ant-design-vue"; | ||||
| import { BarChartOutlined } from "@ant-design/icons-vue"; | ||||
| import yChart from "@/components/common/y-chart.vue"; | ||||
| import { cloneDeep } from "lodash-es"; | ||||
| } from './service'; | ||||
| import { message } from 'ant-design-vue'; | ||||
| import { BarChartOutlined } from '@ant-design/icons-vue'; | ||||
| import yChart from '@/components/common/y-chart.vue'; | ||||
| import { cloneDeep } from 'lodash-es'; | ||||
| 
 | ||||
| const projectSel = ref([]); // 项目下拉 | ||||
| const modularSel = ref([])  // 数据来源下拉 | ||||
| const modularSel = ref([]); // 数据来源下拉 | ||||
| const showTypeSel = ref([]); // 展示类型下拉 | ||||
| const fieldList = ref([]); // 字段列表 | ||||
| const xDataList = ref([]); // x轴数据列表 | ||||
| @ -235,7 +250,7 @@ const nameVisible = ref(false); | ||||
| const previewName = ref(); | ||||
| 
 | ||||
| const previewData = reactive({ | ||||
|   type: "", | ||||
|   type: '', | ||||
|   filterConfig: [], // 筛选条件 | ||||
|   columnConfig: [], // 表格表头 | ||||
|   dataList: [], // 表格数据 | ||||
| @ -308,23 +323,23 @@ const tranformList = (list) => { | ||||
|       value: item.field_id, | ||||
|     }; | ||||
|   }); | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| // 重置配置数据 | ||||
| const resetSelectData = () => { | ||||
|   showTypeId.value = undefined | ||||
|   isExport.value = 0 | ||||
|   showTypeId.value = undefined; | ||||
|   isExport.value = 0; | ||||
|   fieldList.value = []; | ||||
|   fieldIds.value = []; | ||||
|   xDataList.value = []; | ||||
|   yDataList.value = []; | ||||
|   xDataId.value = undefined; | ||||
|   yDataId.value = []; | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| // 重置预览数据 | ||||
| const resetPreviewData = () => { | ||||
|   previewData.type = ""; | ||||
|   previewData.type = ''; | ||||
|   previewData.filterConfig = []; | ||||
|   previewData.columnConfig = []; | ||||
|   previewData.dataList = []; | ||||
| @ -336,14 +351,17 @@ const resetPreviewData = () => { | ||||
| }; | ||||
| 
 | ||||
| // 请求预览数据 | ||||
| const toPreview = ({filter}) => { | ||||
| const toPreview = ({ filter }) => { | ||||
|   previewLoading.value = true; | ||||
|   let filterData | ||||
|   let filterData; | ||||
|   if (!filter) { | ||||
|     const cloneFilter = cloneDeep(previewData.filterConfig) | ||||
|     const cloneFilter = cloneDeep(previewData.filterConfig); | ||||
|     filterData = cloneFilter | ||||
|       .filter((item) => { | ||||
|         return previewData.filterData[item.name] !== undefined && previewData.filterData[item.name] !== null; | ||||
|         return ( | ||||
|           previewData.filterData[item.name] !== undefined && | ||||
|           previewData.filterData[item.name] !== null | ||||
|         ); | ||||
|       }) | ||||
|       .map((item) => { | ||||
|         if (item.type === 'time' && previewData.filterData[item.name]) { | ||||
| @ -351,24 +369,36 @@ const toPreview = ({filter}) => { | ||||
|           return { | ||||
|             name: item.name, | ||||
|             type: item.type, | ||||
|             start_time: previewData.filterData[item.name][0].format('YYYY-MM-DD'), | ||||
|             start_time: | ||||
|               previewData.filterData[item.name][0].format('YYYY-MM-DD'), | ||||
|             end_time: previewData.filterData[item.name][1].format('YYYY-MM-DD'), | ||||
|           }; | ||||
|         } else if (item.type === 'date_time' && previewData.filterData[item.name]) { | ||||
|         } else if ( | ||||
|           item.type === 'date_time' && | ||||
|           previewData.filterData[item.name] | ||||
|         ) { | ||||
|           // 带时分的日期类型参数 | ||||
|           return { | ||||
|             name: item.name, | ||||
|             type: item.type, | ||||
|             start_time: previewData.filterData[item.name][0].format('YYYY-MM-DD HH:mm') + ':00', | ||||
|             end_time: previewData.filterData[item.name][1].format('YYYY-MM-DD HH:mm') + ':59', | ||||
|             start_time: | ||||
|               previewData.filterData[item.name][0].format('YYYY-MM-DD HH:mm') + | ||||
|               ':00', | ||||
|             end_time: | ||||
|               previewData.filterData[item.name][1].format('YYYY-MM-DD HH:mm') + | ||||
|               ':59', | ||||
|           }; | ||||
|         } else if (item.type === 'number_range') { | ||||
|           // 数值区间 | ||||
|           return { | ||||
|             name: item.name, | ||||
|             type: item.type, | ||||
|             min: previewData.filterData[item.name].min ? String(previewData.filterData[item.name].min) : '', | ||||
|             max: previewData.filterData[item.name].max ? String(previewData.filterData[item.name].max) : '', | ||||
|             min: previewData.filterData[item.name].min | ||||
|               ? String(previewData.filterData[item.name].min) | ||||
|               : '', | ||||
|             max: previewData.filterData[item.name].max | ||||
|               ? String(previewData.filterData[item.name].max) | ||||
|               : '', | ||||
|           }; | ||||
|         } else { | ||||
|           return { | ||||
| @ -395,17 +425,22 @@ const toPreview = ({filter}) => { | ||||
|   }) | ||||
|     .then((res) => { | ||||
|       previewData.type = res.data.type; | ||||
|       if (res.data.type === "table") { | ||||
|       if (res.data.type === 'table') { | ||||
|         previewData.filterConfig = res.data.filter; | ||||
|         previewData.filterConfig.forEach((item) => { | ||||
|           if (item.type === 'number_range' && !previewData.filterData[item.name]) { | ||||
|           if ( | ||||
|             item.type === 'number_range' && | ||||
|             !previewData.filterData[item.name] | ||||
|           ) { | ||||
|             previewData.filterData[item.name] = { | ||||
|               ...item, | ||||
|               min: undefined, | ||||
|               max: undefined, | ||||
|             }; | ||||
|           } else { | ||||
|             previewData.filterData[item.name] = previewData.filterData[item.name] | ||||
|             previewData.filterData[item.name] = previewData.filterData[ | ||||
|               item.name | ||||
|             ] | ||||
|               ? previewData.filterData[item.name] | ||||
|               : undefined; | ||||
|           } | ||||
| @ -413,7 +448,7 @@ const toPreview = ({filter}) => { | ||||
|         previewData.columnConfig = res.data.header; | ||||
|         previewData.dataList = res.data.data; | ||||
|         previewData.total = res.data.count; | ||||
|         previewData.isExport = res.data.is_export | ||||
|         previewData.isExport = res.data.is_export; | ||||
|       } else { | ||||
|         previewData.chartCfg = res.data.config; | ||||
|         previewData.filter = res.data.filter; | ||||
| @ -431,7 +466,7 @@ const addViewName = () => { | ||||
| 
 | ||||
| const toSaveView = () => { | ||||
|   if (!previewName.value) { | ||||
|     message.error("请输入名称"); | ||||
|     message.error('请输入名称'); | ||||
|     return; | ||||
|   } | ||||
|   saveView({ | ||||
| @ -443,7 +478,7 @@ const toSaveView = () => { | ||||
|     yDataId: yDataId.value?.toString(), | ||||
|     isExport: isExport.value, | ||||
|   }).then(() => { | ||||
|     message.success("保存成功,可前往视图列表查看"); | ||||
|     message.success('保存成功,可前往视图列表查看'); | ||||
|     nameVisible.value = false; | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import { get, post } from "@/utils/request"; | ||||
| import { get, post } from '@/utils/request'; | ||||
| 
 | ||||
| // 项目-表-字段下拉
 | ||||
| export function getProModularField() { | ||||
|   return get({ | ||||
|     url: "/api/v1/field/get-project-modular-field-drop", | ||||
|     url: '/api/v1/field/get-project-modular-field-drop', | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| @ -17,7 +17,7 @@ export function getShowTypeSelect() { | ||||
| // 字段列表
 | ||||
| export function getFieldOpts({ modularId, showTypeId }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/preview/get-preview-field", | ||||
|     url: '/api/v1/preview/get-preview-field', | ||||
|     params: { | ||||
|       modular_id: modularId, | ||||
|       show_type_id: showTypeId, | ||||
| @ -26,9 +26,19 @@ export function getFieldOpts({ modularId, showTypeId }) { | ||||
| } | ||||
| 
 | ||||
| // 预览
 | ||||
| export function preview({ modularId, fieldIds, page, perPage, filter, showTypeId, xDataId, yDataId, isExport }) { | ||||
| export function preview({ | ||||
|   modularId, | ||||
|   fieldIds, | ||||
|   page, | ||||
|   perPage, | ||||
|   filter, | ||||
|   showTypeId, | ||||
|   xDataId, | ||||
|   yDataId, | ||||
|   isExport, | ||||
| }) { | ||||
|   return post({ | ||||
|     url: "api/v1/preview/view", | ||||
|     url: 'api/v1/preview/view', | ||||
|     data: { | ||||
|       modular_id: modularId, | ||||
|       field_ids: fieldIds, | ||||
| @ -44,9 +54,16 @@ export function preview({ modularId, fieldIds, page, perPage, filter, showTypeId | ||||
| } | ||||
| 
 | ||||
| // 点击保存
 | ||||
| export function saveView({ modularId, fieldIds, previewName, showTypeId, xDataId, yDataId }) { | ||||
| export function saveView({ | ||||
|   modularId, | ||||
|   fieldIds, | ||||
|   previewName, | ||||
|   showTypeId, | ||||
|   xDataId, | ||||
|   yDataId, | ||||
| }) { | ||||
|   return post({ | ||||
|     url: "api/v1/preview/save", | ||||
|     url: 'api/v1/preview/save', | ||||
|     data: { | ||||
|       modular_id: modularId, | ||||
|       field_ids: fieldIds, | ||||
|  | ||||
| @ -83,7 +83,8 @@ | ||||
|           @toFilt=" | ||||
|             (params) => { | ||||
|               toGetViewInfo(params); | ||||
|             }" | ||||
|             } | ||||
|           " | ||||
|         /> | ||||
|         <y-chart | ||||
|           v-else-if="selectViewInfo.type === 'chart'" | ||||
| @ -102,12 +103,12 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script setup> | ||||
| import { onMounted, ref, reactive } from "vue"; | ||||
| import { getProModular, getViewList, getViewInfo, deleteView } from "./service"; | ||||
| import { viewListCols } from "./config"; | ||||
| import yTable from "@/components/common/y-table.vue"; | ||||
| import { message } from "ant-design-vue"; | ||||
| import { BarChartOutlined } from "@ant-design/icons-vue"; | ||||
| import { onMounted, ref, reactive } from 'vue'; | ||||
| import { getProModular, getViewList, getViewInfo, deleteView } from './service'; | ||||
| import { viewListCols } from './config'; | ||||
| import yTable from '@/components/common/y-table.vue'; | ||||
| import { message } from 'ant-design-vue'; | ||||
| import { BarChartOutlined } from '@ant-design/icons-vue'; | ||||
| 
 | ||||
| const projectSel = ref([]); | ||||
| const modularSel = ref([]); | ||||
| @ -116,13 +117,13 @@ const modularId = ref(); | ||||
| const dataList = ref([]); | ||||
| const selectedRowId = ref(); | ||||
| const selectViewInfo = ref({ | ||||
|   type: "", | ||||
|   type: '', | ||||
|   filter: [], | ||||
|   config: { | ||||
|     line: { | ||||
|       data: [] | ||||
|     } | ||||
|   } | ||||
|       data: [], | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const pageState = reactive({ | ||||
| @ -140,7 +141,7 @@ const toGetProModular = () => { | ||||
|     projectSel.value = res.data; | ||||
|     if (res.data.length) { | ||||
|       projectId.value = res.data[0].value; | ||||
|       onProjectChange(projectId.value) | ||||
|       onProjectChange(projectId.value); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| @ -177,7 +178,7 @@ const onModularChange = () => { | ||||
| 
 | ||||
| const toDelete = (previewId) => { | ||||
|   deleteView({ previewId }).then(() => { | ||||
|     message.success("删除成功"); | ||||
|     message.success('删除成功'); | ||||
|     toGetList(); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| @ -1,16 +1,16 @@ | ||||
| import { get, post } from "@/utils/request"; | ||||
| import { get, post } from '@/utils/request'; | ||||
| 
 | ||||
| // 联动下拉
 | ||||
| export function getProModular() { | ||||
|   return get({ | ||||
|     url: "/api/v1/field/get-project-modular-drop", | ||||
|     url: '/api/v1/field/get-project-modular-drop', | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| // 视图列表
 | ||||
| export function getViewList({ modularId, page = 1, perPage = 20 }) { | ||||
|   return get({ | ||||
|     url: "/api/v1/preview/list", | ||||
|     url: '/api/v1/preview/list', | ||||
|     params: { | ||||
|       modular_id: modularId, | ||||
|       page, | ||||
| @ -27,7 +27,7 @@ export function getViewInfo({ | ||||
|   filter = [], | ||||
| }) { | ||||
|   return post({ | ||||
|     url: "/api/v1/preview/info", | ||||
|     url: '/api/v1/preview/info', | ||||
|     data: { | ||||
|       preview_id: previewId, | ||||
|       page, | ||||
| @ -40,7 +40,7 @@ export function getViewInfo({ | ||||
| // 删除视图
 | ||||
| export function deleteView({ previewId }) { | ||||
|   return post({ | ||||
|     url: "/api/v1/preview/del", | ||||
|     url: '/api/v1/preview/del', | ||||
|     data: { | ||||
|       preview_id: previewId, | ||||
|     }, | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import { defineConfig } from "@farmfe/core"; | ||||
| import { defineConfig } from '@farmfe/core'; | ||||
| 
 | ||||
| export default defineConfig({ | ||||
|   compilation: { | ||||
|     input: { | ||||
|       index: "./src/index.ts", | ||||
|       index: './src/index.ts', | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { onLCP, onINP, onCLS } from "web-vitals"; | ||||
| import { onCLS, onINP, onLCP } from 'web-vitals'; | ||||
| // import { initializeApp } from "firebase/app";
 | ||||
| // import { getAnalytics } from "firebase/analytics";
 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 wangxuefeng
						wangxuefeng