a-table
<a-table
:columns="columns"
:dataSource="rows"
:pagination="pagination"
rowKey="id"
@change="getList"
:scroll="{ x: '100%' }"
:loading="loading"
size="middle"
>
<template #name="record">
{{ record.name }}
</template>
<template #action="record">
<a-dropdown>
<a class="ant-dropdown-link">
更多
<a-icon type="down" />
</a>
<template #overlay>
<a-menu @click="menuHandler($event, record)">
<a-menu-item key="auth" v-if="record.isCanSetUser">
授权其他用户
</a-menu-item>
<a-menu-item key="detail">
详情
</a-menu-item>
<a-menu-item key="update">
编辑
</a-menu-item>
<a-menu-item v-if="record.id !== 1" key="delete">
删除
</a-menu-item>
<a-menu-item key="copy">
复制
</a-menu-item>
<a-menu-item key="ping" v-if="record.status === 1">
ping
</a-menu-item>
<a-menu-item key="connect" v-if="record.status === 1">
测试连接
</a-menu-item>
<a-menu-item
key="bindKey"
v-if="
record.authType === MACHINE_AUTH_TYPE.SECRET_KEY.value
"
>
绑定秘钥
</a-menu-item>
<!-- <a-menu-item key="openEnv">
<a @click="$router.push(`/machine/env/${record.id}`)"
>环境变量</a
>
</a-menu-item> -->
<a-menu-item key="openMonitor">
<a
@click="
$router.push(`/machine/monitor/metrics/${record.id}`)
"
>机器监控</a
>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</a-table>
const columns = [
{
title: "序号",
key: "seq",
width: 65,
align: "center",
customRender: (text, record, index) => `${index + 1}`
},
{
title: "机器信息",
key: "name",
ellipsis: true,
scopedSlots: { customRender: "info" }
},
{
title: "操作",
key: "operation",
fixed: "right",
align: "center",
width: 240,
scopedSlots: { customRender: "action" }
}
];
操作栏中对应的
menuHandler({ key }, record) {
const handler = moreMenuHandler[key];
handler && handler.call(this, record);
},
const moreMenuHandler = {
detail(record) {
this.$refs.detailModal.open(record.id);
},
update(record) {
this.$router.push(`/machine/update/${record.id}`);
},
delete(record) {
this.$confirm({
title: "确认删除",
content: "是否删除当前机器? 将会删除机器相关联的所有数据!",
okText: "确认",
okType: "danger",
cancelText: "取消",
onOk: () => {
this.$api
.deleteMachine({
idList: [record.id]
})
.then(() => {
this.$message.success("删除成功");
this.getList({});
});
}
});
},
copy(record) {
this.$confirm({
title: "确认复制",
content: `是否复制机器 ${record.name}?`,
okText: "复制",
cancelText: "取消",
onOk: () => {
this.$api
.copyMachine({
id: record.id
})
.then(() => {
this.$message.success("复制成功");
this.getList();
});
}
});
},
bindKey(record) {
this.$refs.keyBindModal.open(record.id, record.keyId, record.name);
}
};
表格带有勾选
computed: {
rowSelection() {
return {
selectedRowKeys: this.selectedRowKeys,
onChange: e => {
this.selectedRowKeys = e;
},
getCheckboxProps: record => ({
props: {
disabled: record.id === 1
}
})
};
}
},
表格tree(展开/折叠)
<a-table
v-if="rows.length"
:columns="columns"
:data-source="rows"
:pagination="false"
rowKey="id" :defaultExpandAllRows="defaultExpandAllRows"
:key="defaultExpandAllRows ? rows.length : 0"
@change="getList"
:scoll="{ x: '100%' }"
:loading="loading"
size="middle"
childrenColumnName="childs"
>
1.使用v-if的原因,初始化给defaultExpandAllRows赋值true,不起效;
2.动态点击按钮,进行树的展开/折叠,切换defaultExpandAllRows不起效;(文档上说明只在初始化起效);异步加载重置rows也不起效;
3.解决方式:通过defaultExpandAllRows的值更改列表数据的length;即可实现展开,折叠
4.childrenColumnName自定义tree识别的子数组;
a-form
结合的是es7语法糖v-decorator
<template>
<a-modal
v-model="visible"
:title="title"
:width="650"
:okButtonProps="{ props: { disabled: loading } }"
:maskClosable="false"
:destroyOnClose="true"
@ok="check"
@cancel="close"
>
<a-spin :spinning="loading">
<a-form :form="form" v-bind="layout">
<a-form-item
label="上级菜单"
v-show="form.getFieldValue('powerType') == '1'"
>
<a-tree-select
v-decorator="[
decorators.parentId,
{
initialValue: 0,
rules: [
{
required:
form.getFieldValue('powerType') == '1' ? true : false,
message: '请选择上级菜单'
}
]
}
]"
class="machine-input"
tree-data-simple-mode
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:tree-data="treeList"
placeholder="请选择上级菜单"
:replaceFields="replaceFields"
allowClear
@select="onSelect"
/>
</a-form-item>
<a-form-item label="菜单类型">
<a-radio-group
name="radioGroup"
:disabled="!!this.id"
v-decorator="decorators.powerType"
>
<a-radio value="0">目录</a-radio>
<a-radio value="1">菜单</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
:label="
form.getFieldValue('powerType') == '0' ? '目录名称' : '菜单名称'
"
hasFeedback
name="name"
>
<a-input
v-decorator="decorators.name"
:placeholder="
`请输入${
form.getFieldValue('powerType') == '0' ? '目录' : '菜单'
}名称`
"
showCount
allowClear
/>
</a-form-item>
<a-form-item label="排序">
<a-input-number
style="width:100%"
id="inputNumber"
:placeholder="
`请指定${
form.getFieldValue('powerType') == '0' ? '目录' : '菜单'
}位置`
"
v-decorator="decorators.order"
:max="1000"
/>
<!-- <p>数值越大则显示顺序在前</p> -->
</a-form-item>
<a-form-item
:label="
form.getFieldValue('powerType') == '0' ? '目录地址' : '菜单地址'
"
hasFeedback
>
<a-input
v-decorator="[
decorators.path,
{
rules: [
{
required:
form.getFieldValue('powerType') == '1' ? true : false,
message: `请输入${
form.getFieldValue('powerType') == '0' ? '目录' : '菜单'
}地址`
},
{
max: 100,
message: '长度不能大于100位'
}
]
}
]"
:placeholder="
`请输入${
form.getFieldValue('powerType') == '0' ? '目录' : '菜单'
}地址`
"
allowClear
/>
<p>访问的路由地址,如:`/user`</p>
</a-form-item>
<a-form-item
label="重定向地址"
v-if="isTwoMenu && form.getFieldValue('powerType') == '0'"
>
<a-input
v-decorator="decorators.redirect"
placeholder="请输入重定向地址"
allowClear
/>
<p>面包屑重定向路由地址,如:`/test/user`</p>
</a-form-item>
<a-form-item label="组件路径">
<a-input
v-decorator="[
decorators.component,
{
initialValue:
form.getFieldValue('powerType') == '0' ? 'Layout' : ''
}
]"
placeholder="请输入组件路径"
allowClear
/>
<p v-if="form.getFieldValue('powerType') == '1'">
访问的组件路径,如:`system/user/index`,默认在`views`目录下
</p>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import { pick } from "lodash";
import { md5 } from "@/lib/utils";
import { ROLE_TYPE } from "@/lib/enum";
const layout = {
labelCol: { span: 5 },
wrapperCol: { span: 17 }
};
function getDecorators() {
return {
powerType: [
"powerType",
{
initialValue: "0"
}
],
name: [
"name",
{
rules: [
{
required: true,
message: "请输入名称"
},
{
max: 32,
message: "用户名长度不能大于32位"
}
]
}
],
path: ["path"],
order: ["order"],
parentId: ["parentId"],
redirect: ["redirect"],
component: ["component"]
};
}
export default {
name: "AddMenuModal",
props: {
treeList: {
required: true,
default: () => {}
}
},
data: function() {
return {
ROLE_TYPE,
id: null,
visible: false,
title: null,
loading: false,
record: null,
layout,
decorators: getDecorators.call(this),
form: this.$form.createForm(this),
isRoot: false,
isTwoMenu: false,
replaceFields: {
children: "children",
title: "name",
key: "id",
value: "id"
}
};
},
watch: {},
mounted() {},
methods: {
add() {
this.title = "新增菜单";
this.initRecord({});
},
update(id) {
this.title = "修改菜单";
this.$api.getMenuDetail({ id }).then(({ data }) => {
this.initRecord(data);
});
},
addChildren(id) {
this.title = "新增菜单";
this.$api.getMenuDetail({ id }).then(({ data }) => {
this.initRecord(data, "son");
});
},
onSelect(value, node, extray) {
console.log(extray.selectedNodes[0].data.props);
let extraNode = extray.selectedNodes[0].data.props;
this.treeHandleSelect(extraNode);
},
treeHandleSelect(node) {
if (node.component == "Layout") {
this.isTwoMenu = true;
} else {
this.isTwoMenu = false;
}
// if (node.id == "0" || node.id == undefined) {
// this.isRoot = false;
// this.isTwoMenu = false;
// } else {
// this.isRoot = true;
// }
//
this.$nextTick(() => {
this.form.setFieldsValue({
parentId: node.id
});
});
},
async initRecord(row, val) {
this.form.resetFields();
this.visible = true;
this.id = row.id;
await this.treeHandleSelect(row);
this.record = pick(
Object.assign({}, row),
"powerType",
"name",
"order",
"path",
"parentId",
"component",
"redirect"
);
if (val) {
this.record = pick(Object.assign({}, row), "id");
}
this.$nextTick(() => {
this.form.setFieldsValue(this.record);
});
},
check() {
this.loading = true;
this.form.validateFields((err, values) => {
if (err) {
this.loading = false;
return;
}
this.submit(values);
});
},
async submit(values) {
let res;
try {
if (!this.id) {
// 添加
let obj = {
name: values.name,
powerType: values.powerType,
order: values.order || "",
parentId: values.powerType == "0" ? 0 : values.parentId,
path: values.path || "",
component: values.component || "",
redirect: values.redirect || ""
};
// console.log(obj);
// values.password = md5(values.password);
res = await this.$api.addMenu(obj);
} else {
// 修改
res = await this.$api.updateMenu({
...values,
id: this.id
});
}
if (!this.id) {
this.$message.success("添加成功");
this.$emit("added", res.data);
this.$root.updateFlag = true;
} else {
this.$message.success("修改成功");
this.$emit("updated", res.data);
this.$root.updateFlag = true;
}
this.close();
} catch (e) {
this.$root.updateFlag = false;
// ignore
}
this.loading = false;
},
close() {
this.visible = false;
this.loading = false;
}
}
};
</script>
<style scoped>
p {
padding: 0;
margin: 0;
line-height: 12px;
font-size: 12px;
color: #cacdd4;
}
</style>
-
给form表单设定值的时候,不能直接赋值,需要结合
this.form.setFieldsValue({ username: '设置值' })
- 判断条件为false时,dom不渲染(改用v-show,赋值的时候结合 this.$nextTick(() => {
this.form.setFieldsValue({
id: node.id
});
});
//才能不出现warning如下
需要注意的是:使用v-decorator声明的域数据;在表单上面必须使用(使用的时候不在页面显示即可),否则会报错 - 检索栏菜单绑定,用:model的形式,使用v-model会报错
- 结合a-radio的场景,不同值下对应的表单项以及校验规则不同;用行内式实现
- 使用form中的某一项作为显隐条件时;form.getFieldValue('powerType')的形式获取;
- 使用a-radio通常要给予初始值;再初始化form的getDecorators()方法中和校验规则同级的initialValue实现;
- form中v-decorator声明的表单项,必须在表单中使用(v-show='false');否则点击确定时会产生告警
a-tree-select
<a-tree-select
v-decorator="[
decorators.parentId,
{
initialValue: 0,
rules: [
{
required:
form.getFieldValue('menuType') == '1' ? true : false,
message: '请选择上级菜单'
}
]
}
]"
class="machine-input"
tree-data-simple-mode
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:tree-data="treeList"
placeholder="请选择上级菜单"
:replaceFields="replaceFields"
allowClear
@select="onSelect"
/>
1. replaceFields: {
children: "childs",
title: "menuName",
key: "id",
value: "id"
}
每一项都必须声明;
2.由于是以id作为节点标识,选择节点只能拿到节点id
onSelect(value, node, extray) {
//选择节点本身的全部props console.log(extray.selectedNodes[0].data.props);
let extraNode = extray.selectedNodes[0].data.props;
this.treeHandleSelect(extraNode);
},
sql语句输入时的在线editor
插件:vue2-ace-editor
组件封装
<template>
<ace ref="editor"
:value="value"
:lang="lang"
:width="width === 0 ? '100%' : width"
:height="height === 0 ? '100%' : height"
:theme="theme"
:options="options"
@init="initEditor"
v-bind="config">
</ace>
</template>
<script>
import ace from 'vue2-ace-editor'
export default {
name: 'Editor',
components: {
ace
},
props: {
value: {
type: String,
default: ''
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
},
readOnly: {
type: Boolean,
default: false
},
theme: {
type: String,
default: 'iplastic'
},
lang: {
type: String,
default: 'sh'
},
config: {
type: Object,
default: () => {
return {
enableLiveAutocompletion: true,
fontSize: 16
}
}
}
},
computed: {
options() {
return {
enableBasicAutocompletion: true,
enableSnippets: true,
showPrintMargin: false,
fontSize: this.config.fontSize,
enableLiveAutocompletion: this.config.enableLiveAutocompletion,
readOnly: this.readOnly
}
}
},
methods: {
initEditor(editor) {
require('brace/ext/language_tools')
// 设置语言
require('brace/mode/sh')
require('brace/mode/json')
require('brace/mode/xml')
require('brace/mode/yaml')
require('brace/mode/properties')
require('brace/snippets/sh')
require('brace/snippets/json')
require('brace/snippets/xml')
require('brace/snippets/yaml')
require('brace/snippets/properties')
// 设置主题
// 浅色 iplastic sqlserver tomorrow xcode
// 深色 dracula gruvbox idle_fingers merbivore terminal tomorrow_night_bright
require('brace/theme/iplastic')
// 监听值的变化
editor.getSession().on('change', () => {
this.$emit('change', editor.getValue())
})
},
getValue() {
return this.$refs.editor.editor.getValue()
},
setValue(value) {
this.$refs.editor.editor.session.setValue(value)
},
clear() {
this.$refs.editor.editor.session.setValue('')
}
}
}
</script>
<style scoped>
</style>
组件引用-实现预览
<template>
<a-modal v-model="visible"
:width="650"
:footer="null"
:destroyOnClose="true"
@close="close">
<!-- 编辑器 -->
<Editor v-bind="editorConfig" :value="value" :height="350" :readOnly="true"/>
<!-- 标题 -->
<template #title>
<span>{{ title }}</span>
<a @click="$copy(value)" title="复制">
<a-icon class="copy-icon-right" type="copy"/>
</a>
</template>
</a-modal>
</template>
<script>
import Editor from '@/components/editor/Editor'
export default {
name: 'EditorPreview',
components: {
Editor
},
props: {
editorConfig: {
type: Object,
default: () => {
return {}
}
},
title: {
type: String,
default: '预览'
}
},
data() {
return {
visible: false,
value: null
}
},
methods: {
preview(value) {
this.visible = true
this.value = value
},
close() {
this.visible = false
this.value = null
}
}
}
</script>
<style scoped>
</style>
表单中使用时,要给予初始值undefined
<a-form-item label="采集语句" hasFeedback>
<Editor :height="200" v-decorator="decorators.collectSql" />
</a-form-item>