diff --git a/package.json b/package.json index a3d6e86..72471b8 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@vue/tsconfig": "^0.7.0", "@types/lodash-es": "^4.17.7", "@types/three": "^0.176.0", + "@types/jquery": "^3.3.31", "@vicons/fa": "^0.12.0", "npm-run-all2": "^7.0.2", "prettier": "3.5.3", diff --git a/src/SystemOption.d.ts b/src/SystemOption.d.ts new file mode 100644 index 0000000..4c7c70e --- /dev/null +++ b/src/SystemOption.d.ts @@ -0,0 +1,17 @@ +import type { CSSProperties } from 'vue' + +export interface showDialogOption { + confirmButtonText?: string + dangerouslyUseHTMLString?: boolean + showClose?: boolean + callback?: Function + showInput?: boolean + inputPlaceholder?: string + draggable?: boolean + closeOnClickModal?: boolean + showCancelButton?: boolean + closeOnPressEscape?: boolean + autofocus?: boolean + customClass?: string + customStyle?: CSSProperties +} \ No newline at end of file diff --git a/src/base.css b/src/base.css index 92bf09e..f521ca7 100644 --- a/src/base.css +++ b/src/base.css @@ -85,3 +85,300 @@ body { padding: 0; overflow: hidden; } + + +/*== layer提示框==*/ +.yvan-msg { + min-width: 100px; + background-color: rgba(0, 0, 0, 0.6); + color: #fff; + border: none; + box-shadow: none; + margin: 0; + padding: 0; + border-radius: 2px; + position: fixed; + pointer-events: auto; + z-index: 10000; +} + +.yvan-msg-content { + pointer-events: auto; + color: #fff; + padding: 12px 25px; + text-align: center; + position: relative; + line-height: 24px; + word-break: break-all; + overflow: hidden; + font-size: 14px; + overflow-x: hidden; + overflow-y: auto; +} + + +/*== 动画 ==*/ +.yvan-anim { + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + -webkit-animation-duration: 0.3s; + animation-duration: 0.3s; +} + +@-webkit-keyframes yvan-bounceIn { + 0% { + opacity: 0; + -webkit-transform: scale(0.5); + transform: scale(0.5); + } + 100% { + opacity: 1; + -webkit-transform: scale(1); + transform: scale(1); + } +} + +@keyframes yvan-bounceIn { + 0% { + opacity: 0; + -webkit-transform: scale(0.5); + -ms-transform: scale(0.5); + transform: scale(0.5); + } + 100% { + opacity: 1; + -webkit-transform: scale(1); + -ms-transform: scale(1); + transform: scale(1); + } +} + +.yvan-anim-00 { + -webkit-animation-name: yvan-bounceIn; + animation-name: yvan-bounceIn; +} + +@-webkit-keyframes yvan-zoomInDown { + 0% { + opacity: 0; + -webkit-transform: scale(0.1) translatey(-2000px); + transform: scale(0.1) translatey(-2000px); + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + 60% { + opacity: 1; + -webkit-transform: scale(0.475) translatey(60px); + transform: scale(0.475) translatey(60px); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } +} + +@keyframes yvan-zoomInDown { + 0% { + opacity: 0; + -webkit-transform: scale(0.1) translatey(-2000px); + -ms-transform: scale(0.1) translatey(-2000px); + transform: scale(0.1) translatey(-2000px); + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + 60% { + opacity: 1; + -webkit-transform: scale(0.475) translatey(60px); + -ms-transform: scale(0.475) translatey(60px); + transform: scale(0.475) translatey(60px); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } +} + +.yvan-anim-01 { + -webkit-animation-name: yvan-zoomInDown; + animation-name: yvan-zoomInDown; +} + +@-webkit-keyframes yvan-fadeInUpBig { + 0% { + opacity: 0; + -webkit-transform: translatey(2000px); + transform: translatey(2000px); + } + 100% { + opacity: 1; + -webkit-transform: translatey(0); + transform: translatey(0); + } +} + +@keyframes yvan-fadeInUpBig { + 0% { + opacity: 0; + -webkit-transform: translatey(2000px); + -ms-transform: translatey(2000px); + transform: translatey(2000px); + } + 100% { + opacity: 1; + -webkit-transform: translatey(0); + -ms-transform: translatey(0); + transform: translatey(0); + } +} + +.yvan-anim-02 { + -webkit-animation-name: yvan-fadeInUpBig; + animation-name: yvan-fadeInUpBig; +} + +@-webkit-keyframes yvan-zoomInLeft { + 0% { + opacity: 0; + -webkit-transform: scale(0.1) translatex(-2000px); + transform: scale(0.1) translatex(-2000px); + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + 60% { + opacity: 1; + -webkit-transform: scale(0.475) translatex(48px); + transform: scale(0.475) translatex(48px); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } +} + +@keyframes yvan-zoomInLeft { + 0% { + opacity: 0; + -webkit-transform: scale(0.1) translatex(-2000px); + -ms-transform: scale(0.1) translatex(-2000px); + transform: scale(0.1) translatex(-2000px); + -webkit-animation-timing-function: ease-in-out; + animation-timing-function: ease-in-out; + } + 60% { + opacity: 1; + -webkit-transform: scale(0.475) translatex(48px); + -ms-transform: scale(0.475) translatex(48px); + transform: scale(0.475) translatex(48px); + -webkit-animation-timing-function: ease-out; + animation-timing-function: ease-out; + } +} + +.yvan-anim-03 { + -webkit-animation-name: yvan-zoomInLeft; + animation-name: yvan-zoomInLeft; +} + +@-webkit-keyframes yvan-rollIn { + 0% { + opacity: 0; + -webkit-transform: translatex(-100%) rotate(-120deg); + transform: translatex(-100%) rotate(-120deg); + } + 100% { + opacity: 1; + -webkit-transform: translatex(0) rotate(0); + transform: translatex(0) rotate(0); + } +} + +@keyframes yvan-rollIn { + 0% { + opacity: 0; + -webkit-transform: translatex(-100%) rotate(-120deg); + -ms-transform: translatex(-100%) rotate(-120deg); + transform: translatex(-100%) rotate(-120deg); + } + 100% { + opacity: 1; + -webkit-transform: translatex(0) rotate(0); + -ms-transform: translatex(0) rotate(0); + transform: translatex(0) rotate(0); + } +} + +.yvan-anim-04 { + -webkit-animation-name: yvan-rollIn; + animation-name: yvan-rollIn; +} + +@keyframes yvan-fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +.yvan-anim-05 { + -webkit-animation-name: yvan-fadeIn; + animation-name: yvan-fadeIn; +} + +@-webkit-keyframes yvan-shake { + 0%, + 100% { + -webkit-transform: translatex(0); + transform: translatex(0); + } + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translatex(-10px); + transform: translatex(-10px); + } + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translatex(10px); + transform: translatex(10px); + } +} + +@keyframes yvan-shake { + 0%, + 100% { + -webkit-transform: translatex(0); + -ms-transform: translatex(0); + transform: translatex(0); + } + 10%, + 30%, + 50%, + 70%, + 90% { + -webkit-transform: translatex(-10px); + -ms-transform: translatex(-10px); + transform: translatex(-10px); + } + 20%, + 40%, + 60%, + 80% { + -webkit-transform: translatex(10px); + -ms-transform: translatex(10px); + transform: translatex(10px); + } +} + +.yvan-anim-06 { + -webkit-animation-name: yvan-shake; + animation-name: yvan-shake; +} + +@-webkit-keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} diff --git a/src/components/viewMenus/editMenu/EditMenu.ts b/src/components/viewMenus/editMenu/EditMenu.ts new file mode 100644 index 0000000..f18ae39 --- /dev/null +++ b/src/components/viewMenus/editMenu/EditMenu.ts @@ -0,0 +1,48 @@ +import { renderIcon } from '@/utils/webutils.ts' +import { defineMenu } from '@/runtime/DefineMenu.ts' + +export default defineMenu((menus) => { + menus.insertChildren('modelFile', { + name: 'modelFile', + label: '编辑', + icon: renderIcon('ModelFile'), + order: 1 + }, [ + { + name: 'find', label: '查找', icon: renderIcon('ModelFile'), order: 1, tip: 'Ctrl+F', divided: true, + click: () => { + system.msg('查找') + } + }, + { + name: 'undo', label: '撤销', icon: renderIcon('ModelFile'), order: 2, tip: 'Ctrl+Z', + click: () => { + system.msg('撤销') + } + }, + { + name: 'redo', label: '重做', icon: renderIcon('ModelFile'), order: 3, tip: 'Ctrl+Y', divided: true, + click: () => { + system.msg('重做') + } + }, + { + name: 'copy', label: '复制', icon: renderIcon('ModelFile'), order: 4, tip: 'Ctrl+C', + click: () => { + system.msg('复制') + } + }, + { + name: 'cut', label: '剪切', icon: renderIcon('ModelFile'), order: 5, tip: 'Ctrl+X', + click: () => { + system.msg('剪切') + } + }, + { + name: 'paste', label: '粘贴', icon: renderIcon('ModelFile'), order: 6, tip: 'Ctrl+V', + click: () => { + system.msg('粘贴') + } + } + ]) +}) \ No newline at end of file diff --git a/src/components/viewMenus/fileMenu/FileMenu.ts b/src/components/viewMenus/fileMenu/FileMenu.ts new file mode 100644 index 0000000..524a338 --- /dev/null +++ b/src/components/viewMenus/fileMenu/FileMenu.ts @@ -0,0 +1,30 @@ +import { renderIcon } from '@/utils/webutils.ts' +import { defineMenu } from '@/runtime/DefineMenu.ts' + +export default defineMenu((menus) => { + menus.insertChildren('file', + { + name: 'file', label: '模型', icon: renderIcon('ModelFile'), order: 1, disabled: false + }, + [ + { + name: 'open', label: '打开', icon: renderIcon('ModelFile'), order: 1, tip: 'Ctrl+O', + click: () => { + system.msg('打开模型文件') + } + }, + { + name: 'save', label: '保存', icon: renderIcon('ModelFile'), order: 2, tip: 'Ctrl+S', + click: () => { + system.msg('保存模型文件') + } + }, + { + name: 'saveAs', label: '另存为', icon: renderIcon('ModelFile'), order: 3, + click: () => { + system.msg('另存为模型文件') + } + } + ] + ) +}) \ No newline at end of file diff --git a/src/components/viewWidgets/alarm/AlarmMeta.ts b/src/components/viewWidgets/alarm/AlarmMeta.ts new file mode 100644 index 0000000..0d04039 --- /dev/null +++ b/src/components/viewWidgets/alarm/AlarmMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '@/runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import AlarmView from './AlarmView.vue' + +export default defineWidget({ + name: 'alarm', + title: '告警', + icon: renderIcon('AlarmClock'), + side: 'right', + order: 2, + component: AlarmView +}) \ No newline at end of file diff --git a/src/views/alarm/AlarmView.vue b/src/components/viewWidgets/alarm/AlarmView.vue similarity index 100% rename from src/views/alarm/AlarmView.vue rename to src/components/viewWidgets/alarm/AlarmView.vue diff --git a/src/components/viewWidgets/logger/LoggerMeta.ts b/src/components/viewWidgets/logger/LoggerMeta.ts new file mode 100644 index 0000000..73a1004 --- /dev/null +++ b/src/components/viewWidgets/logger/LoggerMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '@/runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import LoggerView from './LoggerView.vue' + +export default defineWidget({ + name: 'logger', + title: '日志', + icon: renderIcon('AlarmClock'), + side: 'bottom', + order: 2, + component: LoggerView +}) \ No newline at end of file diff --git a/src/components/viewWidgets/logger/LoggerView.vue b/src/components/viewWidgets/logger/LoggerView.vue new file mode 100644 index 0000000..40ea256 --- /dev/null +++ b/src/components/viewWidgets/logger/LoggerView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/components/viewWidgets/modeltree/ModeltreeMeta.ts b/src/components/viewWidgets/modeltree/ModeltreeMeta.ts new file mode 100644 index 0000000..3e9e86b --- /dev/null +++ b/src/components/viewWidgets/modeltree/ModeltreeMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '@/runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import ModeltreeView from './ModeltreeView.vue' + +export default defineWidget({ + name: 'modeltree', + title: '模型', + icon: renderIcon('AlarmClock'), + side: 'left', + order: 1, + component: ModeltreeView +}) \ No newline at end of file diff --git a/src/components/viewWidgets/modeltree/ModeltreeView.vue b/src/components/viewWidgets/modeltree/ModeltreeView.vue new file mode 100644 index 0000000..b10d3ed --- /dev/null +++ b/src/components/viewWidgets/modeltree/ModeltreeView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/components/viewWidgets/monitor/MonitorMeta.ts b/src/components/viewWidgets/monitor/MonitorMeta.ts new file mode 100644 index 0000000..6a57c87 --- /dev/null +++ b/src/components/viewWidgets/monitor/MonitorMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '@/runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import MonitorView from './MonitorView.vue' + +export default defineWidget({ + name: 'monitor', + title: '监控', + icon: renderIcon('AlarmClock'), + side: 'left', + order: 2, + component: MonitorView +}) \ No newline at end of file diff --git a/src/components/viewWidgets/monitor/MonitorView.vue b/src/components/viewWidgets/monitor/MonitorView.vue new file mode 100644 index 0000000..571e300 --- /dev/null +++ b/src/components/viewWidgets/monitor/MonitorView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/components/viewWidgets/property/PropertyMeta.ts b/src/components/viewWidgets/property/PropertyMeta.ts new file mode 100644 index 0000000..6c0289f --- /dev/null +++ b/src/components/viewWidgets/property/PropertyMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '@/runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import PropertyView from './PropertyView.vue' + +export default defineWidget({ + name: 'property', + title: '属性', + icon: renderIcon('AlarmClock'), + side: 'right', + order: 1, + component: PropertyView +}) \ No newline at end of file diff --git a/src/components/viewWidgets/property/PropertyView.vue b/src/components/viewWidgets/property/PropertyView.vue new file mode 100644 index 0000000..040b93e --- /dev/null +++ b/src/components/viewWidgets/property/PropertyView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/components/viewWidgets/script/ScriptMeta.ts b/src/components/viewWidgets/script/ScriptMeta.ts new file mode 100644 index 0000000..0aff657 --- /dev/null +++ b/src/components/viewWidgets/script/ScriptMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '../../../runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import ScriptView from './ScriptView.vue' + +export default defineWidget({ + name: 'script', + title: '脚本', + icon: renderIcon('AlarmClock'), + side: 'bottom', + order: 3, + component: ScriptView +}) \ No newline at end of file diff --git a/src/components/viewWidgets/script/ScriptView.vue b/src/components/viewWidgets/script/ScriptView.vue new file mode 100644 index 0000000..dbf5c23 --- /dev/null +++ b/src/components/viewWidgets/script/ScriptView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/components/viewWidgets/task/TaskMeta.ts b/src/components/viewWidgets/task/TaskMeta.ts new file mode 100644 index 0000000..36aeb16 --- /dev/null +++ b/src/components/viewWidgets/task/TaskMeta.ts @@ -0,0 +1,12 @@ +import { defineWidget } from '@/runtime/DefineWidget.ts' +import { renderIcon } from '@/utils/webutils.ts' +import TaskView from './TaskView.vue' + +export default defineWidget({ + name: 'task', + title: '任务', + icon: renderIcon('AlarmClock'), + side: 'bottom', + order: 1, + component: TaskView +}) \ No newline at end of file diff --git a/src/components/viewWidgets/task/TaskView.vue b/src/components/viewWidgets/task/TaskView.vue new file mode 100644 index 0000000..536a286 --- /dev/null +++ b/src/components/viewWidgets/task/TaskView.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/components/webindex.ts b/src/components/webindex.ts index 3f39723..f18b195 100644 --- a/src/components/webindex.ts +++ b/src/components/webindex.ts @@ -3,6 +3,8 @@ import Split from './split/split.vue' import SplitArea from './split/split-area.vue' import draggable from './vuedraggable/vuedraggable' +export { defineWidget } from '../runtime/DefineWidget.ts' + export const install = function(Vue: any) { Vue.component('ag-grid-vue', defineAsyncComponent(() => { return Promise.all([ diff --git a/src/main.ts b/src/main.ts index 5b71b69..a2b50b8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,8 +4,9 @@ import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import * as webIndex from '@/components/webindex' -import Menus from 'vue3-menus' +import { directive, menusEvent, Vue3Menus } from 'vue3-menus' import ElementPlus from 'element-plus' +import System from '@/runtime/System' import 'ag-grid-community/styles/ag-grid.css' import 'ag-grid-community/styles/ag-theme-alpine.css' @@ -16,8 +17,12 @@ const app = createApp(App) app.use(createPinia()) app.use(ElementPlus) -//@ts-ignore -app.use(Menus) +app.component('vue3-menus', Vue3Menus) +app.directive('menus', directive) +app.config.globalProperties.$menusEvent = menusEvent + +window['system'] = new System() + app.use(router) app.use(webIndex) diff --git a/src/runtime/DefineMenu.ts b/src/runtime/DefineMenu.ts new file mode 100644 index 0000000..6ef3527 --- /dev/null +++ b/src/runtime/DefineMenu.ts @@ -0,0 +1,83 @@ +import type { Component } from 'vue' + +export interface MenuOption { + name: string + label?: string + icon?: Component + order?: number + tip?: string + disabled?: boolean | (() => boolean) + divided?: boolean, + click?: () => void + children?: MenuOption[] +} + +export class Menus { + menus: MenuOption[] = [] + + constructor() { + this.menus = [] + } + + insertChildren(parentName: string, parentIfNotExists: MenuOption, menus: MenuOption[]) { + const parent = this.getMenu(parentName) + if (parent) { + if (!parent.children) { + parent.children = [] + } + // 去重 + const existingChildren = parent.children.filter((item) => { + return !menus.some((menu) => menu.name === item.name) + }) + menus = [...existingChildren, ...menus] + // 排序 + menus.sort((a, b) => { + return (a.order || 0) - (b.order || 0) + }) + parent.children = menus + + } else { + const newParent = { ...parentIfNotExists, children: menus } + this.insertMenu(newParent) + } + } + + insertMenu(menu: MenuOption) { + const existingMenu = this.getMenu(menu.name) + if (existingMenu) { + console.log(`Menu ${menu.name} already exists`) + return + } + this.menus.push(menu) + } + + getMenu(name: string): MenuOption | undefined { + return this.menus.find((menu) => menu.name === name) + } +} + +const _menus = new Menus() + +export interface MenuDefineResult { + install(): void +} + +export function getRootMenu() { + return _menus.menus +} + +export function getChildrenMenu(name: string) { + const menu = _menus.getMenu(name) + return menu ? menu.children : [] +} + +/** + * 定义一个 Widget + */ +export function defineMenu(func: (menus: Menus) => void): MenuDefineResult { + return { + install() { + func(_menus) + } + } +} \ No newline at end of file diff --git a/src/runtime/DefineWidget.ts b/src/runtime/DefineWidget.ts new file mode 100644 index 0000000..892cfe7 --- /dev/null +++ b/src/runtime/DefineWidget.ts @@ -0,0 +1,52 @@ +import type { Component } from 'vue' + +export type WidgetSide = 'left' | 'right' | 'bottom' + +export interface WidgetOption { + name: string + title?: string + icon: Component + side: WidgetSide + component: Component + order?: number +} + +const _widgetMap = new Map() + +export class WidgetInfo { + option: WidgetOption + + constructor(option: WidgetOption) { + this.option = option + } + + public install() { + if (!this.option.name) { + throw new Error('Widget name is required') + } + if (!this.option.component) { + throw new Error('Widget component is required') + } + if (_widgetMap.has(this.option.name)) { + console.log(`Widget ${this.option.name} already exists`) + return + } + _widgetMap.set(this.option.name, this.option) + } +} + +/** + * 根据 Widget 定义的位置获取 Widget, 并按 order 排序 + */ +export function getWidgetBySide(side: WidgetSide): WidgetOption[] { + return Array.from(_widgetMap.values()) + .filter((widget) => widget.side === side) + .sort((a, b) => (a.order || 0) - (b.order || 0)) +} + +/** + * 定义一个 Widget + */ +export function defineWidget(option: WidgetOption): WidgetInfo { + return new WidgetInfo(option) +} \ No newline at end of file diff --git a/src/runtime/System.ts b/src/runtime/System.ts new file mode 100644 index 0000000..41a713e --- /dev/null +++ b/src/runtime/System.ts @@ -0,0 +1,188 @@ +import $ from 'jquery' +import _ from 'lodash' +import localforage from 'localforage' +import JSON5 from 'json5' +import { defineComponent, h, markRaw, nextTick, reactive, toRaw, unref, type App, createApp } from 'vue' +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' +import { QuestionFilled } from '@element-plus/icons-vue' +import { renderIcon } from '@/utils/webutils.ts' +import type { showDialogOption } from '@/SystemOption' + +export default class System { + _ = _ + $ = $ + createApp = createApp + toRaw = toRaw + unref = unref + nextTick = nextTick + defer = _.defer + defineComponent = defineComponent + markRaw = markRaw + reactive = reactive + renderIcon = renderIcon + + JSON5 = JSON5 + json5 = JSON5 + app!: App + + errorDialogContent: string[] = reactive([]) + errorDialogIsShowing: boolean = false + + constructor(app: App) { + this.app = app + } + + /** + * 轻量级提示信息 + * @param message 消息内容 + */ + msg(message: string) { + // ElMessage(cc) + console.trace(message) + + const $body = $('body') + + $body.find('[xtype=tooltip]').remove() + const $w = $( + '
' + + '
' + + _.escape(message) + + '
' + ) + $body.append($w) + + const iframeWidth = $w.parent().width() as number + const iframeHeight = $w.parent().height() as number + + const windowWidth = $w.width() as number + const windowHeight = $w.height() as number + + let setWidth = (iframeWidth - windowWidth) / 2 + let setHeight = (iframeHeight - windowHeight) / 2 + if (iframeHeight < windowHeight || setHeight < 0) { + setHeight = 0 + } + if (iframeWidth < windowWidth || setWidth < 0) { + setWidth = 0 + } + $w.css({ left: setWidth, top: setHeight }) + setTimeout(() => { + $w.remove() + }, 3000) + } + + /** + * 弹出用户必须点击确认的错误信息 + */ + showErrorDialog(msgOrTitle: string, msg?: string, dangerouslyUseHTMLString?: boolean, closeCallBack?: () => void) { + let title, message + if (!msg) { + console.trace(msgOrTitle) + title = '错误' + message = msgOrTitle + + } else { + console.trace(msg) + title = msgOrTitle + message = msg + } + + // 如果有一样的内容,就不添加 + if (_.findIndex(this.errorDialogContent, r => r === message) >= 0) { + return + } + this.errorDialogContent.push(message) + if (!this.errorDialogIsShowing) { + // 只有在没有弹出对话框的清空下才弹出 + this.errorDialogIsShowing = true + ElMessageBox.alert( + //@ts-ignore + () => { + if (this.errorDialogContent.length <= 0) { + return '' + } + return _.map(this.errorDialogContent, (item) => + h('div', null, item) + ) + }, + title, + { + dangerouslyUseHTMLString: false, + closeOnPressEscape: true, + closeOnClickModal: true, + confirmButtonText: '关闭', + type: 'error', + draggable: true, + callback: () => { + this.errorDialogIsShowing = false + nextTick(() => { + this.errorDialogContent.splice(0, this.errorDialogContent.length) + }) + if (closeCallBack) closeCallBack() + } + } + ) + } + } + + /** + * 弹出用户必须确认的提示信息 + */ + showInfoDialog(content: string, option: showDialogOption = { + confirmButtonText: '关闭', + draggable: true, + showCancelButton: false, + showClose: false, + dangerouslyUseHTMLString: true, + autofocus: true, + closeOnClickModal: false, + closeOnPressEscape: true + }) { + return this.alert(content, option) + } + + /** + * 信息提示内容,强提示,必须用户点击确认 + */ + alert(msg: string, option?: showDialogOption): Promise { + console.trace(msg) + + // 将 msg 转译为 html enable 格式 + msg = _.join(_.split(_.escape(msg), '\n'), '
') + + const newOption = _.extend({ + confirmButtonText: '关闭', + draggable: true, + showCancelButton: false, + showClose: false, + dangerouslyUseHTMLString: true, + autofocus: true, + closeOnClickModal: false, + closeOnPressEscape: true + }, option) + + //@ts-ignore + return ElMessageBox.alert(msg, '提示', newOption) + } + + /** + * 弹出确认对话框 + * @param msg 消息内容 + */ + confirm(msg: string): Promise { + return new Promise((resolve, reject) => { + ElMessageBox.confirm(msg, '再次确认', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + icon: markRaw(QuestionFilled) + } + ).then(() => { + resolve() + + }).catch(() => { + reject() + }) + }) + } +} \ No newline at end of file diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..91cc49f --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,9 @@ +import _ from 'lodash' +import $ from 'jquery' +import type System from '@/runtime/System' + +declare global { + const $: $ + const _: _ + const system: System +} \ No newline at end of file diff --git a/src/views/ModelMain.vue b/src/views/ModelMain.vue index 2acc2cb..411d7b6 100644 --- a/src/views/ModelMain.vue +++ b/src/views/ModelMain.vue @@ -2,6 +2,11 @@
+ {{ rootMenu.label }} + +