初始化

This commit is contained in:
2026-03-03 09:36:51 +08:00
commit 76c1a8668f
510 changed files with 28241 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
import { createPinia } from 'pinia'
import useUserStore from './modules/user'
import useAppStore from './modules/app'
import useTagStore from './modules/tag'
import useKeepAliveStore from './modules/keepAlive'
import useIframeStore from './modules/iframe'
import useConfigStore from './modules/config'
import useMessageStore from './modules/message'
import useDictStore from './modules/dict'
import useTerminalStore from './modules/terminal'
const pinia = createPinia()
export {
useUserStore,
useAppStore,
useTagStore,
useKeepAliveStore,
useIframeStore,
useConfigStore,
useMessageStore,
useDictStore,
useTerminalStore
}
export default pinia

View File

@@ -0,0 +1,160 @@
let defaultSetting = {
mode: 'light',
tag: true,
menuCollapse: false,
menuWidth: 230,
layout: 'classic',
skin: 'mine',
i18n: false,
language: 'zh_CN',
animation: 'ma-slide-down',
color: '#7166F0',
settingOpen: false,
searchOpen: false,
roundOpen: true,
waterMark: true,
waterContent: 'saiadmin',
ws: false,
registerWangEditorButtonFlag: false
}
import { defineStore } from 'pinia'
import tool from '@/utils/tool'
import { generate, getRgbStr } from '@arco-design/color'
if (!tool.local.get('setting')) {
tool.local.set('setting', defaultSetting)
} else {
defaultSetting = tool.local.get('setting')
}
document.body.setAttribute('arco-theme', defaultSetting.mode)
document.body.setAttribute('mine-skin', defaultSetting.skin)
const useAppStore = defineStore('app', {
state: () => ({ ...defaultSetting }),
getters: {
appCurrentSetting() {
return { ...this.$state }
}
},
actions: {
updateSettings(partial) {
this.$patch(partial)
},
toggleMode(dark) {
this.mode = dark
document.getElementsByTagName('html')[0].className = this.mode
document.body.setAttribute('arco-theme', this.mode)
defaultSetting.mode = this.mode
this.changeColor(this.color)
tool.local.set('setting', defaultSetting)
},
toggleMenu(status) {
this.menuCollapse = status
defaultSetting.menuCollapse = this.menuCollapse
tool.local.set('setting', defaultSetting)
},
toggleRound(status) {
this.roundOpen = status
if (this.roundOpen) {
document.body.style.setProperty(`--border-radius-small`, '4px')
document.body.style.setProperty(`--border-radius-medium`, '6px')
} else {
document.body.style.setProperty(`--border-radius-small`, '2px')
document.body.style.setProperty(`--border-radius-medium`, '4px')
}
defaultSetting.roundOpen = this.roundOpen
tool.local.set('setting', defaultSetting)
},
toggleWater(status) {
this.waterMark = status
defaultSetting.waterMark = this.waterMark
tool.local.set('setting', defaultSetting)
},
changeWaterContent(val) {
this.waterContent = val.target ? val.target.value : val
defaultSetting.waterContent = this.waterContent
tool.local.set('setting', defaultSetting)
},
toggleTag(status) {
this.tag = status
defaultSetting.tag = this.tag
tool.local.set('setting', defaultSetting)
},
toggleI18n(i18n) {
this.i18n = i18n
defaultSetting.i18n = this.i18n
tool.local.set('setting', defaultSetting)
},
toggleWs(val) {
this.ws = val
defaultSetting.ws = this.ws
tool.local.set('setting', defaultSetting)
},
changeMenuWidth(width) {
this.menuWidth = width
defaultSetting.menuWidth = this.menuWidth
tool.local.set('setting', defaultSetting)
},
changeLayout(layout) {
this.layout = layout
defaultSetting.layout = this.layout
tool.local.set('setting', defaultSetting)
},
changeLanguage(language) {
this.language = language
defaultSetting.language = this.language
tool.local.set('setting', defaultSetting)
window.location.reload()
},
changeColor(color) {
if (!/^#[0-9A-Za-z]{6}/.test(color)) return
this.color = color
const list = generate(this.color, {
list: true,
dark: this.mode === 'dark'
})
list.forEach((color, index) => {
const rgbStr = getRgbStr(color)
document.body.style.setProperty(`--primary-${index + 1}`, rgbStr)
document.body.style.setProperty(`--arcoblue-${index + 1}`, rgbStr)
})
defaultSetting.color = this.color
tool.local.set('setting', defaultSetting)
},
changeAnimation(name) {
this.animation = name
defaultSetting.animation = this.animation
tool.local.set('setting', defaultSetting)
},
useSkin(name) {
this.skin = name
defaultSetting.skin = this.skin
document.body.setAttribute('mine-skin', this.skin)
tool.local.set('setting', defaultSetting)
},
setRegisterWangEditorButtonFlag(value) {
this.registerWangEditorButtonFlag = value
}
}
})
export default useAppStore

View File

@@ -0,0 +1,30 @@
import { defineStore } from 'pinia'
let defaultConfig = {
site_name: 'SaiAdmin',
site_keywords: '',
site_desc: '',
site_record_number: '',
site_copyright: '',
site_storage_mode: '',
web_close: '',
}
const useConfigStore = defineStore('config', {
state: () => ({ ...defaultConfig }),
getters: {
appCurrentConfig() {
return { ...this.$state }
},
},
actions: {
updateSettings(partial) {
this.$patch(partial);
},
},
})
export default useConfigStore

View File

@@ -0,0 +1,27 @@
import { defineStore } from 'pinia'
import commonApi from '@/api/common'
// 定义字典store名称是dict
const useDictStore = defineStore('dict', {
// 字典数据是数组我们定义一个data来进行保存
state: () => ({ data: undefined }),
getters: {
// 获取store状态
getState() {
return { ...this.$state }
}
},
actions: {
//给字典数据赋值
setInfo(data) {
this.$patch(data)
},
// 初始化字典数据
async initData() {
const { data } = await commonApi.dictAll()
this.data = data
}
}
})
export default useDictStore

View File

@@ -0,0 +1,41 @@
import { defineStore } from 'pinia'
const useIframeStore = defineStore('iframe', {
state: () => ({
iframes: [],
name: null,
show: true
}),
getters: {
getState() {
return { ...this.$state }
},
},
actions: {
addIframe (component) {
if (! this.iframes.includes(component)) {
this.iframes.push(component)
}
},
removeIframe (component) {
const idx = this.iframes.indexOf(component)
if (idx !== -1) {
this.iframes.splice(idx, 1)
}
},
display () { this.show = true },
hidden () { this.show = false },
setName (name) { this.name = name },
clearIframe() { this.iframes = [] },
},
})
export default useIframeStore

View File

@@ -0,0 +1,46 @@
import { defineStore } from 'pinia'
const useKeepAliveStore = defineStore('keepAlive', {
state: () => ({
keepAlives: [],
show: true
}),
getters: {
getState() {
return { ...this.$state }
}
},
actions: {
addKeepAlive(component) {
if (component.path.indexOf('maIframe') > -1) {
return
}
if (!this.keepAlives.includes(component.name)) {
this.keepAlives.push(component.name)
}
},
removeKeepAlive(component) {
const idx = this.keepAlives.indexOf(component.name)
if (idx !== -1) {
this.keepAlives.splice(idx, 1)
}
},
display() {
this.show = true
},
hidden() {
this.show = false
},
clearKeepAlive() {
this.keepAlives = []
}
}
})
export default useKeepAliveStore

View File

@@ -0,0 +1,24 @@
import { defineStore } from 'pinia'
let defaultType = {
messageList: [],
}
const useMessageStore = defineStore('message', {
state: () => ({ ...defaultType }),
getters: {
getState() {
return { ...this.$state }
},
},
actions: {
updateMessage(partial) {
this.$patch(partial);
},
},
})
export default useMessageStore

View File

@@ -0,0 +1,71 @@
import { defineStore } from 'pinia'
import tool from '@/utils/tool'
const defaultTag = [ { name: 'dashboard', title: '仪表盘', path: '/dashboard', affix: true } ]
const useTagStore = defineStore('tag', {
state: () => ({
tags: (! tool.local.get('tags') || tool.local.get('tags').length === 0 ) ? defaultTag : tool.local.get('tags')
}),
getters: {
getState() {
return { ...this.$state }
},
},
actions: {
addTag(tag) {
const target = this.tags.find( item => item.path === tag.path )
if (! target && tag.path ) {
this.tags.push(tag)
}
this.updateTagsToLocal()
},
removeTag(tag) {
let index = 0
this.tags.map((item, idx) => {
if ( item.path === tag.path && ! item.affix ) {
if (this.tags[(idx + 1)]) {
index = idx
} else if ( idx > 0) {
index = idx - 1
}
this.tags.splice(idx, 1)
}
})
this.updateTagsToLocal()
return this.tags[index]
},
updateTag(tag) {
this.tags.map(item => {
if (item.path == tag.path) {
item = Object.assign(item, tag)
}
})
this.updateTagsToLocal()
},
updateTagTitle(path, title) {
this.tags.map(item => {
if (item.path == path) {
item.customTitle = title
}
})
this.updateTagsToLocal()
},
updateTagsToLocal() {
tool.local.set('tags', this.tags)
},
clearTags() {
this.tags = defaultTag
tool.local.set('tags', defaultTag)
},
},
})
export default useTagStore

View File

@@ -0,0 +1,216 @@
import { defineStore } from 'pinia'
import tool from '@/utils/tool'
import { Message } from '@arco-design/web-vue'
const buildTerminalUrl = (commandKey, uuid, extend) => {
const env = import.meta.env
const baseURL = env.VITE_APP_BASE_URL
const token = tool.local.get(env.VITE_APP_TOKEN_PREFIX)
const terminalUrl = '/app/saipackage/index/terminal'
return (
baseURL +
terminalUrl +
'?command=' +
commandKey +
'&uuid=' +
uuid +
'&extend=' +
extend +
'&token=' +
token
)
}
const useTerminalStore = defineStore('terminal', {
state: () => ({
show: false,
taskList: [],
npmRegistry: 'npm',
packageManager: 'yarn',
composerRegistry: 'composer'
}),
getters: {
getState() {
return { ...this.$state }
}
},
actions: {
setTaskStatus(idx, status) {
this.taskList[idx].status = status
this.setTaskShowMessage(idx, true)
},
addTaskMessage(idx, message) {
this.taskList[idx].message = this.taskList[idx].message.concat(message)
},
setTaskShowMessage(idx, val = !this.taskList[idx].showMessage) {
this.taskList[idx].showMessage = val
},
cleanTaskList() {
this.taskList = []
},
taskCompleted(idx) {
if (typeof this.taskList[idx].callback != 'function') return
const status = this.taskList[idx].status
if (status == 5 || status == 6) {
this.taskList[idx].callback(5)
} else if (status == 4) {
this.taskList[idx].callback(4)
}
},
findTaskIdxFromUuid(uuid) {
for (const key in this.taskList) {
if (this.taskList[key].uuid == uuid) {
return parseInt(key)
}
}
return false
},
findTaskIdxFromGuess(idx) {
if (!this.taskList[idx]) {
let taskKey = -1
for (const key in this.taskList) {
if (
this.taskList[key].status == 2 ||
this.taskList[key].status == 3
) {
taskKey = parseInt(key)
}
}
return taskKey === -1 ? false : taskKey
} else {
return idx
}
},
startEventSource(taskKey) {
const that = this
window.eventSource = new EventSource(
buildTerminalUrl(
that.taskList[taskKey].command,
that.taskList[taskKey].uuid,
that.taskList[taskKey].extend
)
)
window.eventSource.onmessage = function (e) {
const data = JSON.parse(e.data)
if (!data || !data.data) {
return
}
const taskIdx = that.findTaskIdxFromUuid(data.uuid)
if (taskIdx === false) {
return
}
if (data.data == 'exec-error') {
that.setTaskStatus(taskIdx, 5)
window.eventSource.close()
that.taskCompleted(taskIdx)
that.startTask()
} else if (data.data == 'exec-completed') {
window.eventSource.close()
if (that.taskList[taskIdx].status != 4) {
that.setTaskStatus(taskIdx, 5)
}
that.taskCompleted(taskIdx)
that.startTask()
} else if (data.data == 'connection-success') {
that.setTaskStatus(taskIdx, 3)
} else if (data.data == 'exec-success') {
that.setTaskStatus(taskIdx, 4)
} else {
that.addTaskMessage(taskIdx, data.data)
}
}
window.eventSource.onerror = function () {
window.eventSource.close()
const taskIdx = that.findTaskIdxFromGuess(taskKey)
if (taskIdx === false) return
that.setTaskStatus(taskIdx, 5)
that.taskCompleted(taskIdx)
}
},
addNodeTask(command, extend = '', callback = () => {}) {
command =
command +
'.' +
(this.packageManager == 'unknown' ? 'npm' : this.packageManager)
this.addTask(command, extend, callback)
},
addTask(command, extend = '', callback = () => {}) {
this.taskList = this.taskList.concat({
uuid: tool.uuid(),
createTime: tool.dateFormat(),
status: 1,
command: command,
message: [],
showMessage: false,
extend: extend,
callback: callback
})
// 检查是否有已经失败的任务
if (this.show === false) {
for (const key in this.taskList) {
if (
this.taskList[key].status == 5 ||
this.taskList[key].status == 6
) {
Message.warning({
content: '任务列表中存在失败的任务',
duration: 2000
})
break
}
}
}
this.startTask()
},
startTask() {
let taskKey = null
// 寻找可以开始执行的命令
for (const key in this.taskList) {
if (this.taskList[key].status == 1) {
taskKey = parseInt(key)
break
}
if (this.taskList[key].status == 2 || this.taskList[key].status == 3) {
break
}
if (this.taskList[key].status == 4) {
continue
}
if (this.taskList[key].status == 5 || this.taskList[key].status == 6) {
continue
}
}
if (taskKey !== null) {
this.setTaskStatus(taskKey, 2)
this.startEventSource(taskKey)
}
},
retryTask(idx) {
this.taskList[idx].message = []
this.setTaskStatus(idx, 1)
this.startTask()
},
delTask(idx) {
if (this.taskList[idx].status != 2 && this.taskList[idx].status != 3) {
this.taskList.splice(idx, 1)
}
}
},
persist: {
key: 'storeTerminal'
}
})
export default useTerminalStore

View File

@@ -0,0 +1,199 @@
import { defineStore } from 'pinia'
import loginApi from '@/api/login'
import tool from '@/utils/tool'
import router from '@/router'
import webRouter from '@/router/webRouter'
import { isUndefined } from 'lodash'
import { homePage } from '@/router/homePageRoutes'
import { useAppStore, useTagStore, useDictStore } from '@/store'
const useUserStore = defineStore('user', {
state: () => ({
codes: undefined,
roles: undefined,
routers: undefined,
user: undefined,
menus: undefined
}),
getters: {
getState() {
return { ...this.$state }
}
},
actions: {
setToken(token) {
tool.local.set(import.meta.env.VITE_APP_TOKEN_PREFIX, token)
},
getToken() {
return tool.local.get(import.meta.env.VITE_APP_TOKEN_PREFIX)
},
clearToken() {
tool.local.remove(import.meta.env.VITE_APP_TOKEN_PREFIX)
},
setInfo(data) {
this.$patch(data)
},
resetUserInfo() {
this.$reset()
},
setMenu(data) {
const routers = flatAsyncRoutes(filterAsyncRouter(data))
routers.map((item) => {
if (isUndefined(item.meta.layout)) {
router.addRoute('layout', item)
} else {
if (item.meta.layout) {
router.addRoute('layout', item)
} else {
router.addRoute(item)
}
}
})
},
requestUserInfo() {
return new Promise((resolve, reject) => {
loginApi.getInfo().then(async (response) => {
if (!response || !response.data) {
this.clearToken()
await router.push({ name: 'login' })
reject(false)
} else {
this.setInfo(response.data)
const dictStore = useDictStore()
await dictStore.initData()
homePage.children = webRouter[0].children
this.setMenu(this.routers)
this.routers = removeButtonMenu(this.routers)
this.routers.unshift(homePage)
await this.setApp()
resolve(response.data)
}
})
})
},
login(form) {
return loginApi
.login(form)
.then((r) => {
if (r.code === 200) {
this.setToken(r.data.access_token)
return true
} else {
return false
}
})
.catch((e) => {
console.error(e)
return false
})
},
async logout() {
// await loginApi.logout()
const tagStore = useTagStore()
tool.local.remove('tags')
tagStore.clearTags()
this.clearToken()
this.resetUserInfo()
},
async setApp() {
const appStore = useAppStore()
const setting =
typeof this.user.backend_setting === 'string'
? JSON.parse(this.user.backend_setting)
: this.user.backend_setting
appStore.toggleMode(setting?.mode ?? appStore.mode)
appStore.toggleMenu(setting?.menuCollapse ?? appStore.menuCollapse)
appStore.toggleTag(setting?.tag ?? appStore.tag)
appStore.toggleRound(setting?.roundOpen ?? appStore.roundOpen)
appStore.toggleWs(setting?.ws ?? appStore.ws)
appStore.changeMenuWidth(setting?.menuWidth ?? appStore.menuWidth)
appStore.changeLayout(setting?.layout ?? appStore.layout)
appStore.useSkin(setting?.skin ?? appStore.skin)
appStore.changeColor(setting?.color ?? appStore.color)
appStore.toggleWater(setting?.waterMark ?? appStore.waterMark)
appStore.changeWaterContent(
setting?.waterContent ?? appStore.waterContent
)
}
}
})
//路由扁平化
const flatAsyncRoutes = (routes, breadcrumb = []) => {
let res = []
routes.forEach((route) => {
const tmp = { ...route }
if (tmp.children) {
let childrenBreadcrumb = [...breadcrumb]
childrenBreadcrumb.push(route)
let tmpRoute = { ...route }
tmpRoute.meta.breadcrumb = childrenBreadcrumb
delete tmpRoute.children
res.push(tmpRoute)
let childrenRoutes = flatAsyncRoutes(tmp.children, childrenBreadcrumb)
childrenRoutes.map((item) => {
res.push(item)
})
} else {
let tmpBreadcrumb = [...breadcrumb]
tmpBreadcrumb.push(tmp)
tmp.meta.breadcrumb = tmpBreadcrumb
res.push(tmp)
}
})
return res
}
const views = import.meta.glob('../../views/**/**.vue')
const empty = import.meta.glob('../../layout/empty.vue')
// 菜单转换路由
const filterAsyncRouter = (routerMap) => {
const accessedRouters = []
routerMap.forEach((item) => {
if (item.meta.type !== 'B') {
if (item.meta.type === 'I') {
item.meta.url = item.path
item.path = `/maIframe/${item.name}`
}
const route = {
path: item.path,
name: item.name,
hidden: item.hidden === 1,
meta: item.meta,
children: item.children ? filterAsyncRouter(item.children) : null,
component: views[`../../views/${item.component}.vue`]
}
accessedRouters.push(route)
}
})
return accessedRouters
}
// 去除按钮菜单
const removeButtonMenu = (routers) => {
let handlerAfterRouters = []
routers.forEach((item) => {
if (item.meta.type !== 'B' && !item.meta.hidden) {
let route = item
if (item.children && item.children.length > 0) {
route.children = removeButtonMenu(item.children)
}
handlerAfterRouters.push(route)
}
})
return handlerAfterRouters
}
export default useUserStore