mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-03 20:38:52 +08:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
01bf83a538
@ -1,7 +1,77 @@
|
||||
# Vue 3 + Vite
|
||||
## 项目介绍
|
||||
|
||||
本项目采用 [Vue3](https://cn.vuejs.org/guide/introduction.html#what-is-vue) + [Vite](https://vitejs.dev/) + [TypeScript](https://www.typescriptlang.org/) + [Antdv](https://antdv.com/docs/vue/getting-started-cn) + [Pinia](https://pinia.vuejs.org/)构建。
|
||||
|
||||
项目采用Vue 3 `<script setup>` SFC写法,请查看[script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup)了解更多信息。
|
||||
|
||||
### 构建运行
|
||||
|
||||
```bash
|
||||
pnpm dev # 本地运行
|
||||
pnpm build # 打包
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 参与贡献
|
||||
|
||||
### 环境准备
|
||||
|
||||
#### Node.js 和 pnpm
|
||||
|
||||
开发需要 Node.js 16+ 和 `pnpm` v8。
|
||||
|
||||
推荐使用 [`nvm`](https://github.com/nvm-sh/nvm) 管理 Node.js,避免权限问题的同时,还能够随时切换当前使用的 Node.js 的版本。在 Windows 系统下的开发者可以使用 [`nvm-windows`](https://github.com/coreybutler/nvm-windows)。
|
||||
|
||||
推荐使用`pnpm`,节约内存。 在 `pnpm` 的[官网](https://pnpm.io/installation)选择一种方式安装即可。
|
||||
|
||||
#### 编辑器
|
||||
|
||||
这边我们推荐使用 VSCode, 我们我们尽量采用工具化方式来约束开发规范和编码风格, 使用VSCode即可应用现有配置和推荐你安装适合项目的插件。 具体配置看`/.vscode` 目录
|
||||
|
||||
⚠️:因为我们在升级vue3的过程中vue2版本也在不断迭代, 为了确保我们始终是在最新的代码基础上开发,编写某个页面前记得先从`web-vue` 目录中找到同名文件先替换下。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
.
|
||||
├── .vscode
|
||||
│ └── setting.json
|
||||
├── dist
|
||||
├── mock
|
||||
│ └── app.ts|tsx
|
||||
├── src
|
||||
│ ├── components # 公共组件
|
||||
│ ├── assets # 静态资源
|
||||
│ ├── interface # 类型定义
|
||||
│ ├── router # 路由配置
|
||||
│ ├── stores # 状态管理器
|
||||
│ │ └── index.ts
|
||||
│ ├── pages # 页面
|
||||
│ │ ├── login
|
||||
│ │ └── user
|
||||
│ ├── utils # 工具文件
|
||||
│ │ └── index.ts
|
||||
│ ├── api # 接口文件
|
||||
│ │ └── api.ts
|
||||
│ ├── app.vue
|
||||
│ ├── main.ts
|
||||
├── node_modules
|
||||
├── .env
|
||||
├── eslint.json
|
||||
├── vite.config.ts // vite配置
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── type.d.ts
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
3
web-vue3/src/d.ts/auto-import.d.ts
vendored
3
web-vue3/src/d.ts/auto-import.d.ts
vendored
@ -59,6 +59,7 @@ declare global {
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
@ -76,5 +77,5 @@ declare global {
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
}
|
||||
|
@ -1,52 +1,73 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<a-form ref="editForm" :model="temp" :rules="rules" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form
|
||||
ref="editForm"
|
||||
:model="temp"
|
||||
:rules="rules"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
>
|
||||
<a-form-item label="SMTP 服务器" prop="host">
|
||||
<a-auto-complete v-model="temp.host" placeholder="SMTP 服务器域名" option-label-prop="value">
|
||||
<template #dataSource>
|
||||
<a-select-opt-group v-for="group in hostDataSource" :key="group.title">
|
||||
<span #label>
|
||||
{{ group.title }}
|
||||
</span>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value">
|
||||
{{ opt.title }} {{ opt.value }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
<a-auto-complete
|
||||
v-model:value="temp.host"
|
||||
placeholder="SMTP 服务器域名"
|
||||
class="certain-category-search"
|
||||
option-label-prop="value"
|
||||
:options="hostDataSource"
|
||||
>
|
||||
<template #option="item">
|
||||
<template v-if="item.options">
|
||||
<span>
|
||||
{{ item.title }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
{{ item.title }} {{ item.value }}
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
<a-form-item label="SMTP 端口" prop="port">
|
||||
<a-auto-complete v-model="temp.port" placeholder="SMTP 服务器端口" option-label-prop="value">
|
||||
<template #dataSource>
|
||||
<a-select-opt-group v-for="group in portDataSource" :key="group.title">
|
||||
<span #label>
|
||||
{{ group.title }}
|
||||
<a-auto-complete
|
||||
v-model:value="temp.port"
|
||||
placeholder="SMTP 服务器端口"
|
||||
option-label-prop="value"
|
||||
:options="portDataSource"
|
||||
>
|
||||
<template #option="item">
|
||||
<template v-if="item.options">
|
||||
<span>
|
||||
{{ item.title }}
|
||||
</span>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.value">
|
||||
{{ opt.title }} {{ opt.value }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
{{ item.title }} {{ item.value }}
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-auto-complete>
|
||||
</a-form-item>
|
||||
<a-form-item label="用户名" prop="user">
|
||||
<a-input v-model="temp.user" type="text" placeholder="发件人名称" />
|
||||
<a-input v-model:value="temp.user" type="text" placeholder="发件人名称"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="密码" :prop="`${this.temp.type === 'add' ? 'pass' : 'pass-update'}`">
|
||||
<a-input-password v-model="temp.pass" type="text" placeholder="邮箱密码或者授权码" />
|
||||
<a-form-item :label="temp.type === 'add' ? '密码' : '密码更新'" prop="pass">
|
||||
<a-input-password v-model:value="temp.pass" type="password" placeholder="邮箱密码或者授权码"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="邮箱账号" prop="from">
|
||||
<!-- <a-input v-model="temp.from" type="text" placeholder="发送方邮箱账号" /> -->
|
||||
<a-tooltip>
|
||||
<template #title>
|
||||
支持配置发送方:遵循RFC-822标准 发件人可以是以下形式:
|
||||
支持配置发送方:遵循 RFC-822 标准 发件人可以是以下形式:
|
||||
<ul>
|
||||
<li>1. user@xxx.xx</li>
|
||||
<li>2. name <user@xxx.xx></li>
|
||||
</ul>
|
||||
</template>
|
||||
<a-auto-complete
|
||||
v-model="temp.from"
|
||||
v-model:value="temp.from"
|
||||
placeholder="发送方邮箱账号"
|
||||
option-label-prop="value"
|
||||
@search="handleFromSearch"
|
||||
@ -60,182 +81,156 @@
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
<a-form-item label="SSL 连接" prop="sslEnable">
|
||||
<a-switch v-model="temp.sslEnable" checked-children="启用" un-checked-children="停用" />
|
||||
<!-- <a-input v-show="temp.sslEnable" v-model="temp.socketFactoryPort" type="text" placeholder="SSL 端口" /> -->
|
||||
<a-switch v-model:checked="temp.sslEnable" checked-children="启用" un-checked-children="停用"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="超时时间" prop="timeout">
|
||||
<a-input-number
|
||||
style="width: 100%"
|
||||
:min="3"
|
||||
v-model="temp.timeout"
|
||||
type="text"
|
||||
placeholder="单位秒,默认 10 秒,最小 3 秒"
|
||||
/>
|
||||
<a-input-number style="width: 100%" :min="3" v-model:value="temp.timeout" type="number" placeholder="单位秒,默认 10 秒,最小 3 秒"/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
||||
<a-button type="primary" class="btn" :disabled="submitAble" @click="onSubmit">提交</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-alert
|
||||
message="阿里云企业邮箱配置"
|
||||
description="SMTP 地址:smtp.mxhichina.com,端口使用 465 并且开启 SSL,用户名需要和邮件发送人一致,密码为邮箱的登录密码"
|
||||
type="info"
|
||||
show-icon
|
||||
/>
|
||||
<br />
|
||||
<a-alert
|
||||
message="QQ 邮箱配置"
|
||||
type="info"
|
||||
description="SMTP 地址:【smtp.qq.com】,用户名一般是QQ号码,密码是邮箱授权码,端口默认 587/465"
|
||||
show-icon
|
||||
/>
|
||||
<br />
|
||||
<a-alert
|
||||
message="163 邮箱配置"
|
||||
description="SMTP 地址:【smtp.163.com, smtp.126.com...】,密码是邮箱授权码,端口默认 25,SSL 端口 465"
|
||||
type="info"
|
||||
show-icon
|
||||
/>
|
||||
<br />
|
||||
<a-alert message="Gmail 邮箱配置" description="待完善" type="info" show-icon />
|
||||
<a-alert message="阿里云企业邮箱配置" description="SMTP 地址:smtp.mxhichina.com,端口使用 465 并且开启 SSL,用户名需要和邮件发送人一致,密码为邮箱的登录密码" type="info" show-icon/>
|
||||
<br/>
|
||||
<a-alert message="QQ 邮箱配置" type="info" description="SMTP 地址:【smtp.qq.com】,用户名一般是 QQ 号码,密码是邮箱授权码,端口默认 587/465" show-icon/>
|
||||
<br/>
|
||||
<a-alert message="163 邮箱配置" description="SMTP 地址:【smtp.163.com, smtp.126.com...】,密码是邮箱授权码,端口默认 25,SSL 端口 465" type="info" show-icon/>
|
||||
<br/>
|
||||
<a-alert message="Gmail 邮箱配置" description="待完善" type="info" show-icon/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getMailConfigData, editMailConfig } from '@/api/system'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
temp: {},
|
||||
submitAble: false,
|
||||
rules: {
|
||||
host: [{ required: true, message: 'Please input SMTP host', trigger: 'blur' }],
|
||||
pass: [{ required: true, message: 'Please input password', trigger: 'blur' }],
|
||||
user: [{ required: true, message: 'Please input user name', trigger: 'blur' }],
|
||||
from: [{ required: true, message: 'Please input email account', trigger: 'blur' }]
|
||||
<script lang="ts" setup>
|
||||
import {ref, onMounted} from 'vue';
|
||||
import {getMailConfigData, editMailConfig} from "@/api/system";
|
||||
import {message} from "ant-design-vue";
|
||||
|
||||
const temp = ref({});
|
||||
const submitAble = ref(false);
|
||||
const fromResult = ref<string[]>([]);
|
||||
const hostDataSource = [
|
||||
{
|
||||
title: "参考数据",
|
||||
options: [
|
||||
{
|
||||
title: "QQ邮箱",
|
||||
value: "smtp.qq.com",
|
||||
},
|
||||
fromResult: [],
|
||||
hostDataSource: [
|
||||
{
|
||||
title: '参考数据',
|
||||
children: [
|
||||
{
|
||||
title: 'QQ邮箱',
|
||||
value: 'smtp.qq.com'
|
||||
},
|
||||
{
|
||||
title: '163邮箱',
|
||||
value: 'smtp.163.com'
|
||||
},
|
||||
{
|
||||
title: '126邮箱',
|
||||
value: 'smtp.126.com'
|
||||
},
|
||||
{
|
||||
title: '阿里云企业邮箱',
|
||||
value: 'smtp.mxhichina.com'
|
||||
},
|
||||
{
|
||||
title: 'gmail邮箱',
|
||||
value: 'smtp.gmail.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
portDataSource: [
|
||||
{
|
||||
title: 'QQ邮箱',
|
||||
children: [
|
||||
{
|
||||
title: 'QQ邮箱',
|
||||
value: '587'
|
||||
},
|
||||
{
|
||||
title: 'QQ邮箱 SSL',
|
||||
value: '465'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '163邮箱',
|
||||
children: [
|
||||
{
|
||||
title: '163邮箱',
|
||||
value: '25'
|
||||
},
|
||||
{
|
||||
title: '163邮箱 SSL',
|
||||
value: '465'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '阿里云企业邮箱',
|
||||
children: [
|
||||
{
|
||||
title: '阿里云企业邮箱 SSL',
|
||||
value: '465'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '通用邮箱',
|
||||
children: [
|
||||
{
|
||||
title: '通用邮箱 SSL',
|
||||
value: '465'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
title: "163邮箱",
|
||||
value: "smtp.163.com",
|
||||
},
|
||||
{
|
||||
title: "126邮箱",
|
||||
value: "smtp.126.com",
|
||||
},
|
||||
{
|
||||
title: "阿里云企业邮箱",
|
||||
value: "smtp.mxhichina.com",
|
||||
},
|
||||
{
|
||||
title: "gmail邮箱",
|
||||
value: "smtp.gmail.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
mounted() {
|
||||
this.loadData()
|
||||
];
|
||||
const portDataSource = ref([
|
||||
{
|
||||
title: "QQ邮箱",
|
||||
options: [
|
||||
{
|
||||
title: "QQ邮箱",
|
||||
value: "587",
|
||||
},
|
||||
{
|
||||
title: "QQ邮箱 SSL",
|
||||
value: "465",
|
||||
},
|
||||
],
|
||||
},
|
||||
methods: {
|
||||
// load data
|
||||
loadData() {
|
||||
getMailConfigData().then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.temp = res.data || { type: 'add' }
|
||||
if (this.temp.port) {
|
||||
this.temp.port = this.temp.port + ''
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// submit
|
||||
onSubmit() {
|
||||
// disabled submit button
|
||||
this.submitAble = true
|
||||
// if (this.temp.sslsslEnable === false) {
|
||||
// this.temp.socketFactoryPort = "";
|
||||
// }
|
||||
editMailConfig(this.temp).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
}
|
||||
// button recover
|
||||
this.submitAble = false
|
||||
})
|
||||
},
|
||||
handleFromSearch(value) {
|
||||
let result
|
||||
if (!value || value.indexOf('@') >= 0) {
|
||||
result = []
|
||||
} else {
|
||||
result = ['gmail.com', '163.com', 'qq.com'].map((domain) => `${value}@${domain}`)
|
||||
{
|
||||
title: "163邮箱",
|
||||
options: [
|
||||
{
|
||||
title: "163邮箱",
|
||||
value: "25",
|
||||
},
|
||||
{
|
||||
title: "163邮箱 SSL",
|
||||
value: "465",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "阿里云企业邮箱",
|
||||
options: [
|
||||
{
|
||||
title: "阿里云企业邮箱 SSL",
|
||||
value: "465",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "通用邮箱",
|
||||
options: [
|
||||
{
|
||||
title: "通用邮箱 SSL",
|
||||
value: "465",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const rules = {
|
||||
host: [{required: true, message: "Please input SMTP host", trigger: "blur"}],
|
||||
pass: [{required: true, message: "Please input password", trigger: "blur"}],
|
||||
user: [{required: true, message: "Please input user name", trigger: "blur"}],
|
||||
from: [{required: true, message: "Please input email account", trigger: "blur"}],
|
||||
};
|
||||
|
||||
// load data
|
||||
const loadData = () => {
|
||||
getMailConfigData().then((res) => {
|
||||
if (res.code === 200) {
|
||||
temp.value = res.data || {type: "add"};
|
||||
if (temp.value.port) {
|
||||
temp.value.port = temp.value.port + "";
|
||||
}
|
||||
this.fromResult = result
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// submit
|
||||
const onSubmit = () => {
|
||||
// disabled submit button
|
||||
submitAble.value = true;
|
||||
editMailConfig(temp.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
message.success(res.msg);
|
||||
}
|
||||
// button recover
|
||||
submitAble.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const handleFromSearch = (value: string) => {
|
||||
let result;
|
||||
if (!value || value.indexOf("@") >= 0) {
|
||||
result = [];
|
||||
} else {
|
||||
result = ["gmail.com", "163.com", "qq.com"].map((domain) => `${value}@${domain}`);
|
||||
}
|
||||
}
|
||||
fromResult.value = result;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn {
|
||||
margin-left: 20px;
|
||||
|
@ -1,30 +1,34 @@
|
||||
<template>
|
||||
<div class="full-content">
|
||||
<a-alert message="账号如果开启 MFA(两步验证),使用 Oauth2 登录不会验证 MFA(两步验证)" type="info" show-icon />
|
||||
<a-alert
|
||||
message="账号如果开启 MFA(两步验证),使用 Oauth2 登录不会验证 MFA(两步验证)"
|
||||
type="info"
|
||||
show-icon
|
||||
/>
|
||||
<a-tabs>
|
||||
<a-tab-pane key="gitee" tab="Gitee Oauth2">
|
||||
<a-form ref="editForm" :model="gitee" :rules="rules" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-item label="是否开启" prop="enabled">
|
||||
<a-switch v-model="gitee.enabled" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch v-model:checked="gitee.enabled" checked-children="启用" un-checked-children="停用"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="客户端ID" prop="clientId">
|
||||
<a-input v-model="gitee.clientId" type="text" placeholder="请输入客户端ID [clientId]" />
|
||||
<a-input v-model:value="gitee.clientId" type="text" placeholder="请输入客户端ID [clientId]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="客户端密钥" prop="clientSecret">
|
||||
<a-input-password v-model="gitee.clientSecret" placeholder="请输入客户端密钥 [clientSecret]" />
|
||||
<a-input-password v-model:value="gitee.clientSecret" placeholder="请输入客户端密钥 [clientSecret]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="回调 url" prop="redirectUri">
|
||||
<template #help>参考地址:{{ `${this.host}/oauth2-gitee` }}</template>
|
||||
<a-input v-model="gitee.redirectUri" type="text" placeholder="请输入回调重定向 url [redirectUri]" />
|
||||
<template #help>参考地址:{{ `${host}/oauth2-gitee` }}</template>
|
||||
<a-input v-model:value="gitee.redirectUri" type="text" placeholder="请输入回调重定向 url [redirectUri]" />
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label="登录url">
|
||||
<a-input :value="`${this.host}/oauth2-render-gitee`" type="text" />
|
||||
<a-input :value="`${host}/oauth2-render-gitee`" type="text" />
|
||||
</a-form-item> -->
|
||||
<a-form-item label="自动创建用户" prop="autoCreteUser">
|
||||
<a-switch v-model="gitee.autoCreteUser" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch v-model:checked="gitee.autoCreteUser" checked-children="启用" un-checked-children="停用" />
|
||||
</a-form-item>
|
||||
<a-form-item label="忽略校验 state" prop="ignoreCheckState">
|
||||
<a-switch v-model="gitee.ignoreCheckState" checked-children="忽略" un-checked-children="校验" />
|
||||
<a-switch v-model:checked="gitee.ignoreCheckState" checked-children="忽略" un-checked-children="校验" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
||||
@ -35,35 +39,35 @@
|
||||
<a-tab-pane key="maxkey" tab="MaxKey Oauth2">
|
||||
<a-form ref="editForm" :model="maxkey" :rules="rules" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-item label="是否开启" prop="enabled">
|
||||
<a-switch v-model="maxkey.enabled" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch v-model:checked="maxkey.enabled" checked-children="启用" un-checked-children="停用" />
|
||||
</a-form-item>
|
||||
<a-form-item label="客户端ID" prop="clientId">
|
||||
<a-input v-model="maxkey.clientId" type="text" placeholder="请输入客户端ID [clientId]" />
|
||||
<a-input v-model:value="maxkey.clientId" type="text" placeholder="请输入客户端ID [clientId]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="客户端密钥" prop="clientSecret">
|
||||
<a-input-password v-model="maxkey.clientSecret" placeholder="请输入客户端密钥 [clientSecret]" />
|
||||
<a-input-password v-model:value="maxkey.clientSecret" placeholder="请输入客户端密钥 [clientSecret]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="授权 url" prop="authorizationUri">
|
||||
<a-input v-model="maxkey.authorizationUri" type="text" placeholder="请输入授权 url [authorizationUri]" />
|
||||
<a-input v-model:value="maxkey.authorizationUri" type="text" placeholder="请输入授权 url [authorizationUri]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="令牌 url" prop="accessTokenUri">
|
||||
<a-input v-model="maxkey.accessTokenUri" type="text" placeholder="请输入令牌 url [accessTokenUri]" />
|
||||
<a-input v-model:value="maxkey.accessTokenUri" type="text" placeholder="请输入令牌 url [accessTokenUri]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="用户信息 url" prop="userInfoUri">
|
||||
<a-input v-model="maxkey.userInfoUri" type="text" placeholder="请输入用户信息 url [userInfoUri]" />
|
||||
<a-input v-model:value="maxkey.userInfoUri" type="text" placeholder="请输入用户信息 url [userInfoUri]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="回调 url" prop="redirectUri">
|
||||
<template #help>参考地址:{{ `${this.host}/oauth2-maxkey` }}</template>
|
||||
<a-input v-model="maxkey.redirectUri" type="text" placeholder="请输入回调重定向 url [redirectUri]" />
|
||||
<template slot="help">参考地址:{{ `${this.host}/oauth2-maxkey` }}</template>
|
||||
<a-input v-model:value="maxkey.redirectUri" type="text" placeholder="请输入回调重定向 url [redirectUri]" />
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label="登录url">
|
||||
<a-input :value="`${this.host}/oauth2-render-maxkey`" type="text" />
|
||||
</a-form-item> -->
|
||||
<a-form-item label="自动创建用户" prop="autoCreteUser">
|
||||
<a-switch v-model="maxkey.autoCreteUser" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch v-model:checked="maxkey.autoCreteUser" checked-children="启用" un-checked-children="停用" />
|
||||
</a-form-item>
|
||||
<a-form-item label="忽略校验 state" prop="ignoreCheckState">
|
||||
<a-switch v-model="maxkey.ignoreCheckState" checked-children="忽略" un-checked-children="校验" />
|
||||
<a-switch v-model:checked="maxkey.ignoreCheckState" checked-children="忽略" un-checked-children="校验" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
||||
@ -74,27 +78,27 @@
|
||||
<a-tab-pane key="github" tab="Github Oauth2">
|
||||
<a-form ref="editForm" :model="github" :rules="rules" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
|
||||
<a-form-item label="是否开启" prop="enabled">
|
||||
<a-switch v-model="github.enabled" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch v-model:checked="github.enabled" checked-children="启用" un-checked-children="停用" />
|
||||
</a-form-item>
|
||||
<a-form-item label="客户端ID" prop="clientId">
|
||||
<a-input v-model="github.clientId" type="text" placeholder="请输入客户端ID [clientId]" />
|
||||
<a-input v-model:value="github.clientId" type="text" placeholder="请输入客户端ID [clientId]" />
|
||||
</a-form-item>
|
||||
<a-form-item label="客户端密钥" prop="clientSecret">
|
||||
<a-input-password v-model="github.clientSecret" placeholder="请输入客户端密钥 [clientSecret]" />
|
||||
<a-input-password v-model:value="github.clientSecret" placeholder="请输入客户端密钥 [clientSecret]" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="回调 url" prop="redirectUri">
|
||||
<template #help>参考地址:{{ `${this.host}/oauth2-github` }}</template>
|
||||
<a-input v-model="github.redirectUri" type="text" placeholder="请输入回调重定向 url [redirectUri]" />
|
||||
<template slot="help">参考地址:{{ `${this.host}/oauth2-github` }}</template>
|
||||
<a-input v-model:value="github.redirectUri" type="text" placeholder="请输入回调重定向 url [redirectUri]" />
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label="登录url">
|
||||
<a-input :value="`${this.host}/oauth2-render-github`" type="text" />
|
||||
</a-form-item> -->
|
||||
<a-form-item label="自动创建用户" prop="autoCreteUser">
|
||||
<a-switch v-model="github.autoCreteUser" checked-children="启用" un-checked-children="停用" />
|
||||
<a-switch v-model:checked="github.autoCreteUser" checked-children="启用" un-checked-children="停用" />
|
||||
</a-form-item>
|
||||
<a-form-item label="忽略校验 state" prop="ignoreCheckState">
|
||||
<a-switch v-model="github.ignoreCheckState" checked-children="忽略" un-checked-children="校验" />
|
||||
<a-switch v-model:checked="github.ignoreCheckState" checked-children="忽略" un-checked-children="校验" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
||||
@ -105,49 +109,41 @@
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oauthConfigOauth2, oauthConfigOauth2Save } from '@/api/system'
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { oauthConfigOauth2, oauthConfigOauth2Save } from '@/api/system';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
maxkey: {},
|
||||
gitee: {},
|
||||
github: {},
|
||||
rules: {},
|
||||
provides: ['gitee', 'maxkey', 'github'],
|
||||
host: ''
|
||||
const gitee = ref({});
|
||||
const maxkey = ref({});
|
||||
const github = ref({});
|
||||
const provides = ref(['gitee', 'maxkey', 'github']);
|
||||
let host = '';
|
||||
|
||||
const loadData = () => {
|
||||
host = `${location.protocol}//${location.host}`;
|
||||
provides.value.forEach((item) => {
|
||||
oauthConfigOauth2({ provide: item }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (item === 'gitee') gitee.value = Object.assign(res.data || {}, { provide: item });
|
||||
else if (item === 'maxkey') maxkey.value = Object.assign(res.data || {}, { provide: item });
|
||||
else if (item === 'github') github.value = Object.assign(res.data || {}, { provide: item });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = (key) => {
|
||||
oauthConfigOauth2Save(key === 'gitee' ? gitee.value : key === 'maxkey' ? maxkey.value : github.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
message.success( res.msg);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.host = `${location.protocol}//${location.host}`
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
// load data
|
||||
loadData() {
|
||||
this.provides.forEach((item) => {
|
||||
oauthConfigOauth2({
|
||||
provide: item
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
this[item] = Object.assign(res.data || {}, { provide: item })
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
// submit
|
||||
onSubmit(key) {
|
||||
oauthConfigOauth2Save(this[key]).then((res) => {
|
||||
if (res.code === 200) {
|
||||
// 成功
|
||||
$notification.success({
|
||||
message: res.msg
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,11 +28,11 @@
|
||||
<a-tooltip>{{ text }}</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'description'">
|
||||
<template v-else-if="column.dataIndex === 'description'">
|
||||
<a-tooltip>{{ text }}</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<template v-else-if="column.dataIndex === 'operation'">
|
||||
<a-space>
|
||||
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button size="small" type="primary" @click="configMeun(record)">菜单</a-button>
|
||||
|
@ -32,12 +32,15 @@ const routeMenuMap: Record<string, string> = {
|
||||
permission_group: '/user/permission-group',
|
||||
user_log: '/operation/log',
|
||||
user_login_log: '/user/login-log',
|
||||
monitorConfigEmail: '/system/mail',
|
||||
cacheManage: '/system/cache',
|
||||
logManage: '/system/log',
|
||||
update: '/system/upgrade',
|
||||
// 配置管理
|
||||
sysConfig: '/system/config',
|
||||
authConfig: '/system/oauth-config',
|
||||
monitorConfigEmail: '/system/mail',
|
||||
systemExtConfig: '/system/ext-config',
|
||||
|
||||
projectSearch: '/node/search',
|
||||
// 数据库备份
|
||||
backup: '/system/backup',
|
||||
|
@ -1,29 +1,27 @@
|
||||
import { GlobalWindow } from '@/interface/common'
|
||||
|
||||
// 常量池
|
||||
export const USER_NAME_KEY = 'Jpom-UserName'
|
||||
export const USER_NAME_KEY = "Jpom-UserName";
|
||||
|
||||
export const TOKEN_KEY = 'Jpom-Token'
|
||||
export const TOKEN_KEY = "Jpom-Token";
|
||||
|
||||
export const LONG_TERM_TOKEN = 'Jpom-Long-Term-Token'
|
||||
export const LONG_TERM_TOKEN = "Jpom-Long-Term-Token";
|
||||
|
||||
export const USER_INFO_KEY = 'Jpom-User'
|
||||
export const USER_INFO_KEY = "Jpom-User";
|
||||
|
||||
export const MENU_KEY = 'Jpom-Menus'
|
||||
export const MENU_KEY = "Jpom-Menus";
|
||||
|
||||
export const TOKEN_HEADER_KEY = 'Authorization'
|
||||
export const TOKEN_HEADER_KEY = "Authorization";
|
||||
|
||||
export const ACTIVE_TAB_KEY = 'Jpom-ActiveTab'
|
||||
export const ACTIVE_TAB_KEY = "Jpom-ActiveTab";
|
||||
|
||||
export const TAB_LIST_KEY = 'Jpom-TabList'
|
||||
export const TAB_LIST_KEY = "Jpom-TabList";
|
||||
|
||||
export const ACTIVE_MENU_KEY = 'Jpom-ActiveMenu'
|
||||
export const ACTIVE_MENU_KEY = "Jpom-ActiveMenu";
|
||||
|
||||
export const MANAGEMENT_ACTIVE_TAB_KEY = 'Jpom-management-ActiveTab'
|
||||
export const MANAGEMENT_ACTIVE_TAB_KEY = "Jpom-management-ActiveTab";
|
||||
|
||||
export const MANAGEMENT_TAB_LIST_KEY = 'Jpom-management-TabList'
|
||||
export const MANAGEMENT_TAB_LIST_KEY = "Jpom-management-TabList";
|
||||
|
||||
export const MANAGEMENT_ACTIVE_MENU_KEY = 'Jpom-management-ActiveMenu'
|
||||
export const MANAGEMENT_ACTIVE_MENU_KEY = "Jpom-management-ActiveMenu";
|
||||
|
||||
// export const GUIDE_FLAG_KEY = "Jpom-GuideFlag";
|
||||
|
||||
@ -31,37 +29,33 @@ export const MANAGEMENT_ACTIVE_MENU_KEY = 'Jpom-management-ActiveMenu'
|
||||
|
||||
// export const GUIDE_NODE_USED_KEY = "Jpom-Node-Guide-Used";
|
||||
|
||||
export const NO_NOTIFY_KEY = 'tip'
|
||||
export const NO_NOTIFY_KEY = "tip";
|
||||
|
||||
export const NO_LOADING_KEY = 'loading'
|
||||
export const NO_LOADING_KEY = "loading";
|
||||
|
||||
export const LOADING_TIP = 'loadingTip'
|
||||
export const LOADING_TIP = "loadingTip";
|
||||
|
||||
const cachePageLimitKeyName = 'page_limit'
|
||||
const cachePageLimitKeyName = "page_limit";
|
||||
|
||||
export function getCachePageLimit(): number {
|
||||
return Number(localStorage.getItem(cachePageLimitKeyName) || 10)
|
||||
export function getCachePageLimit() {
|
||||
return parseInt(localStorage.getItem(cachePageLimitKeyName) || 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页选择条
|
||||
*/
|
||||
export const PAGE_DEFAULT_SIZW_OPTIONS: string[] = ['5', '10', '15', '20', '25', '30', '35', '40', '50']
|
||||
export const PAGE_DEFAULT_SIZW_OPTIONS = ["5", "10", "15", "20", "25", "30", "35", "40", "50"];
|
||||
|
||||
/**
|
||||
* 展示总条数计算方法
|
||||
* @param {Number} total 总记录数
|
||||
* @returns String
|
||||
*/
|
||||
export function PAGE_DEFAULT_SHOW_TOTAL(total: number) {
|
||||
return `总计 ${total} 条`
|
||||
export function PAGE_DEFAULT_SHOW_TOTAL(total) {
|
||||
return `总计 ${total} 条`;
|
||||
}
|
||||
|
||||
export const PAGE_DEFAULT_LIST_QUERY = {
|
||||
page: 1,
|
||||
limit: isNaN(getCachePageLimit()) ? 10 : getCachePageLimit(),
|
||||
total: 0
|
||||
}
|
||||
export const PAGE_DEFAULT_LIST_QUERY = { page: 1, limit: isNaN(getCachePageLimit) ? 10 : getCachePageLimit, total: 0 };
|
||||
|
||||
/**
|
||||
* 计算分页数据
|
||||
@ -69,10 +63,10 @@ export const PAGE_DEFAULT_LIST_QUERY = {
|
||||
* @param {Array} pageSizeOptions 分页选择条选项
|
||||
* @returns
|
||||
*/
|
||||
export function COMPUTED_PAGINATION(queryParam: any, pageSizeOptions?: []) {
|
||||
export function COMPUTED_PAGINATION(queryParam, pageSizeOptions) {
|
||||
// console.log(queryParam);
|
||||
const limit = queryParam.limit || PAGE_DEFAULT_LIST_QUERY.limit
|
||||
const total = queryParam.total || 0
|
||||
const limit = queryParam.limit || PAGE_DEFAULT_LIST_QUERY.limit;
|
||||
const total = queryParam.total || 0;
|
||||
return {
|
||||
total: total,
|
||||
current: queryParam.page || 1,
|
||||
@ -83,10 +77,10 @@ export function COMPUTED_PAGINATION(queryParam: any, pageSizeOptions?: []) {
|
||||
showLessItems: true,
|
||||
// 只有在分页条数在 小于 2 的时候隐藏,避免设置太大无法切回
|
||||
hideOnSinglePage: limit <= 20,
|
||||
showTotal: (total: number) => {
|
||||
return PAGE_DEFAULT_SHOW_TOTAL(total)
|
||||
}
|
||||
}
|
||||
showTotal: (total) => {
|
||||
return PAGE_DEFAULT_SHOW_TOTAL(total);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,29 +89,29 @@ export function COMPUTED_PAGINATION(queryParam: any, pageSizeOptions?: []) {
|
||||
* @param {JSON} param1
|
||||
* @returns
|
||||
*/
|
||||
export function CHANGE_PAGE(listQuery: any, { pagination, sorter }: any) {
|
||||
export function CHANGE_PAGE(listQuery, { pagination, sorter }) {
|
||||
if (pagination && Object.keys(pagination).length) {
|
||||
listQuery = { ...listQuery, page: pagination.current, limit: pagination.pageSize }
|
||||
listQuery = { ...listQuery, page: pagination.current, limit: pagination.pageSize };
|
||||
//
|
||||
localStorage.setItem(cachePageLimitKeyName, pagination.pageSize)
|
||||
localStorage.setItem(cachePageLimitKeyName, pagination.pageSize);
|
||||
//
|
||||
PAGE_DEFAULT_LIST_QUERY.limit = pagination.pageSize
|
||||
PAGE_DEFAULT_LIST_QUERY.limit = pagination.pageSize;
|
||||
}
|
||||
if (sorter && Object.keys(sorter).length) {
|
||||
listQuery = { ...listQuery, order: sorter.order, order_field: sorter.field }
|
||||
listQuery = { ...listQuery, order: sorter.order, order_field: sorter.field };
|
||||
}
|
||||
return listQuery
|
||||
return listQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存当前的工作空间 ID
|
||||
*/
|
||||
export const CACHE_WORKSPACE_ID = 'workspaceId'
|
||||
export const CACHE_WORKSPACE_ID = "workspaceId";
|
||||
|
||||
/**
|
||||
* 升级 重启检查等待次数
|
||||
*/
|
||||
export const RESTART_UPGRADE_WAIT_TIME_COUNT = 80
|
||||
export const RESTART_UPGRADE_WAIT_TIME_COUNT = 80;
|
||||
|
||||
/**
|
||||
* 定时 cron 默认提示
|
||||
@ -126,131 +120,144 @@ export const RESTART_UPGRADE_WAIT_TIME_COUNT = 80
|
||||
*/
|
||||
export const CRON_DATA_SOURCE = [
|
||||
{
|
||||
title: '取消定时,不再定时执行(支持 ! 前缀禁用定时执行,如:!0 0/1 * * * ?)',
|
||||
title: "取消定时,不再定时执行(支持 ! 前缀禁用定时执行,如:!0 0/1 * * * ?)",
|
||||
children: [
|
||||
{
|
||||
title: '',
|
||||
value: ''
|
||||
}
|
||||
]
|
||||
title: "",
|
||||
value: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '分钟级别',
|
||||
title: "分钟级别",
|
||||
children: [
|
||||
{
|
||||
title: '1分钟',
|
||||
value: '0 0/1 * * * ?'
|
||||
title: "1分钟",
|
||||
value: "0 0/1 * * * ?",
|
||||
},
|
||||
{
|
||||
title: '5分钟',
|
||||
value: '0 0/5 * * * ?'
|
||||
title: "5分钟",
|
||||
value: "0 0/5 * * * ?",
|
||||
},
|
||||
{
|
||||
title: '10分钟',
|
||||
value: '0 0/10 * * * ?'
|
||||
title: "10分钟",
|
||||
value: "0 0/10 * * * ?",
|
||||
},
|
||||
{
|
||||
title: '30分钟',
|
||||
value: '0 0/30 * * * ?'
|
||||
}
|
||||
]
|
||||
title: "30分钟",
|
||||
value: "0 0/30 * * * ?",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '小时级别',
|
||||
title: "小时级别",
|
||||
children: [
|
||||
{
|
||||
title: '每小时',
|
||||
value: '0 0 0/1 * * ?'
|
||||
}
|
||||
]
|
||||
title: "每小时",
|
||||
value: "0 0 0/1 * * ?",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '天级别',
|
||||
title: "天级别",
|
||||
children: [
|
||||
{
|
||||
title: '凌晨0点和中午12点',
|
||||
value: '0 0 0,12 * * ?'
|
||||
title: "凌晨0点和中午12点",
|
||||
value: "0 0 0,12 * * ?",
|
||||
},
|
||||
{
|
||||
title: '凌晨0点',
|
||||
value: '0 0 0 * * ?'
|
||||
}
|
||||
]
|
||||
title: "凌晨0点",
|
||||
value: "0 0 0 * * ?",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '秒级别(默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])',
|
||||
title: "秒级别(默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])",
|
||||
children: [
|
||||
{
|
||||
title: '5秒一次',
|
||||
value: '0/5 * * * * ?'
|
||||
title: "5秒一次",
|
||||
value: "0/5 * * * * ?",
|
||||
},
|
||||
{
|
||||
title: '10秒一次',
|
||||
value: '0/10 * * * * ?'
|
||||
title: "10秒一次",
|
||||
value: "0/10 * * * * ?",
|
||||
},
|
||||
{
|
||||
title: '30秒一次',
|
||||
value: '0/30 * * * * ?'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
title: "30秒一次",
|
||||
value: "0/30 * * * * ?",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 压缩文件格式
|
||||
*/
|
||||
export const ZIP_ACCEPT: string = '.tar,.bz2,.gz,.zip,.tar.bz2,.tar.gz'
|
||||
export const ZIP_ACCEPT = ".tar,.bz2,.gz,.zip,.tar.bz2,.tar.gz";
|
||||
|
||||
/**
|
||||
* mfa app 应用举例
|
||||
*/
|
||||
export const MFA_APP_TIP_ARRAY: string[] = [
|
||||
'<strong>【推荐】微信小程序搜索 数盾OTP',
|
||||
export const MFA_APP_TIP_ARRAY = [
|
||||
"<strong>【推荐】微信小程序搜索 数盾OTP",
|
||||
'<strong>【推荐】腾讯身份验证码</strong> 简单好用 <a href="https://a.app.qq.com/o/simple.jsp?pkgname=com.tencent.authenticator">Android</a>',
|
||||
'<strong>Authy</strong> 功能丰富 专为两步验证码 <a href="https://authy.com/download/">iOS/Android/Windows/Mac/Linux</a> <a href="https://chrome.google.com/webstore/detail/authy/gaedmjdfmmahhbjefcbgaolhhanlaolb?hl=cn">Chrome 扩展</a>',
|
||||
'<strong>Google Authenticator</strong> 简单易用,但不支持密钥导出备份 <a href="https://apps.apple.com/us/app/google-authenticator/id388497605">iOS</a> <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=cn">Android</a>',
|
||||
'<strong>Microsoft Authenticator</strong> 使用微软全家桶的推荐 <a href="https://www.microsoft.com/zh-cn/account/authenticator">iOS/Android</a>',
|
||||
'<strong>1Password</strong> 强大安全的密码管理付费应用<a href="https://1password.com/zh-cn/downloads/">iOS/Android/Windows/Mac/Linux/ChromeOS</a>'
|
||||
]
|
||||
'<strong>1Password</strong> 强大安全的密码管理付费应用<a href="https://1password.com/zh-cn/downloads/">iOS/Android/Windows/Mac/Linux/ChromeOS</a>',
|
||||
];
|
||||
|
||||
/**
|
||||
* 项目 DSL 示例
|
||||
*/
|
||||
export const PROJECT_DSL_DEFATUL =
|
||||
'# scriptId 可以是项目路径下脚本文件名或者系统中的脚本模版ID\r\n' +
|
||||
'description: 测试\r\n' +
|
||||
'run:\r\n' +
|
||||
' start:\r\n' +
|
||||
'# scriptId: project.sh\r\n' +
|
||||
' scriptId: \r\n' +
|
||||
' scriptArgs: start\r\n' +
|
||||
' scriptEnv:\r\n' +
|
||||
"# scriptId 可以是项目路径下脚本文件名或者系统中的脚本模版ID\r\n" +
|
||||
"description: 测试\r\n" +
|
||||
"run:\r\n" +
|
||||
" start:\r\n" +
|
||||
"# scriptId: project.sh\r\n" +
|
||||
" scriptId: \r\n" +
|
||||
" scriptArgs: start\r\n" +
|
||||
" scriptEnv:\r\n" +
|
||||
' "boot_active": test\r\n' +
|
||||
' status:\r\n' +
|
||||
'# scriptId: project.sh\r\n' +
|
||||
' scriptId: \r\n' +
|
||||
' scriptArgs: status\r\n' +
|
||||
' stop:\r\n' +
|
||||
'# scriptId: project.sh\r\n' +
|
||||
' scriptId: \r\n' +
|
||||
' scriptArgs: stop\r\n' +
|
||||
'# restart:\r\n' +
|
||||
'## scriptId: project.sh\r\n' +
|
||||
'# scriptId: \r\n' +
|
||||
'# scriptArgs: restart\r\n' +
|
||||
'# scriptEnv:\r\n' +
|
||||
" status:\r\n" +
|
||||
"# scriptId: project.sh\r\n" +
|
||||
" scriptId: \r\n" +
|
||||
" scriptArgs: status\r\n" +
|
||||
" stop:\r\n" +
|
||||
"# scriptId: project.sh\r\n" +
|
||||
" scriptId: \r\n" +
|
||||
" scriptArgs: stop\r\n" +
|
||||
"# restart:\r\n" +
|
||||
"## scriptId: project.sh\r\n" +
|
||||
"# scriptId: \r\n" +
|
||||
"# scriptArgs: restart\r\n" +
|
||||
"# scriptEnv:\r\n" +
|
||||
'# "boot_active": test\r\n' +
|
||||
'file:\r\n' +
|
||||
'# 备份文件保留个数\r\n' +
|
||||
'# backupCount: 5\r\n' +
|
||||
'# 限制备份指定文件后缀(支持正则)\r\n' +
|
||||
"file:\r\n" +
|
||||
"# 备份文件保留个数\r\n" +
|
||||
"# backupCount: 5\r\n" +
|
||||
"# 限制备份指定文件后缀(支持正则)\r\n" +
|
||||
"# backupSuffix: [ '.jar','.html','^.+\\.(?i)(txt)$' ]\r\n" +
|
||||
'# 项目文件备份路径\r\n' +
|
||||
'# backupPath: /data/jpom_backup\r\n' +
|
||||
'config:\r\n' +
|
||||
'# 是否开启日志备份功能\r\n' +
|
||||
'# autoBackToFile: true\r\n' +
|
||||
'\r\n'
|
||||
"# 项目文件备份路径\r\n" +
|
||||
"# backupPath: /data/jpom_backup\r\n" +
|
||||
"config:\r\n" +
|
||||
"# 是否开启日志备份功能\r\n" +
|
||||
"# autoBackToFile: true\r\n" +
|
||||
"\r\n";
|
||||
|
||||
/**
|
||||
* 获取 socket 地址
|
||||
* @param {String} url 二级地址
|
||||
* @param {String} parameter 参数
|
||||
* @returns
|
||||
*/
|
||||
export function getWebSocketUrl(url, parameter) {
|
||||
const protocol = location.protocol === "https:" ? "wss://" : "ws://";
|
||||
const domain = window.routerBase;
|
||||
const fullUrl = (domain + url).replace(new RegExp("//", "gm"), "/");
|
||||
return `${protocol}${location.host}${fullUrl}?${parameter}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 并发执行
|
||||
@ -259,55 +266,54 @@ export const PROJECT_DSL_DEFATUL =
|
||||
* @params asyncHandle {Function} - 对`list`的每一个项的处理函数,参数为当前处理项,必须 return 一个Promise来确定是否继续进行迭代
|
||||
* @return {Promise} - 返回一个 Promise 值来确认所有数据是否迭代完成
|
||||
*/
|
||||
export function concurrentExecution<T>(list: T[], limit: number, asyncHandle: (arg: T) => Promise<any>) {
|
||||
export function concurrentExecution(list, limit, asyncHandle) {
|
||||
// 递归执行
|
||||
const recursion = (arr: T[]): Promise<T> => {
|
||||
const recursion = (arr) => {
|
||||
// 执行方法 arr.shift() 取出并移除第一个数据
|
||||
return asyncHandle(arr.shift() as T).then((res: T) => {
|
||||
return asyncHandle(arr.shift()).then((res) => {
|
||||
// 数组还未迭代完,递归继续进行迭代
|
||||
if (arr.length !== 0) {
|
||||
return recursion(arr)
|
||||
return recursion(arr);
|
||||
} else {
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
// 创建新的并发数组
|
||||
const listCopy = ([] as T[]).concat(list)
|
||||
let listCopy = [].concat(list);
|
||||
// 正在进行的所有并发异步操作
|
||||
let asyncList = [] as Promise<T>[]
|
||||
limit = limit > listCopy.length ? listCopy.length : limit
|
||||
let asyncList = [];
|
||||
limit = limit > listCopy.length ? listCopy.length : limit;
|
||||
|
||||
while (limit--) {
|
||||
asyncList.push(recursion(listCopy))
|
||||
asyncList.push(recursion(listCopy));
|
||||
}
|
||||
// 所有并发异步操作都完成后,本次并发控制迭代完成
|
||||
return Promise.all(asyncList)
|
||||
return Promise.all(asyncList);
|
||||
}
|
||||
|
||||
export function readJsonStrField(json: string, key: string) {
|
||||
export function readJsonStrField(json, key) {
|
||||
try {
|
||||
const data = JSON.parse(json)[key] || ''
|
||||
if (Object.prototype.toString.call(data) === '[object Object]') {
|
||||
return JSON.stringify(data)
|
||||
const data = JSON.parse(json)[key] || "";
|
||||
if (Object.prototype.toString.call(data) === "[object Object]") {
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
return data
|
||||
return data;
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
|
||||
export function randomStr(len = 2) {
|
||||
const $chars = 'ABCDEFGHJKMNPQRSTWXYZ0123456789'
|
||||
const $chars = "ABCDEFGHJKMNPQRSTWXYZ0123456789";
|
||||
/****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
||||
const maxPos = $chars.length
|
||||
let repliccaId = ''
|
||||
const maxPos = $chars.length;
|
||||
let repliccaId = "";
|
||||
for (let i = 0; i < len; i++) {
|
||||
repliccaId += $chars.charAt(Math.floor(Math.random() * maxPos))
|
||||
repliccaId += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return repliccaId
|
||||
return repliccaId;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,51 +321,46 @@ export function randomStr(len = 2) {
|
||||
* @param {*} time
|
||||
* @param {*} cFormat
|
||||
*/
|
||||
export function parseTime(time: string | number | Date | null, cFormat?: string | undefined | null) {
|
||||
export function parseTime(time, cFormat) {
|
||||
if (arguments.length === 0) {
|
||||
return '-'
|
||||
return "-";
|
||||
}
|
||||
if (!time) {
|
||||
return '-'
|
||||
return "-";
|
||||
}
|
||||
// 处理 time 参数
|
||||
if (isNaN(Number(time)) === false) {
|
||||
time = Number(time)
|
||||
time = Number(time);
|
||||
}
|
||||
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
|
||||
let date
|
||||
if (typeof time === 'object') {
|
||||
date = time
|
||||
const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
|
||||
let date;
|
||||
if (typeof time === "object") {
|
||||
date = time;
|
||||
} else {
|
||||
if (('' + time).length === 10) {
|
||||
time = parseInt('' + time) * 1000
|
||||
}
|
||||
date = new Date(time)
|
||||
if (("" + time).length === 10) time = parseInt(time) * 1000;
|
||||
date = new Date(time);
|
||||
}
|
||||
if (!(date instanceof Date)) {
|
||||
return time
|
||||
}
|
||||
const formatObj: any = {
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
}
|
||||
a: date.getDay(),
|
||||
};
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key]
|
||||
let value = formatObj[key];
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === 'a') {
|
||||
return ['日', '一', '二', '三', '四', '五', '六'][value]
|
||||
if (key === "a") {
|
||||
return ["日", "一", "二", "三", "四", "五", "六"][value];
|
||||
}
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = '0' + value
|
||||
value = "0" + value;
|
||||
}
|
||||
return value || 0
|
||||
})
|
||||
return time_str
|
||||
return value || 0;
|
||||
});
|
||||
return time_str;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,8 +369,8 @@ export function parseTime(time: string | number | Date | null, cFormat?: string
|
||||
* @param defaultValue
|
||||
* @returns
|
||||
*/
|
||||
export function renderSize(value: any, defaultValue = '-') {
|
||||
return formatUnits(value, 1024, ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], defaultValue)
|
||||
export function renderSize(value, defaultValue = "-") {
|
||||
return formatUnits(value, 1024, ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -378,33 +379,31 @@ export function renderSize(value: any, defaultValue = '-') {
|
||||
* @param defaultValue
|
||||
* @returns
|
||||
*/
|
||||
export function renderBpsSize(value: any, defaultValue = '-') {
|
||||
return formatUnits(value, 1024, ['bps', 'Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps', 'Ebps', 'Zbps', 'Ybps'], defaultValue)
|
||||
export function renderBpsSize(value, defaultValue = "-") {
|
||||
return formatUnits(value, 1024, ["bps", "Kbps", "Mbps", "Gbps", "Tbps", "Pbps", "Ebps", "Zbps", "Ybps"], defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param {*} value
|
||||
* @param defaultValue
|
||||
* @param unitArr 单位
|
||||
* @param base 基础值
|
||||
* @returns
|
||||
*/
|
||||
export function formatUnits(value: any, base: number, unitArr: string[], defaultValue = '-') {
|
||||
if (null == value || value === '') {
|
||||
return defaultValue
|
||||
export function formatUnits(value, base, unitArr, defaultValue = "-") {
|
||||
if (null == value || value === "") {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var index: number = 0
|
||||
var srcsize: number = parseFloat(value)
|
||||
var index = 0;
|
||||
var srcsize = parseFloat(value);
|
||||
if (srcsize <= 0) {
|
||||
return defaultValue
|
||||
return defaultValue;
|
||||
}
|
||||
// console.log(value, srcsize);
|
||||
index = Math.floor(Math.log(srcsize) / Math.log(base))
|
||||
var size: number = srcsize / Math.pow(base, index)
|
||||
//保留的小数位数
|
||||
return size.toFixed(2) + unitArr[index]
|
||||
index = Math.floor(Math.log(srcsize) / Math.log(base));
|
||||
var size = srcsize / Math.pow(base, index);
|
||||
size = size.toFixed(2); //保留的小数位数
|
||||
return size + unitArr[index];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -412,39 +411,39 @@ export function formatUnits(value: any, base: number, unitArr: string[], default
|
||||
* @param {function} group
|
||||
* @returns Object
|
||||
*/
|
||||
;(Array.prototype as any).groupBy = function (group: Function) {
|
||||
return group && typeof group === 'function'
|
||||
Array.prototype.groupBy = function (group) {
|
||||
return group && typeof group === "function"
|
||||
? Array.prototype.reduce.call(
|
||||
this,
|
||||
function (c: any, v) {
|
||||
var k = group(v)
|
||||
c[k] = v
|
||||
return c
|
||||
function (c, v) {
|
||||
var k = group(v);
|
||||
c[k] = v;
|
||||
return c;
|
||||
},
|
||||
{}
|
||||
)
|
||||
: this
|
||||
}
|
||||
: this;
|
||||
};
|
||||
//
|
||||
export function itemGroupBy(arr: any[], groupKey: string, key: string, dataKey: string) {
|
||||
key = key || 'type'
|
||||
dataKey = dataKey || 'data'
|
||||
export function itemGroupBy(arr, groupKey, key, dataKey) {
|
||||
key = key || "type";
|
||||
dataKey = dataKey || "data";
|
||||
|
||||
let newArr: any[] = [],
|
||||
types: any = {},
|
||||
let newArr = [],
|
||||
types = {},
|
||||
// newItem,
|
||||
i,
|
||||
j,
|
||||
cur
|
||||
cur;
|
||||
for (i = 0, j = arr.length; i < j; i++) {
|
||||
cur = arr[i]
|
||||
cur = arr[i];
|
||||
if (!(cur[groupKey] in types)) {
|
||||
types[cur[groupKey]] = { [key]: cur[groupKey], [dataKey]: [] }
|
||||
newArr.push(types[cur[groupKey]])
|
||||
types[cur[groupKey]] = { [key]: cur[groupKey], [dataKey]: [] };
|
||||
newArr.push(types[cur[groupKey]]);
|
||||
}
|
||||
types[cur[groupKey]][dataKey].push(cur)
|
||||
types[cur[groupKey]][dataKey].push(cur);
|
||||
}
|
||||
return newArr
|
||||
return newArr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -454,102 +453,106 @@ export function itemGroupBy(arr: any[], groupKey: string, key: string, dataKey:
|
||||
* @param {String} levelCount 格式化个数
|
||||
* @returns
|
||||
*/
|
||||
export function formatDuration(ms: any, seg: string, levelCount: number) {
|
||||
if (isNaN(Number(ms))) {
|
||||
return ms
|
||||
export function formatDuration(ms, seg, levelCount) {
|
||||
let msNum = Number(ms);
|
||||
if (isNaN(msNum)) {
|
||||
return ms;
|
||||
}
|
||||
seg = seg || ''
|
||||
levelCount = levelCount || 5
|
||||
if (ms < 0) ms = -ms
|
||||
if (msNum === 0) {
|
||||
return "-";
|
||||
}
|
||||
|
||||
seg = seg || "";
|
||||
levelCount = levelCount || 5;
|
||||
if (msNum < 0) msNum = -msNum;
|
||||
const time = {
|
||||
天: Math.floor(ms / 86400000),
|
||||
小时: Math.floor(ms / 3600000) % 24,
|
||||
分钟: Math.floor(ms / 60000) % 60,
|
||||
秒: Math.floor(ms / 1000) % 60,
|
||||
毫秒: Math.floor(ms) % 1000
|
||||
}
|
||||
天: Math.floor(msNum / 86400000),
|
||||
小时: Math.floor(msNum / 3600000) % 24,
|
||||
分钟: Math.floor(msNum / 60000) % 60,
|
||||
秒: Math.floor(msNum / 1000) % 60,
|
||||
毫秒: Math.floor(msNum) % 1000,
|
||||
};
|
||||
return Object.entries(time)
|
||||
.filter((val) => val[1] !== 0)
|
||||
.map(([key, val]) => `${val}${key}`)
|
||||
.splice(0, levelCount)
|
||||
.join(seg)
|
||||
.join(seg);
|
||||
}
|
||||
|
||||
//小数转换为分数(小数先转换成number类型,再乘以100,并且保留2位小数)
|
||||
export function formatPercent(point: any, keep = 2) {
|
||||
export function formatPercent(point, keep = 2) {
|
||||
if (!point) {
|
||||
return '-'
|
||||
return "-";
|
||||
}
|
||||
return formatPercent2(Number(point) * 100, keep)
|
||||
return formatPercent2(Number(point) * 100, keep);
|
||||
}
|
||||
|
||||
//小数转换为分数(小数先转换成number类型,并且保留2位小数)
|
||||
export function formatPercent2(point: any, keep = 2) {
|
||||
export function formatPercent2(point, keep = 2) {
|
||||
if (null == point) {
|
||||
return '-'
|
||||
return "-";
|
||||
}
|
||||
var percent: string = Number(point).toFixed(keep)
|
||||
percent += '%'
|
||||
return percent
|
||||
var percent = Number(point).toFixed(keep);
|
||||
percent += "%";
|
||||
return percent;
|
||||
}
|
||||
|
||||
//小数转换为分数(小数先转换成number类型,再乘以100,并且保留2位小数)
|
||||
export function formatPercent2Number(point: any, keep = 2) {
|
||||
export function formatPercent2Number(point, keep = 2) {
|
||||
if (null == point) {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
return Number(Number(point).toFixed(keep))
|
||||
return Number(Number(point).toFixed(keep));
|
||||
}
|
||||
|
||||
export function compareVersion(version1: any, version2: any) {
|
||||
export function compareVersion(version1, version2) {
|
||||
if (version1 == null && version2 == null) {
|
||||
return 0
|
||||
return 0;
|
||||
} else if (version1 == null) {
|
||||
// null视为最小版本,排在前
|
||||
return -1
|
||||
return -1;
|
||||
} else if (version2 == null) {
|
||||
return 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (version1 === version2) {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
const v1s = version1.split('.')
|
||||
const v2s = version2.split('.')
|
||||
const v1s = version1.split(".");
|
||||
const v2s = version2.split(".");
|
||||
|
||||
let diff: number = 0
|
||||
const minLength: number = Math.min(v1s.length, v2s.length) // 取最小长度值
|
||||
let diff = 0;
|
||||
const minLength = Math.min(v1s.length, v2s.length); // 取最小长度值
|
||||
|
||||
let i: number
|
||||
for (i = 0; i < minLength; i++) {
|
||||
let v1 = v1s[i]
|
||||
let v2 = v2s[i]
|
||||
for (let i = 0; i < minLength; i++) {
|
||||
let v1 = v1s[i];
|
||||
let v2 = v2s[i];
|
||||
// 先比较长度
|
||||
diff = v1.length - v2.length
|
||||
diff = v1.length - v2.length;
|
||||
if (0 === diff) {
|
||||
diff = v1.localeCompare(v2)
|
||||
diff = v1.localeCompare(v2);
|
||||
}
|
||||
if (diff !== 0) {
|
||||
//已有结果,结束
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果已经分出大小,则直接返回,如果未分出大小,则再比较位数,有子版本的为大;
|
||||
return diff !== 0 ? diff : v1s.length - v2s.length
|
||||
return diff !== 0 ? diff : v1s.length - v2s.length;
|
||||
}
|
||||
|
||||
// 当前页面构建信息
|
||||
export function pageBuildInfo() {
|
||||
const htmlVersion: any = document.head?.querySelector('[name~=jpom-version][content]')
|
||||
const buildTime: any = document.head?.querySelector('[name~=build-time][content]')
|
||||
const buildEnv: any = document.head?.querySelector('[name~=build-env][content]')
|
||||
const htmlVersion = document.head.querySelector("[name~=jpom-version][content]").content;
|
||||
const buildTime = document.head.querySelector("[name~=build-time][content]").content;
|
||||
const buildEnv = document.head.querySelector("[name~=build-env][content]").content;
|
||||
return {
|
||||
v: htmlVersion?.content,
|
||||
t: buildTime?.content,
|
||||
e: buildEnv?.content,
|
||||
df: (document.title || '').toLowerCase().includes('jpom'),
|
||||
t2: Date.now()
|
||||
}
|
||||
v: htmlVersion,
|
||||
t: buildTime,
|
||||
e: buildEnv,
|
||||
df: (document.title || "").toLowerCase().includes("jpom"),
|
||||
t2: Date.now(),
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user