feat(全局): 增加hook组件, 用于对话框、消息提示; 修改系统标题

This commit is contained in:
liufu 2022-12-15 17:06:53 +08:00
parent 1e51acbcc7
commit e596df5305
13 changed files with 267 additions and 14 deletions

View file

@ -1,4 +1,4 @@
# 开发环境
Vue: 3.2.45
# 系统环境
Vue + TS: 3.2.45
Vite: 4.0.0
Node: 16.13.1

View file

@ -5,7 +5,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
<title>ModernWMS</title>
</head>
<body>

View file

@ -0,0 +1,8 @@
.messageItems {
position: fixed;
/* top: 10px; */
left: 50%;
max-width: 90%;
transform: translate(-50%, 0);
transition: all 0.2s;
}

View file

@ -0,0 +1,21 @@
import '@/assets/css/hookComponent.css' // import hookComponent style
// Use import.meta.globEager to read the files in the components folder, distinguished by the suffix ts
const componentsList: any = import.meta.globEager('../hookComponent/**')
const List: any = {}
export default function (app: any) {
Object.keys(componentsList).forEach((key) => {
// Filter out ts suffix
if (key.includes('.ts')) {
// Assignment function component, thrown later, imported for use
List[`$${ componentsList[key].default.name }`] = componentsList[key].default
// Define function components into global variables, and use them in script in vue through proxy
app.config.globalProperties[`$${ componentsList[key].default.name }`] = componentsList[key].default
}
})
}
// Export function components for import
export const funComponentList = List

View file

@ -0,0 +1,20 @@
import { createApp } from 'vue'
import DialogComponent from './dialog.vue'
import { DialogOptions } from '@/types/system/hookComponent'
// Use vuetify
import { vuetify } from '@/plugins/vuetify/index'
// Use the createApp of vue3 and the mount and unmount methods to create a mounted instance
export default function dialog(options: DialogOptions) {
const mountNode = document.createElement('div')
document.body.appendChild(mountNode)
const app = createApp(DialogComponent, {
...options,
visible: true,
removeComponent: () => {
app.unmount()
document.body.removeChild(mountNode)
}
}).use(vuetify)
return app.mount(mountNode)
}

View file

