A - 通用 - 添加系统设置,添加发布钉钉通知。

pull/11/head
张玉坡 2019-04-21 18:58:23 +08:00
parent a614e377ed
commit 892db7b9e1
27 changed files with 1183 additions and 30 deletions

View File

@ -16,6 +16,7 @@ args = AttrDict(
group=Argument('group', help='请选择分组名称!'),
name=Argument('name', help='请输入应用名称!'),
desc=Argument('desc', help='请输入应用描述!'),
notify_way_id=Argument('notify_way_id', type=int, help='请选择通知方式!'),
identify=Argument('identify', help='请输入应用标识!'),
image_id=Argument('image_id', type=int, help='请选择应用使用的Docker镜像')
)
@ -36,7 +37,13 @@ def get():
apps = query.filter(App.id.in_(app_ids.split(','))).all()
else:
apps = []
return json_response(apps)
data_list = []
for i in apps:
data = i.to_json()
data['notify_way_name'] = i.notify_way.name if i.notify_way else ''
data['images'] = i.image.name
data_list.append(data)
return json_response(data_list)
@blueprint.route('/', methods=['POST'])

View File

@ -13,6 +13,7 @@ blueprint = Blueprint(__name__, __name__)
def get():
return json_response(Image.query.all())
@blueprint.route('/add', methods=['POST'])
@require_permission('publish_image_add')
def add():

View File

@ -1,5 +1,6 @@
from public import db
from libs.model import ModelMixin
from apps.system.models import NotifyWay
class Image(db.Model, ModelMixin):
@ -73,8 +74,10 @@ class App(db.Model, ModelMixin):
group = db.Column(db.String(50))
image_id = db.Column(db.Integer, db.ForeignKey('deploy_images.id'))
notify_way_id = db.Column(db.Integer, db.ForeignKey('notify_way.id'))
image = db.relationship(Image)
notify_way = db.relationship(NotifyWay)
menus = db.relationship('DeployMenu', secondary='deploy_app_menu_rel')
fields = db.relationship('DeployField', secondary='deploy_app_field_rel')
@ -120,6 +123,7 @@ class DeployMenu(db.Model, ModelMixin):
def __repr__(self):
return '<DeployMenu %r>' % self.name
class AppMenuRel(db.Model, ModelMixin):
__tablename__ = 'deploy_app_menu_rel'

View File

@ -15,6 +15,9 @@ import tarfile
import uuid
import time
import os
from apps.system.models import NotifyWay
from libs.utils import send_ding_msg
blueprint = Blueprint(__name__, __name__)
@ -113,10 +116,12 @@ def do_update(q, form, host_id):
send_message('启动容器成功!', update=True)
# 执行发布操作
send_message('正在执行应用更新 . . . ')
send_publish_message(pro.notify_way_id, pro.name + ' 开始更新 . . .')
exec_code, exec_output = ctr.exec_command_with_base64(hooks['应用发布'], form.deploy_message, timeout=120,
with_exit_code=True)
if exec_code != 0:
send_message('执行应用更新失败,退出状态码:{0}'.format(exec_code), level='error')
send_publish_message(pro.notify_way_id, pro.name + ' 发布失败!')
send_message(exec_output, level='console')
return
else:
@ -127,6 +132,7 @@ def do_update(q, form, host_id):
ctr.restart(timeout=3)
send_message('重启容器成功!', update=True)
# 整个流程正常结束
send_publish_message(pro.notify_way_id, pro.name + ' 发布成功')
send_message('完成发布!', level='success')
deploy_success = True
except Exception as e:
@ -154,3 +160,9 @@ class PublishMessage(object):
cls.start_time = time.time()
data['duration'] = duration
q.put(data)
def send_publish_message(notify_way_id, message):
if notify_way_id:
notice_value = NotifyWay.query.filter_by(id=notify_way_id).first()
send_ding_msg(token=notice_value.value, contacts=[], msg=message)

View File

@ -12,3 +12,15 @@ class GlobalConfig(db.Model, ModelMixin):
def __repr__(self):
return '<GlobalConfig %r>' % self.name
class NoticeWay(db.Model, ModelMixin):
__tablename__ = 'notice_way'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True)
value = db.Column(db.Text)
desc = db.Column(db.String(255))
def __repr__(self):
return '<NoticeWay %r>' % self.name

View File

@ -0,0 +1,5 @@
from apps.system import notify
def register_blueprint(app):
app.register_blueprint(notify.blueprint, url_prefix='/system/notify')

View File

@ -0,0 +1,14 @@
from public import db
from libs.model import ModelMixin
class NotifyWay(db.Model, ModelMixin):
__tablename__ = 'notify_way'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True)
value = db.Column(db.Text)
desc = db.Column(db.String(255))
def __repr__(self):
return '<NotifyWay %r>' % self.name

View File

@ -0,0 +1,60 @@
from flask import Blueprint, request
from libs.tools import json_response, JsonParser, Argument
from .models import NotifyWay
from libs.decorators import require_permission
blueprint = Blueprint(__name__, __name__)
@blueprint.route('/', methods=['GET'])
@require_permission('system_notify_view | system_notify_add | system_notify_edit | system_notify_del')
def get():
form, error = JsonParser(Argument('page', type=int, default=1, required=False),
Argument('pagesize', type=int, default=10, required=False),
Argument('notify_query', type=dict, required=False), ).parse(request.args)
if error is None:
notify_data = NotifyWay.query
if form.page == -1:
return json_response({'data': [x.to_json() for x in notify_data.all()], 'total': -1})
if form.notify_query.get('name_field'):
notify_data = notify_data.filter(NotifyWay.name.like('%{}%'.format(form.notify_query['name_field'])))
result = notify_data.limit(form.pagesize).offset((form.page - 1) * form.pagesize).all()
return json_response({'data': [x.to_json() for x in result], 'total': notify_data.count()})
return json_response(message=error)
@blueprint.route('/', methods=['POST'])
@require_permission('system_notify_add')
def post():
form, error = JsonParser('name', 'value',
Argument('desc', nullable=True)).parse()
if error is None:
notify_is_exist = NotifyWay.query.filter_by(name=form.name).first()
if notify_is_exist:
return json_response(message="通知名称已存在")
NotifyWay(**form).save()
return json_response()
return json_response(message=error)
@blueprint.route('/<int:u_id>', methods=['DELETE'])
@require_permission('system_notify_del')
def delete(u_id):
NotifyWay.query.get_or_404(u_id).delete()
return json_response(), 204
@blueprint.route('/<int:n_id>', methods=['PUT'])
@require_permission('system_notify_edit')
def put(n_id):
form, error = JsonParser('name', 'value',
Argument('desc', nullable=True)).parse()
if error is None:
notify_info = NotifyWay.query.get_or_404(n_id)
if not notify_info.update(**form):
notify_info.save()
return json_response(notify_info)
return json_response(message=error)

