项目初始化
This commit is contained in:
604
web/src/views/backend/module/index.ts
Normal file
604
web/src/views/backend/module/index.ts
Normal file
@@ -0,0 +1,604 @@
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { isArray } from 'lodash-es'
|
||||
import { state } from './store'
|
||||
import { moduleInstallState, type moduleState } from './types'
|
||||
import {
|
||||
changeState,
|
||||
createOrder,
|
||||
getInstallState,
|
||||
index,
|
||||
info,
|
||||
modules,
|
||||
payCheck,
|
||||
payOrder,
|
||||
postInstallModule,
|
||||
preDownload,
|
||||
} from '/@/api/backend/module'
|
||||
import { i18n } from '/@/lang/index'
|
||||
import router from '/@/router/index'
|
||||
import { useBaAccount } from '/@/stores/baAccount'
|
||||
import { SYSTEM_ZINDEX } from '/@/stores/constant/common'
|
||||
import { taskStatus } from '/@/stores/constant/terminalTaskStatus'
|
||||
import type { UserInfo } from '/@/stores/interface'
|
||||
import { useTerminal } from '/@/stores/terminal'
|
||||
import { fullUrl } from '/@/utils/common'
|
||||
import { uuid } from '/@/utils/random'
|
||||
import { changeListenDirtyFileSwitch, closeHotUpdate } from '/@/utils/vite'
|
||||
|
||||
export const loadData = () => {
|
||||
state.loading.table = true
|
||||
if (!state.table.indexLoaded) {
|
||||
loadIndex().then(() => {
|
||||
getModules()
|
||||
})
|
||||
} else {
|
||||
getModules()
|
||||
}
|
||||
}
|
||||
|
||||
export const onRefreshTableData = () => {
|
||||
state.table.indexLoaded = false
|
||||
for (const key in state.table.modulesEbak) {
|
||||
state.table.modulesEbak[key] = undefined
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
|
||||
const loadIndex = () => {
|
||||
return index().then((res) => {
|
||||
state.table.indexLoaded = true
|
||||
state.sysVersion = res.data.sysVersion
|
||||
state.nuxtVersion = res.data.nuxtVersion
|
||||
state.installedModule = res.data.installed
|
||||
|
||||
const installedModuleUids: string[] = []
|
||||
const installedModuleVersions: { uid: string; version: string }[] = []
|
||||
if (res.data.installed) {
|
||||
state.installedModule.forEach((item) => {
|
||||
installedModuleUids.push(item.uid)
|
||||
installedModuleVersions.push({
|
||||
uid: item.uid,
|
||||
version: item.version,
|
||||
})
|
||||
})
|
||||
state.installedModuleUids = installedModuleUids
|
||||
state.installedModuleVersions = installedModuleVersions
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getModules = () => {
|
||||
if (typeof state.table.modulesEbak[state.table.params.activeTab] != 'undefined') {
|
||||
state.table.modules[state.table.params.activeTab] = modulesOnlyLocalHandle(state.table.modulesEbak[state.table.params.activeTab])
|
||||
state.loading.table = false
|
||||
return
|
||||
}
|
||||
const params: anyObj = {}
|
||||
for (const key in state.table.params) {
|
||||
if (state.table.params[key] != '') {
|
||||
params[key] = state.table.params[key]
|
||||
}
|
||||
}
|
||||
const moduleUids: string[] = []
|
||||
params['installed'] = state.installedModuleVersions
|
||||
params['sysVersion'] = state.sysVersion
|
||||
modules(params)
|
||||
.then((res) => {
|
||||
if (params.activeTab == 'all') {
|
||||
res.data.rows.forEach((item: anyObj) => {
|
||||
moduleUids.push(item.uid)
|
||||
})
|
||||
|
||||
state.installedModule.forEach((item) => {
|
||||
if (moduleUids.indexOf(item.uid) === -1) {
|
||||
if (state.table.params.quickSearch) {
|
||||
if (item.title.includes(state.table.params.quickSearch)) res.data.rows.push(item)
|
||||
} else {
|
||||
res.data.rows.push(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
state.table.remark = res.data.remark
|
||||
state.table.modulesEbak[params.activeTab] = res.data.rows.map((item: anyObj) => {
|
||||
const idx = state.installedModuleUids.indexOf(item.uid)
|
||||
if (idx !== -1) {
|
||||
item.state = state.installedModule[idx].state
|
||||
item.title = state.installedModule[idx].title
|
||||
item.version = state.installedModule[idx].version
|
||||
item.website = state.installedModule[idx].website
|
||||
item.stateTag = moduleStatus(item.state)
|
||||
|
||||
if (!isArray(item.tags)) item.tags = []
|
||||
item.tags.push({
|
||||
name: `${i18n.global.t('module.installed')} v${state.installedModule[idx].version}`,
|
||||
type: 'primary',
|
||||
})
|
||||
} else {
|
||||
item.state = 0
|
||||
}
|
||||
|
||||
if (item.new_version && item.tags) {
|
||||
item.tags.push({
|
||||
name: i18n.global.t('module.New version'),
|
||||
type: 'danger',
|
||||
})
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
state.table.modules[params.activeTab] = modulesOnlyLocalHandle(state.table.modulesEbak[params.activeTab])
|
||||
state.table.category = res.data.category
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading.table = false
|
||||
})
|
||||
}
|
||||
|
||||
export const showInfo = (uid: string) => {
|
||||
state.dialog.goodsInfo = true
|
||||
state.loading.goodsInfo = true
|
||||
|
||||
const localItem = state.installedModule.find((item) => {
|
||||
return item.uid == uid
|
||||
})
|
||||
|
||||
info({
|
||||
uid: uid,
|
||||
localVersion: localItem?.version,
|
||||
sysVersion: state.sysVersion,
|
||||
})
|
||||
.then((res) => {
|
||||
if (localItem) {
|
||||
if (res.data.info.type == 'local') {
|
||||
res.data.info = localItem
|
||||
res.data.info.images = [fullUrl('/static/images/local-module-logo.png')]
|
||||
res.data.info.type = 'local' // 纯本地模块
|
||||
} else {
|
||||
res.data.info.type = 'online'
|
||||
res.data.info.state = localItem.state
|
||||
res.data.info.version = localItem.version
|
||||
}
|
||||
res.data.info.enable = localItem.state === moduleInstallState.DISABLE ? false : true
|
||||
} else {
|
||||
res.data.info.state = 0
|
||||
res.data.info.type = 'online'
|
||||
}
|
||||
state.goodsInfo = res.data.info
|
||||
})
|
||||
.catch((err) => {
|
||||
if (loginExpired(err)) {
|
||||
state.dialog.goodsInfo = false
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading.goodsInfo = false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
* @param renew 是否是续费订单
|
||||
*/
|
||||
export const onBuy = (renew = false) => {
|
||||
state.dialog.buy = true
|
||||
state.loading.buy = true
|
||||
createOrder({
|
||||
goods_id: state.goodsInfo.id,
|
||||
})
|
||||
.then((res) => {
|
||||
state.loading.buy = false
|
||||
state.buy.renew = renew
|
||||
state.buy.info = res.data.info
|
||||
})
|
||||
.catch((err) => {
|
||||
state.dialog.buy = false
|
||||
state.loading.buy = false
|
||||
loginExpired(err)
|
||||
})
|
||||
}
|
||||
|
||||
export const onPay = (payType: 'score' | 'wx' | 'balance' | 'zfb') => {
|
||||
state.common.payType = payType
|
||||
state.loading.common = true
|
||||
payOrder(state.buy.info.id, payType)
|
||||
.then((res) => {
|
||||
// 关闭其他弹窗
|
||||
state.dialog.buy = false
|
||||
state.dialog.goodsInfo = false
|
||||
|
||||
if (payType == 'wx' || payType == 'zfb') {
|
||||
// 显示支付二维码
|
||||
state.dialog.pay = true
|
||||
state.payInfo = res.data
|
||||
|
||||
// 轮询获取支付状态
|
||||
const timer = setInterval(() => {
|
||||
payCheck(state.payInfo.info.sn)
|
||||
.then(() => {
|
||||
state.payInfo.pay.status = 'success'
|
||||
clearInterval(timer)
|
||||
if (state.buy.renew) {
|
||||
showInfo(res.data.info.uid)
|
||||
} else {
|
||||
onPreInstallModule(res.data.info.uid, res.data.info.id, true)
|
||||
}
|
||||
state.dialog.pay = false
|
||||
})
|
||||
.catch(() => {})
|
||||
}, 3000)
|
||||
} else {
|
||||
if (state.buy.renew) {
|
||||
showInfo(res.data.info.uid)
|
||||
} else {
|
||||
onPreInstallModule(res.data.info.uid, res.data.info.id, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
loginExpired(err)
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading.common = false
|
||||
})
|
||||
}
|
||||
|
||||
export const showCommonLoading = (loadingTitle: moduleState['common']['loadingTitle']) => {
|
||||
state.common.type = 'loading'
|
||||
state.common.loadingTitle = loadingTitle
|
||||
state.common.loadingComponentKey = uuid()
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块预安装
|
||||
*/
|
||||
export const onPreInstallModule = (uid: string, id: number, needGetInstallableVersion: boolean, update: boolean = false) => {
|
||||
state.dialog.common = true
|
||||
showCommonLoading('init')
|
||||
state.common.dialogTitle = i18n.global.t('module.Install')
|
||||
|
||||
const nextStep = (moduleState: number) => {
|
||||
if (needGetInstallableVersion) {
|
||||
// 获取模块版本列表
|
||||
showCommonLoading('getInstallableVersion')
|
||||
preDownload({
|
||||
uid,
|
||||
orderId: id,
|
||||
sysVersion: state.sysVersion,
|
||||
nuxtVersion: state.nuxtVersion,
|
||||
installed: state.installedModuleUids,
|
||||
})
|
||||
.then((res) => {
|
||||
state.common.uid = uid
|
||||
state.common.update = update
|
||||
state.common.type = 'selectVersion'
|
||||
state.common.dialogTitle = i18n.global.t('module.Select Version')
|
||||
state.common.versions = res.data.versions
|
||||
|
||||
// 关闭其他弹窗
|
||||
state.dialog.baAccount = false
|
||||
state.dialog.buy = false
|
||||
state.dialog.goodsInfo = false
|
||||
})
|
||||
.catch((res) => {
|
||||
if (loginExpired(res)) return
|
||||
state.dialog.common = false
|
||||
})
|
||||
} else {
|
||||
// 立即安装(上传安装、继续安装)
|
||||
showCommonLoading(moduleState === moduleInstallState.UNINSTALLED ? 'download' : 'install')
|
||||
execInstall(uid, id, '', update)
|
||||
|
||||
// 关闭其他弹窗
|
||||
state.dialog.baAccount = false
|
||||
state.dialog.buy = false
|
||||
state.dialog.goodsInfo = false
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
nextStep(moduleInstallState.DISABLE)
|
||||
} else {
|
||||
// 获取安装状态
|
||||
getInstallState(uid).then((res) => {
|
||||
if (
|
||||
res.data.state === moduleInstallState.INSTALLED ||
|
||||
res.data.state === moduleInstallState.DISABLE ||
|
||||
res.data.state === moduleInstallState.DIRECTORY_OCCUPIED
|
||||
) {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message:
|
||||
res.data.state === moduleInstallState.INSTALLED || res.data.state === moduleInstallState.DISABLE
|
||||
? i18n.global.t('module.Installation cancelled because module already exists!')
|
||||
: i18n.global.t('module.Installation cancelled because the directory required by the module is occupied!'),
|
||||
})
|
||||
state.dialog.common = false
|
||||
return
|
||||
}
|
||||
|
||||
nextStep(res.data.state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行安装请求,还包含启用、安装时的冲突处理
|
||||
*/
|
||||
export const execInstall = (uid: string, id: number, version: string = '', update: boolean = false, extend: anyObj = {}) => {
|
||||
postInstallModule(uid, id, version, update, extend)
|
||||
.then(() => {
|
||||
state.common.dialogTitle = i18n.global.t('module.Installation complete')
|
||||
state.common.moduleState = moduleInstallState.INSTALLED
|
||||
state.common.type = 'done'
|
||||
onRefreshTableData()
|
||||
})
|
||||
.catch((res) => {
|
||||
if (loginExpired(res)) return
|
||||
if (res.code == -1) {
|
||||
state.common.uid = res.data.uid
|
||||
state.common.type = 'installConflict'
|
||||
state.common.dialogTitle = i18n.global.t('module.A conflict is found Please handle it manually')
|
||||
state.common.fileConflict = res.data.fileConflict
|
||||
state.common.dependConflict = res.data.dependConflict
|
||||
} else if (res.code == -2) {
|
||||
state.common.type = 'done'
|
||||
state.common.uid = res.data.uid
|
||||
state.common.dialogTitle = i18n.global.t('module.Wait for dependent installation')
|
||||
state.common.moduleState = moduleInstallState.DEPENDENT_WAIT_INSTALL
|
||||
state.common.waitInstallDepend = res.data.wait_install
|
||||
state.common.dependInstallState = 'executing'
|
||||
const terminal = useTerminal()
|
||||
if (res.data.wait_install.includes('npm_dependent_wait_install')) {
|
||||
terminal.addTaskPM('web-install', true, 'module-install:' + res.data.uid, (res: number) => {
|
||||
terminalTaskExecComplete(res, 'npm_dependent_wait_install')
|
||||
})
|
||||
}
|
||||
if (res.data.wait_install.includes('nuxt_npm_dependent_wait_install')) {
|
||||
terminal.addTaskPM('nuxt-install', true, 'module-install:' + res.data.uid, (res: number) => {
|
||||
terminalTaskExecComplete(res, 'nuxt_npm_dependent_wait_install')
|
||||
})
|
||||
}
|
||||
if (res.data.wait_install.includes('composer_dependent_wait_install')) {
|
||||
terminal.addTask('composer.update', true, 'module-install:' + res.data.uid, (res: number) => {
|
||||
terminalTaskExecComplete(res, 'composer_dependent_wait_install')
|
||||
})
|
||||
}
|
||||
} else if (res.code == 0) {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: res.msg,
|
||||
zIndex: SYSTEM_ZINDEX,
|
||||
})
|
||||
state.dialog.common = false
|
||||
onRefreshTableData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading.common = false
|
||||
})
|
||||
}
|
||||
|
||||
const terminalTaskExecComplete = (res: number, type: string) => {
|
||||
if (res == taskStatus.Success) {
|
||||
state.common.waitInstallDepend = state.common.waitInstallDepend.filter((depend: string) => {
|
||||
return depend != type
|
||||
})
|
||||
if (state.common.waitInstallDepend.length == 0) {
|
||||
state.common.dependInstallState = 'success'
|
||||
|
||||
// 仅在命令全部执行完毕才刷新数据
|
||||
if (router.currentRoute.value.name === 'moduleStore/moduleStore') {
|
||||
onRefreshTableData()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const terminal = useTerminal()
|
||||
terminal.toggle(true)
|
||||
state.common.dependInstallState = 'fail'
|
||||
|
||||
// 有命令执行失败了,刷新一次数据
|
||||
if (router.currentRoute.value.name === 'moduleStore/moduleStore') {
|
||||
onRefreshTableData()
|
||||
}
|
||||
}
|
||||
|
||||
// 连续安装模块的情况中,首个模块的命令执行完毕时,自动启动了热更新
|
||||
if (router.currentRoute.value.name === 'moduleStore/moduleStore') {
|
||||
closeHotUpdate('modules')
|
||||
}
|
||||
}
|
||||
|
||||
export const onDisable = (confirmConflict = false) => {
|
||||
state.loading.common = true
|
||||
|
||||
// 拼装依赖处理方案
|
||||
if (confirmConflict) {
|
||||
const dependConflict: anyObj = {}
|
||||
for (const key in state.common.disableDependConflict) {
|
||||
if (state.common.disableDependConflict[key]['solution'] != 'delete') {
|
||||
continue
|
||||
}
|
||||
if (typeof dependConflict[state.common.disableDependConflict[key].env] == 'undefined') {
|
||||
dependConflict[state.common.disableDependConflict[key].env] = []
|
||||
}
|
||||
dependConflict[state.common.disableDependConflict[key].env].push(state.common.disableDependConflict[key].depend)
|
||||
}
|
||||
state.common.disableParams['confirmConflict'] = 1
|
||||
state.common.disableParams['dependConflictSolution'] = dependConflict
|
||||
}
|
||||
|
||||
changeState(state.common.disableParams)
|
||||
.then(() => {
|
||||
ElNotification({
|
||||
type: 'success',
|
||||
message: i18n.global.t('module.The operation succeeds Please clear the system cache and refresh the browser ~'),
|
||||
zIndex: SYSTEM_ZINDEX,
|
||||
})
|
||||
state.dialog.common = false
|
||||
onRefreshTableData()
|
||||
})
|
||||
.catch((res) => {
|
||||
if (res.code == -1) {
|
||||
state.dialog.common = true
|
||||
state.common.dialogTitle = i18n.global.t('module.Deal with conflict')
|
||||
state.common.type = 'disableConfirmConflict'
|
||||
state.common.disableDependConflict = res.data.dependConflict
|
||||
if (res.data.conflictFile && res.data.conflictFile.length) {
|
||||
const conflictFile = []
|
||||
for (const key in res.data.conflictFile) {
|
||||
conflictFile.push({
|
||||
file: res.data.conflictFile[key],
|
||||
})
|
||||
}
|
||||
state.common.disableConflictFile = conflictFile
|
||||
}
|
||||
} else if (res.code == -2) {
|
||||
state.dialog.common = true
|
||||
const commandsData = {
|
||||
type: 'disable',
|
||||
commands: res.data.wait_install,
|
||||
}
|
||||
state.common.uid = state.goodsInfo.uid
|
||||
execCommand(commandsData)
|
||||
} else if (res.code == -3) {
|
||||
// 更新
|
||||
onPreInstallModule(state.goodsInfo.uid, state.goodsInfo.purchased, true, true)
|
||||
} else {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: res.msg,
|
||||
zIndex: SYSTEM_ZINDEX,
|
||||
})
|
||||
if (state.common.disableParams && state.common.disableParams.uid) {
|
||||
showInfo(state.common.disableParams.uid)
|
||||
} else {
|
||||
onRefreshTableData()
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading.common = false
|
||||
})
|
||||
}
|
||||
|
||||
export const onEnable = (uid: string) => {
|
||||
state.loading.common = true
|
||||
changeState({
|
||||
uid: uid,
|
||||
state: 1,
|
||||
})
|
||||
.then(() => {
|
||||
state.dialog.common = true
|
||||
showCommonLoading('init')
|
||||
state.common.dialogTitle = i18n.global.t('Enable')
|
||||
|
||||
execInstall(uid, 0)
|
||||
state.dialog.goodsInfo = false
|
||||
})
|
||||
.catch((res) => {
|
||||
ElNotification({
|
||||
type: 'error',
|
||||
message: res.msg,
|
||||
zIndex: SYSTEM_ZINDEX,
|
||||
})
|
||||
state.loading.common = false
|
||||
})
|
||||
}
|
||||
|
||||
export const loginExpired = (res: ApiResponse) => {
|
||||
const baAccount = useBaAccount()
|
||||
if (res.code == 301 || res.code == 408) {
|
||||
baAccount.removeToken()
|
||||
state.dialog.baAccount = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const modulesOnlyLocalHandle = (modules: anyObj) => {
|
||||
if (!state.table.onlyLocal) return modules
|
||||
return modules.filter((item: anyObj) => {
|
||||
return item.installed
|
||||
})
|
||||
}
|
||||
|
||||
export const execCommand = (data: anyObj) => {
|
||||
if (data.type == 'disable') {
|
||||
state.dialog.common = true
|
||||
state.common.type = 'done'
|
||||
state.common.dialogTitle = i18n.global.t('module.Wait for dependent installation')
|
||||
state.common.moduleState = moduleInstallState.DISABLE
|
||||
state.common.dependInstallState = 'executing'
|
||||
const terminal = useTerminal()
|
||||
data.commands.forEach((item: anyObj) => {
|
||||
state.common.waitInstallDepend.push(item.type)
|
||||
if (item.pm) {
|
||||
if (item.command == 'web-install') {
|
||||
changeListenDirtyFileSwitch(false)
|
||||
}
|
||||
terminal.addTaskPM(item.command, true, '', (res: number) => {
|
||||
terminalTaskExecComplete(res, item.type)
|
||||
if (item.command == 'web-install') {
|
||||
changeListenDirtyFileSwitch(true)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
terminal.addTask(item.command, true, '', (res: number) => {
|
||||
terminalTaskExecComplete(res, item.type)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const specificUserName = (userInfo: Partial<UserInfo>) => {
|
||||
return userInfo.nickname + '(' + (userInfo.email || userInfo.mobile || 'ID:' + userInfo.id) + ')'
|
||||
}
|
||||
|
||||
export const currency = (price: number, val: number) => {
|
||||
if (typeof price == 'undefined' || typeof val == 'undefined') {
|
||||
return '-'
|
||||
}
|
||||
if (val == 0) {
|
||||
return parseInt(price.toString()) + i18n.global.t('Integral')
|
||||
} else {
|
||||
return '¥' + price
|
||||
}
|
||||
}
|
||||
|
||||
export const moduleStatus = (state: number) => {
|
||||
switch (state) {
|
||||
case moduleInstallState.INSTALLED:
|
||||
return {
|
||||
type: '',
|
||||
text: i18n.global.t('module.installed'),
|
||||
}
|
||||
case moduleInstallState.WAIT_INSTALL:
|
||||
return {
|
||||
type: 'success',
|
||||
text: i18n.global.t('module.Wait for installation'),
|
||||
}
|
||||
case moduleInstallState.CONFLICT_PENDING:
|
||||
return {
|
||||
type: 'danger',
|
||||
text: i18n.global.t('module.Conflict pending'),
|
||||
}
|
||||
case moduleInstallState.DEPENDENT_WAIT_INSTALL:
|
||||
return {
|
||||
type: 'warning',
|
||||
text: i18n.global.t('module.Dependency to be installed'),
|
||||
}
|
||||
case moduleInstallState.DISABLE:
|
||||
return {
|
||||
type: 'warning',
|
||||
text: i18n.global.t('Disable'),
|
||||
}
|
||||
default:
|
||||
return {
|
||||
type: 'info',
|
||||
text: i18n.global.t('Unknown'),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user