@ -0,0 +1,86 @@
<template>
<v-dialog v-model="data.dialogVisible" persistent :width="dialogWidth">
<v-card>
<v-card-title class="text-h5"> {{ title }} </v-card-title>
<v-card-text>{{ content }}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green-darken-1" variant="text" @click="data.dialogVisible = false">
{{ cancleText }}
</v-btn>
<v-btn
v-if="typeof dialogProps.handleConfirm === 'function'"
color="green-darken-1"
variant="text"
@click="method._sure()"
>
{{ confirmText }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts" setup>
import { watch, reactive } from 'vue'
import i18n from '@/languages/i18n'
const dialogProps = defineProps({
visible: {
type: Boolean,
default: false
},
confirmText: {
type: String,
default: i18n.global.t('system.hookComponent.dialog.defaultConfirm')
},
cancleText: {
type: String,
default: i18n.global.t('system.hookComponent.dialog.defaultClose')
},
handleConfirm: {
type: Function,
default: null
},
// Remove dom function
removeComponent: {
type: Function,
default: null
},
content: {
type: String,
default: ''
},
title: {
type: String,
default: i18n.global.t('system.hookComponent.dialog.defaultTitle')
},
dialogWidth: {
type: [String, Number],
default: 700
}
})
const data = reactive({
dialogVisible: false,
dialogText: ''
})
// Init dialog visible
data.dialogVisible = Boolean(dialogProps.visible) // Avoid shallow copies
const method = reactive({
_sure: async () => {
await dialogProps.handleConfirm()
data.dialogVisible = false
}
})
// Shut dialog run remove function
watch(
() => data.dialogVisible,
(val) => {
!val && dialogProps.removeComponent()
}
)
</script>

View file

@ -0,0 +1,55 @@
import { createApp } from 'vue'
import MessageComponent from './message.vue'
import { MessageOptions } from '@/types/system/hookComponent'
// Use vuetify
import { vuetify } from '@/plugins/vuetify/index'
// Use the createApp of vue3 and the mount and unmount methods to create a mounted instance
export default function message(options: MessageOptions) {
const mountNode = document.createElement('div')
mountNode.className = 'messageItems'
mountNode.style.top = `${ getNewMsgTop() }px`
document.body.appendChild(mountNode)
const app = createApp(MessageComponent, {
...options,
removeComponent: () => {
setTimeout(() => {
mountNode.style.opacity = '0'
setTimeout(() => {
app.unmount()
document.body.removeChild(mountNode)
resetMsgTop()
}, 200)
}, options.shutDelay ? options.shutDelay - 200 : 1300)
}
}).use(vuetify)
return app.mount(mountNode)
}
// Reset message top
function resetMsgTop() {
const messageDomList: any = document.body.getElementsByClassName('messageItems')
for (let i = 0; i < messageDomList.length; i++) {
messageDomList[i].style.top = `${ getNewMsgTop(i) }px`
}
}
// Get message top
function getNewMsgTop(index = -1) {
const messageDomList = document.body.querySelectorAll('.messageItems')
let top = 10
if (index < 0) {
for (let i = 0; i < messageDomList.length; i++) {
top += messageDomList[i].clientHeight + 10
}
} else {
for (let i = 0; i < messageDomList.length; i++) {
if (i < index) {
top += messageDomList[i].clientHeight + 10
} else {
break
}
}
}
return top
}

View file

@ -0,0 +1,33 @@
<template>
<v-alert
:type="type || 'info'"
:variant="variant || 'flat'"
:border="border || false"
:color="color || undefined"
:width="messageWidth"
:min-width="250"
>
<!-- :max-height="60" -->
{{ content }}
</v-alert>
</template>
<script lang="ts" setup>
import { onBeforeMount } from 'vue'
// Vue3 does not support external interfaces
const messageProps = defineProps<{
type?: 'success' | 'info' | 'warning' | 'error'
variant?: 'flat' | 'elevated' | 'tonal' | 'outlined' | 'text' | 'plain'
content: string
border?: boolean | 'top' | 'end' | 'bottom' | 'start'
color?: string
messageWidth?: string | number // message width pixel or percentage
removeComponent(): void
}>()
onBeforeMount(() => {
messageProps.removeComponent()
})
</script>
<style lang="less" scoped></style>

View file

@ -25,7 +25,6 @@
></v-text-field>
<v-checkbox v-model="data.remember" :label="$t('login.rememberTips')"></v-checkbox>
<v-btn color="purple" @click="method.login()">{{ $t('login.mainButtonLabel') }}</v-btn>
<!-- <v-btn color="purple" @click="method.testApi()">{{ '测试接口' }}</v-btn> -->
</v-form>
</div>
</div>
@ -38,6 +37,7 @@ import i18n from '@/languages/i18n'
import { login } from '@/api/sys/login'
// import { getNotify } from '@/api/sys/test'
import { store } from '@/store'
// import { funComponentList } from '@/components/common/function/index'
// Get v-form ref
const VFormRef = ref()
@ -77,16 +77,6 @@ const method = reactive({
store.commit('user/setEffectiveMinutes', loginRes.data.expire)
}
}
// testApi: async () => {
// const { data: res } = await getNotify({
// ifall: true,
// iftodo: true
// })
// if (res.isSuccess) {
// console.log('', res)
// }
// }
})
</script>

View file

@ -1,4 +1,13 @@
export default {
system: {
hookComponent: {
dialog: {
defaultClose: '关闭',
defaultConfirm: '确认',
defaultTitle: '提示'
}
}
},
login: {
welcomeTitle: '欢迎来到ModernWMS!👋🏻',
userNameMustInput: '请填写用户名!',

View file

@ -1,4 +1,13 @@
export default {
system: {
hookComponent: {
dialog: {
defaultClose: 'Close',
defaultConfirm: 'Agree',
defaultTitle: 'Tips'
}
}
},
login: {
welcomeTitle: 'Welcome to ModernWMS!👋🏻',
userNameMustInput: 'Please fill in the userName!',

View file

@ -8,6 +8,7 @@ import App from './App.vue'
// import router
import { router } from './router'
import { store } from './store/index'
import hookComponent from '@/components/common/function/index'
const app = createApp(App)
@ -15,5 +16,6 @@ app.use(router)
app.use(store)
app.use(vuetify)
app.use(i18n)
app.use(hookComponent)
app.mount('#app')

View file

@ -0,0 +1,20 @@
// Dialog modal options
export interface DialogOptions {
confirmText?: string // confirm btn text
cancleText?: string // cancle btn text
handleConfirm?(): void // confirm trigger method
content: string // dialog content text
title?: string // dialog title text
dialogWidth?: string | number // dialog width pixel or percentage
}
// System message options
export interface MessageOptions {
type?: 'success' | 'info' | 'warning' | 'error'
variant?: 'flat' | 'elevated' | 'tonal' | 'outlined' | 'text' | 'plain'
content: string
border?: boolean | 'top' | 'end' | 'bottom' | 'start'
shutDelay?: number
color?: string
messageWidth?: string | number // message width pixel or percentage
}