View File

@ -112,4 +112,10 @@ INSERT INTO account_permissions (id, name, `desc`) VALUES (1202, 'publish_field_
INSERT INTO account_permissions (id, name, `desc`) VALUES (1203, 'publish_field_edit', '自定义字段 - 编辑');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1204, 'publish_field_del', '自定义字段 - 删除');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1205, 'publish_field_rel_view', '关联配置 - 查看');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1206, 'publish_field_rel_edit', '关联配置 - 编辑');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1206, 'publish_field_rel_edit', '关联配置 - 编辑');
-- 系统管理 -> 通知设置
INSERT INTO account_permissions (id, name, `desc`) VALUES (1301, 'system_notify_view', '系统通知列表');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1302, 'system_notify_add', '添加通知设置');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1303, 'system_notify_edit', '编辑通知设置');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1304, 'system_notify_del', '删除通知设置');

View File

@ -0,0 +1,20 @@
-- start update 1.0.0 --
CREATE TABLE `notify_way` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`desc` varchar(255) DEFAULT NULL,
`value` text,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
ALTER TABLE `spug`.`deploy_apps` ADD COLUMN `notify_way_id` int(11) NULL DEFAULT NULL AFTER `group`;
ALTER TABLE `spug`.`deploy_apps` ADD CONSTRAINT `deploy_apps_ibfk_2` FOREIGN KEY (`notify_way_id`) REFERENCES `spug`.`notify_way` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE `spug`.`deploy_apps` ADD INDEX `notify_way_id`(`notify_way_id`) USING BTREE;
INSERT INTO account_permissions (id, name, `desc`) VALUES (1301, 'system_notify_view', '系统通知列表');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1302, 'system_notify_add', '添加通知设置');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1303, 'system_notify_edit', '编辑通知设置');
INSERT INTO account_permissions (id, name, `desc`) VALUES (1304, 'system_notify_del', '删除通知设置');
-- end update 1.0.0 --

View File

@ -223,3 +223,21 @@ class Git(object):
def __repr__(self):
return '<Git %r>' % self.work_tree
def send_ding_msg(token_url='', contacts=[], msg=''):
payload = {
"msgtype": "text",
"text": {
"content": msg,
"isAtAll": False
},
"at": {
"atMobiles": contacts
}
}
req = requests.post(token_url, json=payload)
if req.status_code == 200:
return True
else:
return False

View File

@ -9,6 +9,7 @@ from apps import apis
from apps import schedule
from apps import home
from apps import common
from apps import system
middleware.init_app(app)
account.register_blueprint(app)
@ -19,6 +20,7 @@ apis.register_blueprint(app)
schedule.register_blueprint(app)
home.register_blueprint(app)
common.register_blueprint(app)
system.register_blueprint(app)
if __name__ == '__main__':

View File

@ -9,7 +9,7 @@ click==6.7
cryptography==2.0.3
docker==2.5.1
docker-pycreds==0.2.1
Flask==0.12.2
Flask ~> 0.12.3
Flask-Excel==0.0.7
Flask-SQLAlchemy==2.2
idna==2.6

View File

@ -187,6 +187,13 @@
<tag-td :item="codes['job_task_log']"></tag-td>
<td></td>
</tr>
<tr>
<td rowspan="1">系统管理</td>
<td rowspan="1">通知设置</td>
<tag-td :item="codes['system_notify_view']"></tag-td>
<tag-td :item="codes['system_notify_add']"></tag-td>
<tag-td :item="codes['system_notify_edit']"></tag-td>
</tr>
</table>
<el-table v-else v-loading="loading"></el-table>
<div slot="footer">

View File

