mirror of https://github.com/certd/certd
refactor: ui
parent
fd130f86fd
commit
86b1e9959b
|
@ -5,8 +5,11 @@ out
|
||||||
gen
|
gen
|
||||||
node_modules/
|
node_modules/
|
||||||
/test/*.private.*
|
/test/*.private.*
|
||||||
/other
|
|
||||||
/other/node-acme-client/.idea/
|
|
||||||
/*.log
|
/*.log
|
||||||
/other/certd-run
|
|
||||||
/other/node-acme-client
|
/other/*/.idea
|
||||||
|
|
||||||
|
/other/*/node_modules
|
||||||
|
|
||||||
|
/packages/*/node_modules
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 08b48c522b5a35f6c58130e7a43f525be0ac94df
|
|
@ -0,0 +1,3 @@
|
||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
|
@ -0,0 +1,5 @@
|
||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
|
@ -0,0 +1,28 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'@vue/standard'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'**/__tests__/*.{j,t}s?(x)',
|
||||||
|
'**/tests/unit/**/*.spec.{j,t}s?(x)'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
mocha: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
|
@ -0,0 +1,29 @@
|
||||||
|
# certd-ui
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run your unit tests
|
||||||
|
```
|
||||||
|
npm run test:unit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
],
|
||||||
|
plugins: ['@babel/plugin-proposal-optional-chaining']
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"name": "certd-ui",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"test:unit": "vue-cli-service test:unit",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
|
||||||
|
"@certd/plugins": "^0.1.11",
|
||||||
|
"@certd/providers": "^0.1.11",
|
||||||
|
"ant-design-vue": "^2.0.0-rc.8",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"lodash-es": "^4.17.20",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-i18n": "^9.0.0-rc.2",
|
||||||
|
"vue-router": "^4.0.0-0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-unit-mocha": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
|
"@vue/test-utils": "^2.0.0-0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"chai": "^4.1.2",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
|
"eslint-plugin-vue": "^7.0.0-0",
|
||||||
|
"less": "^3.0.4",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"lint-staged": "^9.5.0"
|
||||||
|
},
|
||||||
|
"gitHooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx,vue}": [
|
||||||
|
"vue-cli-service lint",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<template>
|
||||||
|
<a-config-provider :locale="locale">
|
||||||
|
|
||||||
|
<a-layout class="page-layout">
|
||||||
|
<a-layout-header>Cert-D</a-layout-header>
|
||||||
|
<a-layout style="flex:1">
|
||||||
|
<router-view/>
|
||||||
|
</a-layout>
|
||||||
|
<a-layout-footer>
|
||||||
|
by greper
|
||||||
|
</a-layout-footer>
|
||||||
|
|
||||||
|
</a-layout>
|
||||||
|
|
||||||
|
</a-config-provider>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
locale: zhCN
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup () {
|
||||||
|
const { t } = useI18n() // call `useI18n`, and spread `t` from `useI18n` returning
|
||||||
|
return { t } // return render context that included `t`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.page-layout{
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
.ant-layout-header{
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { request } from './service'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
list () {
|
||||||
|
return request({
|
||||||
|
url: '/providers/list'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { assign, map } from 'lodash'
|
||||||
|
import { service, request } from './service'
|
||||||
|
|
||||||
|
const files = require.context('./modules', false, /\.js$/)
|
||||||
|
const generators = files.keys().map(key => files(key).default)
|
||||||
|
|
||||||
|
export default assign({}, ...map(generators, generator => generator({
|
||||||
|
service,
|
||||||
|
request
|
||||||
|
})))
|
|
@ -0,0 +1,95 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
import { get } from 'lodash-es'
|
||||||
|
import { errorLog, errorCreate } from './tools'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建请求实例
|
||||||
|
*/
|
||||||
|
function createService () {
|
||||||
|
// 创建一个 axios 实例
|
||||||
|
const service = axios.create()
|
||||||
|
// 请求拦截
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => config,
|
||||||
|
error => {
|
||||||
|
// 发送失败
|
||||||
|
console.log(error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 响应拦截
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
// dataAxios 是 axios 返回数据中的 data
|
||||||
|
const dataAxios = response.data
|
||||||
|
// 这个状态码是和后端约定的
|
||||||
|
const { code } = dataAxios
|
||||||
|
// 根据 code 进行判断
|
||||||
|
if (code === undefined) {
|
||||||
|
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
|
||||||
|
if (response.config.unpack) {
|
||||||
|
return dataAxios
|
||||||
|
}
|
||||||
|
return dataAxios.data
|
||||||
|
} else {
|
||||||
|
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||||
|
switch (code) {
|
||||||
|
case 0:
|
||||||
|
// [ 示例 ] code === 0 代表没有错误
|
||||||
|
// TODO 可能结果还需要code和msg进行后续处理,所以返回全部结果
|
||||||
|
return dataAxios.data
|
||||||
|
case 'xxx':
|
||||||
|
// [ 示例 ] 其它和后台约定的 code
|
||||||
|
errorCreate(`[ code: xxx ] ${dataAxios.msg}: ${response.config.url}`)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
// 不是正确的 code
|
||||||
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
const status = get(error, 'response.status')
|
||||||
|
switch (status) {
|
||||||
|
case 400: error.message = '请求错误'; break
|
||||||
|
case 401: error.message = '未授权,请登录'; break
|
||||||
|
case 403: error.message = '拒绝访问'; break
|
||||||
|
case 404: error.message = `请求地址出错: ${error.response.config.url}`; break
|
||||||
|
case 408: error.message = '请求超时'; break
|
||||||
|
case 500: error.message = '服务器内部错误'; break
|
||||||
|
case 501: error.message = '服务未实现'; break
|
||||||
|
case 502: error.message = '网关错误'; break
|
||||||
|
case 503: error.message = '服务不可用'; break
|
||||||
|
case 504: error.message = '网关超时'; break
|
||||||
|
case 505: error.message = 'HTTP版本不受支持'; break
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
errorLog(error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建请求方法
|
||||||
|
* @param {Object} service axios 实例
|
||||||
|
*/
|
||||||
|
function createRequestFunction (service) {
|
||||||
|
return function (config) {
|
||||||
|
const configDefault = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': get(config, 'headers.Content-Type', 'application/json')
|
||||||
|
},
|
||||||
|
timeout: 5000,
|
||||||
|
baseURL: process.env.VUE_APP_API,
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
return service(Object.assign(configDefault, config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于真实网络请求的实例和请求方法
|
||||||
|
export const service = createService()
|
||||||
|
export const request = createRequestFunction(service)
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { notification } from 'ant-design-vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 安全地解析 json 字符串
|
||||||
|
* @param {String} jsonString 需要解析的 json 字符串
|
||||||
|
* @param {String} defaultValue 默认值
|
||||||
|
*/
|
||||||
|
export function parse (jsonString = '{}', defaultValue = {}) {
|
||||||
|
let result = defaultValue
|
||||||
|
try {
|
||||||
|
result = JSON.parse(jsonString)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接口请求返回
|
||||||
|
* @param {Any} data 返回值
|
||||||
|
* @param {String} msg 状态信息
|
||||||
|
* @param {Number} code 状态码
|
||||||
|
*/
|
||||||
|
export function response (data = {}, msg = '', code = 0) {
|
||||||
|
return [
|
||||||
|
200,
|
||||||
|
{ code, msg, data }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接口请求返回 正确返回
|
||||||
|
* @param {Any} data 返回值
|
||||||
|
* @param {String} msg 状态信息
|
||||||
|
*/
|
||||||
|
export function responseSuccess (data = {}, msg = '成功') {
|
||||||
|
return response(data, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接口请求返回 错误返回
|
||||||
|
* @param {Any} data 返回值
|
||||||
|
* @param {String} msg 状态信息
|
||||||
|
* @param {Number} code 状态码
|
||||||
|
*/
|
||||||
|
export function responseError (data = {}, msg = '请求失败', code = 500) {
|
||||||
|
return response(data, msg, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 记录和显示错误
|
||||||
|
* @param {Error} error 错误对象
|
||||||
|
*/
|
||||||
|
export function errorLog (error) {
|
||||||
|
// 打印到控制台
|
||||||
|
console.log(error)
|
||||||
|
// 显示提示
|
||||||
|
notification({
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建一个错误
|
||||||
|
* @param {String} msg 错误信息
|
||||||
|
*/
|
||||||
|
export function errorCreate (msg) {
|
||||||
|
const error = new Error(msg)
|
||||||
|
errorLog(error)
|
||||||
|
throw error
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
|
@ -0,0 +1,60 @@
|
||||||
|
<template>
|
||||||
|
<div class="hello">
|
||||||
|
<h1>{{ msg }}</h1>
|
||||||
|
<p>
|
||||||
|
For a guide and recipes on how to configure / customize this project,<br>
|
||||||
|
check out the
|
||||||
|
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||||
|
</p>
|
||||||
|
<h3>Installed CLI Plugins</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-mocha" target="_blank" rel="noopener">unit-mocha</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Essential Links</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||||
|
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||||
|
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||||
|
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||||
|
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Ecosystem</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||||
|
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||||
|
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||||
|
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'HelloWorld',
|
||||||
|
props: {
|
||||||
|
msg: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped lang="less">
|
||||||
|
h3 {
|
||||||
|
margin: 40px 0 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<div class="d-container">
|
||||||
|
<div class="box">
|
||||||
|
<div class="inner">
|
||||||
|
<div class="header">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'd-container'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.d-container{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
.box {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
.inner{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.header{
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.body{
|
||||||
|
overflow-y: auto;
|
||||||
|
flex:1
|
||||||
|
}
|
||||||
|
.footer{
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import zh from '@/locales/zh.json'
|
||||||
|
import en from '@/locales/en.json'
|
||||||
|
export const i18n = createI18n({
|
||||||
|
// something vue-i18n options here ...
|
||||||
|
locale: 'zh', // set current locale
|
||||||
|
messages: {
|
||||||
|
en,
|
||||||
|
zh
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,26 @@
|
||||||
|
import _ from 'lodash'
|
||||||
|
import {
|
||||||
|
PlusCircleOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
CheckOutlined, EditOutlined,
|
||||||
|
ArrowRightOutlined,
|
||||||
|
NodeIndexOutlined,
|
||||||
|
ThunderboltOutlined,
|
||||||
|
DeleteOutlined
|
||||||
|
} from '@ant-design/icons-vue'
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
PlusCircleOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
CheckOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
ArrowRightOutlined,
|
||||||
|
NodeIndexOutlined,
|
||||||
|
ThunderboltOutlined,
|
||||||
|
DeleteOutlined
|
||||||
|
}
|
||||||
|
export default function (app) {
|
||||||
|
_.forEach(icons, item => {
|
||||||
|
app.component(item.name, item)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"hello": "你好",
|
||||||
|
"domain": "域名",
|
||||||
|
"next": "下一步",
|
||||||
|
"submit": "提交",
|
||||||
|
"reset": "重置",
|
||||||
|
"please.input.domain": "请输入域名",
|
||||||
|
"email": "邮箱"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import router from './router'
|
||||||
|
import App from './App.vue'
|
||||||
|
import Antd from 'ant-design-vue'
|
||||||
|
import 'ant-design-vue/dist/antd.css'
|
||||||
|
import '@/style/common.less'
|
||||||
|
import { i18n } from '@/i18n'
|
||||||
|
import icons from './icons'
|
||||||
|
import DContainer from '@/components/d-container'
|
||||||
|
const app = createApp(App)
|
||||||
|
app.config.productionTip = false
|
||||||
|
app.use(i18n)
|
||||||
|
app.use(Antd)
|
||||||
|
icons(app)
|
||||||
|
app.component('d-container', DContainer)
|
||||||
|
app.use(router).mount('#app')
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
import Home from '../views/Home.vue'
|
||||||
|
import Detail from '../views/detail/index.vue'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'Home',
|
||||||
|
component: Home
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/detail',
|
||||||
|
name: 'detail',
|
||||||
|
component: Detail
|
||||||
|
// component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
|
@ -0,0 +1,78 @@
|
||||||
|
div#app {
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-10{
|
||||||
|
margin-left:10px;
|
||||||
|
}
|
||||||
|
.mt-10{
|
||||||
|
margin-top:10px;
|
||||||
|
}
|
||||||
|
.mr-10{
|
||||||
|
margin-right:10px;
|
||||||
|
}
|
||||||
|
.mb-10{
|
||||||
|
margin-bottom:10px;
|
||||||
|
}
|
||||||
|
.ant-layout {
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-drawer-body{
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
.ant-drawer-content {
|
||||||
|
|
||||||
|
.ant-drawer-wrapper-body{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.ant-drawer-body {
|
||||||
|
position: relative;
|
||||||
|
flex:1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.d-dialog{
|
||||||
|
.ant-modal-body{
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-height:600px) and (max-height:700px){
|
||||||
|
.ant-modal-body {
|
||||||
|
max-height: 50vh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height:600px) {
|
||||||
|
.ant-modal-body {
|
||||||
|
max-height: 40vh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<div class="page-index flex-center">
|
||||||
|
<H2 class="title">CERT-D</H2>
|
||||||
|
<div class="page-body">
|
||||||
|
|
||||||
|
<a-tabs @change="callback">
|
||||||
|
<a-tab-pane key="1" tab="创建新证书">
|
||||||
|
<div class="create-from-domains">
|
||||||
|
<div class="input-row flex-row">
|
||||||
|
<a-select
|
||||||
|
size="large"
|
||||||
|
mode="tags"
|
||||||
|
:placeholder="$t('please.input.domain')"
|
||||||
|
v-model:value="formData.cert.domains"
|
||||||
|
:open="false"
|
||||||
|
></a-select>
|
||||||
|
<div class="row-append">
|
||||||
|
<a-button size="large" type="primary" @click="createFromDomain">创建新证书</a-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="helper">
|
||||||
|
支持泛域名(例如*.test.yourdomain.com)<br/>
|
||||||
|
支持多个域名打包到一张证书(输入一个域名后回车,再输下一个)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" tab="从配置导入" force-render>
|
||||||
|
<a-textarea class="textarea" type="textarea" :auto-size="autoSize" allow-clear></a-textarea>
|
||||||
|
<a-button class="mt-10" type="primary" >导入</a-button>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { reactive, toRaw } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
export default {
|
||||||
|
setup () {
|
||||||
|
const formData = reactive({
|
||||||
|
cert: {
|
||||||
|
domains: ['*.docmirror.cn'],
|
||||||
|
email: 'xiaojunnuo@qq.com',
|
||||||
|
dnsProvider: 'aliyun'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const router = useRouter()
|
||||||
|
const createFromDomain = () => {
|
||||||
|
router.push({ name: 'detail', params: { options: JSON.stringify(formData) } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const autoSize = reactive({ minRows: 8, maxRows: 10 })
|
||||||
|
return {
|
||||||
|
createFromDomain,
|
||||||
|
formData,
|
||||||
|
autoSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.page-index{
|
||||||
|
background-color: #fff;
|
||||||
|
height: 100%;
|
||||||
|
&.flex-center{
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title{
|
||||||
|
margin:50px;
|
||||||
|
}
|
||||||
|
.page-body{
|
||||||
|
min-width: 700px;width: 60%
|
||||||
|
}
|
||||||
|
.create-from-domains{
|
||||||
|
width:100%;
|
||||||
|
.input-row{
|
||||||
|
width:100%;
|
||||||
|
.ant-select{
|
||||||
|
flex:1;
|
||||||
|
}
|
||||||
|
.row-append{
|
||||||
|
padding-left:10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.helper{
|
||||||
|
margin-top:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tabs-bar {
|
||||||
|
margin: 0 0 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,214 @@
|
||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
title="证书申请配置"
|
||||||
|
placement="right"
|
||||||
|
:closable="true"
|
||||||
|
width="500px"
|
||||||
|
v-model:visible="visible"
|
||||||
|
:after-visible-change="afterVisibleChange"
|
||||||
|
>
|
||||||
|
|
||||||
|
<d-container>
|
||||||
|
<a-form class="domain-form" :scrollToFirstError="true" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
|
||||||
|
<h3>域名信息</h3>
|
||||||
|
<a-form-item :label="$t('domain')" v-bind="validateInfos.domains">
|
||||||
|
<a-select
|
||||||
|
mode="tags"
|
||||||
|
:placeholder="$t('please.input.domain')"
|
||||||
|
v-model:value="formData.domains"
|
||||||
|
:open="false"
|
||||||
|
></a-select>
|
||||||
|
<div class="helper">例如:*.yourdomain.com</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item :label="$t('email')" v-bind="validateInfos.email">
|
||||||
|
<a-input v-model:value="formData.email"/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="dns验证" v-bind="validateInfos.dnsProvider">
|
||||||
|
<provider-selector v-model:value="formData.dnsProvider"
|
||||||
|
:providers="accessProviders"
|
||||||
|
@update:providers="accessProvidersUpdate"
|
||||||
|
></provider-selector>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<h3>CSR <span>必须全英文</span></h3>
|
||||||
|
<a-form-item label="国家" v-bind="validateInfos['csr.country']">
|
||||||
|
<a-input v-model:value="formData.csr.country"/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="省份" v-bind="validateInfos['csr.state']">
|
||||||
|
<a-input v-model:value="formData.csr.state"/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="市区" v-bind="validateInfos['csr.locality']">
|
||||||
|
<a-input v-model:value="formData.csr.locality"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="组织" v-bind="validateInfos['csr.organization']">
|
||||||
|
<a-input v-model:value="formData.csr.organization"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="部门" v-bind="validateInfos['csr.organizationUnit']">
|
||||||
|
<a-input v-model:value="formData.csr.organizationUnit"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="联系人邮箱">
|
||||||
|
<a-input v-model:value="formData.csr.emailAddress"/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
||||||
|
<a-button type="primary" @click="onSubmit">
|
||||||
|
确定
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</d-container>
|
||||||
|
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { reactive, toRaw, ref, watch } from 'vue'
|
||||||
|
import { useForm } from '@ant-design-vue/use'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
import ProviderSelector from '@/views/detail/components/provider-selector'
|
||||||
|
|
||||||
|
function useDrawer () {
|
||||||
|
const visible = ref(false)
|
||||||
|
const afterVisibleChange = (val) => {
|
||||||
|
console.log('visible', val)
|
||||||
|
}
|
||||||
|
const open = () => {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
const close = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
afterVisibleChange,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'cert-form',
|
||||||
|
components: { ProviderSelector },
|
||||||
|
emits: ['update:accessProviders', 'update:cert'],
|
||||||
|
// 属性定义
|
||||||
|
props: {
|
||||||
|
cert: {
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
accessProviders: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup (props, context) {
|
||||||
|
const drawer = useDrawer()
|
||||||
|
|
||||||
|
const certFormData = {
|
||||||
|
domains: [],
|
||||||
|
email: undefined,
|
||||||
|
dnsProvider: '',
|
||||||
|
csr: {
|
||||||
|
country: '',
|
||||||
|
state: 'GuangDong',
|
||||||
|
locality: 'ShengZhen',
|
||||||
|
organization: 'CertD Org.',
|
||||||
|
organizationUnit: 'IT Department',
|
||||||
|
emailAddress: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const formData = reactive(certFormData)
|
||||||
|
watch(props.cert, () => {
|
||||||
|
console.log('cert props')
|
||||||
|
_.merge(formData, props.cert)
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
domains: [{
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
message: '请输入域名'
|
||||||
|
}],
|
||||||
|
email: [{
|
||||||
|
type: 'email',
|
||||||
|
required: true,
|
||||||
|
message: '请输入正确的邮箱'
|
||||||
|
}],
|
||||||
|
dnsProvider: [{
|
||||||
|
required: true,
|
||||||
|
message: '请选择dns授权提供者'
|
||||||
|
}],
|
||||||
|
'csr.country': [{ required: true, message: '请输入国家代码' }],
|
||||||
|
'csr.state': [{ required: true, message: '请输入省份' }],
|
||||||
|
'csr.locality': [{ required: true, message: '请输入市区' }],
|
||||||
|
'csr.organization': [{ required: false, message: '请输入组织名称' }],
|
||||||
|
'csr.organizationUnit': [{ required: false, message: '请输入部门名称' }],
|
||||||
|
'csr.emailAddress': [{ required: false, message: '请输入邮箱' }]
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(formData, rules)
|
||||||
|
|
||||||
|
const onSubmit = async e => {
|
||||||
|
e.preventDefault()
|
||||||
|
try {
|
||||||
|
const res = await validate()
|
||||||
|
console.log('validation', res, toRaw(formData))
|
||||||
|
|
||||||
|
context.emit('update:cert', formData)
|
||||||
|
console.log('1111')
|
||||||
|
drawer.close()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('表单校验错误', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const reset = () => {
|
||||||
|
resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerManagerRef = ref(null)
|
||||||
|
const providerManagerOpen = () => {
|
||||||
|
console.log('providerManagerRef', providerManagerRef)
|
||||||
|
if (providerManagerRef.value) {
|
||||||
|
providerManagerRef.value.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const accessProvidersUpdate = (val) => {
|
||||||
|
console.log('accessUpdate', val)
|
||||||
|
context.emit('update:accessProviders', val)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
labelCol: { span: 4 },
|
||||||
|
wrapperCol: { span: 18 },
|
||||||
|
formData,
|
||||||
|
onSubmit,
|
||||||
|
reset,
|
||||||
|
validateInfos,
|
||||||
|
providerManagerRef,
|
||||||
|
providerManagerOpen,
|
||||||
|
accessProvidersUpdate,
|
||||||
|
...drawer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.ant-form.domain-form {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px 24px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
span {
|
||||||
|
font-weight: 200;
|
||||||
|
margin-left: 5px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,268 @@
|
||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
title="授权管理"
|
||||||
|
placement="right"
|
||||||
|
:closable="true"
|
||||||
|
width="500px"
|
||||||
|
v-model:visible="visible"
|
||||||
|
:after-visible-change="onAfterVisibleChange"
|
||||||
|
>
|
||||||
|
<div class="d-container provider-manager">
|
||||||
|
<a-button @click="add">
|
||||||
|
添加授权
|
||||||
|
</a-button>
|
||||||
|
<a-list
|
||||||
|
class="list"
|
||||||
|
item-layout="horizontal"
|
||||||
|
:data-source="providerList"
|
||||||
|
>
|
||||||
|
<template #renderItem="{ item ,index }">
|
||||||
|
<a-list-item>
|
||||||
|
<template #actions>
|
||||||
|
<a-button type="primary" @click="openEdit(item,index)"><template #icon><EditOutlined /></template></a-button>
|
||||||
|
<a-button type="danger" @click="remove(item,index)"><template #icon ><DeleteOutlined /></template></a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-radio :checked="item.key===selectedKey" @update:checked="selectedKey = item.key">【{{item.key}}】 {{ item.name }}</a-radio>
|
||||||
|
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a-button @click="onProviderSelectSubmit">确定</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<a-modal v-model:visible="editVisible" dialogClass="d-dialog" width="700px" title="编辑授权" @ok="onSubmit">
|
||||||
|
|
||||||
|
<a-form ref="formRef" class="domain-form" :model="formData" labelWidth="150px" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
<a-form-item label="类型" :rules="rules.type">
|
||||||
|
<a-radio-group :disabled="editIndex!=null" v-model:value="formData.type" @change="onTypeChanged" >
|
||||||
|
<a-radio-button v-for="(option) of providerDefineList" :key="option.name" :value="option.name">
|
||||||
|
{{option.label}}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<template v-if="formData.type && currentProvider">
|
||||||
|
<a-form-item label="key" name="key" :rules="rules.key">
|
||||||
|
<a-input :disabled="editIndex!=null" v-model:value="formData.key"/>
|
||||||
|
<div class="helper">不重复的key</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="名称" name="name" :rules="rules.name">
|
||||||
|
<a-input v-model:value="formData.name"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-for="(item,key) in currentProvider.input"
|
||||||
|
:key="key"
|
||||||
|
:label="item.label || key"
|
||||||
|
:name="key"
|
||||||
|
:rules="[{ required: true, message: '必填项' }]">
|
||||||
|
<a-input v-model:value="formData[key]" v-bind="item.attrs" ></a-input>
|
||||||
|
<div class="helper">{{item.desc}}</div>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, nextTick, watch } from 'vue'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { useForm } from '@ant-design-vue/use'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
import providerApi from '@/api/api.providers'
|
||||||
|
function useEdit (props, context, providerList, onSave) {
|
||||||
|
const formData = reactive({
|
||||||
|
key: '',
|
||||||
|
name: '',
|
||||||
|
type: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
type: [{
|
||||||
|
required: true,
|
||||||
|
message: '请选择类型'
|
||||||
|
}],
|
||||||
|
key: [{
|
||||||
|
required: true,
|
||||||
|
message: '请输入key'
|
||||||
|
}, {
|
||||||
|
validator (rule, value) {
|
||||||
|
const providers = providerList.value
|
||||||
|
if (!providers || providers.length === 0) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
if (editIndex.value != null) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
const filter = providers.filter(item => item.key === value)
|
||||||
|
console.log('validate', filter)
|
||||||
|
if (filter.length === 0) {
|
||||||
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line prefer-promise-reject-errors
|
||||||
|
return Promise.reject('key不能重复')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
message: 'key不能与其他授权配置重复'
|
||||||
|
}],
|
||||||
|
name: [{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
// const { resetFields, validate, validateInfos } = useForm(formData, rules)
|
||||||
|
const onSubmit = async e => {
|
||||||
|
e.preventDefault()
|
||||||
|
await formRef.value.validate()
|
||||||
|
const newProvider = _.cloneDeep(formData)
|
||||||
|
onSave(newProvider, editIndex.value)
|
||||||
|
closeEdit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const editVisible = ref(false)
|
||||||
|
const editIndex = ref(null)
|
||||||
|
const openEdit = (item, index) => {
|
||||||
|
if (item) {
|
||||||
|
editIndex.value = index
|
||||||
|
_.forEach(formData, (value, key) => {
|
||||||
|
formData[key] = null
|
||||||
|
})
|
||||||
|
_.merge(formData, item)
|
||||||
|
changeType(item.type)
|
||||||
|
} else {
|
||||||
|
editIndex.value = null
|
||||||
|
}
|
||||||
|
editVisible.value = true
|
||||||
|
}
|
||||||
|
const add = () => {
|
||||||
|
openEdit()
|
||||||
|
}
|
||||||
|
const closeEdit = () => {
|
||||||
|
editVisible.value = false
|
||||||
|
}
|
||||||
|
const providerDefineList = ref([])
|
||||||
|
const onCreated = async () => {
|
||||||
|
providerDefineList.value = await providerApi.list()
|
||||||
|
}
|
||||||
|
onCreated()
|
||||||
|
const currentProvider = ref(null)
|
||||||
|
const onTypeChanged = (e) => {
|
||||||
|
const value = e.target.value
|
||||||
|
changeType(value)
|
||||||
|
// 遍历input 设置到form rules
|
||||||
|
}
|
||||||
|
const changeType = (type) => {
|
||||||
|
if (providerDefineList.value == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const item of providerDefineList.value) {
|
||||||
|
if (item.name === type) {
|
||||||
|
currentProvider.value = item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editIndex.value == null) {
|
||||||
|
formData.key = currentProvider.value.name
|
||||||
|
formData.name = currentProvider.value.label || currentProvider.value.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
labelCol: { span: 6 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
formData,
|
||||||
|
onSubmit,
|
||||||
|
rules,
|
||||||
|
editVisible,
|
||||||
|
formRef,
|
||||||
|
currentProvider,
|
||||||
|
providerDefineList,
|
||||||
|
editIndex,
|
||||||
|
openEdit,
|
||||||
|
onTypeChanged,
|
||||||
|
add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'provider-manager',
|
||||||
|
props: {
|
||||||
|
value: {},
|
||||||
|
providers: {}
|
||||||
|
},
|
||||||
|
emits: ['update:value', 'update:providers'],
|
||||||
|
setup (props, context) {
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAfterVisibleChange = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
const providerList = ref([])
|
||||||
|
const selectedKey = ref(null)
|
||||||
|
|
||||||
|
watch(() => props.providers, () => {
|
||||||
|
providerList.value = _.cloneDeep(props.providers || [])
|
||||||
|
}, { immediate: true })
|
||||||
|
watch(() => props.value, () => {
|
||||||
|
selectedKey.value = props.value
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
const onEditSave = (newProvier, editIndex) => {
|
||||||
|
if (editIndex == null) {
|
||||||
|
providerList.value.push(newProvier)
|
||||||
|
} else {
|
||||||
|
_.merge(providerList.value[editIndex], newProvier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editModule = useEdit(props, context, providerList, onEditSave)
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
visible.value = true
|
||||||
|
if (providerList.value.length === 0) {
|
||||||
|
nextTick(() => {
|
||||||
|
editModule.add()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const remove = (item, index) => {
|
||||||
|
providerList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onProviderSelectSubmit = () => {
|
||||||
|
context.emit('update:providers', providerList.value)
|
||||||
|
context.emit('update:value', selectedKey.value)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
providerList,
|
||||||
|
visible,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
onAfterVisibleChange,
|
||||||
|
remove,
|
||||||
|
selectedKey,
|
||||||
|
onProviderSelectSubmit,
|
||||||
|
...editModule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.provider-manager{
|
||||||
|
padding:10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,76 @@
|
||||||
|
<template>
|
||||||
|
<div class="provider-selector">
|
||||||
|
<a-select
|
||||||
|
:value="value"
|
||||||
|
@update:value="valueUpdate"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="item of providers" :key="item.key" :value="item.key">
|
||||||
|
{{ item.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-button class="suffix" @click="providerManagerOpen">
|
||||||
|
管理授权
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<provider-manager ref="providerManagerRef"
|
||||||
|
:providers="providers"
|
||||||
|
:value="value"
|
||||||
|
@update:value="valueUpdate"
|
||||||
|
@update:providers="providersUpdate"
|
||||||
|
></provider-manager>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ProviderManager from './provider-manager'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'provider-selector',
|
||||||
|
components: { ProviderManager },
|
||||||
|
emits: ['update:providers', 'update:value'],
|
||||||
|
// 属性定义
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
providers: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup (props, context) {
|
||||||
|
const providerManagerRef = ref(null)
|
||||||
|
const providerManagerOpen = () => {
|
||||||
|
console.log('providerManagerRef', providerManagerRef)
|
||||||
|
if (providerManagerRef.value) {
|
||||||
|
providerManagerRef.value.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const providersUpdate = (val) => {
|
||||||
|
console.log('accessUpdate', val)
|
||||||
|
context.emit('update:providers', val)
|
||||||
|
}
|
||||||
|
const valueUpdate = (val) => {
|
||||||
|
context.emit('update:value', val)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
providersUpdate,
|
||||||
|
valueUpdate,
|
||||||
|
providerManagerOpen,
|
||||||
|
providerManagerRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.provider-selector{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.ant-select{
|
||||||
|
flex:1;
|
||||||
|
}
|
||||||
|
.suffix{
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left:5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,243 @@
|
||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
title="编辑任务"
|
||||||
|
placement="right"
|
||||||
|
:closable="true"
|
||||||
|
width="600px"
|
||||||
|
v-model:visible="taskDrawerVisible"
|
||||||
|
:after-visible-change="taskDrawerOnAfterVisibleChange"
|
||||||
|
>
|
||||||
|
|
||||||
|
<template v-if="currentTask">
|
||||||
|
<d-container v-if="currentTask._isAdd" class="task-edit-form">
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col v-for="(item,index) of taskPluginDefineList" :key="index" class="task-plugin" :span="12">
|
||||||
|
<a-card hoverable :class="{'current':item.name === currentTask.type}"
|
||||||
|
@click="taskTypeSelected(item)" @dblclick="taskTypeSelected(item);taskTypeSave()">
|
||||||
|
<a-card-meta>
|
||||||
|
<template #title>
|
||||||
|
<a-avatar :src="item.icon||'/images/plugin.png'"/>
|
||||||
|
<span class="title">{{ item.label }}</span>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<span :title="item.desc">{{ item.desc }}</span>
|
||||||
|
</template>
|
||||||
|
</a-card-meta>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-button type="primary" @click="taskTypeSave">
|
||||||
|
确定
|
||||||
|
</a-button>
|
||||||
|
</d-container>
|
||||||
|
<d-container v-else class="d-container" >
|
||||||
|
<a-form class="task-form" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
<a-form-item label="任务名称">
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入任务名称"
|
||||||
|
v-model:value="currentTask.taskName"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-for="(value,key) in currentPlugin.input" :key="key" :label="value.label">
|
||||||
|
<a-input v-model:value="currentTask[key]"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
|
||||||
|
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
|
||||||
|
<a-button type="primary" @click="taskSave">
|
||||||
|
确定
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
</d-container>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</a-drawer>
|
||||||
|
<provider-manager ref="providerManager"></provider-manager>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import ProviderManager from '@/views/detail/components/provider-manager'
|
||||||
|
import pluginsApi from '@/api/api.plugins'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
/**
|
||||||
|
* task drawer
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function useTaskForm (context) {
|
||||||
|
const taskPluginDefineList = ref([])
|
||||||
|
const onCreated = async () => {
|
||||||
|
const plugins = await pluginsApi.list()
|
||||||
|
taskPluginDefineList.value = plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
onCreated()
|
||||||
|
|
||||||
|
const currentTask = ref()
|
||||||
|
const currentTaskIndex = ref()
|
||||||
|
const currentDeploy = ref()
|
||||||
|
const currentPlugin = ref(null)
|
||||||
|
const taskFormRef = ref(null)
|
||||||
|
const taskDrawerVisible = ref(false)
|
||||||
|
|
||||||
|
const taskAdd = (deploy) => {
|
||||||
|
const task = { taskName: '新任务', type: undefined, _isAdd: true }
|
||||||
|
currentDeploy.value = deploy
|
||||||
|
currentDeploy.value.tasks.push(task)
|
||||||
|
currentTask.value = deploy.tasks[deploy.tasks.length - 1]
|
||||||
|
taskDrawerShow()
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskTypeSelected = (item) => {
|
||||||
|
currentTask.value.type = item.name
|
||||||
|
currentTask.value.taskName = item.label
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskTypeSave = () => {
|
||||||
|
currentTask.value._isAdd = false
|
||||||
|
if (currentTask.value.type == null) {
|
||||||
|
message.warn('请先选择类型')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
changeCurrentPlugin(currentTask.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskDrawerShow = () => {
|
||||||
|
taskDrawerVisible.value = true
|
||||||
|
}
|
||||||
|
const taskDrawerClose = () => {
|
||||||
|
taskDrawerVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskDrawerOnAfterVisibleChange = (val) => {
|
||||||
|
console.log('taskDrawerOnAfterVisibleChange', val)
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskEdit = (deploy, task, index) => {
|
||||||
|
if (task) {
|
||||||
|
currentTask.value = task
|
||||||
|
currentTaskIndex.value = index
|
||||||
|
}
|
||||||
|
currentDeploy.value = deploy
|
||||||
|
changeCurrentPlugin(currentTask.value)
|
||||||
|
|
||||||
|
taskDrawerShow()
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCurrentPlugin = (task) => {
|
||||||
|
const taskType = task.type
|
||||||
|
const currentPlugins = taskPluginDefineList.value.filter(p => {
|
||||||
|
return p.name === taskType
|
||||||
|
})
|
||||||
|
if (currentPlugins.length <= 0) {
|
||||||
|
task.type = undefined
|
||||||
|
task._isAdd = true
|
||||||
|
throw new Error('未知插件:' + taskType)
|
||||||
|
}
|
||||||
|
currentPlugin.value = currentPlugins[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskSave = () => {
|
||||||
|
console.log('currentTask', currentTask)
|
||||||
|
// context.emit('update', currentTask.value)
|
||||||
|
taskDrawerClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
taskTypeSelected,
|
||||||
|
taskTypeSave,
|
||||||
|
taskPluginDefineList,
|
||||||
|
taskFormRef,
|
||||||
|
taskAdd,
|
||||||
|
taskEdit,
|
||||||
|
taskDrawerShow,
|
||||||
|
taskDrawerVisible,
|
||||||
|
taskDrawerOnAfterVisibleChange,
|
||||||
|
currentTask,
|
||||||
|
currentTaskIndex,
|
||||||
|
currentPlugin,
|
||||||
|
taskSave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useProviderManager () {
|
||||||
|
const providerManager = ref(null)
|
||||||
|
const providerManagerOpen = () => {
|
||||||
|
providerManager.value.open()
|
||||||
|
}
|
||||||
|
return { providerManager, providerManagerOpen }
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'task-form',
|
||||||
|
components: { ProviderManager },
|
||||||
|
emits: ['update'],
|
||||||
|
props: {
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
setup (props, context) {
|
||||||
|
return {
|
||||||
|
...useTaskForm(context),
|
||||||
|
...useProviderManager(),
|
||||||
|
labelCol: { span: 6 },
|
||||||
|
wrapperCol: { span: 16 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.task-edit-form{
|
||||||
|
.body{
|
||||||
|
padding:10px;
|
||||||
|
.ant-card {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&.current {
|
||||||
|
border-color: #00B7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-card-meta-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-avatar {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-left: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 1;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 14px;
|
||||||
|
height: 100px;
|
||||||
|
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
.ant-card-meta-description {
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 20px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,406 @@
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div class="page-detail">
|
||||||
|
|
||||||
|
<div class="flow">
|
||||||
|
<div class="flow-group flow-cert">
|
||||||
|
<h3 class="group-head">
|
||||||
|
证书申请
|
||||||
|
</h3>
|
||||||
|
<a-divider></a-divider>
|
||||||
|
<div class="cert-display">
|
||||||
|
<a-button class="cert-edit-btn" type="link" @click="certFormOpen">
|
||||||
|
编辑
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<div class="label-list">
|
||||||
|
<div class="title">证书</div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>域名:</label>
|
||||||
|
<div class="value">
|
||||||
|
<a-tag type="primary" v-for="item of options.cert.domains " :key="item">
|
||||||
|
{{ item }}
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="label-item">
|
||||||
|
<label>邮箱:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.email }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>CA:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.ca }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title">CSR
|
||||||
|
<span>必须全英文</span></div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>country:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.csr.country }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>state:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.csr.state }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>locality:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.csr.locality }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>org:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.csr.organization }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="label-item">
|
||||||
|
<label>orgUnit:</label>
|
||||||
|
<div>
|
||||||
|
{{ options.cert.csr.organizationUnit }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flow-group flow-deploy">
|
||||||
|
<h3 class="group-head">
|
||||||
|
部署流程
|
||||||
|
<PlusCircleOutlined title="添加部署流程" class="add-icon" @click="deployAdd"/>
|
||||||
|
</h3>
|
||||||
|
<a-divider></a-divider>
|
||||||
|
<div class="deploy-list">
|
||||||
|
|
||||||
|
<a-card class="deploy-item" v-for="(deploy,index) of options.deploy" :key="index">
|
||||||
|
<template #title>
|
||||||
|
<div class="deploy-name">
|
||||||
|
<template v-if="deploy._isEdit">
|
||||||
|
<a-input v-model:value="deploy.deployName"
|
||||||
|
:validateStatus="deploy.deployName?'':'error'"
|
||||||
|
placeholder="请输入流程名称"
|
||||||
|
@keyup.enter="deployCloseEditMode(deploy)"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<CheckOutlined @click="deployCloseEditMode(deploy)" style="color: rgba(0,0,0,.45)"/>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span @click="deployNameEdit"> <NodeIndexOutlined/> {{ deploy.deployName }}</span>
|
||||||
|
<EditOutlined class="ml-10 edit-icon" @click="deployOpenEditMode(deploy)"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="task-list">
|
||||||
|
<div class="task-item-wrapper" v-for="(task,iindex) of deploy.tasks" :key="iindex">
|
||||||
|
<a-button class="task-item" shape="round" @click="taskEdit(deploy,task,index)">
|
||||||
|
<ThunderboltOutlined/>
|
||||||
|
{{ task.taskName }}
|
||||||
|
</a-button>
|
||||||
|
<ArrowRightOutlined class="task-next-icon"/>
|
||||||
|
</div>
|
||||||
|
<div class="task-item-wrapper">
|
||||||
|
<a-button type="primary" class="task-item" shape="round" @click="taskAdd(deploy)">
|
||||||
|
<PlusOutlined/>
|
||||||
|
添加新任务
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<cert-form ref="certFormRef" v-model:cert="options.cert" v-model:access-providers="options.accessProviders"></cert-form>
|
||||||
|
|
||||||
|
<task-form ref="taskFormRef" ></task-form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { reactive, ref, toRef } from 'vue'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import CertForm from '@/views/detail/components/cert-form'
|
||||||
|
import TaskForm from './components/task-form'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
|
||||||
|
function useDeploy (options) {
|
||||||
|
const deployAdd = () => {
|
||||||
|
options.deploy.push({
|
||||||
|
deployName: `D${options.deploy.length + 1}-新部署流程`,
|
||||||
|
_isEdit: false,
|
||||||
|
tasks: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deployCloseEditMode = (deploy) => {
|
||||||
|
if (!deploy.deployName) {
|
||||||
|
message.error('请输入流程名称')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
deploy._isEdit = false
|
||||||
|
console.log('options', options)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deployOpenEditMode = (deploy) => {
|
||||||
|
deploy._isEdit = true
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
deployAdd, deployCloseEditMode, deployOpenEditMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { CertForm, TaskForm },
|
||||||
|
setup () {
|
||||||
|
const route = useRoute()
|
||||||
|
console.log('route', route)
|
||||||
|
const optionParams = route.params.options ? JSON.parse(route.params.options) : {}
|
||||||
|
const optionsDefault = {
|
||||||
|
cert: {
|
||||||
|
csr: {
|
||||||
|
country: 'CN',
|
||||||
|
state: 'GuangDong',
|
||||||
|
locality: 'ShengZhen',
|
||||||
|
organization: 'CertD Org.',
|
||||||
|
organizationUnit: 'IT Department'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accessProviders: [{ key: 'aliyun', type: 'aliyun', name: 'aliyun' }],
|
||||||
|
deploy: []
|
||||||
|
}
|
||||||
|
_.merge(optionsDefault, optionParams)
|
||||||
|
const options = reactive(optionsDefault)
|
||||||
|
|
||||||
|
const certFormChanged = (value) => {
|
||||||
|
console.log('certFormChanged', value)
|
||||||
|
options.cert = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const certFormRef = ref(null)
|
||||||
|
const certFormOpen = () => {
|
||||||
|
certFormRef.value.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskFormRef = ref(null)
|
||||||
|
const taskAdd = (deploy) => {
|
||||||
|
taskFormRef.value.taskAdd(deploy)
|
||||||
|
}
|
||||||
|
const taskEdit = (deploy, task, index) => {
|
||||||
|
taskFormRef.value.taskEdit(deploy, task, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
options,
|
||||||
|
certFormChanged,
|
||||||
|
certFormRef,
|
||||||
|
certFormOpen,
|
||||||
|
...useDeploy(options),
|
||||||
|
taskFormRef,
|
||||||
|
taskAdd,
|
||||||
|
taskEdit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.page-detail {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
|
.label-list {
|
||||||
|
.title{
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
color:#555;
|
||||||
|
span{
|
||||||
|
font-weight: 200;
|
||||||
|
margin-left:5px;
|
||||||
|
font-size: 12px;
|
||||||
|
color:#888;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.label-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: baseline;
|
||||||
|
padding: 8px 0px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
width: 70px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5715;
|
||||||
|
text-align: end;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.flow-group {
|
||||||
|
min-width: 300px;
|
||||||
|
height: 100%;
|
||||||
|
border-right: 1px #eee solid;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.group-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
margin-left: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #737070;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-cert {
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
.cert-display {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.cert-edit-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-deploy {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.deploy-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.deploy-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// min-width:70%;
|
||||||
|
.deploy-name {
|
||||||
|
max-width: 300px;
|
||||||
|
|
||||||
|
.edit-icon {
|
||||||
|
color: #737070;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-form.task-form {
|
||||||
|
padding: 10px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-item-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-item {
|
||||||
|
//border: 1px solid #eee;
|
||||||
|
//padding: 10px 20px;
|
||||||
|
//border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-add-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-next-icon {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-type-selector {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-form {
|
||||||
|
.task-plugin-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-plugin {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import { shallowMount } from '@vue/test-utils'
|
||||||
|
import HelloWorld from '@/components/HelloWorld.vue'
|
||||||
|
|
||||||
|
describe('HelloWorld.vue', () => {
|
||||||
|
it('renders props.msg when passed', () => {
|
||||||
|
const msg = 'new message'
|
||||||
|
const wrapper = shallowMount(HelloWorld, {
|
||||||
|
props: { msg }
|
||||||
|
})
|
||||||
|
expect(wrapper.text()).to.include(msg)
|
||||||
|
})
|
||||||
|
})
|
|
@ -12,15 +12,19 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
|
||||||
type: String,
|
type: String,
|
||||||
desc: 'accessKeyId',
|
desc: 'accessKeyId',
|
||||||
attrs: {
|
attrs: {
|
||||||
placeholder: 'accessKeyId'
|
placeholder: 'accessKeyId',
|
||||||
}
|
rules: [{ required: true, message: '必填项' }]
|
||||||
|
},
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
accessKeySecret: {
|
accessKeySecret: {
|
||||||
type: String,
|
type: String,
|
||||||
desc: 'accessKeySecret',
|
desc: 'accessKeySecret',
|
||||||
attrs: {
|
attrs: {
|
||||||
placeholder: 'accessKeySecret'
|
placeholder: 'accessKeySecret',
|
||||||
|
rules: [{ required: true, message: '必填项' }]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|
Loading…
Reference in New Issue