This commit is contained in:
bwcx_jzy 2023-04-09 15:49:02 +08:00
parent 9253785610
commit d9d57c01e9
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
8 changed files with 229 additions and 56 deletions

View File

@ -26,20 +26,126 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="favicon.ico" />
<link rel="icon" href="<%- base_url %>favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="keywords" content="Jpom,Java项目管理,Jar管理,Java管理系统,服务器项目运维" />
<meta name="description" content="Jpom-项目管理系统,简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="build-time" content="2023-12-12" />
<meta name="build-env" content="生产" />
<meta name="jpom-version" content="2.2.30" />
<title>Jpom项目管理系统</title>
<meta name="build-time" content="<%- build %>" />
<meta name="build-env" content="<%- env %>" />
<meta name="jpom-version" content="<%- buildVersion %>" />
<title><%- title %></title>
<style>
.first-loading-wrp {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
min-height: 420px;
height: 100%;
}
.first-loading-wrp > h1 {
font-size: 128px;
}
.first-loading-wrp .loading-wrp {
padding: 98px;
display: flex;
justify-content: center;
align-items: center;
}
.dot {
animation: antRotate 1.2s infinite linear;
transform: rotate(45deg);
position: relative;
display: inline-block;
font-size: 32px;
width: 32px;
height: 32px;
box-sizing: border-box;
}
.dot i {
width: 14px;
height: 14px;
position: absolute;
display: block;
background-color: #1890ff;
border-radius: 100%;
transform: scale(0.75);
transform-origin: 50% 50%;
opacity: 0.3;
animation: antSpinMove 1s infinite linear alternate;
}
.dot i:nth-child(1) {
top: 0;
left: 0;
}
.dot i:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.dot i:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.dot i:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
</style>
</head>
<body>
<div id="app"></div>
<noscript>
<strong
>We're sorry but <%- title %> doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<script>
window.routerBase = '<routerBase>'
window.apiTimeout = '<apiTimeout>'
window.uploadFileSliceSize = '<uploadFileSliceSize>'
window.uploadFileConcurrent = '<uploadFileConcurrent>'
window.oauth2Provide = '<oauth2Provide>'
</script>
<div id="app">
<div class="first-loading-wrp">
<div class="loading-wrp">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
<div style="display: flex; justify-content: center; align-items: center"><%- title %></div>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
<div id="jpomCommonJs"><!--Don't delete this line, place for public JS --></div>
</body>
</html>

View File

@ -1,7 +1,7 @@
{
"name": "web-vue3",
"name": "jpom-vue3",
"private": true,
"version": "0.0.0",
"version": "2.10.39",
"type": "module",
"scripts": {
"dev": "vite --mode dev",
@ -42,6 +42,7 @@
"eslint": "^8.36.0",
"https": "^1.0.0",
"typescript": "^5.0.2",
"vite": "^4.1.0"
"vite": "^4.1.0",
"vite-plugin-html": "^3.2.0"
}
}

View File

@ -10,6 +10,8 @@ import { useMenuStore } from '@/stores/menu'
const _window = window as any
const delTimeout = 20 * 1000
const apiTimeout = _window.apiTimeout === '<apiTimeout>' ? delTimeout : _window.apiTimeout
// debug routerBase
const routerBase = _window.routerBase === '<routerBase>' ? '' : _window.routerBase
const pro = process.env.NODE_ENV === 'production'
@ -30,15 +32,13 @@ instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const userStore = useUserStore()
const { headers } = config
const accessToken = localStorage.getItem('accessToken')
headers['Authorization'] = accessToken ? 'Bearer ' + accessToken : ''
headers[TOKEN_HEADER_KEY] = userStore.token
headers[CACHE_WORKSPACE_ID] = appStore.getWorkspaceId
// if (_window.routerBase) {
// // 防止 url 出现 //
// config.url = (_window.routerBase + config.url).replace(new RegExp('//', 'gm'), '/')
// }
if (routerBase) {
// 防止 url 出现 //
config.url = (routerBase + config.url).replace(new RegExp('//', 'gm'), '/')
}
return config
})

View File

@ -1,10 +1,18 @@
export interface SystemType {
disabledCaptcha: bollean
disabledGuide: bollean
inDocker: bollean
loginTitle: string
name: string
notificationPlacement: string
routerBase: string
subTitle: string
disabledCaptcha: bollean
disabledGuide: bollean
inDocker: bollean
loginTitle: string
name: string
notificationPlacement: string
routerBase: string
subTitle: string
}
export interface GlobalWindow {
routerBase: string
apiTimeout: string
uploadFileSliceSize: string
uploadFileConcurrent: string
oauth2Provide: string
}

View File

