chore: 容器框架升级,修复项目命令行异常问题

This commit is contained in:
wangxuefeng
2025-03-11 10:05:28 +08:00
parent de679d4289
commit 3e1a1b4a66
1187 changed files with 95352 additions and 12509 deletions

12
apps/designer/src/App.vue Normal file
View File

@@ -0,0 +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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1711803009570" class="icon" viewBox="0 0 1280 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1500" width="320" height="256" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M557.85 1023l-122-35.4c-12.8-3.6-20-17-16.4-29.8L692.45 17.4c3.6-12.8 17-20 29.8-16.4l122 35.4c12.8 3.6 20 17 16.4 29.8L587.65 1006.6c-3.8 12.8-17 20.2-29.8 16.4z m-228-224.4l87-92.8c9.2-9.8 8.6-25.4-1.6-34.4L234.05 512l181.2-159.4c10.2-9 11-24.6 1.6-34.4l-87-92.8c-9-9.6-24.2-10.2-34-1L7.65 494.4c-10.2 9.4-10.2 25.6 0 35l288.2 270.2c9.8 9.2 25 8.8 34-1z m654.4 1.2l288.2-270.2c10.2-9.4 10.2-25.6 0-35L984.25 224.2c-9.6-9-24.8-8.6-34 1L863.25 318c-9.2 9.8-8.6 25.4 1.6 34.4L1046.05 512l-181.2 159.4c-10.2 9-11 24.6-1.6 34.4l87 92.8c9 9.8 24.2 10.2 34 1.2z" fill="#0157fe" p-id="1501"></path></svg>

After

Width:  |  Height:  |  Size: 931 B

88
apps/designer/src/auto-imports.d.ts vendored Normal file
View File

@@ -0,0 +1,88 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// 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']
}
// 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')
}

View File

@@ -0,0 +1,2 @@
// @ts-ignore
export const currentEnv = __APP_ENV__;

View File

@@ -0,0 +1 @@
export * from './env';

View File

@@ -0,0 +1 @@
export const ACCESS_STORAGE_KEY = 'RRO_IDE_ACCESS_STORAGE__';

28
apps/designer/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,28 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}
declare namespace NodeJS {
interface ProcessEnv {
[key: string]: any;
}
}
declare module global {
interface Window {}
}
declare module 'vue' {
interface ComponentCustomProperties {
$uploader: any;
$reqeust: any;
$apis: any;
$libs: any;
}
}
export {};

View File

@@ -0,0 +1,21 @@
import instance from './instance';
export const getApiList = async () => {
const response = await instance.get('/api/v1/api');
return response.data;
};
export const createApi = async (data: any) => {
const response = await instance.post('/api/v1/api', data);
return response.data;
};
export const updateApi = async (id: string, data: any) => {
const response = await instance.put(`/api/v1/api/${id}`, data);
return response.data;
};
export const deleteApi = async (id: string) => {
const response = await instance.delete(`/api/v1/api/${id}`);
return response.data;
};

View File

@@ -0,0 +1,21 @@
import instance from './instance';
export const getApplicationList = async () => {
const response = await instance.get('/api/v1/applications');
return response.data;
};
export const createApplication = async (data: any) => {
const response = await instance.post('/api/v1/applications', data);
return response.data;
};
export const updateApplication = async (id: string, data: any) => {
const response = await instance.put(`/api/v1/applications/${id}`, data);
return response.data;
};
export const deleteApplication = async (id: string) => {
const response = await instance.delete(`/api/v1/applications/${id}`);
return response.data;
};

View File

@@ -0,0 +1,21 @@
import instance from './instance';
export const getBlockList = async () => {
const response = await instance.get('/api/v1/blocks');
return response.data;
};
export const createBlock = async (data: any) => {
const response = await instance.post('/api/v1/blocks', data);
return response.data;
};
export const updateBlock = async (id: string, data: any) => {
const response = await instance.put(`/api/v1/blocks/${id}`, data);
return response.data;
};
export const deleteBlock = async (id: string) => {
const response = await instance.delete(`/api/v1/blocks/${id}`);
return response.data;
};

View File

