From 62a96af74b1bcbea8325b9f6f328e2c680e370cc Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 6 Aug 2026 17:55:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=20ORM=20=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E7=BC=96=E8=BE=91=E5=8C=BA=E5=9F=9F=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E9=AB=98=E5=BA=A6=EF=BC=8C=E5=AE=9E=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E5=8D=A1=E7=89=87=E5=8C=96=E5=92=8C=20Saga?= =?UTF-8?q?=20=E6=A8=A1=E5=BC=8F=E7=AE=A1=E7=90=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 10 +- package.json | 2 +- src/components/CrudTable.vue | 135 +++++++++++++-- src/components/RichTextEditor.vue | 42 ++--- src/router/index.ts | 6 + src/types/api.ts | 4 +- src/views/system/Database.vue | 19 ++- src/views/system/DbTable.vue | 18 +- src/views/system/MSyncServers.vue | 270 ++++++++++++++++++++++++++++++ src/views/workorder/Admin.vue | 5 +- src/views/workorder/My.vue | 13 +- vite.config.ts | 21 ++- 12 files changed, 477 insertions(+), 68 deletions(-) create mode 100644 src/views/system/MSyncServers.vue diff --git a/package-lock.json b/package-lock.json index 190280e..4fe97d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@vitejs/plugin-vue": "^6.0.8", "@vue/tsconfig": "^0.9.1", "sass-embedded": "^1.100.0", - "tinymce": "^8.8.2", + "tinymce": "^6.8.5", "typescript": "~6.0.2", "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^32.1.0", @@ -3130,11 +3130,11 @@ } }, "node_modules/tinymce": { - "version": "8.8.2", - "resolved": "https://registry.npmmirror.com/tinymce/-/tinymce-8.8.2.tgz", - "integrity": "sha512-wcUGaaAf4XFrQtbZtClbDN/QCqklXLlPt3+7UWliEoosuhnTOJUkXwf7Fzlq7agPnwkKKp1e4JGOhx3O1gWH3g==", + "version": "6.8.5", + "resolved": "https://registry.npmmirror.com/tinymce/-/tinymce-6.8.5.tgz", + "integrity": "sha512-qAL/FxL7cwZHj4BfaF818zeJJizK9jU5IQzTcSLL4Rj5MaJdiVblEj7aDr80VCV1w9h4Lak9hlnALhq/kVtN1g==", "dev": true, - "license": "SEE LICENSE IN license.md" + "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", diff --git a/package.json b/package.json index f137505..567c34e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@vitejs/plugin-vue": "^6.0.8", "@vue/tsconfig": "^0.9.1", "sass-embedded": "^1.100.0", - "tinymce": "^8.8.2", + "tinymce": "^6.8.5", "typescript": "~6.0.2", "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^32.1.0", diff --git a/src/components/CrudTable.vue b/src/components/CrudTable.vue index d71c7fb..7dd8d88 100644 --- a/src/components/CrudTable.vue +++ b/src/components/CrudTable.vue @@ -1,6 +1,6 @@ diff --git a/src/router/index.ts b/src/router/index.ts index dfc214f..25dfe2f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -229,6 +229,12 @@ export const staticRoutes: RouteRecordRaw[] = [ component: () => import('@/views/system/Database.vue'), meta: { title: '数据库管理', requiresAuth: true, menuGuarded: true }, }, + { + path: 'system/msync-servers', + name: 'SystemMSyncServers', + component: () => import('@/views/system/MSyncServers.vue'), + meta: { title: '服务器管理', requiresAuth: true }, + }, { path: 'system/dbtable', name: 'SystemDbTable', diff --git a/src/types/api.ts b/src/types/api.ts index 6d5f74e..13a30f8 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -177,7 +177,9 @@ export interface ColumnSchema { /** 表单中隐藏 */ hideInForm?: boolean /** 仅在新增表单显示,编辑表单隐藏(虚拟级联列等场景) */ - hideInEditForm?: boolean + hideInEditForm?: boolean + /** 仅在编辑表单显示,新增表单隐藏(如工单"解决方案"——处理时才填) */ + hideInCreateForm?: boolean /** 是否必填 */ required?: boolean /** 必填但允许提交空值(原生特有,跳过 required 校验) */ diff --git a/src/views/system/Database.vue b/src/views/system/Database.vue index 403b6e4..297a3e4 100644 --- a/src/views/system/Database.vue +++ b/src/views/system/Database.vue @@ -19,22 +19,23 @@ * - 返回服务器列表改用 router.push(替代 GERP.Navigate) */ import { ref, computed, onMounted, watch } from 'vue' -import { useRouter } from 'vue-router' +import { useRoute, useRouter } from 'vue-router' import { get } from '@/api/request' -import { setPageTitle, getServer, isDefaultServer } from '@/utils/serverScope' +import { setPageTitle, DEFAULT_SERVER } from '@/utils/serverScope' import { useUserStore } from '@/stores/user' import AdminBadge from '@/components/AdminBadge.vue' +const route = useRoute() const router = useRouter() const userStore = useUserStore() const isAdmin = computed(() => userStore.isAdmin) // 无权访问:CheckAdmin 加载完成且确认非管理员(加载中不显示无权访问,避免刷新后闪误判) const denied = computed(() => userStore.adminChecked && !isAdmin.value) -/** 服务器标识(主实例为 gvsds-master) */ -const serverName = getServer() +/** 服务器标识:优先 URL query(子服务器场景由 msync-servers 等入口带入),缺省主实例 */ +const serverName = computed(() => (route.query.server as string) || DEFAULT_SERVER) /** 是否主实例 */ -const isMain = isDefaultServer() +const isMain = computed(() => serverName.value === DEFAULT_SERVER) /** 表项(对齐后端字段) */ interface TableItem { @@ -51,7 +52,7 @@ const staticTables = ref([]) const dynamicTables = ref([]) /** 页面标题 */ -const pageTitle = computed(() => (isMain ? '数据库管理' : `${serverName} — 数据库管理`)) +const pageTitle = computed(() => (isMain.value ? '数据库管理' : `${serverName.value} — 数据库管理`)) setPageTitle(pageTitle.value) @@ -74,7 +75,7 @@ async function loadMainTables(): Promise { async function loadSubTables(): Promise { loading.value = true try { - const data = await get(`/api/gerp/msync/servers/${encodeURIComponent(serverName)}/form/`, { + const data = await get(`/api/gerp/msync/servers/${encodeURIComponent(serverName.value)}/form/`, { Action: 'tables', }) dynamicTables.value = Array.isArray(data) ? data : [] @@ -91,7 +92,7 @@ function viewTable(table: TableItem): void { table: table.name, title: table.title || table.name, } - if (!isMain) query.server = serverName + if (!isMain.value) query.server = serverName.value router.push({ path: '/system/dbtable', query }) } @@ -128,7 +129,7 @@ onMounted(() => { /** CheckAdmin 确认管理员后加载数据(非管理员不加载,展示无权访问) */ function loadIfAdmin(): void { if (!isAdmin.value) return - if (isMain) { + if (isMain.value) { loadMainTables() } else { loadSubTables() diff --git a/src/views/system/DbTable.vue b/src/views/system/DbTable.vue index 2933971..b57cf05 100644 --- a/src/views/system/DbTable.vue +++ b/src/views/system/DbTable.vue @@ -22,7 +22,7 @@ */ import { computed, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' -import { setPageTitle, getServer, isDefaultServer } from '@/utils/serverScope' +import { setPageTitle, DEFAULT_SERVER } from '@/utils/serverScope' import { useUserStore } from '@/stores/user' import AdminBadge from '@/components/AdminBadge.vue' import CrudTable from '@/components/CrudTable.vue' @@ -34,29 +34,29 @@ const isAdmin = computed(() => userStore.isAdmin) // 无权访问:CheckAdmin 加载完成且确认非管理员(加载中不显示无权访问,避免刷新后闪误判) const denied = computed(() => userStore.adminChecked && !isAdmin.value) -/** 服务器标识(主实例为 gvsds-master) */ -const serverName = getServer() +/** 服务器标识:优先 URL query(子服务器场景由数据库管理页带入),缺省主实例 */ +const serverName = computed(() => (route.query.server as string) || DEFAULT_SERVER) /** 是否主实例 */ -const isMain = isDefaultServer() +const isMain = computed(() => serverName.value === DEFAULT_SERVER) /** 表名(路由 query,computed 响应式:同一 tab 复用且 query 变化时自动更新) */ const tableName = computed(() => (route.query.table as string) || '') /** 表标题(路由 query,缺省回退表名) */ const tableTitle = computed(() => (route.query.title as string) || tableName.value) /** 页面标题 */ -const pageTitle = computed(() => (isMain ? tableTitle.value : `${serverName} — ${tableTitle.value}`)) +const pageTitle = computed(() => (isMain.value ? tableTitle.value : `${serverName.value} — ${tableTitle.value}`)) setPageTitle(pageTitle.value) -/** 表格最大高度:视口高度 60%(窗口尺寸变化时响应式更新) */ -const tableMaxHeight = ref(Math.floor(window.innerHeight * 0.6)) +/** 表格最大高度:视口高度 55%(窗口尺寸变化时响应式更新) */ +const tableMaxHeight = ref(Math.floor(window.innerHeight * 0.55)) window.addEventListener('resize', () => { - tableMaxHeight.value = Math.floor(window.innerHeight * 0.6) + tableMaxHeight.value = Math.floor(window.innerHeight * 0.55) }) /** 返回数据库管理页 */ function goBack(): void { const query: Record = {} - if (!isMain) query.server = serverName + if (!isMain.value) query.server = serverName.value router.push({ path: '/system/database', query }) } diff --git a/src/views/system/MSyncServers.vue b/src/views/system/MSyncServers.vue new file mode 100644 index 0000000..04202bf --- /dev/null +++ b/src/views/system/MSyncServers.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/src/views/workorder/Admin.vue b/src/views/workorder/Admin.vue index 3887159..de56952 100644 --- a/src/views/workorder/Admin.vue +++ b/src/views/workorder/Admin.vue @@ -134,7 +134,7 @@ onMounted(() => { - + { pageable stretch selectable + card-mode + card-title-field="Title" + :card-fields="['Status', 'Priority', 'Category', 'AssigneeUUID', 'CreatorUUID', 'CreateTime']" :on-view="goDetail" /> diff --git a/src/views/workorder/My.vue b/src/views/workorder/My.vue index 123457c..a5cd9d9 100644 --- a/src/views/workorder/My.vue +++ b/src/views/workorder/My.vue @@ -36,7 +36,18 @@ function goDetail(row: Record): void { diff --git a/vite.config.ts b/vite.config.ts index f937c8a..e756986 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,10 +1,28 @@ -import { defineConfig } from 'vite' +import { defineConfig, type Plugin } from 'vite' import { fileURLToPath, URL } from 'node:url' +import { cpSync } from 'node:fs' +import { resolve } from 'node:path' import vue from '@vitejs/plugin-vue' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' +const projectRoot = fileURLToPath(new URL('.', import.meta.url)) + +/** 富文本编辑器自托管:构建完成后把 tinymce 整目录拷到 dist/tinymce, + * 运行时经 /tinymce/ 以