@ -1,5 +1,6 @@
/**
* Created by aka on 2017/5/22.
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import User from './User.vue';

View File

@ -1,5 +1,6 @@
/**
* Created by aka on 2017/5/22.
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import Host from './Host.vue';

View File

@ -1,5 +1,6 @@
/**
* Created by aka on 2017/5/22.
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import App from './App.vue'
import Service from './Service.vue'

View File

@ -14,19 +14,31 @@
<el-table :data="tableData" v-loading="tableLoading" style="width: 100%; margin-top: 20px">
<el-table-column prop="group" label="分组" min-width="100"></el-table-column>
<el-table-column prop="name" label="名称" min-width="100"></el-table-column>
<el-table-column prop="notify_way_name" label="通知名称"></el-table-column>
<el-table-column prop="desc" label="描述" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button size="small" type="primary" @click="go_deploy(scope.row)" style="margin-right: 15px">管理
</el-button>
<el-dropdown trigger="click" @command="do_action" v-if="has_permission('publish_app_edit|publish_app_del|publish_app_ctr_view|publish_app_var_view|publish_app_menu_view')">
<el-dropdown trigger="click" @command="do_action"
v-if="has_permission('publish_app_edit|publish_app_del|publish_app_ctr_view|publish_app_var_view|publish_app_menu_view')">
<el-button type="text">更多<i class="el-icon-caret-bottom el-icon--right"></i></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-if="has_permission('publish_app_edit')" :command="`edit ${scope.$index}`"></el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_del')" :command="`del ${scope.$index}`"></el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_ctr_view')" divided :command="`set ${scope.$index}`"></el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_var_view')" :command="`env ${scope.$index}`"></el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_menu_view')" :command="`menu ${scope.$index}`"></el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_edit')"
:command="`edit ${scope.$index}`">编辑
</el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_del')" :command="`del ${scope.$index}`">
删除
</el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_ctr_view')" divided
:command="`set ${scope.$index}`">容器设置
</el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_var_view')"
:command="`env ${scope.$index}`">应用设置
</el-dropdown-item>
<el-dropdown-item v-if="has_permission('publish_app_menu_view')"
:command="`menu ${scope.$index}`">菜单管理
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
@ -43,6 +55,13 @@
<el-form-item label="应用名称" required>
<el-input v-model="form.name" placeholder="请输入应用名称"></el-input>
</el-form-item>
<el-form-item label="通知名称" required>
<el-select v-model="form.notify_way_id" placeholder="选择通知名称">
<el-option v-for="n in notifyWays" :value="n.id" :label="n.name"
:key="n.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="应用描述" required>
<el-input type="textarea" autosize v-model="form.desc" placeholder="请输入应用描述"></el-input>
</el-form-item>
@ -62,8 +81,10 @@
<el-button type="primary" @click="saveCommit" :loading="btnSaveLoading">保存</el-button>
</div>
</el-dialog>
<app-setting v-if="dialogSetVisible" :owner="form" :environments="environments" @close="dialogSetVisible = false"></app-setting>
<app-config v-if="dialogEnvVisible" :owner="form" :environments="environments" @close="dialogEnvVisible = false"></app-config>
<app-setting v-if="dialogSetVisible" :owner="form" :environments="environments"
@close="dialogSetVisible = false"></app-setting>
<app-config v-if="dialogEnvVisible" :owner="form" :environments="environments"
@close="dialogEnvVisible = false"></app-config>
<app-menu v-if="dialogMenuVisible" :owner="form" @close="dialogMenuVisible = false"></app-menu>
</div>
</template>
@ -73,13 +94,14 @@
import AppSetting from './AppSetting.vue'
import AppConfig from './AppConfig.vue'
import AppMenu from './AppMenu.vue'
export default {
components: {
'app-setting': AppSetting,
'app-config': AppConfig,
'app-menu': AppMenu
},
data () {
data() {
return {
group: null,
dialogAddVisible: false,
@ -92,23 +114,25 @@
environments: undefined,
tableData: [],
images: [],
notifyWays: [],
groups: [],
}
},
methods: {
init_form () {
init_form() {
return {
identify: '',
name: '',
desc: '',
group: '',
notify_way_id: '',
image_id: ''
}
},
go_deploy (row) {
go_deploy(row) {
this.$router.push({name: 'publish_deploy', params: {app_id: row.id}})
},
fetch (force) {
fetch(force) {
this.tableLoading = true;
let api_uri = '/api/deploy/apps/';
if (this.group) api_uri += '?group=' + this.group;
@ -119,28 +143,36 @@
this.groups = res.result
}, res => this.$layer_message(res.result))
},
fetchEnvironments () {
fetchEnvironments() {
if (this.environments === undefined) {
this.$http.get('/api/configuration/environments/').then(res => {
this.environments = res.result
}, res => this.$layer_message(res.result))
}
},
fetchImages () {
fetchImages() {
this.$http.get('/api/deploy/images/').then(res => this.images = res.result, res => this.$layer_message(res.result))
},
do_action (command) {
fetchNotifyWay() {
this.$http.get('/api/system/notify/', {params: {page: -1}}).then(res=>{
this.notifyWays = res.result.data;
}, res => this.$layer_message(res.result))
},
do_action(command) {
let [action, index] = command.split(' ');
this.form = this.$deepCopy(this.tableData[index]);
if (action === 'edit') {
if (this.images.length === 0) this.fetchImages();
this.dialogAddVisible = true
this.dialogAddVisible = true;
this.fetchNotifyWay();
} else if (action === 'del') {
this.$confirm(`此操作将永久删除 ${this.form.name},是否继续?`, '删除确认', {type: 'warning'}).then(() => {
this.$http.delete(`/api/deploy/apps/${this.form.id}`).then(() => {
this.fetch()
}, res => this.$layer_message(res.result))
}).catch(() => {})
}).catch(() => {
})
} else if (action === 'set') {
this.fetchEnvironments();
this.dialogSetVisible = true
@ -151,7 +183,7 @@
this.dialogMenuVisible = true
}
},
saveCommit () {
saveCommit() {
this.btnSaveLoading = true;
let request;
if (this.form.id) {
@ -164,12 +196,13 @@
this.fetch(true)
}, res => this.$layer_message(res.result)).finally(() => this.btnSaveLoading = false)
},
addOpen () {
addOpen() {
// this.$router.push({name: 'app_add'})
this.form = this.init_form();
this.dialogAddVisible = true;
if (this.images.length === 0) this.fetchImages()
},
addGroup () {
addGroup() {
this.$prompt('请输入新分组名称', '提示', {
inputPattern: /.+/,
inputErrorMessage: '请输入分组名称!'
@ -183,7 +216,7 @@
this.$emit('routerChange')
}
},
created () {
created() {
this.fetch(true)
}
}

View File

@ -0,0 +1,623 @@
<template>
<div>
<el-steps :active="appStep">
<el-step title="项目信息" description="基本信息"></el-step>
<el-step title="容器设置" description="容器启动端口,目录,主机名等设置"></el-step>
<el-step title="容器变量" description="容器内部参数变量设置"></el-step>
<el-step title="发布参数" description="容器启动,项目更新部署相关设置"></el-step>
<el-step title="主机设置" description="部署的主机"></el-step>
<el-step title="完成" description="确认配置信息"></el-step>
</el-steps>
<p style="text-align: center; margin: 30px 0 20px"></p>
<div :style="base_display">
<el-form ref="appInfo" :model="appInfo" :rules="baseRules" label-width="80px">
<el-form-item label="项目类型" prop="group" required>
<el-select v-model="appInfo.group" placeholder="项目类型">
<el-option v-for="v in group_options" :value="v" :key="v"></el-option>
</el-select>
<el-button style="margin-left: 15px" type="text" @click="addAppType"></el-button>
</el-form-item>
<el-form-item prop="name" label="项目名称" required>
<el-input v-model="appInfo.name" auto-complete="off" ></el-input>
</el-form-item>
<el-form-item label="关联镜像" >
<el-cascader placeholder="输入要搜索的镜像" clearable
:options="app_images_options" filterable
@focus="fetch_images_options"
@change="bindImageTag"
@active-item-change="loadImageTag" >
<!--@active-item-change="loadImageTag" >-->
</el-cascader>
</el-form-item>
<el-form-item prop="desc" label="项目描述">
<el-input v-model="appInfo.desc" auto-complete="off"></el-input>
</el-form-item>
</el-form>
</div>
<!--<p style="text-align: center; margin: 20px 0 20px"></p>-->
<div>
<el-table :data="dockerData" border style="width: 100%" :style="docker_display">
<el-table-column fixed prop="desc" label="名称" ></el-table-column>
<el-table-column v-for="item in this.environments" :key="item.id" :label="item.name" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.value[item.id]">{{scope.row.value[item.id]}}</span>
<span v-else style="color: #D3DCE6">{{scope.row.tip}}</span>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="80px" v-if="has_permission('publish_app_ctr_edit')">
<template slot-scope="scope">
<el-button size="small" type="primary"
@click="dockerForm = scope.row; dockerVisible = true">编辑
</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :title="`设置 - ${dockerForm.desc}`" :visible.sync="dockerVisible" :close-on-click-modal="false">
<el-form :model="dockerForm" label-width="80px" label-position="left">
<el-form-item v-for="env in this.environments" :key="env.id" :label="env.name">
<el-input v-model="dockerForm.value[env.id]" :placeholder="dockerForm.tip"></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="dockerVisible = false">取消</el-button>
<el-button type="primary" @click="dockerVisible = false" :loading="dockerSaveLoading">保存</el-button>
</div>
</el-dialog>
</div>
<div :style="env_display">
<el-table :data="envData" border style="width: 100%" >
<el-table-column fixed prop="name" label="名称" show-overflow-tooltip></el-table-column>
<el-table-column v-for="item in this.environments" :key="item.id" :label="item.name" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.value[item.id]">{{scope.row.value[item.id]}}</span>
</template>
</el-table-column>
<el-table-column prop="desc" label="描述" show-overflow-tooltip></el-table-column>
<el-table-column fixed="right" label="操作" v-if="has_permission('publish_app_var_edit|publish_app_var_del')">
<template slot-scope="scope">
<el-button v-if="has_permission('publish_app_var_edit')" size="small" @click="editEnv(scope.row)"></el-button>
<el-button v-if="has_permission('publish_app_var_del')" size="small" type="danger" @click="delEnv(scope.row)"
:loading="envDelLoading[scope.row.id]">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<p style="text-align: center; margin: 20px 0 20px"></p>
<div slot="footer" style="text-align: right">
<el-button v-if="has_permission('publish_app_var_add')" type="primary" @click="addEnv"></el-button>
</div>
<el-dialog title="编辑配置" :visible.sync="envVisible" :close-on-click-modal="false">
<el-form :model="envForm" label-width="80px" label-position="left">
<el-form-item label="名称" required>
<el-input v-model="envForm.name" placeholder="请输入变量名称"></el-input>
</el-form-item>
<el-form-item label="描述" >
<el-input v-model="envForm.desc" type="textarea" placeholder="请输入变量描述"></el-input>
</el-form-item>
<el-form-item v-for="env in environments" :key="env.id" :label="env.name" required>
<el-input v-model="envForm.value[env.id]" placeholder="请输入变量值"></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="envVisible = false">取消</el-button>
<el-button type="primary" @click="envSave" :loading="envSaveLoading">保存</el-button>
</div>
</el-dialog>
</div>
<div :style="menu_display">
<el-table :data="menuData" >
<el-table-column prop="name" label="菜单名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="desc" label="菜单描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="command" label="菜单命令" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" v-if="has_permission('publish_app_menu_view')">
<template slot-scope="scope">
<el-button size="small" type="primary" @click="menuEditOpen(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="应用发布" :title="menuDialogTitle" :visible.sync="menuVisible" :close-on-click-modal="false">
<el-form :model="menuForm" label-width="80px" label-position="left">
<el-form-item label="命令内容">
<color-input v-model="menuForm.command"></color-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="menuVisible = false">取消</el-button>
<el-button v-if="has_permission('publish_app_menu_edit')" type="primary" @click="menuSave"
:loading="menuSaveLoading">保存</el-button>
</div>
</el-dialog>
</div>
<host-relationship :style="host_display" :owner="environments" ></host-relationship>
<!--<template>-->
<!--&lt;!&ndash;<div style="text-align: left" :style="host_display">&ndash;&gt;-->
<!--<div :style="host_display">-->
<!--<el-select placeholder="请选择环境" v-model="env_id" style="margin-bottom: 15px" >-->
<!--<el-option v-for="item in environments" :key="item.id" :value="item.id" :label="item.name"></el-option>-->
<!--</el-select>-->
<!--<el-transfer-->
<!--v-model="deploy_host" :props="{key: 'key',label: 'name'}"-->
<!--filterable-->
<!--:titles="['未选主机', '已选主机']"-->
<!--:filter-method="hostSearch"-->
<!--:data="ecs_host" >-->
<!--&lt;!&ndash;<span slot-scope="{ option }">{{ option.key }} - [ {{ option.name }} ]</span>&ndash;&gt;-->
<!--</el-transfer>-->
<!--</div>-->
<!--</template>-->
<div :style="complete_display">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>项目信息:</span>
</div>
<div class="text item">
{{'项目类型 ' + appInfo.group }}
{{'项目名称 ' + appInfo.name }}
{{'关联镜像 ' + appInfo.image_tag_name }}
<p v-if="appInfo.desc">{{' ' + appInfo.desc }}</p>
</div>
</el-card>
<p style="text-align: center; margin: 5px 0 5px"></p>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>容器设置:</span>
</div>
<div class="text item">
<el-table :data="dockerData" border style="width: 100%" >
<el-table-column fixed prop="desc" label="名称" ></el-table-column>
<el-table-column v-for="item in this.environments" :key="item.id" :label="item.name" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.value[item.id]">{{scope.row.value[item.id]}}</span>
<span v-else style="color: #D3DCE6">{{scope.row.tip}}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<p style="text-align: center; margin: 5px 0 5px"></p>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>容器参数:</span>
</div>
<div class="text item">
<el-table :data="envData" border style="width: 100%" >
<el-table-column fixed prop="name" label="名称" show-overflow-tooltip></el-table-column>
<el-table-column v-for="item in this.environments" :key="item.id" :label="item.name" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.value[item.id]">{{scope.row.value[item.id]}}</span>
</template>
</el-table-column>
<el-table-column prop="desc" label="描述" show-overflow-tooltip></el-table-column>
</el-table>
</div>
</el-card>
<p style="text-align: center; margin: 5px 0 5px"></p>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>发布设置:</span>
</div>
<div class="text item">
<el-table :data="menuData" >
<el-table-column prop="name" label="菜单名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="desc" label="菜单描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="command" label="菜单命令" show-overflow-tooltip></el-table-column>
</el-table>
</div>
</el-card>
<p style="text-align: center; margin: 5px 0 5px"></p>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>主机信息:</span>
</div>
<div v-for="n in deploy_host" :key="n" class="text item">
{{'主机ID: ' + n }}
</div>
</el-card>
</div>
<el-dialog title="应用发布" :visible.sync="dialogDeployVisible" width="80%"
:close-on-click-modal="false">
<el-collapse :value="showItem" accordion>
<el-collapse-item v-for="item in updateHosts" :key="item.id" :name="item.id">
<template slot="title">
<el-tag :type="item.type" style="margin-right: 15px">{{item.instance_name}}</el-tag>
{{item.latest}}
</template>
<pre v-for="line in item.detail">{{line}}</pre>
</el-collapse-item>
</el-collapse>
</el-dialog>
<p style="text-align: center; margin: 20px 0 20px"></p>
<el-button-group>
<el-button type="primary" :style="base_display" @click="baseNext('appInfo')">
<i class="el-icon-arrow-right el-icon--right"></i></el-button>
<el-button type="primary" :style="docker_display" @click="basePrev" icon="el-icon-arrow-left">上一步</el-button>
<el-button type="primary" :style="docker_display" @click="dockerNext"><i class="el-icon-arrow-right el-icon--right"></i></el-button>
<el-button type="primary" :style="env_display" @click="dockePrev" icon="el-icon-arrow-left">上一步</el-button>
<el-button type="primary" :style="env_display" @click="envNext" >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
<el-button type="primary" :style="menu_display" @click="envPrev" icon="el-icon-arrow-left">上一步</el-button>
<el-button type="primary" :style="menu_display" @click="menuNext" >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
<el-button type="primary" :style="host_display" @click="menuPrev" icon="el-icon-arrow-left">上一步</el-button>
<el-button type="primary" :style="host_display" @click="host_next" >下一步<i class="el-icon-arrow-right el-icon--right"></i></el-button>
<el-button type="primary" :style="complete_display" @click="host_prev" icon="el-icon-arrow-left">上一步</el-button>
<el-button type="primary" :style="complete_display" :loading="CompleteLoading" @click="complete_next">
<i class="el-icon-arrow-right el-icon--right"></i></el-button>
<el-button type="primary" :style="dep_display" @click="dep_prev" icon="el-icon-arrow-left">上一步</el-button>
</el-button-group>
</div>
</template>
<style>
.transfer-footer {
margin-left: 20px;
padding: 6px 5px;
}
</style>
<script>
import ColorInput from '../publish/ColorInput.vue'
import HostRelationship from './HostRel.vue'
export default {
components: {
'color-input': ColorInput,
'host-relationship': HostRelationship
},
data () {
return {
appStep: 1,
ecs_host: [],
deploy_host: [],
CompleteLoading: false,
dockerSaveLoading: false,
envSaveLoading: false,
menuSaveLoading: false,
envDelLoading: false,
group_options:[],
app_images_options: [],
menuDialogTitle: '',
appInfo: {
group: '',
name: '',
},
images: [],
groups: [],
updateHosts: [],
dockerVisible: false,
menuVisible: false,
envVisible: false,
dialogDeployVisible: false,
base_display: '',
host_display: 'display:none',
complete_display: 'display:none',
env_display: 'display:none',
exec_display: 'display:none',
docker_display: 'display:none',
menu_display: 'display:none',
dep_display: 'display:none',
environments: undefined,
env_id: '',
exec_output: [],
exec_token: null,
dockerData: [
{value: {}, desc: '限制内存', name: '__MEM_LIMIT', tip: '默认无限制'},
{value: {}, desc: '网络模式', name: '__NETWORK_MODE', tip: 'default'},
{value: {}, desc: '映射端口', name: '__EXPOSE_PORT', tip: '示例127.0.0.1:80:3000'},
{value: {}, desc: '映射目录', name: '__BIND_VOLUME', tip: '示例:/home/user1:/mnt/vol1:ro'},
{value: {}, desc: 'DNS地址', name: '__DNS_SERVER', tip: '示例8.8.8.8;4.4.4.4'},
{value: {}, desc: '主机名称', name: '__HOST_NAME', tip: '默认随机名称'}
],
dockerForm: {
value: {}
},
envData: [],
// envData: [{value: {}, name: '', desc: ''}],
envForm: {
name: '',
desc: '',
value: {}
},
menuData: [
{command: '',name: "容器初始化", desc: '容器被创建后,执行的初始化命令'},
{command: '',name: "应用发布", desc: '点击发布按钮,执行的更新命令'},
{command: '/usr/sbin/nginx -g "daemon off;"',name: "容器启动", desc: '容器启动时,执行的命令'},
],
menuForm: {},
baseRules: {
group: [
{ required: true, message: '请选择项目类型', trigger: 'change' }
],
name: [
{ required: true, message: '请输入项目名称', trigger: 'blur' },
],
}
};
},
computed: {
showItem() {
return (this.updateHosts.length === 1) ? this.updateHosts[0].id : ''
},
deployForm() {
return {
appInfo: this.appInfo,
dockerData: this.dockerData,
envData: this.envData,
menuData: this.menuData,
host_ids: this.deploy_host.map(x => x.split(',')[0]),
env_id: this.env_id
// host_ids: this.deploy_host.map(x => x.id)
}
}
},
methods: {
get_ecs_host () {
// this.tableLoading = true;
this.$http.get(`/api/assets/hosts/`,{params: {page: -1, host_query: {}}}).then(res => {
console.log('res.result.data', res.result.data);
for (let n of res.result.data){
const name = `${n.name} - ${n.ssh_ip}`;
this.ecs_host.push({
key: n.id + ',' + name,
name: name,
// label: `${ n.instance_name }`
// "ip": n.primary_ip,
// "n_type": n.instance_network_type,
})
}
}, res => this.$layer_message(res.result)).finally(() => this.tableLoading = false)
},
hostSearch(query, item) {
if (item.name){
return item.name.indexOf(query) > -1;
}
},
handleChange(value, direction, movedKeys) {
console.log(13, this.selectd_ng_host);
console.log(value, 11, direction, 22, movedKeys);
},
fetchEnvironments () {
if (this.environments === undefined) {
this.$http.get('/api/configuration/environments/').then(res => {
this.environments = res.result
}, res => this.$layer_message(res.result))
}
},
fetchImages () {
this.$http.get('/api/deploy/images/').then(res => this.images = res.result, res => this.$layer_message(res.result))
},
// saveCommit () {
// console.log('savedata', this.appInfo, this.deploy_host);
//
// this.CompleteLoading = true;
// this.exec_display = 'display:block';
// this.$http.post('/api/deploy/pre_app/', {
// app_info: this.appInfo,
// deploy_host: this.deploy_host}).then(res => {
//
// console.log('res.result', res.result);
// this.exec_token = res.result;
// this.fetchExecResult();
// }, res => this.$layer_message(res.result)).finally(() => this.CompleteLoading = false)
//
// },
fetchExecResult() {
this.$http.get(`/api/common/queue/state/${this.exec_token}`).then(res => {
if (res.result['complete'] === true) return;
this.fetchExecResult(this.exec_token);
console.log('fetcheexecresult', res.result);
this.exec_output.push(res.result);
console.log('this.exec_output', this.exec_output);
}, res => this.$layer_message(res.result))
},
addOpen () {
this.form = this.init_form();
if (this.images.length === 0) this.fetchImages()
},
baseNext(baseName) {
this.$refs[baseName].validate((valid) => {
if (valid) {
this.base_display = 'display:none';
this.docker_display = 'display:block';
this.appStep = 1;
} else {
return false;
}
});
},
basePrev(){
this.base_display = 'display:block';
// this.host_display = 'display:none';
this.docker_display = 'display:none';
this.appStep = 0;
},
dockerNext(){
this.docker_display = 'display:none';
this.env_display = 'display:block';
this.appStep = 2;
},
dockePrev(){
this.docker_display = 'display:block';
this.env_display = 'display:none';
this.appStep = 1;
},
envNext(){
this.env_display = 'display:none';
this.menu_display = 'display:block';
this.appStep = 3;
},
envPrev(){
this.env_display = 'display:block';
this.menu_display = 'display:none';
this.appStep = 2;
},
menuNext(){
this.menu_display = 'display:none';
this.host_display = 'display:block';
this.appStep = 4;
},
menuPrev(){
this.menu_display = 'display:block';
this.host_display = 'display:none';
this.appStep = 3;
},
host_next(){
this.host_display = 'display:none';
this.complete_display = 'display:block';
this.appStep = 5;
},
host_prev(){
this.host_display = 'display:block';
this.complete_display = 'display:none';
this.appStep = 4;
},
dep_prev(){
this.complete_display = 'display:block';
},
complete_next(){
console.log(2003);
// this.complete_display = 'display:none';
// this.saveCommit()
// this.dep_display = 'display:block';
this.handleDeploy();
},
addGroup () {
this.$prompt('请输入新分组名称', '提示', {
inputPattern: /.+/,
inputErrorMessage: '请输入分组名称!'
}).then(({value}) => {
this.form.group = value
}).catch(() => {
})
},
addAppType () {
this.$prompt('请输入项目类型', '提示', {
inputPattern: /.+/,
inputErrorMessage: '请输入类型!'
}).then(({value}) => {
this.appInfo.group = value
}).catch(() => {
})
},
fetch_type_options(){
this.$http.get('/api/deploy/apps/groups/').then(res => {
this.group_options = res.result;
}, res => this.$layer_message(res.result))
},
fetch_images_options () {
this.$http.get('/api/deploy/images/').then(res => {
this.app_images_options = res.result.map(x => Object({label: x.name, value: x.id, children: []}));
}, res => this.$layer_message(res.result)).finally(() => this.tableLoading = false)
},
loadImageTag(val) {
console.log('appInfo', val);
this.$http.get(`/api/deploy/images/${val}/tags/`).then(res => {
for (let tag of this.app_images_options) {
if (tag.value == val) {
tag.children = res.result.map(x => Object({label: x.name, value: x.id}));
}
}
}, res => this.$layer_message(res.result))
},
fetchEnvironments () {
if (this.environments === undefined) {
this.$http.get('/api/configuration/environments/').then(res => {
this.environments = res.result
}, res => this.$layer_message(res.result))
}
},
bindImageTag(val){
this.appInfo['image_id'] = val[0];
this.appInfo['image_tag_id'] = val[1];
},
addEnv () {
this.envForm = { name: '', desc: '', value: {}};
this.envVisible = true
},
envSave(){
this.envVisible = false;
if (this.envData.length !== 0 ) {
for (let e of this.envData ){
if (e.name == this.envForm.name){
e = this.envForm;
return;
}
}
this.envData.push(this.envForm);
}else {
this.envData.push(this.envForm);
}
},
delEnv(row){
for (let x in this.envData){
if (this.envData[x].name == row.name){
this.envData.splice(x, 1)
}
}
},
editEnv(row){
this.envVisible = true;
this.envForm = row;
console.log('row', row);
},
menuEditOpen(row) {
this.menuForm = row;
this.menuDialogTitle = '菜单编辑 - ' + row['name'];
this.menuVisible = true;
},
menuSave(){
this.menuVisible = false;
console.log(this.menuData);
},
handleDeploy() {
this.dialogDeployVisible = true;
this.$http.post('/api/deploy/publish/deploy', this.deployForm).then(res => {
this.updateHosts = this.deploy_host.map(item => {
const [id, instance_name] = item.split(',', 2);
return {id, instance_name, type: 'info', detail: []}
});
this.fetchDeployResult(res.result);
}, res => this.$layer_message(res.result))
},
fetchDeployResult(token) {
this.$http.get(`/api/common/queue/state/${token}`).then(res => {
console.log('result', res.result);
if (res.result['complete'] === true) return;
this.fetchDeployResult(token);
for (let [index, item] of Object.entries(this.updateHosts)) {
if (item.id === res.result.hid) {
if (res.result.update === true) item.detail.pop();
item.detail.push(res.result.msg);
if (res.result.level !== 'console') item.latest = res.result.msg;
if (res.result.level === 'error') item.type = 'danger';
if (res.result.level === 'success') item.type = 'success';
this.$set(this.updateHosts, index, item);
break
}
}
}, res => this.$layer_message(res.result))
},
},
created () {
this.fetch_type_options();
// this.get_ecs_host();
this.fetchEnvironments();
}
}
</script>

View File

@ -0,0 +1,92 @@
<template>
<div>
<el-select v-model="type" style="margin-bottom: 15px" placeholder="请选择环境">
<el-option v-for="env in this.owner" :label="env.name" :value="env.id">
</el-option>
</el-select>
<el-transfer :titles="['未选主机', '已选主机']" v-if="type === ''" :data="ecs_host"></el-transfer>
<el-transfer v-for="item in this.owner" :titles="['未选主机', '已选主机']" v-if="type === item.id" v-model="app_result" :data="ecs_host"></el-transfer>
<!--<el-transfer :titles="['未选主机', '已选主机']" v-if="type === 'app'" v-model="app_result" :data="apps"></el-transfer>-->
<!--<el-transfer :titles="['未选主机', '已选主机']" v-if="type === 'service'" v-model="service_result" :data="services"></el-transfer>-->
<div slot="footer" v-if="has_permission('config_app_rel_edit')">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="saveCommit" :loading="btnSaveLoading">保存</el-button>
</div>
</div>
</template>
<script>
export default {
props: ['owner'],
data () {
return {
visible: true,
btnSaveLoading: false,
type: '',
app_result: [],
service_result: [],
apps: [],
services: [],
ecs_host: []
}
},
methods: {
get_ecs_host () {
console.log('this.owner', this.owner);
this.$http.get(`/api/assets/hosts/`,{params: {page: -1, host_query: {}}}).then(res => {
console.log('res.result.data', res.result.data);
for (let n of res.result.data){
const name = `${n.name} - ${n.ssh_ip}`;
this.ecs_host.push({
key: n.id,
label: name,
})
}
}, res => this.$layer_message(res.result)).finally(() => this.tableLoading = false)
},
// fetch_apps() {
// this.$http.get(`/api/deploy/apps/`).then(res => {
// for (let item of res.result) {
// if (item.id === this.owner.id) continue;
// this.apps.push({
// key: item.id,
// label: item.name,
// disabled: false
// })
// }
// }, res => this.$layer_message(res.result))
// },
fetch_services() {
this.$http.get(`/api/configuration/services/`).then(res => {
for (let item of res.result) {
this.services.push({
key: item.id,
label: item.name,
disabled: false
})
}
}, res => this.$layer_message(res.result))
},
fetch_relationship () {
this.$http.get(`/api/configuration/apps/${this.owner.id}/bind/relationship`).then(res => {
if (res.result.hasOwnProperty('app_ids')) this.app_result = res.result['app_ids'];
if (res.result.hasOwnProperty('service_ids')) this.service_result = res.result['service_ids']
}, res => this.$layer_message(res.result))
},
saveCommit () {
this.btnSaveLoading = true;
this.$http.post(`/api/configuration/apps/${this.owner.id}/bind/relationship`, {
app_ids: this.app_result,
service_ids: this.service_result
}).then(res => this.visible = false, res => this.$layer_message(res.result)).finally(() => this.btnSaveLoading = false)
}
},
created () {
// this.fetch_apps();
this.get_ecs_host();
// this.fetch_services();
// this.fetch_relationship()
}
}
</script>

View File

@ -1,8 +1,10 @@
/**
* Created by aka on 2017/5/22.
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import App from './App.vue'
import AppAdd from './AppAdd.vue'
import Deploy from './Deploy.vue'
import Image from './Image.vue'
import Field from './Field.vue'
@ -16,6 +18,14 @@ export default [
permission: 'publish_app_view'
}
},
{
path: 'app_add',
name: 'app_add',
component: AppAdd,
meta: {
permission: 'publish_app_add'
}
},
{
path: 'deploy/:app_id',
name: 'publish_deploy',

View File

@ -1,5 +1,6 @@
/**
* Created by aka on 2017/6/28.
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import Job from './Job.vue'

View File

@ -0,0 +1,187 @@
<template>
<div>
<el-row>
<el-col :span="16">
<el-form :inline="true" :model="notify_query">
<el-form-item>
<el-input clearable v-model="notify_query.name_field" placeholder="通知名称"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="search" @click="nameSearch()"></el-button>
</el-form-item>
</el-form>
</el-col>
<el-col :span="8" style="text-align: right">
<el-button @click="refresh()"></el-button>
<el-button v-if="has_permission('system_notify_add')" style="float: right" type="primary"
@click="handleAdd()">添加通知
</el-button>
</el-col>
</el-row>
<el-table :data="tableData.data" stripe border v-loading="listLoading" style="width: 100%"
:default-sort="{prop: 'username', order: 'descending'}">
<el-table-column type="index" width="60"></el-table-column>
<el-table-column prop="name" label="通知名称" sortable></el-table-column>
<el-table-column prop="value" label="通知URL" sortable></el-table-column>
<el-table-column prop="desc" label="备注"></el-table-column>
<el-table-column v-if="has_permission('system_notify_view|system_notify_add|system_notify_edit')" label="操作">
<template slot-scope="scope">
<el-button v-if="has_permission('system_notify_edit')" size="small" @click="handleEdit(scope.$index, scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<!--分页-->
<div class="pagination-bar" v-if="tableData.total > 10">
<el-pagination
@current-change="handleCurrentChange"
:current-page="currentPage" layout="total, prev, pager, next"
:total="tableData.total">
</el-pagination>
</div>
<!--编辑新增界面-->
<el-dialog :title="editFormTitle" :visible.sync="dialogShow" :close-on-click-modal="false">
<el-form ref="editForm" :model="editForm" :rules="rules" label-width="80px">
<el-form-item prop="name" label="通知名称">
<el-input v-model="editForm.name" ></el-input>
</el-form-item>
<el-form-item prop="value" label="通知URL" >
<el-input placeholder="输入钉钉webhook" type="textarea" :rows="3" v-model="editForm.value" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="desc" label="备注信息">
<el-input v-model="editForm.desc" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button type="text" @click.native="dialogShow = false">取消</el-button>
<el-button type="primary" :loading="editLoading" @click.native="editSubmit">保存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
notify_query: {
name_field: ''
},
error: '',
dialogShow: false,
tableData: {},
roles: undefined,
roles_map: {},
display: '',
listLoading: false,
btnDelLoading: {},
editFormTitle: '',
editLoading: false,
is_disabled: '',
currentPage: 1,
addForm: {
id: 0,
name: '',
value: '',
desc: '',
},
editForm: {},
rules: {
name: [
{required: true, message: '请输入通知名称', trigger: 'blur'}
],
value: [
{required: true, message: '请输入通知URL', trigger: 'blur'}
],
},
}
},
methods: {
handleCurrentChange(val) {
this.currentPage = val;
this.getNotify(this.currentPage);
},
//
nameSearch(){
this.currentPage = 1;
this.getNotify();
},
//
refresh(){
this.getNotify(this.currentPage);
},
//
getNotify(page, callback) {
if (!page) page = 1;
this.listLoading = true;
this.$http.get('/api/system/notify/', {
params: {notify_query: this.notify_query}
}).then((response) => {
this.tableData = response.result;
if (callback) callback(this)
}, (response) => this.$layer_message(response.result)).finally(() => this.listLoading = false)
},
//
handleAdd: function () {
this.is_disabled = false;
this.dialogShow = true;
this.editFormTitle = '添加通知';
this.display = 'display:block';
this.editForm = this.addForm;
},
//
handleEdit: function (index, row) {
this.is_disabled = true;
this.dialogShow = true;
this.editFormTitle = '编辑通知';
this.display = 'display:none';
//this.editForm = Object.assign({}, row);
this.editForm = this.$deepCopy(row);
},
EditData: function (row, msg, pwd) {
this.$http.put(`/api/account/users/${row.id}`, this.editForm).then(response => {
this.listLoading = false;
this.$layer_message(msg + pwd, 'success');
this.getUsers(this.currentPage);
}, response => this.$layer_message(response.result)).finally(() => this.btnDelLoading = {});
},
editSubmit: function () {
this.$refs.editForm.validate((valid) => {
if (valid) {
this.editLoading = true;
this.error = '';
if (this.editForm.id) {
this.$http.put(`/api/system/notify/${this.editForm.id}`, this.editForm).then(this.resp,
response => this.$layer_message(response.result)).finally(() => this.editLoading = false)
} else {
this.$http.post('/api/system/notify/', this.editForm).then(this.resp,
response => this.$layer_message(response.result)).finally(() => this.editLoading = false)
}
}
});
},
resp: function (response) {
this.editLoading = false;
this.$layer_message('提交成功', 'success');
this.editForm = {};
this.addForm = {role_id: ''};
this.dialogShow = false;
this.getNotify(this.currentPage);
}
},
mounted() {
this.getNotify();
}
}
</script>

View File

@ -0,0 +1,17 @@
/**
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import Notify from './Notify.vue';
export default [
{
path: 'notify',
component: Notify,
meta: {
permission: 'system_notify_view'
}
}
]

View File

@ -1,3 +1,7 @@
/**
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
// 环境配置
// 后端API接口地址

View File

@ -1,3 +1,7 @@
/**
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
let menu = {
menus: [
{
@ -44,6 +48,11 @@ let menu = {
{key: 'alarm_contact', desc: '报警联系人'},
]
},
{
key: '8', desc: '系统管理', icon: 'el-icon-setting', permission: 'system_notify_view', subs: [
{key: '/system/notify', permission: 'system_notify_view', desc: '通知设置'},
]
},
]
};

View File

@ -1,5 +1,6 @@
/**
* Created by Yooke on 2017/2/13.
* Created by zyupo on 2017/04/20.
* https://github.com/openspug/spug
*/
import Home from './components/Home.vue'
@ -12,6 +13,7 @@ import publish_routes from './components/publish/routes'
import configuration_routes from './components/configuration/routes'
import assets_routes from './components/assets/routes'
import schedule_routes from './components/schedule/routes'
import system_routes from './components/system/routes'
const routes = [
{
@ -44,6 +46,10 @@ const routes = [
path: 'schedule',
routes: schedule_routes
},
{
path: 'system',
routes: system_routes
},
{
path: '*',
redirect: '/'