@@ -0,0 +1,91 @@
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;
};
function transformFile(file: LowCodeFileSchema): LowCodeFileSchema {
return {
project_id: file.project_id,
publish: file.publish,
active: file.active,
// @ts-ignore
dsl: JSON.stringify(file.dsl),
file_path: file.file_path,
file_id: file.file_id
};
}
export const getFileList = async () => {
const response = await instance.get('/api/v1/files');
return response.data;
};
export const getFile = async (id: string) => {
const response = await instance.get(`/api/v1/files/${id}`);
return response.data;
};
export const createFile = async (data: LowCodeFileSchema) => {
const response = await instance.post('/api/v1/files', transformFile(data));
return response.data;
};
export const updateFile = async (id: string, data: LowCodeFileSchema) => {
const response = await instance.put(
`/api/v1/files/${id}`,
transformFile(data)
);
return response.data;
};
type PublishResponse = {
code: string;
data: object;
id?: string;
message: string;
};
export const deleteFile = async (id: string) => {
const response = await instance.delete(`/api/v1/files/${id}`);
return response.data;
};
/**
* 发布指定项目的所有文件
* @param {number} projectId - 需要发布的项目ID
* @returns {Promise<any>} 包含发布操作结果的Promise对象
* @example
* // 发布项目ID为123的所有文件
* await publishAllFile(123)
*/
export const publishAllFile = async (
projectId: number
): Promise<PublishResponse> => {
const response = await instance.post('/api/v1/files/publish', {
project_id: projectId
});
return response.data;
};
/**
* 发布单个文件
* @param {string} fileId - 需要发布的文件ID
* @returns {Promise<any>} 包含发布操作结果的Promise对象
* @example
* // 发布文件ID为45tnbgeme的文件
* await publishFile('45tnbgeme')
*/
export const publishFile = async (fileId: string): Promise<PublishResponse> => {
const response = await instance.post('/api/v1/files/publish-file', {
file_id: fileId
});
return response.data;
};

View File

@@ -0,0 +1,76 @@
import instance from './instance';
import { type HistorySchema } from '@vtj/core';
export type LowCodeHistorySchema = {
project_id: number;
file_id: string;
history_id: string;
id?: string;
dsl?: HistorySchema;
};
function transformHistoryData(data: LowCodeHistorySchema) {
return {
...data,
dsl: JSON.stringify(data.dsl || {})
};
}
export type HistoriesResponse = {
code: number;
data: {
list: Array<{
id: number;
project_id: number;
file_id: string;
history_id: string;
dsl: Record<string, any>;
created_at: string;
updated_at: string;
}>;
total: number;
};
message: string;
};
export type GetHistoriesParams = {
project_id: number;
file_id: string;
page?: number;
per_page?: number;
};
export const getHistories = async (params: GetHistoriesParams) => {
const response = await instance.get<HistoriesResponse>('/api/v1/histories', {
params: {
project_id: params.project_id,
file_id: params.file_id,
...(params.page && { page: params.page }),
...(params.per_page && { per_page: params.per_page })
}
});
return response.data;
};
export const getHistory = async (id: string) => {
const response = await instance.get(`/api/v1/histories/${id}`);
return response.data;
};
export const createHistory = async (data: LowCodeHistorySchema) => {
const response = await instance.post(
'/api/v1/histories',
transformHistoryData(data)
);
return response.data;
};
export const updateHistory = async (id: string, data: any) => {
const response = await instance.put(`/api/v1/histories/${id}`, data);
return response.data;
};
export const deleteHistory = async (id: string) => {
const response = await instance.delete(`/api/v1/histories/${id}`);
return response.data;
};

View File

@@ -0,0 +1,7 @@
export * from './api';
export * from './block';
export * from './file';
export * from './materials';
export * from './project';
export * from './application';
export * from './history';

View File

@@ -0,0 +1,30 @@
import axios from 'axios';
const apiBase = import.meta.env.VITE_BASE_API_URL;
// 创建独立实例
const instance = axios.create({
baseURL: apiBase // 基础URL直接放在实例配置中
});
// 请求拦截器改为使用实例
instance.interceptors.request.use(
(config) => {
// 可在此处添加统一请求头等配置
return config;
},
(error) => {
return Promise.reject(error);
}
);
instance.interceptors.response.use(
(response) => {
return response.data;
},
(error) => {
return Promise.reject(error);
}
);
// 导出实例
export default instance;

View File