@ -1,6 +1,14 @@
<template>
<div class="init-wrapper">
<svg width="100%" height="100%" viewBox="0 0 1440 500" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<svg
width="100%"
height="100%"
viewBox="0 0 1440 500"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
>
<g>
<circle stroke="#13C2C2" cx="500" cy="-20" r="6"></circle>
<circle fill-opacity="0.4" fill="#9EE6E6" cx="166" cy="76" r="8"></circle>
@ -12,18 +20,25 @@
<g>
<path
d="M1182.79367,448.230356 L1186.00213,453.787581 C1186.55442,454.744166 1186.22667,455.967347 1185.27008,456.519632 C1184.96604,456.695168 1184.62116,456.787581 1184.27008,456.787581 L1177.85315,456.787581 C1176.74858,456.787581 1175.85315,455.89215 1175.85315,454.787581 C1175.85315,454.436507 1175.94556,454.091619 1176.1211,453.787581 L1179.32957,448.230356 C1179.88185,447.273771 1181.10503,446.946021 1182.06162,447.498305 C1182.36566,447.673842 1182.61813,447.926318 1182.79367,448.230356 Z"
stroke="#CED4D9"></path>
stroke="#CED4D9"
></path>
<path
d="M1376.79367,204.230356 L1380.00213,209.787581 C1380.55442,210.744166 1380.22667,211.967347 1379.27008,212.519632 C1378.96604,212.695168 1378.62116,212.787581 1378.27008,212.787581 L1371.85315,212.787581 C1370.74858,212.787581 1369.85315,211.89215 1369.85315,210.787581 C1369.85315,210.436507 1369.94556,210.091619 1370.1211,209.787581 L1373.32957,204.230356 C1373.88185,203.273771 1375.10503,202.946021 1376.06162,203.498305 C1376.36566,203.673842 1376.61813,203.926318 1376.79367,204.230356 Z"
stroke="#2F54EB"></path>
stroke="#2F54EB"
></path>
</g>
<g>
<rect stroke="#13C2C2" stroke-opacity="0.6" x="120" y="322" width="12" height="12" rx="1"></rect>
<rect stroke="#CED4D9" x="108" y="1" width="9" height="9" rx="1"></rect>
</g>
</svg>
<a-card v-if="canInstall" class="card-box" :style="`${setpCurrent === 1 ? 'width: 60vw' : 'width: 550px'}`" hoverable
:bodyStyle="{ padding: '24px 0', overflow: 'auto' }">
<a-card
v-if="canInstall"
class="card-box"
:style="`${setpCurrent === 1 ? 'width: 60vw' : 'width: 550px'}`"
hoverable
:bodyStyle="{ padding: '24px 0', overflow: 'auto' }"
>
<template #title>
<a-steps :current="setpCurrent">
<a-step title="初始化系统" status="process" description="设置一个超级管理员账号">
@ -41,20 +56,34 @@
<a-row type="flex" justify="center">
<a-col :span="16" v-if="setpCurrent === 0">
<a-card-meta title="初始化系统账户" style="textalign: center" description="您需要创建一个账户用以后续登录管理系统,请牢记超级管理员账号密码" />
<a-card-meta
title="初始化系统账户"
style="textalign: center"
description="您需要创建一个账户用以后续登录管理系统,请牢记超级管理员账号密码"
/>
<br />
<a-form :model="loginForm" name="login" :label-col="{ span: 0 }" :wrapper-col="{ span: 24 }"
@finish="handleLogin" class="init-form">
<a-form
:model="loginForm"
name="login"
:label-col="{ span: 0 }"
:wrapper-col="{ span: 24 }"
@finish="handleLogin"
class="init-form"
>
<a-form-item class="init-user-name" name="userName" :rules="[{ required: true, message: '请输入账户名' }]">
<a-input v-model:value="loginForm.userName" placeholder="账户名称" />
</a-form-item>
<a-form-item class="init-user-password" name="userPwd" :rules="[
{ required: true, message: '请输入密码' },
{
pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,18}$/,
message: '密码必须包含数字字母字符且大于6位',
},
]">
<a-form-item
class="init-user-password"
name="userPwd"
:rules="[
{ required: true, message: '请输入密码' },
{
pattern: /^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$).{6,18}$/,
message: '密码必须包含数字字母字符且大于6位',
},
]"
>
<a-input-password v-model:value="loginForm.userPwd" placeholder="密码6-18位数字、字母、符号组合" />
</a-form-item>
<a-form-item>
@ -82,8 +111,13 @@
</a-col>
<a-divider type="vertical" />
<a-col :span="20">
<a-form :model="mfaForm" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }" @submit="handleMfaSure"
class="init-form">
<a-form
:model="mfaForm"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 18 }"
@finish="handleMfaSure"
class="init-form"
>
<a-form-item label="二维码" style="margin-bottom: 5px">
<div class="qrcode">
<qrcode-vue :value="qrCode.value" :size="qrCode.size" level="H" />
@ -91,18 +125,21 @@
</a-form-item>
<a-form-item label="MFA key" name="mfa">
<a-input-group compact>
<a-input v-model:value="mfaForm.mfa" disabled style="width: calc(100% - 32px)">
</a-input>
<a-button style="padding: 4px 6px;">
<a-input v-model:value="mfaForm.mfa" disabled style="width: calc(100% - 32px)"> </a-input>
<a-button style="padding: 4px 6px">
<a-typography-paragraph :copyable="{ text: mfaForm.mfa }"></a-typography-paragraph>
</a-button>
</a-input-group>
</a-form-item>
<a-form-item label="验证码" name="twoCode" :rules="[
{ required: true, message: '请输入两步验证码' },
{ pattern: /^\d{6}$/, message: '验证码 6 为纯数字' },
]">
<a-form-item
label="验证码"
name="twoCode"
:rules="[
{ required: true, message: '请输入两步验证码' },
{ pattern: /^\d{6}$/, message: '验证码 6 为纯数字' },
]"
>
<a-input v-model:value="mfaForm.twoCode" placeholder="两步验证码" />
</a-form-item>
@ -138,7 +175,7 @@ import sha1 from 'js-sha1'
import { checkSystem } from '@/api/install'
import { initInstall } from '@/api/install'
import { onMounted, reactive, ref } from 'vue'
import { UserOutlined, SolutionOutlined, CopyOutlined } from '@ant-design/icons-vue'
import { UserOutlined, SolutionOutlined } from '@ant-design/icons-vue'
import QrcodeVue from 'qrcode.vue'
import { Modal, notification } from 'ant-design-vue'
import { useUserStore } from '@/stores/user'
@ -153,7 +190,7 @@ const loginForm = reactive({
})
const mfaForm = reactive({
twoCode: '',
mfa: ''
mfa: '',
})
const setpCurrent = ref(0)
@ -183,7 +220,7 @@ const handleLogin = (values: any) => {
setpCurrent.value = 1
qrCode.value = res.data.url
userStore.login({ token: tokenData.token, longTermToken: tokenData.longTermToken })
router.push({ path: '/' })
const firstWorkspace = tokenData.bindWorkspaceModels[0]
appStore.changeWorkspace(firstWorkspace.id)
}
@ -212,7 +249,7 @@ const handleIgnoreBindMfa = () => {
onOk: () => {
router.push({ path: '/' })
},
});
})
}
const goHome = () => {

View File

@ -2,9 +2,13 @@ import SparkMD5 from 'spark-md5'
import { concurrentExecution } from '@/utils/const'
import { generateShardingId } from '@/api/common'
import Vue from 'vue'
import { GlobalWindow } from '@/interface/common'
const uploadFileSliceSize = window.uploadFileSliceSize === '<uploadFileSliceSize>' ? 1 : window.uploadFileSliceSize
const uploadFileConcurrent = window.uploadFileConcurrent === '<uploadFileConcurrent>' ? 1 : window.uploadFileConcurrent
const _window = window as unknown as GlobalWindow
const uploadFileSliceSize = _window.uploadFileSliceSize === '<uploadFileSliceSize>' ? 1 : _window.uploadFileSliceSize
const uploadFileConcurrent =
_window.uploadFileConcurrent === '<uploadFileConcurrent>' ? 1 : _window.uploadFileConcurrent
/**
*

View File

@ -1,7 +1,8 @@
export function getHashQuery() {
const querys: Record<string, any> = {}
location.hash.replace(/[?&]+([^=&]+)=([^&]*)/gi, (_m: string, key: string, value: any) => {
const querys: Record<string, string> = {}
location.hash.replace(/[?&]+([^=&]+)=([^&]*)/gi, (_match: string, key: string, value: string) => {
querys[key] = value
return ''
})
return querys
}

View File

@ -24,6 +24,7 @@ import path from 'node:path'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { createHtmlPlugin } from 'vite-plugin-html'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
@ -84,6 +85,21 @@ export default defineConfig(({ mode }) => {
},
},
},
plugins: [vue(), vueJsx()],
plugins: [
vue(),
vueJsx(),
createHtmlPlugin({
minify: true,
inject: {
data: {
title: env.JPOM_APP_TITLE,
base_url: env.JPOM_BASE_URL,
build: new Date().getTime(),
env: process.env.NODE_ENV,
buildVersion: process.env.npm_package_version,
},
},
}),
],
}
})