@@ -0,0 +1,112 @@
import { type MaterialDescription } from '@vtj/core';
import instance from './instance';
// 定义响应类型
interface MaterialResponse {
code: number;
data: MaterialData | MaterialData[];
message: string;
}
/** 创建物料请求参数 */
interface CreateMaterialRequest {
project_id: number;
value: string;
}
/** 创建物料响应类型 */
interface CreateMaterialResponse {
code: number;
data: { id: string };
message: string;
}
/** 删除物料响应类型 */
interface DeleteMaterialResponse {
code: number;
data: { id: string };
message: string;
}
/**
* 获取物料列表
* @param params 查询参数
* @returns 物料列表
*/
export const getMaterialsList = async (
params?: Record<string, any>
): Promise<MaterialResponse> => {
const response = await instance.get('/api/v1/materials', { params });
return response.data;
};
/**
* 根据ID获取单个物料
* @param id 物料ID
* @returns 物料详情
*/
export const getMaterials = async (id: number): Promise<MaterialResponse> => {
const response = await instance.get(`/api/v1/materials/${id}`);
return response.data;
};
type MaterialData = {
project_id: number;
value: Record<string, MaterialDescription>;
// 从原interface合并的字段
id?: number;
name?: string;
created_at?: string;
updated_at?: string;
};
function transformMaterialData(data: MaterialData) {
return {
...data,
value: JSON.stringify(data.value)
};
}
/**
* 创建新物料
* @param data 物料数据(注意 value 需要是 JSON 字符串)
* @example
* postMaterials({ project_id: 1, value: '{"Authorization": "Bearer token"}' })
*/
export const postMaterials = async (
data: MaterialData
): Promise<CreateMaterialResponse> => {
const response = await instance.post(
'/api/v1/materials',
transformMaterialData(data)
);
return response.data;
};
/**
* 更新物料
* @param data 物料数据
* @returns 更新操作结果
*/
export const updateMaterials = async (data: MaterialData): Promise<any> => {
const response = await instance.put(
'/api/v1/materials',
transformMaterialData(data)
);
return response.data;
};
/**
* 删除指定物料
* @param project_id 要删除的物料所属项目ID
* @returns 删除操作结果
* @example
* deleteMaterial('123').then(() => console.log('删除成功'))
*/
export const deleteMaterials = async (
project_id: number
): Promise<DeleteMaterialResponse> => {
const response = await instance.delete(`/api/v1/materials/${project_id}`);
return response.data;
};

View File

@@ -0,0 +1,28 @@
import instance from './instance';
export const getProjectList = async (data?: Record<string, any>) => {
const response = await instance.get('/api/v1/projects', {
params: data
});
return response.data;
};
export const getProject = async (id: string) => {
const response = await instance.get(`/api/v1/projects/${id}`);
return response.data;
};
export const createProject = async (data: any) => {
const response = await instance.post('/api/v1/projects', data);
return response.data;
};
export const updateProject = async (id: string, data: any) => {
const response = await instance.put(`/api/v1/projects/${id}`, data);
return response.data;
};
export const deleteProject = async (id: string) => {
const response = await instance.delete(`/api/v1/projects/${id}`);
return response.data;
};

20
apps/designer/src/main.ts Normal file
View File

@@ -0,0 +1,20 @@
import { createApp } from 'vue';
import { createPersistedState } from 'pinia-plugin-persistedstate';
import router from './router';
import App from './App.vue';
import './style/index.scss';
import { pinia } from './store';
// 添加持久化插件
pinia.use(
createPersistedState({
auto: true, // 自动持久化所有 store
storage: localStorage
})
);
const app = createApp(App);
app.use(router);
app.use(pinia);
app.mount('#app');

View File

@@ -0,0 +1,31 @@
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
{
path: '/',
name: 'home',
component: () => import('@/views/index.vue')
},
{
path: '/preview/:id',
name: 'preview',
component: () => import('@/views/preview.vue')
},
{
path: '/unauthorized',
name: 'Unauthorized',
component: () => import('@/views/unauthorized.vue')
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/not-found.vue')
}
];
const router = createRouter({
history: createWebHashHistory(),
routes
});
export default router;

View File

@@ -0,0 +1,249 @@
// @ts-nocheck
import {
type ProjectSchema,
type BlockSchema,
type HistorySchema,
type HistoryItem,
type MaterialDescription,
type ExtensionConfig,
type PageFile,
type BlockFile,
type NodeFromPlugin,
ProjectModel,
HistoryModel
} 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,
getHistories as getLowCodeHistories,
publishFile as publishLowCodeFile,
publishAllFile as publishLowCodeAllFile,
getMaterials as getLowCodeMaterials,
postMaterials as postLowCodeMaterials,
updateMaterials as updateLowCodeMaterials,
deleteMaterials as deleteLowCodeMaterials
} from '@/io';
const storage = new Storage({
type: 'local',
expired: 0
});
const stringifyFields = [
'config',
'pages',
'dependencies',
'blocks',
'apis',
'meta'
];
let initProject: ProjectSchema;
export class LowCodeService extends BaseService {
public async init(project: ProjectSchema): Promise<ProjectSchema> {
console.log('init', project);
initProject = project;
const remoteProject = await getProject(initProject.id);
const arrayFields = ['pages', 'blocks', 'apis', 'meta', 'dependencies'];
arrayFields.forEach((field) => {
if (isEmpty(remoteProject[field])) {
remoteProject[field] = [];
}
});
console.log('remoteProject', remoteProject);
const model = new ProjectModel(remoteProject);
console.log('model', model || { id: initProject.id });
const dsl = model.toDsl();
return Promise.resolve(dsl);
}
public getExtension(): Promise<ExtensionConfig | undefined> {
const extension = storage.get('extension');
console.log('ExtensionConfig', extension);
return Promise.resolve(extension as ExtensionConfig | undefined);
}
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);
return Promise.resolve(true);
}
public getPluginMaterial(
from: NodeFromPlugin
): Promise<MaterialDescription | null> {
return Promise.resolve(null);
}
// TODO: 物料存储只有在发布为其他端 (比如 uinapp) 时才有用,当前版本时不需要的,暂且保留
public async saveMaterials(
project: ProjectSchema,
materials: Map<string, MaterialDescription>
): Promise<boolean> {
const materialData = mapToObject(materials);
// storage.save(`materials_${project.id}`, materialData);
// console.log('saveMaterials', materialData);
// @ts-ignore
const existMaterials = await getLowCodeMaterials(project?.id);
if (existMaterials) {
// 更新物料
await updateLowCodeMaterials({
project_id: project?.id,
value: materialData
});
} else {
// 创建物料
await postLowCodeMaterials({
project_id: project?.id,
value: materialData
});
}
// @ts-ignore
// await deleteLowCodeMaterials(project.id);
return Promise.resolve(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);
};
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);
});
}
}

View File

@@ -0,0 +1,60 @@
import { ref, computed } from 'vue';
import axios from 'axios';
import { createPinia, defineStore } from 'pinia';
// 创建 pinia 实例
export const pinia = createPinia();
// 用户模块 store
export const useUserStore = defineStore('user', () => {
// 状态定义
const token = ref<string>(localStorage.getItem('y-code-access-token') || '');
const userProfile = ref<null>(null);
// getter 计算属性
const isLoggedIn = computed(() => !!token.value);
// 同步 action
const setToken = (newToken: string) => {
token.value = newToken;
localStorage.setItem('y-code-access-token', newToken);
};
// 清理方法
const logout = () => {
token.value = '';
userProfile.value = null;
localStorage.removeItem('y-code-access-token');
};
return {
token,
userProfile,
isLoggedIn,
setToken,
logout
};
});
// 应用配置 store
export const useAppStore = defineStore('app', () => {
const theme = ref<'light' | 'dark'>('light');
const sidebarCollapsed = ref(false);
// 持久化配置
const persist = {
paths: ['theme', 'sidebarCollapsed'],
storage: localStorage
};
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light';
};
return {
theme,
sidebarCollapsed,
toggleTheme,
persist
};
});

View File

@@ -0,0 +1,15 @@
@use '@vtj/web/src/index.scss';
html,
body,
#app {
padding: 0;
margin: 0;
font-size: 14px;
height: 100%;
overflow: hidden;
}
#vtjLink {
display: none;
}

View File

@@ -0,0 +1,81 @@
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';
export type ExtensionOptions = ExtensionConfig;
export type ExtensionFactory = (
config: ExtensionConfig
) => Partial<EngineOptions> | void;
export interface ExtensionOutput {
options: Partial<EngineOptions>;
adapters: Record<string, any>;
}
export class Extension {
private urls: string[] = [];
private library: string = '';
private params: any[] = [];
private __BASE_PATH__: string = '/';
private __adapters__: Record<string, any> = {};
constructor(private options: ExtensionOptions) {
const __VTJ_PRO__ = {
...core,
...designer,
...renderer
};
(window as any).Vue = Vue;
(window as any).__VTJ_PRO__ = __VTJ_PRO__;
(window as any).VtjUtils = VtjUtils;
(window as any).VtjIcons = VtjIcons;
(window as any).VtjUI = VtjUI;
(window as any).ElementPlus = ElementPlus;
const {
urls = [],
library,
params = [],
__BASE_PATH__ = '/',
__adapters__ = {}
} = options || {};
this.urls = urls;
this.library = library;
this.params = params;
this.__BASE_PATH__ = __BASE_PATH__;
this.__adapters__ = __adapters__;
}
async load(): Promise<ExtensionOutput> {
let options: Partial<EngineOptions> = {};
if (this.library) {
const base = this.__BASE_PATH__;
const css = this.urls
.filter((n) => renderer.isCSSUrl(n))
.map((n) => `${base}${n}`);
const scripts: string[] = this.urls
.filter((n) => renderer.isJSUrl(n))
.map((n) => `${base}${n}`);
renderer.loadCssUrl(css);
if (scripts.length) {
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 || {};
}
}
}
return {
options,
adapters: this.__adapters__
};
}
}

View File

@@ -0,0 +1,10 @@
import 'element-plus/theme-chalk/dark/css-vars.css';
import 'element-plus/theme-chalk/index.css';
// import 'vxe-table/es/style.min.css';
import '@vtj/ui/dist/style.css';
import '@vtj/icons/dist/style.css';
export * from '@vtj/core';
export * from '@vtj/designer';
export * from '@vtj/renderer';
export * from './extension';

View File

@@ -0,0 +1,74 @@
<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';
const container = ref();
const service = new LowCodeService();
const userStore = useUserStore();
onMounted(async () => {
// 数据模型
const model = {
name: '',
url: '',
applicationId: -1,
projectId: -1,
accessToken: ''
};
const handshake = new Postmate.Model({});
await handshake.then((parent) => {
parent.emit('sync-context', 'y-code-designer is ready');
Object.assign(model, parent.model);
// console.log('get parent model', model);
userStore.setToken(model.accessToken);
request.useRequest((req) => {
req.headers.set('Authorization', `Bearer ${model.accessToken}`);
return req;
});
const engine = new Engine({
container,
service,
project: {
// @ts-ignore
id: model.projectId,
name: model.name
},
adapter: {
request,
jsonp
}
});
widgetManager.set('Previewer', {
props: {
path: (block: any) => {
const pathname = location.pathname;
return `${pathname}#/preview/${block.id}`;
}
}
});
widgetManager.set('Templates', {
invisible: true
});
});
});
</script>
<template>
<div
class="designer-container"
ref="container"
:token="userStore.token"></div>
</template>
<style scoped>
.designer-container {
height: 100%;
width: 100%;
}
</style>

View File

@@ -0,0 +1,10 @@
<template>
<XContainer class="not-found" fit justify="center">
<ElEmpty description="找不到页面【404】"></ElEmpty>
</XContainer>
</template>
<script lang="ts" setup>
import { XContainer } from '@vtj/web';
import { ElEmpty } from 'element-plus';
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,46 @@
<script lang="ts" setup>
import { getCurrentInstance, ref } from 'vue';
import { useRoute } from 'vue-router';
import { LowCodeService } from '@/service';
import { useUserStore } from '@/store';
import { ContextMode, createProvider } from '@vtj/pro';
import { jsonp, request } from '@vtj/utils';
const userStore = useUserStore();
const service = new LowCodeService();
request.useRequest((req) => {
req.headers.set('Authorization', `Bearer ${userStore.token}`);
return req;
});
const { provider, onReady } = createProvider({
mode: ContextMode.Runtime,
service,
project: {
// @ts-ignore
id: 4
},
adapter: {
request,
jsonp
},
dependencies: {
Vue: () => import('vue'),
VueRouter: () => import('vue-router'),
ElementPlus: () => import('element-plus')
}
});
const route = useRoute();
const renderer = ref();
const instance = getCurrentInstance();
onReady(async () => {
instance?.appContext.app.use(provider);
renderer.value = await provider.getRenderComponent(
route.params.id.toString()
);
});
</script>
<template>
<component v-if="renderer" :is="renderer" v-bind="$attrs" />
</template>

View File

@@ -0,0 +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>
<style lang="scss" scoped></style>