init
This commit is contained in:
831
src/views/authority/apikey.vue
Normal file
831
src/views/authority/apikey.vue
Normal file
@@ -0,0 +1,831 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #apiKey="{ row }">
|
||||
<el-tag>{{ row.apiKey }}</el-tag>
|
||||
</template>
|
||||
<template #expireTime="{ row }">
|
||||
<el-tag>{{ row.expireTimeInfo }}</el-tag>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-check"
|
||||
v-if="row.status === -1 && userInfo.authority.includes('admin')"
|
||||
@click.stop="handleEnable(row)"
|
||||
>启用
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-close"
|
||||
v-if="row.status === 1 && userInfo.authority.includes('admin')"
|
||||
@click.stop="handleDisable(row)"
|
||||
>禁用
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-notebook"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
@click.stop="handleRowLog(row)"
|
||||
>日志
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 访问权限动态表单 -->
|
||||
<template #apiPath-form="{ type }">
|
||||
<el-row
|
||||
v-for="(item, index) in apiPathList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
v-model="apiPathList[index]"
|
||||
placeholder="请输入URL路径"
|
||||
:disabled="type === 'view'"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="2" v-if="type !== 'view'">
|
||||
<el-button type="danger" text icon="el-icon-delete" @click="removeApiPath(index)" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="type !== 'view'" :gutter="8">
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" text icon="el-icon-circle-plus" @click="addApiPath"
|
||||
>添加路径</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 8px">
|
||||
<el-text type="info" size="small">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
提示: 支持 Ant 风格路径匹配,路径需以服务前缀开头,如 /** 匹配所有、/blade-system/user/*
|
||||
匹配单层、/blade-system/user/** 匹配多层。
|
||||
</el-text>
|
||||
</el-row>
|
||||
</template>
|
||||
<!-- 扩展参数动态表单 -->
|
||||
<template #extParams-form="{ type }">
|
||||
<el-row
|
||||
v-for="(item, index) in extParamsList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
>
|
||||
<el-col :span="8">
|
||||
<el-input v-model="item.key" placeholder="参数名" :disabled="type === 'view'" />
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-input v-model="item.value" placeholder="参数值" :disabled="type === 'view'" />
|
||||
</el-col>
|
||||
<el-col :span="2" v-if="type !== 'view'">
|
||||
<el-button type="danger" text icon="el-icon-delete" @click="removeExtParam(index)" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="type !== 'view'" :gutter="8">
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" text icon="el-icon-circle-plus" @click="addExtParam"
|
||||
>添加参数</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 8px">
|
||||
<el-text type="info" size="small">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
提示: 可配置 deptId、postId、roleId 等参数自定义 API Key 属性。
|
||||
</el-text>
|
||||
</el-row>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- 单个密钥调用日志 Drawer -->
|
||||
<el-drawer
|
||||
:title="`[${logKeyName}] 调用日志`"
|
||||
v-model="logDrawerVisible"
|
||||
append-to-body
|
||||
size="1200px"
|
||||
>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="logOption"
|
||||
:table-loading="logLoading"
|
||||
:data="logData"
|
||||
v-model:page="logPage"
|
||||
ref="logCrud"
|
||||
v-model="logForm"
|
||||
:before-open="beforeOpenLog"
|
||||
@search-change="logSearchChange"
|
||||
@search-reset="logSearchReset"
|
||||
@current-change="logCurrentChange"
|
||||
@size-change="logSizeChange"
|
||||
@refresh-change="logRefreshChange"
|
||||
@on-load="onLoadLog"
|
||||
>
|
||||
<template #requestUri="{ row }">
|
||||
<el-tag>{{ row.requestUri }}</el-tag>
|
||||
</template>
|
||||
<template #method="{ row }">
|
||||
<el-tag>{{ row.method }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</el-drawer>
|
||||
|
||||
<!-- API Key 创建成功弹框 -->
|
||||
<el-dialog
|
||||
v-model="apiKeyDialogVisible"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
width="620px"
|
||||
align-center
|
||||
title=""
|
||||
>
|
||||
<el-row justify="center">
|
||||
<el-icon :size="48" color="#10b981"><CircleCheckFilled /></el-icon>
|
||||
</el-row>
|
||||
<el-row justify="center" style="margin-top: 16px">
|
||||
<el-text size="large" tag="b">API Key 创建成功</el-text>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 24px">
|
||||
<el-divider content-position="left">
|
||||
<el-text type="primary" tag="b">密钥信息</el-text>
|
||||
</el-divider>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 12px">
|
||||
<el-card
|
||||
shadow="never"
|
||||
style="width: 100%; background: #f5f7fa"
|
||||
:body-style="{ textAlign: 'center', padding: '12px' }"
|
||||
>
|
||||
<el-text tag="code" style="word-break: break-all">{{ createdApiKey }}</el-text>
|
||||
</el-card>
|
||||
</el-row>
|
||||
<el-row justify="center" style="margin-top: 12px">
|
||||
<el-space :size="6">
|
||||
<el-icon color="#3b82f6" :size="16"><Clock /></el-icon>
|
||||
<el-text size="small" v-if="createdExpireTime">过期时间:{{ createdExpireTime }}</el-text>
|
||||
<el-text size="small" v-else>过期时间:永久有效</el-text>
|
||||
</el-space>
|
||||
</el-row>
|
||||
<el-row justify="center" style="margin-top: 12px">
|
||||
<el-space :size="6">
|
||||
<el-icon color="#f59e0b" :size="16"><WarningFilled /></el-icon>
|
||||
<el-text size="small" type="warning">请尽快复制保存,关闭后将无法再次查看</el-text>
|
||||
</el-space>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 24px">
|
||||
<el-divider content-position="left">
|
||||
<el-text type="primary" tag="b">使用说明</el-text>
|
||||
</el-divider>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 12px">
|
||||
<el-text type="info" size="small"> 在调用 API 时,需要在请求头中携带以下信息: </el-text>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 12px">
|
||||
<el-card
|
||||
shadow="never"
|
||||
style="width: 100%; background: #f5f7fa"
|
||||
:body-style="{ padding: '12px' }"
|
||||
>
|
||||
<el-space direction="vertical" :size="8" style="width: 100%; align-items: flex-start">
|
||||
<el-text size="small" style="word-break: break-all">
|
||||
<el-text type="primary" tag="b">Blade-Auth:</el-text> {{ createdApiKey }}
|
||||
</el-text>
|
||||
<el-text size="small">
|
||||
<el-text type="primary" tag="b">Blade-Requested-With:</el-text> BladeHttpRequest
|
||||
</el-text>
|
||||
</el-space>
|
||||
</el-card>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 16px">
|
||||
<el-text type="info" size="small">cURL 调用示例:</el-text>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 8px">
|
||||
<el-card
|
||||
shadow="never"
|
||||
style="width: 100%; background: #1e1e1e"
|
||||
:body-style="{ padding: '12px' }"
|
||||
>
|
||||
<el-text
|
||||
type="success"
|
||||
size="small"
|
||||
tag="code"
|
||||
style="
|
||||
color: #a6e3a1;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Courier New', monospace;
|
||||
line-height: 1.6;
|
||||
"
|
||||
>{{ curlCommand }}</el-text
|
||||
>
|
||||
</el-card>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<el-row justify="center" :gutter="12">
|
||||
<el-col :span="12" style="text-align: right; padding-right: 6px">
|
||||
<el-button type="primary" size="large" @click="copyAndCloseDialog" style="width: 100%">
|
||||
<el-space>
|
||||
<el-icon><DocumentCopy /></el-icon>
|
||||
<span>复制 API Key</span>
|
||||
</el-space>
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12" style="text-align: left; padding-left: 6px">
|
||||
<el-button size="large" @click="copyCurlCommand" style="width: 100%">
|
||||
<el-space>
|
||||
<el-icon><DocumentCopy /></el-icon>
|
||||
<span>复制 cURL 命令</span>
|
||||
</el-space>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, remove, add, update, enable, disable } from '@/api/system/apikey';
|
||||
import { getList as getLogList, getDetail as getLogDetail } from '@/api/system/apikeylog';
|
||||
import { getUserPage, getUser } from '@/api/system/user';
|
||||
import { mapGetters } from 'vuex';
|
||||
import {
|
||||
CircleCheckFilled,
|
||||
Clock,
|
||||
WarningFilled,
|
||||
DocumentCopy,
|
||||
Delete,
|
||||
Plus,
|
||||
Notebook,
|
||||
} from '@element-plus/icons-vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CircleCheckFilled,
|
||||
Clock,
|
||||
WarningFilled,
|
||||
DocumentCopy,
|
||||
Delete,
|
||||
Plus,
|
||||
Notebook,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
apiPathList: [],
|
||||
extParamsList: [],
|
||||
apiKeyDialogVisible: false,
|
||||
createdApiKey: '',
|
||||
createdExpireTime: '',
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 700,
|
||||
menuWidth: 350,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: 'API Key',
|
||||
prop: 'apiKey',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
span: 24,
|
||||
width: 280,
|
||||
},
|
||||
{
|
||||
label: '密钥名称',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
editDisabled: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密钥名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '绑定用户',
|
||||
prop: 'userId',
|
||||
type: 'table',
|
||||
editDisabled: true,
|
||||
props: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
children: {
|
||||
border: true,
|
||||
column: [
|
||||
{
|
||||
label: '账号',
|
||||
prop: 'account',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '角色',
|
||||
prop: 'roleName',
|
||||
},
|
||||
{
|
||||
label: '部门',
|
||||
prop: 'deptName',
|
||||
},
|
||||
{
|
||||
label: '岗位',
|
||||
prop: 'postName',
|
||||
},
|
||||
],
|
||||
},
|
||||
formatter(row) {
|
||||
if (!row.name) return '';
|
||||
return row.name + '(' + row.account + ')';
|
||||
},
|
||||
onLoad: ({ page, value, data }, callback) => {
|
||||
if (value) {
|
||||
getUser(value).then(res => {
|
||||
callback(res.data.data);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 首次打开弹窗时设置默认每页5条
|
||||
if (page && (!page.pageSizes || !page.pageSizes.includes(5))) {
|
||||
page.pageSize = 5;
|
||||
page.pageSizes = [5, 10, 20];
|
||||
}
|
||||
let currentPage = page ? page.currentPage : 1;
|
||||
let pageSize = page ? page.pageSize : 5;
|
||||
let param = {};
|
||||
if (data) {
|
||||
param = data;
|
||||
}
|
||||
getUserPage(currentPage, pageSize, param.account, param.name).then(res => {
|
||||
callback({
|
||||
total: res.data.data.total,
|
||||
data: res.data.data.records,
|
||||
});
|
||||
});
|
||||
},
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择用户',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '过期时间',
|
||||
prop: 'expireTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
span: 24,
|
||||
width: 180,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
search: true,
|
||||
dicData: [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 },
|
||||
],
|
||||
value: 1,
|
||||
span: 24,
|
||||
display: false,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '访问权限',
|
||||
labelTip: '配置允许访问的 API 路径,留空则表示不限制访问路径',
|
||||
prop: 'apiPath',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '扩展参数',
|
||||
labelTip: '配置扩展参数,可用于自定义 API Key 属性',
|
||||
prop: 'extParams',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
// 单个密钥日志
|
||||
logDrawerVisible: false,
|
||||
logKeyName: '',
|
||||
logApiKeyId: null,
|
||||
logForm: {},
|
||||
logQuery: {},
|
||||
logLoading: false,
|
||||
logData: [],
|
||||
logPage: { pageSize: 10, currentPage: 1, total: 0 },
|
||||
logOption: {
|
||||
height: 'auto',
|
||||
calcHeight: 70,
|
||||
tip: false,
|
||||
header: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 100,
|
||||
dialogType: 'drawer',
|
||||
dialogWidth: 800,
|
||||
column: [
|
||||
{ label: '服务ID', prop: 'serviceId', width: 120 },
|
||||
{ label: '服务host', prop: 'serverHost', hide: true },
|
||||
{ label: '服务IP', prop: 'serverIp', hide: true, width: 160 },
|
||||
{ label: '软件环境', prop: 'env', hide: true, width: 85 },
|
||||
{ label: '请求接口', prop: 'requestUri', search: true, slot: true },
|
||||
{ label: '请求方法', prop: 'method', slot: true, width: 85 },
|
||||
{ label: '调用IP', prop: 'remoteIp', search: true, width: 140 },
|
||||
{ label: '耗时(ms)', prop: 'time', width: 85 },
|
||||
{ label: '日志时间', prop: 'createTime', span: 24, width: 180 },
|
||||
{ label: '用户代理', prop: 'userAgent', span: 24, hide: true },
|
||||
{ label: '请求数据', prop: 'params', type: 'textarea', span: 24, minRows: 2, hide: true },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
curlCommand() {
|
||||
if (!this.createdApiKey) return '';
|
||||
return `curl -X GET 'https://api.bladex.cn/blade-system/user/info' \\
|
||||
-H 'Blade-Auth: ${this.createdApiKey}' \\
|
||||
-H 'Blade-Requested-With: BladeHttpRequest'`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
copyAndCloseDialog() {
|
||||
navigator.clipboard
|
||||
.writeText(this.createdApiKey)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: 'API Key 已复制到剪贴板',
|
||||
});
|
||||
this.apiKeyDialogVisible = false;
|
||||
this.createdApiKey = '';
|
||||
this.createdExpireTime = '';
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message({
|
||||
type: 'error',
|
||||
message: '复制失败,请手动复制',
|
||||
});
|
||||
});
|
||||
},
|
||||
copyCurlCommand() {
|
||||
navigator.clipboard
|
||||
.writeText(this.curlCommand)
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: 'cURL 命令已复制到剪贴板',
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message({
|
||||
type: 'error',
|
||||
message: '复制失败,请手动复制',
|
||||
});
|
||||
});
|
||||
},
|
||||
showApiKeyDialog(apiKey, expireTime) {
|
||||
this.createdApiKey = apiKey;
|
||||
this.createdExpireTime = expireTime || '';
|
||||
this.apiKeyDialogVisible = true;
|
||||
},
|
||||
addApiPath() {
|
||||
this.apiPathList.push('');
|
||||
},
|
||||
removeApiPath(index) {
|
||||
this.apiPathList.splice(index, 1);
|
||||
},
|
||||
parseApiPath(apiPath) {
|
||||
if (!apiPath) return [];
|
||||
return apiPath
|
||||
.split(',')
|
||||
.map(path => path.trim())
|
||||
.filter(path => path.length > 0);
|
||||
},
|
||||
buildApiPath() {
|
||||
const paths = this.apiPathList.filter(path => path && path.trim());
|
||||
return paths.length > 0 ? paths.join(',') : '';
|
||||
},
|
||||
addExtParam() {
|
||||
this.extParamsList.push({ key: '', value: '' });
|
||||
},
|
||||
removeExtParam(index) {
|
||||
this.extParamsList.splice(index, 1);
|
||||
},
|
||||
parseExtParams(extParams) {
|
||||
if (!extParams) return [];
|
||||
try {
|
||||
const obj = typeof extParams === 'string' ? JSON.parse(extParams) : extParams;
|
||||
return Object.entries(obj).map(([key, value]) => ({ key, value: String(value) }));
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
buildExtParams() {
|
||||
const result = {};
|
||||
this.extParamsList.forEach(item => {
|
||||
if (item.key && item.key.trim()) {
|
||||
result[item.key.trim()] = item.value;
|
||||
}
|
||||
});
|
||||
return Object.keys(result).length > 0 ? JSON.stringify(result) : '';
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const data = {
|
||||
...row,
|
||||
apiPath: this.buildApiPath(),
|
||||
extParams: this.buildExtParams(),
|
||||
};
|
||||
add(data).then(
|
||||
res => {
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
// 显示 API Key 弹框
|
||||
this.showApiKeyDialog(res.data.data, row.expireTime);
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const data = {
|
||||
...row,
|
||||
apiPath: this.buildApiPath(),
|
||||
extParams: this.buildExtParams(),
|
||||
};
|
||||
update(data).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleEnable(row) {
|
||||
this.$confirm('确定启用该 API Key?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return enable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '启用成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDisable(row) {
|
||||
this.$confirm('确定禁用该 API Key?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return disable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '禁用成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add'].includes(type)) {
|
||||
this.apiPathList = [];
|
||||
this.extParamsList = [];
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
this.apiPathList = this.parseApiPath(this.form.apiPath);
|
||||
this.extParamsList = this.parseExtParams(this.form.extParams);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
// ==================== 单个密钥日志 ====================
|
||||
handleRowLog(row) {
|
||||
this.logKeyName = row.name || 'API Key';
|
||||
this.logApiKeyId = row.id;
|
||||
this.logDrawerVisible = true;
|
||||
this.logPage.currentPage = 1;
|
||||
this.logQuery = {};
|
||||
this.onLoadLog(this.logPage);
|
||||
},
|
||||
beforeOpenLog(done, type) {
|
||||
if (['view'].includes(type)) {
|
||||
getLogDetail(this.logForm.id).then(res => {
|
||||
this.logForm = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
logSearchChange(params, done) {
|
||||
this.logQuery = params;
|
||||
this.logPage.currentPage = 1;
|
||||
this.onLoadLog(this.logPage, params);
|
||||
done();
|
||||
},
|
||||
logSearchReset() {
|
||||
this.logQuery = {};
|
||||
this.onLoadLog(this.logPage);
|
||||
},
|
||||
logCurrentChange(currentPage) {
|
||||
this.logPage.currentPage = currentPage;
|
||||
},
|
||||
logSizeChange(pageSize) {
|
||||
this.logPage.pageSize = pageSize;
|
||||
},
|
||||
logRefreshChange() {
|
||||
this.onLoadLog(this.logPage, this.logQuery);
|
||||
},
|
||||
onLoadLog(page, params = {}) {
|
||||
this.logLoading = true;
|
||||
const query = Object.assign({ apiKeyId: this.logApiKeyId }, params, this.logQuery);
|
||||
getLogList(page.currentPage, page.pageSize, query).then(res => {
|
||||
const data = res.data.data;
|
||||
this.logPage.total = data.total;
|
||||
this.logData = data.records;
|
||||
this.logLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
733
src/views/authority/apiscope.vue
Normal file
733
src/views/authority/apiscope.vue
Normal file
@@ -0,0 +1,733 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<!-- ========== 列表模式 ========== -->
|
||||
<avue-crud
|
||||
v-if="viewMode === 'list'"
|
||||
ref="crudList"
|
||||
v-model="formList"
|
||||
:option="optionList"
|
||||
:table-loading="listLoading"
|
||||
:data="dataList"
|
||||
:page="pageList"
|
||||
:permission="permissionListScope"
|
||||
:before-open="beforeOpenList"
|
||||
@row-del="rowDelList"
|
||||
@row-update="rowUpdateList"
|
||||
@row-save="rowSaveList"
|
||||
@search-change="searchChangeList"
|
||||
@search-reset="searchResetList"
|
||||
@selection-change="selectionChangeList"
|
||||
@current-change="currentChangeList"
|
||||
@size-change="sizeChangeList"
|
||||
@refresh-change="refreshChangeList"
|
||||
@on-load="onLoadList"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteList"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu-right>
|
||||
<el-button :type="viewMode === 'list' ? 'primary' : ''" @click="viewMode = 'list'"
|
||||
>列表模式
|
||||
</el-button>
|
||||
<el-button :type="viewMode === 'tree' ? 'primary' : ''" @click="viewMode = 'tree'"
|
||||
>树形模式
|
||||
</el-button>
|
||||
</template>
|
||||
<template #scopeType="{ row }">
|
||||
<el-tag>{{ row.scopeTypeName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- ========== 树形模式 ========== -->
|
||||
<avue-crud
|
||||
v-if="viewMode === 'tree'"
|
||||
ref="crudTree"
|
||||
:option="optionTree"
|
||||
:table-loading="treeLoading"
|
||||
:data="dataTree"
|
||||
@search-change="searchChangeTree"
|
||||
@search-reset="searchResetTree"
|
||||
@refresh-change="refreshChangeTree"
|
||||
@on-load="onLoadTree"
|
||||
@tree-load="treeLoad"
|
||||
>
|
||||
<template #menu-right>
|
||||
<el-button :type="viewMode === 'list' ? 'primary' : ''" @click="viewMode = 'list'"
|
||||
>列表模式
|
||||
</el-button>
|
||||
<el-button :type="viewMode === 'tree' ? 'primary' : ''" @click="viewMode = 'tree'"
|
||||
>树形模式
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
v-if="permission.api_scope_setting"
|
||||
plain
|
||||
style="border: 0; background-color: transparent !important"
|
||||
@click.stop="handleDataScope(row)"
|
||||
>权限配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<i :class="row.source" style="margin-right: 5px" />
|
||||
<span>{{ row.name }}</span>
|
||||
</template>
|
||||
<template #source="{ row }">
|
||||
<div style="text-align: center">
|
||||
<i :class="row.source" />
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- ========== 接口权限配置抽屉(树形模式) ========== -->
|
||||
<el-drawer
|
||||
:title="`[${scopeMenuName}] 接口权限配置`"
|
||||
v-model="drawerVisible"
|
||||
:direction="direction"
|
||||
append-to-body
|
||||
:before-close="handleDrawerClose"
|
||||
size="1200px"
|
||||
>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="optionScope"
|
||||
:data="dataScope"
|
||||
:page="pageScope"
|
||||
v-model="formScope"
|
||||
:table-loading="scopeLoading"
|
||||
ref="crudScope"
|
||||
@row-del="rowDelScope"
|
||||
@row-update="rowUpdateScope"
|
||||
@row-save="rowSaveScope"
|
||||
:before-open="beforeOpenScope"
|
||||
@search-change="searchChangeScope"
|
||||
@search-reset="searchResetScope"
|
||||
@selection-change="selectionChangeScope"
|
||||
@current-change="currentChangeScope"
|
||||
@size-change="sizeChangeScope"
|
||||
@on-load="onLoadScope"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteScope"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #scopeType="{ row }">
|
||||
<el-tag>{{ row.scopeTypeName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</el-drawer>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLazyMenuList } from '@/api/system/menu';
|
||||
import {
|
||||
addApiScope,
|
||||
removeApiScope,
|
||||
updateApiScope,
|
||||
getListApiScope,
|
||||
getMenuApiScope,
|
||||
} from '@/api/system/scope';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
viewMode: 'list',
|
||||
// ========== 列表模式 ==========
|
||||
formList: {},
|
||||
queryList: {},
|
||||
selectionListData: [],
|
||||
listLoading: true,
|
||||
dataList: [],
|
||||
pageList: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
optionList: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '所属菜单',
|
||||
labelTip: '仅用于权限规则的分组归类,不参与实际鉴权与数据过滤逻辑',
|
||||
prop: 'menuId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/menu/tree',
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
search: true,
|
||||
span: 24,
|
||||
width: 200,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择所属菜单',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限名称',
|
||||
prop: 'scopeName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限编号',
|
||||
prop: 'resourceCode',
|
||||
search: true,
|
||||
width: 180,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限路径',
|
||||
prop: 'scopePath',
|
||||
width: 180,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限路径',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '接口类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=api_scope_type',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
width: 100,
|
||||
prop: 'scopeType',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择接口类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
// ========== 树形模式 ==========
|
||||
dataTree: [],
|
||||
treeLoading: true,
|
||||
parentId: 0,
|
||||
queryTree: {},
|
||||
optionTree: {
|
||||
lazy: true,
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: false,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 200,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '菜单名称',
|
||||
prop: 'name',
|
||||
slot: true,
|
||||
width: 300,
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '菜单编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '路由地址',
|
||||
prop: 'path',
|
||||
},
|
||||
{
|
||||
label: '菜单图标',
|
||||
prop: 'source',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '菜单别名',
|
||||
prop: 'alias',
|
||||
},
|
||||
{
|
||||
label: '菜单排序',
|
||||
prop: 'sort',
|
||||
width: 85,
|
||||
},
|
||||
],
|
||||
},
|
||||
// ========== 接口权限抽屉(树形模式) ==========
|
||||
drawerVisible: false,
|
||||
direction: 'rtl',
|
||||
scopeMenuId: 0,
|
||||
scopeMenuName: '菜单',
|
||||
scopeLoading: false,
|
||||
formScope: {},
|
||||
selectionListScope: [],
|
||||
pageScope: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
optionScope: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '所属菜单',
|
||||
labelTip: '仅用于权限规则的分组归类,不参与实际鉴权与数据过滤逻辑',
|
||||
prop: 'menuId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/menu/tree',
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
span: 24,
|
||||
disabled: true,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择所属菜单',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限名称',
|
||||
prop: 'scopeName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限编号',
|
||||
prop: 'resourceCode',
|
||||
search: true,
|
||||
width: 180,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限路径',
|
||||
prop: 'scopePath',
|
||||
width: 180,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限路径',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '接口类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=api_scope_type',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
width: 100,
|
||||
prop: 'scopeType',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择接口类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
dataScope: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
viewMode() {
|
||||
this.drawerVisible = false;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionListScope() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.api_scope_setting, false),
|
||||
viewBtn: true,
|
||||
delBtn: this.validData(this.permission.api_scope_setting, false),
|
||||
editBtn: this.validData(this.permission.api_scope_setting, false),
|
||||
};
|
||||
},
|
||||
listIds() {
|
||||
let ids = [];
|
||||
this.selectionListData.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
scopeIds() {
|
||||
let ids = [];
|
||||
this.selectionListScope.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// ==================== 列表模式 ====================
|
||||
rowSaveList(row, done, loading) {
|
||||
addApiScope(row).then(
|
||||
() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateList(row, index, done, loading) {
|
||||
updateApiScope(row).then(
|
||||
() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelList(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeApiScope(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDeleteList() {
|
||||
if (this.selectionListData.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeApiScope(this.listIds);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudList.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenList(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getMenuApiScope(this.formList.id).then(res => {
|
||||
this.formList = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchResetList() {
|
||||
this.queryList = {};
|
||||
this.pageList.currentPage = 1;
|
||||
this.onLoadList(this.pageList);
|
||||
},
|
||||
searchChangeList(params, done) {
|
||||
this.queryList = params;
|
||||
this.pageList.currentPage = 1;
|
||||
this.onLoadList(this.pageList, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeList(list) {
|
||||
this.selectionListData = list;
|
||||
},
|
||||
currentChangeList(currentPage) {
|
||||
this.pageList.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeList(pageSize) {
|
||||
this.pageList.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeList() {
|
||||
this.onLoadList(this.pageList, this.queryList);
|
||||
},
|
||||
onLoadList(page, params = {}) {
|
||||
this.listLoading = true;
|
||||
getListApiScope(page.currentPage, page.pageSize, Object.assign(params, this.queryList)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageList.total = data.total;
|
||||
this.dataList = data.records;
|
||||
this.selectionListData = [];
|
||||
this.listLoading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// ==================== 树形模式 ====================
|
||||
onLoadTree(page, params = {}) {
|
||||
this.treeLoading = true;
|
||||
getLazyMenuList(this.parentId, Object.assign(params, this.queryTree)).then(res => {
|
||||
this.dataTree = res.data.data;
|
||||
this.treeLoading = false;
|
||||
});
|
||||
},
|
||||
treeLoad(tree, treeNode, resolve) {
|
||||
const parentId = tree.id;
|
||||
getLazyMenuList(parentId).then(res => {
|
||||
resolve(res.data.data);
|
||||
});
|
||||
},
|
||||
searchResetTree() {
|
||||
this.queryTree = {};
|
||||
this.parentId = 0;
|
||||
this.onLoadTree();
|
||||
},
|
||||
searchChangeTree(params, done) {
|
||||
this.queryTree = params;
|
||||
this.parentId = '';
|
||||
this.onLoadTree({}, params);
|
||||
done();
|
||||
},
|
||||
refreshChangeTree() {
|
||||
this.onLoadTree({}, this.queryTree);
|
||||
},
|
||||
handleDataScope(row) {
|
||||
this.drawerVisible = true;
|
||||
this.scopeMenuId = row.id;
|
||||
this.scopeMenuName = row.name;
|
||||
this.onLoadScope(this.pageScope);
|
||||
},
|
||||
handleDrawerClose(hide) {
|
||||
hide();
|
||||
},
|
||||
|
||||
// ==================== 抽屉内权限 CRUD ====================
|
||||
rowSaveScope(row, done, loading) {
|
||||
row = {
|
||||
...row,
|
||||
menuId: this.scopeMenuId,
|
||||
};
|
||||
addApiScope(row).then(
|
||||
() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateScope(row, index, done, loading) {
|
||||
row = {
|
||||
...row,
|
||||
menuId: this.scopeMenuId,
|
||||
};
|
||||
updateApiScope(row).then(
|
||||
() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelScope(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeApiScope(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDeleteScope() {
|
||||
if (this.selectionListScope.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeApiScope(this.scopeIds);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudScope.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenScope(done, type) {
|
||||
if (['add'].includes(type)) {
|
||||
this.formScope.menuId = this.scopeMenuId;
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getMenuApiScope(this.formScope.id).then(res => {
|
||||
this.formScope = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchResetScope() {
|
||||
this.onLoadScope(this.pageScope);
|
||||
},
|
||||
searchChangeScope(params, done) {
|
||||
this.onLoadScope(this.pageScope, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeScope(list) {
|
||||
this.selectionListScope = list;
|
||||
},
|
||||
currentChangeScope(currentPage) {
|
||||
this.pageScope.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeScope(pageSize) {
|
||||
this.pageScope.pageSize = pageSize;
|
||||
},
|
||||
onLoadScope(page, params = {}) {
|
||||
this.scopeLoading = true;
|
||||
const values = {
|
||||
...params,
|
||||
menuId: this.scopeMenuId,
|
||||
};
|
||||
getListApiScope(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.pageScope.total = data.total;
|
||||
this.dataScope = data.records;
|
||||
this.selectionListScope = [];
|
||||
this.scopeLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
868
src/views/authority/datascope.vue
Normal file
868
src/views/authority/datascope.vue
Normal file
@@ -0,0 +1,868 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<!-- ========== 列表模式 ========== -->
|
||||
<avue-crud
|
||||
v-if="viewMode === 'list'"
|
||||
ref="crudList"
|
||||
v-model="formList"
|
||||
:option="optionList"
|
||||
:table-loading="listLoading"
|
||||
:data="dataList"
|
||||
:page="pageList"
|
||||
:permission="permissionListScope"
|
||||
:before-open="beforeOpenList"
|
||||
@row-del="rowDelList"
|
||||
@row-update="rowUpdateList"
|
||||
@row-save="rowSaveList"
|
||||
@search-change="searchChangeList"
|
||||
@search-reset="searchResetList"
|
||||
@selection-change="selectionChangeList"
|
||||
@current-change="currentChangeList"
|
||||
@size-change="sizeChangeList"
|
||||
@refresh-change="refreshChangeList"
|
||||
@on-load="onLoadList"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteList"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu-right>
|
||||
<el-button :type="viewMode === 'list' ? 'primary' : ''" @click="viewMode = 'list'"
|
||||
>列表模式
|
||||
</el-button>
|
||||
<el-button :type="viewMode === 'tree' ? 'primary' : ''" @click="viewMode = 'tree'"
|
||||
>树形模式
|
||||
</el-button>
|
||||
</template>
|
||||
<template #scopeType="{ row }">
|
||||
<el-tag>{{ row.scopeTypeName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- ========== 树形模式 ========== -->
|
||||
<avue-crud
|
||||
v-if="viewMode === 'tree'"
|
||||
ref="crudTree"
|
||||
:option="optionTree"
|
||||
:table-loading="treeLoading"
|
||||
:data="dataTree"
|
||||
@search-change="searchChangeTree"
|
||||
@search-reset="searchResetTree"
|
||||
@refresh-change="refreshChangeTree"
|
||||
@on-load="onLoadTree"
|
||||
@tree-load="treeLoad"
|
||||
>
|
||||
<template #menu-right>
|
||||
<el-button :type="viewMode === 'list' ? 'primary' : ''" @click="viewMode = 'list'"
|
||||
>列表模式
|
||||
</el-button>
|
||||
<el-button :type="viewMode === 'tree' ? 'primary' : ''" @click="viewMode = 'tree'"
|
||||
>树形模式
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
v-if="permission.data_scope_setting"
|
||||
plain
|
||||
style="border: 0; background-color: transparent !important"
|
||||
@click.stop="handleDataScope(row)"
|
||||
>权限配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<i :class="row.source" style="margin-right: 5px" />
|
||||
<span>{{ row.name }}</span>
|
||||
</template>
|
||||
<template #source="{ row }">
|
||||
<div style="text-align: center">
|
||||
<i :class="row.source" />
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- ========== 数据权限配置抽屉(树形模式) ========== -->
|
||||
<el-drawer
|
||||
:title="`[${scopeMenuName}] 数据权限配置`"
|
||||
v-model="drawerVisible"
|
||||
:direction="direction"
|
||||
append-to-body
|
||||
:before-close="handleDrawerClose"
|
||||
size="1200px"
|
||||
>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="optionScope"
|
||||
:data="dataScope"
|
||||
:page="pageScope"
|
||||
v-model="formScope"
|
||||
:table-loading="scopeLoading"
|
||||
ref="crudScope"
|
||||
@row-del="rowDelScope"
|
||||
@row-update="rowUpdateScope"
|
||||
@row-save="rowSaveScope"
|
||||
:before-open="beforeOpenScope"
|
||||
@search-change="searchChangeScope"
|
||||
@search-reset="searchResetScope"
|
||||
@selection-change="selectionChangeScope"
|
||||
@current-change="currentChangeScope"
|
||||
@size-change="sizeChangeScope"
|
||||
@on-load="onLoadScope"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDeleteScope"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #scopeType="{ row }">
|
||||
<el-tag>{{ row.scopeTypeName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</el-drawer>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLazyMenuList } from '@/api/system/menu';
|
||||
import {
|
||||
addDataScope,
|
||||
removeDataScope,
|
||||
updateDataScope,
|
||||
getListDataScope,
|
||||
getMenuDataScope,
|
||||
} from '@/api/system/scope';
|
||||
import { mapGetters } from 'vuex';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
viewMode: 'list',
|
||||
// ========== 列表模式 ==========
|
||||
formList: {},
|
||||
queryList: {},
|
||||
selectionListData: [],
|
||||
listLoading: true,
|
||||
dataList: [],
|
||||
pageList: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
optionList: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '所属菜单',
|
||||
labelTip: '仅用于权限规则的分组归类,不参与实际鉴权与数据过滤逻辑',
|
||||
prop: 'menuId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/menu/tree',
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
search: true,
|
||||
span: 24,
|
||||
width: 200,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择所属菜单',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限名称',
|
||||
prop: 'scopeName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限编号',
|
||||
prop: 'resourceCode',
|
||||
search: true,
|
||||
width: 120,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限字段',
|
||||
prop: 'scopeColumn',
|
||||
width: 130,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入权限字段',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '规则类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=data_scope_type',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
width: 140,
|
||||
prop: 'scopeType',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择规则类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '可见字段',
|
||||
prop: 'scopeField',
|
||||
span: 24,
|
||||
hide: true,
|
||||
value: '*',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限可见的字段',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限类名',
|
||||
prop: 'scopeClass',
|
||||
span: 24,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入MybatisMapper对应方法的完整类名路径',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '规则值',
|
||||
prop: 'scopeValue',
|
||||
span: 24,
|
||||
minRows: 5,
|
||||
type: 'textarea',
|
||||
display: false,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
// ========== 树形模式 ==========
|
||||
dataTree: [],
|
||||
treeLoading: true,
|
||||
parentId: 0,
|
||||
queryTree: {},
|
||||
optionTree: {
|
||||
lazy: true,
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: false,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 200,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '菜单名称',
|
||||
prop: 'name',
|
||||
slot: true,
|
||||
width: 300,
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '菜单编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '路由地址',
|
||||
prop: 'path',
|
||||
},
|
||||
{
|
||||
label: '菜单图标',
|
||||
prop: 'source',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '菜单别名',
|
||||
prop: 'alias',
|
||||
},
|
||||
{
|
||||
label: '菜单排序',
|
||||
prop: 'sort',
|
||||
width: 85,
|
||||
},
|
||||
],
|
||||
},
|
||||
// ========== 数据权限抽屉(树形模式) ==========
|
||||
drawerVisible: false,
|
||||
direction: 'rtl',
|
||||
scopeMenuId: 0,
|
||||
scopeMenuCode: '',
|
||||
scopeMenuName: '菜单',
|
||||
scopeLoading: false,
|
||||
watchMode: true,
|
||||
formScope: {},
|
||||
selectionListScope: [],
|
||||
pageScope: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
optionScope: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '所属菜单',
|
||||
labelTip: '仅用于权限规则的分组归类,不参与实际鉴权与数据过滤逻辑',
|
||||
prop: 'menuId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/menu/tree',
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
span: 24,
|
||||
disabled: true,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择所属菜单',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限名称',
|
||||
prop: 'scopeName',
|
||||
search: true,
|
||||
value: '',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限编号',
|
||||
prop: 'resourceCode',
|
||||
search: true,
|
||||
width: 100,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限字段',
|
||||
prop: 'scopeColumn',
|
||||
width: 130,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '规则类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=data_scope_type',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
width: 140,
|
||||
prop: 'scopeType',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '可见字段',
|
||||
prop: 'scopeField',
|
||||
span: 24,
|
||||
hide: true,
|
||||
value: '*',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据权限可见的字段',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限类名',
|
||||
prop: 'scopeClass',
|
||||
span: 24,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入MybatisMapper对应方法的完整类名路径',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '规则值',
|
||||
prop: 'scopeValue',
|
||||
span: 24,
|
||||
minRows: 5,
|
||||
type: 'textarea',
|
||||
display: true,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
dataScope: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'formList.scopeType'() {
|
||||
this.initListScope();
|
||||
},
|
||||
'formScope.scopeType'() {
|
||||
this.initScope();
|
||||
},
|
||||
viewMode() {
|
||||
this.drawerVisible = false;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionListScope() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.data_scope_setting, false),
|
||||
viewBtn: true,
|
||||
delBtn: this.validData(this.permission.data_scope_setting, false),
|
||||
editBtn: this.validData(this.permission.data_scope_setting, false),
|
||||
};
|
||||
},
|
||||
listIds() {
|
||||
let ids = [];
|
||||
this.selectionListData.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
scopeIds() {
|
||||
let ids = [];
|
||||
this.selectionListScope.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// ==================== 列表模式 ====================
|
||||
initListScope() {
|
||||
const scopeType = func.toInt(this.formList.scopeType);
|
||||
const column = this.$refs.crudList?.option?.column;
|
||||
if (column) {
|
||||
column.forEach(item => {
|
||||
if (item.prop === 'scopeValue') {
|
||||
item.display = scopeType === 5;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
rowSaveList(row, done, loading) {
|
||||
addDataScope(row).then(
|
||||
() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateList(row, index, done, loading) {
|
||||
updateDataScope(row).then(
|
||||
() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelList(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeDataScope(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDeleteList() {
|
||||
if (this.selectionListData.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeDataScope(this.listIds);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadList(this.pageList);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudList.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenList(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getMenuDataScope(this.formList.id).then(res => {
|
||||
this.formList = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchResetList() {
|
||||
this.queryList = {};
|
||||
this.pageList.currentPage = 1;
|
||||
this.onLoadList(this.pageList);
|
||||
},
|
||||
searchChangeList(params, done) {
|
||||
this.queryList = params;
|
||||
this.pageList.currentPage = 1;
|
||||
this.onLoadList(this.pageList, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeList(list) {
|
||||
this.selectionListData = list;
|
||||
},
|
||||
currentChangeList(currentPage) {
|
||||
this.pageList.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeList(pageSize) {
|
||||
this.pageList.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeList() {
|
||||
this.onLoadList(this.pageList, this.queryList);
|
||||
},
|
||||
onLoadList(page, params = {}) {
|
||||
this.listLoading = true;
|
||||
getListDataScope(page.currentPage, page.pageSize, Object.assign(params, this.queryList)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageList.total = data.total;
|
||||
this.dataList = data.records;
|
||||
this.selectionListData = [];
|
||||
this.listLoading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// ==================== 树形模式 ====================
|
||||
onLoadTree(page, params = {}) {
|
||||
this.treeLoading = true;
|
||||
getLazyMenuList(this.parentId, Object.assign(params, this.queryTree)).then(res => {
|
||||
this.dataTree = res.data.data;
|
||||
this.treeLoading = false;
|
||||
});
|
||||
},
|
||||
treeLoad(tree, treeNode, resolve) {
|
||||
const parentId = tree.id;
|
||||
getLazyMenuList(parentId).then(res => {
|
||||
resolve(res.data.data);
|
||||
});
|
||||
},
|
||||
searchResetTree() {
|
||||
this.queryTree = {};
|
||||
this.parentId = 0;
|
||||
this.onLoadTree();
|
||||
},
|
||||
searchChangeTree(params, done) {
|
||||
this.queryTree = params;
|
||||
this.parentId = '';
|
||||
this.onLoadTree({}, params);
|
||||
done();
|
||||
},
|
||||
refreshChangeTree() {
|
||||
this.onLoadTree({}, this.queryTree);
|
||||
},
|
||||
handleDataScope(row) {
|
||||
this.drawerVisible = true;
|
||||
this.scopeMenuId = row.id;
|
||||
this.scopeMenuCode = row.code;
|
||||
this.scopeMenuName = row.name;
|
||||
this.onLoadScope(this.pageScope);
|
||||
},
|
||||
handleDrawerClose(hide) {
|
||||
hide();
|
||||
},
|
||||
|
||||
// ==================== 抽屉内权限 CRUD ====================
|
||||
initScope() {
|
||||
const scopeType = func.toInt(this.formScope.scopeType);
|
||||
const watchMode = this.watchMode;
|
||||
let column = '-',
|
||||
name = '暂无';
|
||||
if (scopeType === 1) {
|
||||
column = '-';
|
||||
name = '全部可见';
|
||||
} else if (scopeType === 2) {
|
||||
column = 'create_user';
|
||||
name = '本人可见';
|
||||
} else if (scopeType === 3) {
|
||||
column = 'create_dept';
|
||||
name = '所在机构可见';
|
||||
} else if (scopeType === 4) {
|
||||
column = 'create_dept';
|
||||
name = '所在机构可见及子级可见';
|
||||
} else if (scopeType === 5) {
|
||||
column = '';
|
||||
name = '自定义';
|
||||
}
|
||||
this.$refs.crudScope.option.column.filter(item => {
|
||||
if (watchMode) {
|
||||
if (item.prop === 'scopeName') {
|
||||
this.formScope.scopeName = `${this.scopeMenuName} [${name}]`;
|
||||
}
|
||||
if (item.prop === 'resourceCode') {
|
||||
this.formScope.resourceCode = this.scopeMenuCode;
|
||||
}
|
||||
if (item.prop === 'scopeColumn') {
|
||||
this.formScope.scopeColumn = column;
|
||||
}
|
||||
}
|
||||
if (item.prop === 'scopeValue') {
|
||||
item.display = scopeType === 5;
|
||||
}
|
||||
});
|
||||
},
|
||||
rowSaveScope(row, done, loading) {
|
||||
row = {
|
||||
...row,
|
||||
menuId: this.scopeMenuId,
|
||||
};
|
||||
addDataScope(row).then(
|
||||
() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateScope(row, index, done, loading) {
|
||||
row = {
|
||||
...row,
|
||||
menuId: this.scopeMenuId,
|
||||
};
|
||||
updateDataScope(row).then(
|
||||
() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelScope(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeDataScope(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDeleteScope() {
|
||||
if (this.selectionListScope.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeDataScope(this.scopeIds);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadScope(this.pageScope);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudScope.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenScope(done, type) {
|
||||
if (['add'].includes(type)) {
|
||||
this.formScope.menuId = this.scopeMenuId;
|
||||
this.watchMode = true;
|
||||
this.initScope();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
this.watchMode = false;
|
||||
getMenuDataScope(this.formScope.id).then(res => {
|
||||
this.formScope = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchResetScope() {
|
||||
this.onLoadScope(this.pageScope);
|
||||
},
|
||||
searchChangeScope(params, done) {
|
||||
this.onLoadScope(this.pageScope, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeScope(list) {
|
||||
this.selectionListScope = list;
|
||||
},
|
||||
currentChangeScope(currentPage) {
|
||||
this.pageScope.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeScope(pageSize) {
|
||||
this.pageScope.pageSize = pageSize;
|
||||
},
|
||||
onLoadScope(page, params = {}) {
|
||||
this.scopeLoading = true;
|
||||
const values = {
|
||||
...params,
|
||||
menuId: this.scopeMenuId,
|
||||
};
|
||||
getListDataScope(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.pageScope.total = data.total;
|
||||
this.dataScope = data.records;
|
||||
this.selectionListScope = [];
|
||||
this.scopeLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
639
src/views/authority/ratelimit.vue
Normal file
639
src/views/authority/ratelimit.vue
Normal file
@@ -0,0 +1,639 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="userInfo.authority.includes('administrator')"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #limitPattern="{ row }">
|
||||
<el-tag
|
||||
v-for="(pattern, index) in parseLimitPattern(row.limitPattern)"
|
||||
:key="index"
|
||||
style="margin: 2px 4px 2px 0"
|
||||
>{{ pattern }}</el-tag
|
||||
>
|
||||
</template>
|
||||
<template #requestMethod="{ row }">
|
||||
<el-tag>{{ requestMethodLabel(row.requestMethod) }}</el-tag>
|
||||
</template>
|
||||
<template #limitDimension="{ row }">
|
||||
<el-tag>{{ limitDimensionLabel(row.limitDimension) }}</el-tag>
|
||||
</template>
|
||||
<template #rate="{ row }">
|
||||
{{ row.limitCount }} 次 / {{ row.limitPeriod }} {{ timeUnitLabel(row.timeUnit) }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-check"
|
||||
v-if="row.status !== 1 && userInfo.authority.includes('administrator')"
|
||||
@click.stop="handleEnable(row)"
|
||||
>启用
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-close"
|
||||
v-if="row.status === 1 && userInfo.authority.includes('administrator')"
|
||||
@click.stop="handleDisable(row)"
|
||||
>禁用
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-notebook"
|
||||
v-if="userInfo.authority.includes('administrator')"
|
||||
@click.stop="handleRowLog(row)"
|
||||
>日志
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 匹配路径动态表单 -->
|
||||
<template #limitPattern-form="{ type }">
|
||||
<el-row
|
||||
v-for="(item, index) in limitPatternList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
v-model="limitPatternList[index]"
|
||||
placeholder="请输入URL匹配路径,如 /blade-system/user/**"
|
||||
:disabled="type === 'view'"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="2" v-if="type !== 'view'">
|
||||
<el-button
|
||||
type="danger"
|
||||
text
|
||||
icon="el-icon-delete"
|
||||
@click="removeLimitPattern(index)"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="type !== 'view'" :gutter="8">
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" text icon="el-icon-circle-plus" @click="addLimitPattern"
|
||||
>添加路径</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 8px">
|
||||
<el-text type="info" size="small">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
提示: 支持 Ant 风格路径匹配;多路径命中任一即生效,共享同一配额。
|
||||
</el-text>
|
||||
</el-row>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- 单条规则触发日志 Drawer -->
|
||||
<el-drawer :title="`[${logLimitName}] 触发日志`" v-model="logDrawerVisible" append-to-body size="1200px">
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="logOption"
|
||||
:table-loading="logLoading"
|
||||
:data="logData"
|
||||
v-model:page="logPage"
|
||||
ref="logCrud"
|
||||
v-model="logForm"
|
||||
:before-open="beforeOpenLog"
|
||||
@search-change="logSearchChange"
|
||||
@search-reset="logSearchReset"
|
||||
@current-change="logCurrentChange"
|
||||
@size-change="logSizeChange"
|
||||
@refresh-change="logRefreshChange"
|
||||
@on-load="onLoadLog"
|
||||
>
|
||||
<template #requestUri="{ row }">
|
||||
<el-tag>{{ row.requestUri }}</el-tag>
|
||||
</template>
|
||||
<template #method="{ row }">
|
||||
<el-tag>{{ row.method }}</el-tag>
|
||||
</template>
|
||||
<template #limitDimension="{ row }">
|
||||
<el-tag>{{ limitDimensionLabel(row.limitDimension) }}</el-tag>
|
||||
</template>
|
||||
<template #rate="{ row }">
|
||||
<span v-if="row.limitCount"
|
||||
>{{ row.limitCount }} 次 / {{ row.limitPeriod }} {{ timeUnitLabel(row.timeUnit) }}</span
|
||||
>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template #rate-form>
|
||||
<span v-if="logForm.limitCount"
|
||||
>{{ logForm.limitCount }} 次 / {{ logForm.limitPeriod }} {{ timeUnitLabel(logForm.timeUnit) }}</span
|
||||
>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</el-drawer>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, remove, add, update, enable, disable } from '@/api/system/ratelimit';
|
||||
import { getList as getLogList, getDetail as getLogDetail } from '@/api/system/ratelimitlog';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { WarningFilled } from '@element-plus/icons-vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WarningFilled,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
limitPatternList: [],
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
timeUnitDic: [
|
||||
{ label: '秒', value: 'SECONDS' },
|
||||
{ label: '分钟', value: 'MINUTES' },
|
||||
{ label: '小时', value: 'HOURS' },
|
||||
],
|
||||
requestMethodDic: [
|
||||
{ label: '全部', value: 'ALL' },
|
||||
{ label: 'GET', value: 'GET' },
|
||||
{ label: 'POST', value: 'POST' },
|
||||
{ label: 'PUT', value: 'PUT' },
|
||||
{ label: 'DELETE', value: 'DELETE' },
|
||||
],
|
||||
limitDimensionDic: [
|
||||
{ label: '全局', value: 'GLOBAL' },
|
||||
{ label: '租户', value: 'TENANT' },
|
||||
{ label: 'IP', value: 'IP' },
|
||||
{ label: '用户', value: 'USER' },
|
||||
],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 700,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
labelWidth: 110,
|
||||
searchLabelWidth: 90,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
menuWidth: 350,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '规则名称',
|
||||
prop: 'limitName',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请输入规则名称', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '匹配路径',
|
||||
prop: 'limitPattern',
|
||||
slot: true,
|
||||
formslot: true,
|
||||
span: 24,
|
||||
labelTip:
|
||||
'支持 Ant 风格路径匹配,可配置多个,如 /blade-system/user/** 匹配多层、/api/* 匹配单层;多个路径共享该规则的同一份配额',
|
||||
rules: [{ required: true, message: '请输入匹配路径', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '请求方法',
|
||||
prop: 'requestMethod',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
search: true,
|
||||
value: 'ALL',
|
||||
width: 100,
|
||||
labelTip: '指定规则生效的 HTTP 请求方法,选择「全部」时对所有方法生效,否则仅对所选方法限流',
|
||||
dicData: [
|
||||
{ label: '全部', value: 'ALL' },
|
||||
{ label: 'GET', value: 'GET' },
|
||||
{ label: 'POST', value: 'POST' },
|
||||
{ label: 'PUT', value: 'PUT' },
|
||||
{ label: 'DELETE', value: 'DELETE' },
|
||||
],
|
||||
rules: [{ required: true, message: '请选择请求方法', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '限流维度',
|
||||
prop: 'limitDimension',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
search: true,
|
||||
value: 'IP',
|
||||
width: 100,
|
||||
labelTip: '全局:所有请求共享配额;租户:按租户隔离;IP:按来源IP隔离;用户:按登录用户隔离',
|
||||
dicData: [
|
||||
{ label: '全局', value: 'GLOBAL' },
|
||||
{ label: '租户', value: 'TENANT' },
|
||||
{ label: 'IP', value: 'IP' },
|
||||
{ label: '用户', value: 'USER' },
|
||||
],
|
||||
rules: [{ required: true, message: '请选择限流维度', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '请求阈值',
|
||||
prop: 'limitCount',
|
||||
type: 'number',
|
||||
minRows: 1,
|
||||
value: 100,
|
||||
hide: true,
|
||||
labelTip: '单个时间窗口内允许通过的最大请求数,超出后触发限流并返回 429',
|
||||
rules: [{ required: true, message: '请输入请求阈值', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '时间窗口',
|
||||
prop: 'limitPeriod',
|
||||
type: 'number',
|
||||
minRows: 1,
|
||||
value: 1,
|
||||
hide: true,
|
||||
labelTip: '限流统计周期的数值,与时间单位组合使用,如时间窗口 1 + 时间单位分钟表示每分钟一个周期',
|
||||
rules: [{ required: true, message: '请输入时间窗口', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '时间单位',
|
||||
prop: 'timeUnit',
|
||||
type: 'select',
|
||||
value: 'MINUTES',
|
||||
hide: true,
|
||||
labelTip: '限流统计周期的计量单位,与请求阈值、时间窗口共同构成限流速率,如 100 次 / 分钟',
|
||||
dicData: [
|
||||
{ label: '秒', value: 'SECONDS' },
|
||||
{ label: '分钟', value: 'MINUTES' },
|
||||
{ label: '小时', value: 'HOURS' },
|
||||
],
|
||||
rules: [{ required: true, message: '请选择时间单位', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '限流速率',
|
||||
prop: 'rate',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '优先级',
|
||||
prop: 'priority',
|
||||
type: 'number',
|
||||
value: 1,
|
||||
hide: true,
|
||||
labelTip: '数值越小越优先匹配,同一路径命中多条规则时取优先级最高的一条',
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
value: 1,
|
||||
display: false,
|
||||
width: 90,
|
||||
dicData: [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: -1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 2,
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
// 单条规则触发日志
|
||||
logDrawerVisible: false,
|
||||
logLimitName: '',
|
||||
logLimitId: null,
|
||||
logForm: {},
|
||||
logQuery: {},
|
||||
logLoading: false,
|
||||
logData: [],
|
||||
logPage: { pageSize: 10, currentPage: 1, total: 0 },
|
||||
logOption: {
|
||||
height: 'auto',
|
||||
calcHeight: 70,
|
||||
tip: false,
|
||||
header: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 100,
|
||||
dialogType: 'drawer',
|
||||
dialogWidth: 800,
|
||||
column: [
|
||||
{ label: '请求接口', prop: 'requestUri', search: true, slot: true },
|
||||
{ label: '请求方法', prop: 'method', slot: true, width: 90 },
|
||||
{
|
||||
label: '限流维度',
|
||||
prop: 'limitDimension',
|
||||
type: 'select',
|
||||
slot: true,
|
||||
width: 100,
|
||||
dicData: [
|
||||
{ label: '全局', value: 'GLOBAL' },
|
||||
{ label: '租户', value: 'TENANT' },
|
||||
{ label: 'IP', value: 'IP' },
|
||||
{ label: '用户', value: 'USER' },
|
||||
],
|
||||
},
|
||||
{ label: '限流速率', prop: 'rate', slot: true, formslot: true, width: 150 },
|
||||
{ label: '租户ID', prop: 'tenantId', hide: true },
|
||||
{ label: '账号', prop: 'createBy', hide: true },
|
||||
{ label: '触发IP', prop: 'remoteIp', search: true, width: 140 },
|
||||
{ label: '服务ID', prop: 'serviceId', hide: true },
|
||||
{ label: '服务IP', prop: 'serverIp', hide: true, width: 160 },
|
||||
{ label: '环境', prop: 'env', hide: true, width: 85 },
|
||||
{ label: '用户代理', prop: 'userAgent', span: 24, hide: true },
|
||||
{ label: '触发时间', prop: 'createTime', span: 24, width: 170 },
|
||||
{ label: '请求数据', prop: 'params', type: 'textarea', span: 24, minRows: 2, hide: true },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 同步动态多行输入到表单字段,使匹配路径的必填校验生效
|
||||
limitPatternList: {
|
||||
handler() {
|
||||
this.form.limitPattern = this.buildLimitPattern();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
timeUnitLabel(value) {
|
||||
const item = this.timeUnitDic.find(unit => unit.value === value);
|
||||
return item ? item.label : value;
|
||||
},
|
||||
requestMethodLabel(value) {
|
||||
const item = this.requestMethodDic.find(method => method.value === value);
|
||||
return item ? item.label : value;
|
||||
},
|
||||
limitDimensionLabel(value) {
|
||||
const item = this.limitDimensionDic.find(dimension => dimension.value === value);
|
||||
return item ? item.label : value;
|
||||
},
|
||||
addLimitPattern() {
|
||||
this.limitPatternList.push('');
|
||||
},
|
||||
removeLimitPattern(index) {
|
||||
this.limitPatternList.splice(index, 1);
|
||||
},
|
||||
parseLimitPattern(limitPattern) {
|
||||
if (!limitPattern) return [];
|
||||
return limitPattern
|
||||
.split(',')
|
||||
.map(pattern => pattern.trim())
|
||||
.filter(pattern => pattern.length > 0);
|
||||
},
|
||||
buildLimitPattern() {
|
||||
const patterns = this.limitPatternList
|
||||
.map(pattern => pattern && pattern.trim())
|
||||
.filter(pattern => pattern && pattern.length > 0);
|
||||
return patterns.length > 0 ? patterns.join(',') : '';
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleEnable(row) {
|
||||
this.$confirm('确定启用该限流规则?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return enable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '启用成功!' });
|
||||
});
|
||||
},
|
||||
handleDisable(row) {
|
||||
this.$confirm('确定禁用该限流规则?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return disable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '禁用成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add'].includes(type)) {
|
||||
this.limitPatternList = [''];
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
const patternList = this.parseLimitPattern(this.form.limitPattern);
|
||||
this.limitPatternList = patternList.length > 0 ? patternList : [''];
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
// ==================== 单条规则触发日志 ====================
|
||||
handleRowLog(row) {
|
||||
this.logLimitName = row.limitName || '限流规则';
|
||||
this.logLimitId = row.id;
|
||||
this.logDrawerVisible = true;
|
||||
this.logPage.currentPage = 1;
|
||||
this.logQuery = {};
|
||||
this.onLoadLog(this.logPage);
|
||||
},
|
||||
beforeOpenLog(done, type) {
|
||||
if (['view'].includes(type)) {
|
||||
getLogDetail(this.logForm.id).then(res => {
|
||||
this.logForm = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
logSearchChange(params, done) {
|
||||
this.logQuery = params;
|
||||
this.logPage.currentPage = 1;
|
||||
this.onLoadLog(this.logPage, params);
|
||||
done();
|
||||
},
|
||||
logSearchReset() {
|
||||
this.logQuery = {};
|
||||
this.onLoadLog(this.logPage);
|
||||
},
|
||||
logCurrentChange(currentPage) {
|
||||
this.logPage.currentPage = currentPage;
|
||||
},
|
||||
logSizeChange(pageSize) {
|
||||
this.logPage.pageSize = pageSize;
|
||||
},
|
||||
logRefreshChange() {
|
||||
this.onLoadLog(this.logPage, this.logQuery);
|
||||
},
|
||||
onLoadLog(page, params = {}) {
|
||||
this.logLoading = true;
|
||||
const query = Object.assign({ limitId: this.logLimitId }, params, this.logQuery);
|
||||
getLogList(page.currentPage, page.pageSize, query).then(res => {
|
||||
const data = res.data.data;
|
||||
this.logPage.total = data.total;
|
||||
this.logData = data.records;
|
||||
this.logLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
655
src/views/authority/role.vue
Normal file
655
src/views/authority/role.vue
Normal file
@@ -0,0 +1,655 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.role_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-setting"
|
||||
@click="handleRole"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
plain
|
||||
>权限设置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
plain
|
||||
style="border: 0; background-color: transparent !important"
|
||||
@click.stop="handleRowRole(row)"
|
||||
>权限设置
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog
|
||||
title="角色权限配置"
|
||||
append-to-body
|
||||
v-model="box"
|
||||
width="345px"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="菜单权限">
|
||||
<el-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||
>
|
||||
<span style="display: inline-flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="menuLinked"
|
||||
active-text="节点联动"
|
||||
size="small"
|
||||
@change="handleLinkedChange('treeMenu')"
|
||||
/>
|
||||
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
|
||||
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button size="small" plain @click="handleSelectAll('treeMenu', menuGrantList)"
|
||||
>全选</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
@click="handleInvertSelect('treeMenu', menuGrantList, menuLinked)"
|
||||
>反选</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<el-tree
|
||||
:data="menuGrantList"
|
||||
show-checkbox
|
||||
:check-strictly="!menuLinked"
|
||||
node-key="id"
|
||||
ref="treeMenu"
|
||||
:default-checked-keys="menuTreeObj"
|
||||
:props="props"
|
||||
>
|
||||
</el-tree>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="数据权限">
|
||||
<el-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||
>
|
||||
<span style="display: inline-flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="dataScopeLinked"
|
||||
active-text="节点联动"
|
||||
size="small"
|
||||
@change="handleLinkedChange('treeDataScope')"
|
||||
/>
|
||||
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
|
||||
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
@click="handleSelectAll('treeDataScope', dataScopeGrantList)"
|
||||
>全选</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
@click="handleInvertSelect('treeDataScope', dataScopeGrantList, dataScopeLinked)"
|
||||
>反选</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<el-tree
|
||||
:data="dataScopeGrantList"
|
||||
show-checkbox
|
||||
:check-strictly="!dataScopeLinked"
|
||||
node-key="id"
|
||||
ref="treeDataScope"
|
||||
:default-checked-keys="dataScopeTreeObj"
|
||||
:props="props"
|
||||
>
|
||||
</el-tree>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="接口权限">
|
||||
<el-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||
>
|
||||
<span style="display: inline-flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="apiScopeLinked"
|
||||
active-text="节点联动"
|
||||
size="small"
|
||||
@change="handleLinkedChange('treeApiScope')"
|
||||
/>
|
||||
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
|
||||
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
@click="handleSelectAll('treeApiScope', apiScopeGrantList)"
|
||||
>全选</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
@click="handleInvertSelect('treeApiScope', apiScopeGrantList, apiScopeLinked)"
|
||||
>反选</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<el-tree
|
||||
:data="apiScopeGrantList"
|
||||
show-checkbox
|
||||
:check-strictly="!apiScopeLinked"
|
||||
node-key="id"
|
||||
ref="treeApiScope"
|
||||
:default-checked-keys="apiScopeTreeObj"
|
||||
:props="props"
|
||||
>
|
||||
</el-tree>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="box = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
add,
|
||||
getList,
|
||||
getRole,
|
||||
getRoleAlias,
|
||||
getRoleTreeById,
|
||||
grant,
|
||||
grantTree,
|
||||
remove,
|
||||
update,
|
||||
} from '@/api/system/role';
|
||||
import { mapGetters } from 'vuex';
|
||||
import website from '@/config/website';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
box: false,
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
},
|
||||
menuGrantList: [],
|
||||
dataScopeGrantList: [],
|
||||
apiScopeGrantList: [],
|
||||
apiGrantList: [],
|
||||
menuTreeObj: [],
|
||||
dataScopeTreeObj: [],
|
||||
apiScopeTreeObj: [],
|
||||
menuLinked: false,
|
||||
dataScopeLinked: false,
|
||||
apiScopeLinked: false,
|
||||
currentRoleIds: [],
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
menuWidth: 350,
|
||||
dialogWidth: 600,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '上级角色',
|
||||
prop: 'parentId',
|
||||
dicData: [],
|
||||
type: 'tree',
|
||||
hide: true,
|
||||
span: 24,
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: '请选择上级角色',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '角色名称',
|
||||
prop: 'roleName',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入角色名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '所属租户',
|
||||
prop: 'tenantId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/tenant/select',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: website.tenantMode,
|
||||
span: 24,
|
||||
props: {
|
||||
label: 'tenantName',
|
||||
value: 'tenantId',
|
||||
},
|
||||
hide: !website.tenantMode,
|
||||
search: website.tenantMode,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入所属租户',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '角色别名',
|
||||
labelTip: '用于权限校验与标注的属性,可手动录入也可点击现有别名快速录入',
|
||||
prop: 'roleAlias',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入角色别名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '现有别名',
|
||||
labelTip: '当前系统已存在的角色别名,点击可快速录入',
|
||||
prop: 'currentAlias',
|
||||
type: 'radio',
|
||||
dicData: [],
|
||||
props: {
|
||||
label: 'roleName',
|
||||
value: 'roleAlias',
|
||||
},
|
||||
hide: true,
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '角色排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入角色排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.role_add, false),
|
||||
viewBtn: this.validData(this.permission.role_view, false),
|
||||
delBtn: this.validData(this.permission.role_delete, false),
|
||||
editBtn: this.validData(this.permission.role_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
idsArray() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.currentAlias'() {
|
||||
const alias = this.form.currentAlias;
|
||||
if (!validatenull(alias)) {
|
||||
this.form.roleAlias = alias;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData(roleId) {
|
||||
getRoleTreeById(roleId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
getAllNodeKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
keys.push(node.id);
|
||||
if (node.children && node.children.length > 0) {
|
||||
keys = keys.concat(this.getAllNodeKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
getLeafKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
keys.push(node.id);
|
||||
} else {
|
||||
keys = keys.concat(this.getLeafKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
handleSelectAll(treeRef, dataList) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
const allKeys = this.getAllNodeKeys(dataList);
|
||||
tree.setCheckedKeys(allKeys);
|
||||
},
|
||||
handleInvertSelect(treeRef, dataList, isLinked) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
const checkedKeys = new Set(tree.getCheckedKeys());
|
||||
if (isLinked) {
|
||||
const leafKeys = this.getLeafKeys(dataList);
|
||||
const invertedKeys = leafKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
} else {
|
||||
const allKeys = this.getAllNodeKeys(dataList);
|
||||
const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
}
|
||||
},
|
||||
handleLinkedChange(treeRef) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
const checkedKeys = tree.getCheckedKeys();
|
||||
const halfCheckedKeys = tree.getHalfCheckedKeys();
|
||||
this.$nextTick(() => {
|
||||
tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
|
||||
});
|
||||
},
|
||||
// el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放,
|
||||
// 复用的树实例需在打开配置时整体覆盖勾选状态,避免残留上一次配置的勾选
|
||||
applyCheckedKeys(treeRef, keys) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
tree.setCheckedKeys(keys);
|
||||
},
|
||||
submit() {
|
||||
const menuList = this.$refs.treeMenu.getCheckedKeys();
|
||||
const dataScopeList = this.$refs.treeDataScope.getCheckedKeys();
|
||||
const apiScopeList = this.$refs.treeApiScope.getCheckedKeys();
|
||||
grant(this.currentRoleIds, menuList, dataScopeList, apiScopeList).then(() => {
|
||||
this.box = false;
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.currentRoleIds = [];
|
||||
this.selectionList = [];
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData(this.form.id);
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
if (this.form.parentId === '0') {
|
||||
this.form.parentId = '';
|
||||
}
|
||||
}
|
||||
getRoleAlias().then(res => {
|
||||
const column = this.findColumn(this.option.column, 'currentAlias');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
done();
|
||||
},
|
||||
handleRole() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择一条数据');
|
||||
return;
|
||||
}
|
||||
if (this.selectionList.length > 1) {
|
||||
this.$message.warning('只能选择一条数据');
|
||||
return;
|
||||
}
|
||||
this.menuTreeObj = [];
|
||||
this.dataScopeTreeObj = [];
|
||||
this.apiScopeTreeObj = [];
|
||||
// 保存当前要配置权限的角色ID
|
||||
this.currentRoleIds = this.idsArray;
|
||||
grantTree().then(res => {
|
||||
this.menuGrantList = res.data.data.menu;
|
||||
this.dataScopeGrantList = res.data.data.dataScope;
|
||||
this.apiScopeGrantList = res.data.data.apiScope;
|
||||
getRole(this.ids).then(res => {
|
||||
this.menuTreeObj = res.data.data.menu;
|
||||
this.dataScopeTreeObj = res.data.data.dataScope;
|
||||
this.apiScopeTreeObj = res.data.data.apiScope;
|
||||
this.box = true;
|
||||
this.$nextTick(() => {
|
||||
this.applyCheckedKeys('treeMenu', this.menuTreeObj);
|
||||
this.applyCheckedKeys('treeDataScope', this.dataScopeTreeObj);
|
||||
this.applyCheckedKeys('treeApiScope', this.apiScopeTreeObj);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowRole(row) {
|
||||
this.menuTreeObj = [];
|
||||
this.dataScopeTreeObj = [];
|
||||
this.apiScopeTreeObj = [];
|
||||
// 保存当前要配置权限的角色ID(单个行操作)
|
||||
this.currentRoleIds = [row.id];
|
||||
grantTree().then(res => {
|
||||
this.menuGrantList = res.data.data.menu;
|
||||
this.dataScopeGrantList = res.data.data.dataScope;
|
||||
this.apiScopeGrantList = res.data.data.apiScope;
|
||||
getRole(row.id).then(res => {
|
||||
this.menuTreeObj = res.data.data.menu;
|
||||
this.dataScopeTreeObj = res.data.data.dataScope;
|
||||
this.apiScopeTreeObj = res.data.data.apiScope;
|
||||
this.box = true;
|
||||
this.$nextTick(() => {
|
||||
this.applyCheckedKeys('treeMenu', this.menuTreeObj);
|
||||
this.applyCheckedKeys('treeDataScope', this.dataScopeTreeObj);
|
||||
this.applyCheckedKeys('treeApiScope', this.apiScopeTreeObj);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDialogClose() {
|
||||
// 对话框关闭时清空选择状态和权限树数据
|
||||
this.menuTreeObj = [];
|
||||
this.dataScopeTreeObj = [];
|
||||
this.apiScopeTreeObj = [];
|
||||
this.menuLinked = false;
|
||||
this.dataScopeLinked = false;
|
||||
this.apiScopeLinked = false;
|
||||
this.selectionClear();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
this.data = res.data.data;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
472
src/views/base/region.vue
Normal file
472
src/views/base/region.vue
Normal file
@@ -0,0 +1,472 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="9">
|
||||
<div class="box">
|
||||
<el-scrollbar>
|
||||
<basic-container>
|
||||
<avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick" />
|
||||
</basic-container>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="15">
|
||||
<basic-container>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
v-if="permission.region_add"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="addChildren"
|
||||
>新增下级
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permission.region_delete"
|
||||
type="primary"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete"
|
||||
>删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permission.region_import"
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
@click="handleImport"
|
||||
>导入
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permission.region_export"
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
@click="handleExport"
|
||||
>导出
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permission.region_debug"
|
||||
type="primary"
|
||||
icon="el-icon-video-play"
|
||||
@click="handleDebug"
|
||||
>调试
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</basic-container>
|
||||
<basic-container>
|
||||
<avue-form ref="form" :option="regionOption" v-model="regionForm" @submit="handleSubmit">
|
||||
<template #code="{}">
|
||||
<el-input placeholder="请输入 区划子编号" v-model="regionForm.subCode">
|
||||
<template #prepend>{{ regionForm.parentCode }}</template>
|
||||
</el-input>
|
||||
</template>
|
||||
</avue-form>
|
||||
<el-dialog title="行政区划数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
<el-dialog title="行政区划数据调试" append-to-body v-model="debugBox" width="350px">
|
||||
<avue-form :option="debugOption" v-model="debugForm" />
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLazyTree, getDetail, submit, remove } from '@/api/base/region';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
topCode: '00',
|
||||
treeCode: '',
|
||||
treeParentCode: '',
|
||||
treeData: [],
|
||||
treeOption: {
|
||||
nodeKey: 'id',
|
||||
lazy: true,
|
||||
treeLoad: function (node, resolve) {
|
||||
const parentCode = node.level === 0 ? '00' : node.data.id;
|
||||
getLazyTree(parentCode).then(res => {
|
||||
resolve(
|
||||
res.data.data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
leaf: !item.hasChildren,
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
addBtn: false,
|
||||
menu: false,
|
||||
size: 'small',
|
||||
props: {
|
||||
labelText: '标题',
|
||||
label: 'title',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
regionForm: {},
|
||||
regionOption: {
|
||||
labelWidth: 100,
|
||||
column: [
|
||||
{
|
||||
label: '父区划编号',
|
||||
prop: 'parentCode',
|
||||
span: 24,
|
||||
disabled: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入父区划编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '父区划名称',
|
||||
prop: 'parentName',
|
||||
span: 24,
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: '区划编号',
|
||||
prop: 'code',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入区划编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '区划子编号',
|
||||
prop: 'subCode',
|
||||
display: false,
|
||||
},
|
||||
{
|
||||
label: '区划名称',
|
||||
prop: 'name',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入区划名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '区划等级',
|
||||
prop: 'regionLevel',
|
||||
type: 'radio',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=region',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择区划等级',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '区划排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入区划排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '区划备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 6,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '模板上传',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
action: '/blade-system/region/import-region',
|
||||
},
|
||||
{
|
||||
label: '数据覆盖',
|
||||
prop: 'isCovered',
|
||||
type: 'switch',
|
||||
align: 'center',
|
||||
width: 80,
|
||||
dicData: [
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
value: 0,
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择是否覆盖',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '模板下载',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
debugBox: false,
|
||||
debugForm: {},
|
||||
debugOption: {
|
||||
labelWidth: 50,
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '省份',
|
||||
prop: 'province',
|
||||
type: 'select',
|
||||
props: {
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
},
|
||||
cascader: ['city'],
|
||||
dicUrl: '/blade-system/region/select',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '地市',
|
||||
prop: 'city',
|
||||
type: 'select',
|
||||
props: {
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
},
|
||||
cascader: ['district'],
|
||||
dicFlag: false,
|
||||
dicUrl: '/blade-system/region/select?code={{province}}',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '区县',
|
||||
prop: 'district',
|
||||
type: 'select',
|
||||
props: {
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
},
|
||||
dicFlag: false,
|
||||
dicUrl: '/blade-system/region/select?code={{city}}',
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'regionForm.subCode'() {
|
||||
this.regionForm.code = this.regionForm.parentCode + this.regionForm.subCode;
|
||||
},
|
||||
'excelForm.isCovered'() {
|
||||
if (this.excelForm.isCovered !== '') {
|
||||
const column = this.findColumn(this.excelOption.column, 'excelFile');
|
||||
column.action = `/blade-system/region/import-region?isCovered=${this.excelForm.isCovered}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.region_add, false),
|
||||
viewBtn: this.validData(this.permission.region_view, false),
|
||||
delBtn: this.validData(this.permission.region_delete, false),
|
||||
editBtn: this.validData(this.permission.region_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initTree() {
|
||||
this.treeData = [];
|
||||
getLazyTree(this.topCode).then(res => {
|
||||
this.treeData = res.data.data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
leaf: !item.hasChildren,
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
nodeClick(data) {
|
||||
const column = this.findColumn(this.regionOption.column, 'parentCode');
|
||||
column.disabled = true;
|
||||
this.treeCode = data.id;
|
||||
this.treeParentCode = data.parentId;
|
||||
getDetail(this.treeCode).then(res => {
|
||||
this.regionForm = res.data.data;
|
||||
this.regionForm.subCode = this.regionForm.code.replace(this.regionForm.parentCode, '');
|
||||
});
|
||||
},
|
||||
addChildren() {
|
||||
if (validatenull(this.regionForm.code) || validatenull(this.regionForm.name)) {
|
||||
this.$message.warning('请先选择一项区划');
|
||||
return;
|
||||
}
|
||||
this.regionForm.parentCode = this.regionForm.code;
|
||||
this.regionForm.parentName = this.regionForm.name;
|
||||
this.regionForm.code = '';
|
||||
this.regionForm.subCode = '';
|
||||
this.regionForm.name = '';
|
||||
this.regionForm.regionLevel =
|
||||
this.regionForm.regionLevel === 5 ? 5 : this.regionForm.regionLevel + 1;
|
||||
},
|
||||
handleSubmit(form, done) {
|
||||
const parentCode = form.parentCode === this.topCode ? '' : form.parentCode;
|
||||
form.code = parentCode + form.subCode;
|
||||
submit(form).then(
|
||||
() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.initTree();
|
||||
this.regionForm.subCode = '';
|
||||
this.$refs.form.resetForm();
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
done();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
handleDelete() {
|
||||
if (validatenull(this.regionForm.code)) {
|
||||
this.$message.warning('请先选择一项区划');
|
||||
return;
|
||||
}
|
||||
this.$confirm(`确定将 [${this.regionForm.name}] 数据删除?`, {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.treeCode);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.initTree();
|
||||
this.regionForm.subCode = '';
|
||||
this.$refs.form.resetForm();
|
||||
});
|
||||
},
|
||||
uploadAfter(res, done, loading, column) {
|
||||
window.console.log(column);
|
||||
this.excelBox = false;
|
||||
this.initTree();
|
||||
done();
|
||||
},
|
||||
handleDebug() {
|
||||
this.debugBox = true;
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出行政区划数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob(
|
||||
`/blade-system/region/export-region?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, `行政区划数据${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-system/region/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '行政区划模板.xlsx');
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
height: 800px;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.box .el-scrollbar__wrap {
|
||||
overflow: scroll;
|
||||
}
|
||||
</style>
|
||||
290
src/views/data/record.vue
Normal file
290
src/views/data/record.vue
Normal file
@@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #userAgent-form="{}">
|
||||
<code-editor v-model="form.userAgent" :readonly="true" theme="nord" height="50px" />
|
||||
</template>
|
||||
<template #oldData-form="{}">
|
||||
<code-editor
|
||||
v-model="form.oldData"
|
||||
:json="true"
|
||||
:readonly="true"
|
||||
theme="nord"
|
||||
height="200px"
|
||||
/>
|
||||
</template>
|
||||
<template #newData-form="{}">
|
||||
<code-editor
|
||||
v-model="form.newData"
|
||||
:json="true"
|
||||
:readonly="true"
|
||||
theme="nord"
|
||||
height="200px"
|
||||
/>
|
||||
</template>
|
||||
<template #recordResult-form="{}">
|
||||
<code-editor
|
||||
v-model="form.recordResult"
|
||||
:json="true"
|
||||
:readonly="true"
|
||||
theme="nord"
|
||||
height="350px"
|
||||
/>
|
||||
</template>
|
||||
<template #recordMessage-form="{}">
|
||||
<code-editor v-model="form.recordMessage" :readonly="true" theme="nord" height="350px" />
|
||||
</template>
|
||||
<template #env="{ row }">
|
||||
<el-tag>{{ row.env }}</el-tag>
|
||||
</template>
|
||||
<template #tableName="{ row }">
|
||||
<el-tag>{{ row.tableName }}</el-tag>
|
||||
</template>
|
||||
<template #method="{ row }">
|
||||
<el-tag>{{ row.method }}</el-tag>
|
||||
</template>
|
||||
<template #recordLevel="{ row }">
|
||||
<el-tag>{{ row.recordLevel }}</el-tag>
|
||||
</template>
|
||||
<template #remoteIp="{ row }">
|
||||
<el-tag>{{ row.remoteIp }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRecordList, getRecordDetail } from '@/api/data/record';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchIndex: 3,
|
||||
searchIcon: true,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 120,
|
||||
labelWidth: 120,
|
||||
dialogType: 'drawer',
|
||||
column: [
|
||||
{
|
||||
label: '服务ID',
|
||||
prop: 'serviceId',
|
||||
width: '120',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '服务器名',
|
||||
prop: 'serverHost',
|
||||
width: '140',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '服务器IP',
|
||||
prop: 'serverIp',
|
||||
width: '180',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '环境',
|
||||
prop: 'env',
|
||||
width: '80',
|
||||
search: true,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '审计级别',
|
||||
prop: 'recordLevel',
|
||||
width: '100',
|
||||
search: true,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '数据表名',
|
||||
prop: 'tableName',
|
||||
width: '150',
|
||||
search: true,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '操作方式',
|
||||
prop: 'method',
|
||||
width: '100',
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '请求URI',
|
||||
prop: 'requestUri',
|
||||
search: true,
|
||||
overHidden: true,
|
||||
},
|
||||
{
|
||||
label: '操作类型',
|
||||
prop: 'operation',
|
||||
width: '100',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '操作IP',
|
||||
prop: 'remoteIp',
|
||||
width: '140',
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '记录人',
|
||||
prop: 'recordUserName',
|
||||
width: '120',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '记录耗时',
|
||||
prop: 'recordCost',
|
||||
width: '100',
|
||||
suffix: 'ms',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '记录时间',
|
||||
prop: 'recordTime',
|
||||
width: '180',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '用户代理',
|
||||
prop: 'userAgent',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '操作前数据',
|
||||
prop: 'oldData',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 3,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '操作后数据',
|
||||
prop: 'newData',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 3,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '审计结果',
|
||||
prop: 'recordResult',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 3,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '审计消息',
|
||||
prop: 'recordMessage',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
viewBtn: this.validData(this.permission.record_view, false),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getRecordDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getRecordList(page.currentPage, page.pageSize, Object.assign(params, this.query))
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 可以根据需要添加自定义样式 */
|
||||
</style>
|
||||
309
src/views/desk/notice-composition.vue
Normal file
309
src/views/desk/notice-composition.vue
Normal file
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
v-model:search="search"
|
||||
ref="crud"
|
||||
@row-del="rowDel"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.notice_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
import { getList, remove, update, add, getNotice } from '@/api/desk/notice';
|
||||
|
||||
// 定义响应式数据
|
||||
const form = ref({});
|
||||
const search = ref({});
|
||||
const loading = ref(true);
|
||||
const page = ref({
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
});
|
||||
const selectionList = ref([]);
|
||||
const data = ref([]);
|
||||
const option = reactive({
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 950,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
excelBtn: true,
|
||||
dialogClickModal: false,
|
||||
grid: false,
|
||||
column: [
|
||||
{
|
||||
label: '通知标题',
|
||||
prop: 'title',
|
||||
span: 24,
|
||||
row: true,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知标题',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=notice',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知时间',
|
||||
prop: 'releaseTimeRange',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
searchRange: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知时间',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知日期',
|
||||
prop: 'releaseTime',
|
||||
type: 'date',
|
||||
gridRow: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知日期',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知内容',
|
||||
prop: 'content',
|
||||
component: 'avue-ueditor',
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
},
|
||||
hide: true,
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 使用 vuex store
|
||||
const store = useStore();
|
||||
const permission = computed(() => store.getters.permission);
|
||||
|
||||
// 计算权限
|
||||
const permissionList = computed(() => {
|
||||
const validData = (value, defaultVal = false) => (value ? true : defaultVal);
|
||||
return {
|
||||
addBtn: validData(permission.value.notice_add, false),
|
||||
viewBtn: validData(permission.value.notice_view, false),
|
||||
delBtn: validData(permission.value.notice_delete, false),
|
||||
editBtn: validData(permission.value.notice_edit, false),
|
||||
};
|
||||
});
|
||||
|
||||
// 计算选中的id
|
||||
const ids = computed(() => {
|
||||
return selectionList.value.map(ele => ele.id).join(',');
|
||||
});
|
||||
|
||||
// 新增
|
||||
const rowSave = (row, done, loading) => {
|
||||
add(row).then(
|
||||
() => {
|
||||
onLoad();
|
||||
ElMessage.success('操作成功!');
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
console.error(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// 修改
|
||||
const rowUpdate = (row, index, done, loading) => {
|
||||
update(row).then(
|
||||
() => {
|
||||
onLoad();
|
||||
ElMessage.success('操作成功!');
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
console.error(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const rowDel = row => {
|
||||
ElMessageBox.confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
onLoad();
|
||||
ElMessage.success('操作成功!');
|
||||
});
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const searchReset = () => {
|
||||
search.value = {};
|
||||
page.value.currentPage = 1;
|
||||
onLoad();
|
||||
};
|
||||
|
||||
// 查询
|
||||
const searchChange = (params, done) => {
|
||||
page.value.currentPage = 1;
|
||||
onLoad();
|
||||
done();
|
||||
};
|
||||
|
||||
// 勾选事件
|
||||
const selectionChange = list => {
|
||||
selectionList.value = list;
|
||||
};
|
||||
|
||||
// 重置勾选
|
||||
const selectionClear = () => {
|
||||
selectionList.value = [];
|
||||
// 假设你有一个 ref 为 'crud' 的组件,可以调用 toggleSelection 方法
|
||||
// if (crudRef.value) crudRef.value.toggleSelection();
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const handleDelete = () => {
|
||||
if (selectionList.value.length === 0) {
|
||||
ElMessage.success('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(ids.value))
|
||||
.then(() => {
|
||||
onLoad();
|
||||
ElMessage.success('操作成功!');
|
||||
});
|
||||
};
|
||||
|
||||
// 打开前事件
|
||||
const beforeOpen = (done, type) => {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getNotice(form.value.id).then(res => {
|
||||
form.value = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
};
|
||||
|
||||
// 刷新事件
|
||||
const refreshChange = () => {
|
||||
onLoad();
|
||||
};
|
||||
|
||||
// 加载数据
|
||||
const onLoad = () => {
|
||||
const { releaseTimeRange } = search.value;
|
||||
let values = { ...search.value };
|
||||
if (releaseTimeRange) {
|
||||
values = {
|
||||
...values,
|
||||
releaseTime_datege: releaseTimeRange[0],
|
||||
releaseTime_datelt: releaseTimeRange[1],
|
||||
};
|
||||
values.releaseTimeRange = null;
|
||||
}
|
||||
loading.value = true;
|
||||
getList(page.value.currentPage, page.value.pageSize, values).then(res => {
|
||||
const { total, records } = res.data.data;
|
||||
page.value.total = total;
|
||||
data.value = records;
|
||||
loading.value = false;
|
||||
selectionClear();
|
||||
});
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
// 可以在这里执行组件挂载后的逻辑
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 你的样式代码 */
|
||||
</style>
|
||||
317
src/views/desk/notice.vue
Normal file
317
src/views/desk/notice.vue
Normal file
@@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
ref="crud"
|
||||
@row-del="rowDel"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.notice_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, remove, update, add, getNotice } from '@/api/desk/notice';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 950,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
excelBtn: true,
|
||||
dialogClickModal: false,
|
||||
grid: false,
|
||||
column: [
|
||||
{
|
||||
label: '通知标题',
|
||||
prop: 'title',
|
||||
span: 24,
|
||||
row: true,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知标题',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=notice',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知时间',
|
||||
prop: 'releaseTimeRange',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
searchRange: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知时间',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知日期',
|
||||
prop: 'releaseTime',
|
||||
type: 'date',
|
||||
gridRow: true,
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入通知日期',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '通知内容',
|
||||
prop: 'content',
|
||||
component: 'avue-ueditor',
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
},
|
||||
hide: true,
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
activated() {
|
||||
// 解析菜单路由携带的参数 (如 /desk/notice?test=123),激活钩子覆盖首次进入与标签切回两种场景
|
||||
const { test } = this.$route.query;
|
||||
if (!validatenull(test)) {
|
||||
this.$message.info(`query 参数 test = ${test}`);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.notice_add, false),
|
||||
viewBtn: this.validData(this.permission.notice_view, false),
|
||||
delBtn: this.validData(this.permission.notice_delete, false),
|
||||
editBtn: this.validData(this.permission.notice_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getNotice(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const { releaseTimeRange } = this.query;
|
||||
let values = {
|
||||
...params,
|
||||
...this.query,
|
||||
};
|
||||
if (releaseTimeRange) {
|
||||
values = {
|
||||
...values,
|
||||
releaseTime_datege: releaseTimeRange[0],
|
||||
releaseTime_datelt: releaseTimeRange[1],
|
||||
};
|
||||
values.releaseTimeRange = null;
|
||||
}
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
160
src/views/flow/deploy.vue
Normal file
160
src/views/flow/deploy.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-form
|
||||
ref="form"
|
||||
:option="option"
|
||||
v-model="form"
|
||||
:upload-before="uploadBefore"
|
||||
:upload-after="uploadAfter"
|
||||
/>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { deployUpload } from '@/api/flow/flow';
|
||||
import { flowCategory } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
flowCategory: '',
|
||||
tenantId: '',
|
||||
flowFile: [],
|
||||
file: {},
|
||||
},
|
||||
option: {
|
||||
labelWidth: 120,
|
||||
menuBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '流程类型',
|
||||
prop: 'flowCategory',
|
||||
type: 'select',
|
||||
dicUrl: `/blade-system/dict/dictionary?code=flow`,
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
row: true,
|
||||
span: 12,
|
||||
dataType: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择流程类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '流程模式',
|
||||
prop: 'flowMode',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{
|
||||
label: '通用流程',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '定制流程',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
value: 1,
|
||||
row: true,
|
||||
span: 12,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择流程模式',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '所属租户',
|
||||
prop: 'tenantId',
|
||||
type: 'tree',
|
||||
multiple: true,
|
||||
dicUrl: '/blade-system/tenant/select',
|
||||
props: {
|
||||
label: 'tenantName',
|
||||
value: 'tenantId',
|
||||
},
|
||||
display: false,
|
||||
row: true,
|
||||
span: 12,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择所属租户',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附件上传',
|
||||
prop: 'flowFile',
|
||||
type: 'upload',
|
||||
loadText: '附件上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
tip: '请上传 bpmn20.xml 标准格式文件',
|
||||
action: '/blade-flow/manager/check-upload',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.flowMode'() {
|
||||
this.$refs.form.option.column.filter(item => {
|
||||
if (item.prop === 'tenantId') {
|
||||
item.display = this.form.flowMode === 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
uploadBefore(file, done) {
|
||||
this.$message.success('部署提交');
|
||||
this.file = file;
|
||||
done();
|
||||
},
|
||||
uploadAfter(res, done, loading) {
|
||||
if (!this.form.flowCategory) {
|
||||
this.$message.warning('清先选择流程类型');
|
||||
loading();
|
||||
return false;
|
||||
}
|
||||
if (this.form.flowMode === 2 && !this.form.tenantId) {
|
||||
this.$message.warning('清先选择对应租户');
|
||||
loading();
|
||||
return false;
|
||||
}
|
||||
if (res.success) {
|
||||
deployUpload(
|
||||
flowCategory(this.form.flowCategory),
|
||||
this.form.tenantId ? this.form.tenantId.join(',') : '',
|
||||
[this.file]
|
||||
).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
done();
|
||||
} else {
|
||||
this.$message.error(data.msg);
|
||||
loading();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$message.warning('请上传 bpmn20.xml 标准格式文件');
|
||||
loading();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
201
src/views/flow/follow.vue
Normal file
201
src/views/flow/follow.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #suspensionState="{ row }">
|
||||
<el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="流程删除" append-to-body v-model="followBox" width="20%">
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form-item label="删除理由">
|
||||
<el-input v-model="deleteReason" placeholder="请输入删除理由" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="followBox = false">关 闭</el-button>
|
||||
<el-button type="primary" @click="handleDelete">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { followList, deleteProcessInstance } from '@/api/flow/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionId: '',
|
||||
processInstanceId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
followBox: false,
|
||||
deleteReason: '',
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 100,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '执行id',
|
||||
prop: 'executionId',
|
||||
search: true,
|
||||
width: 320,
|
||||
},
|
||||
{
|
||||
label: '流程key',
|
||||
prop: 'processDefinitionKey',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '实例id',
|
||||
prop: 'processInstanceId',
|
||||
search: true,
|
||||
width: 320,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'suspensionState',
|
||||
slot: true,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '发起人',
|
||||
prop: 'startUser',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '开始时间',
|
||||
prop: 'startTime',
|
||||
width: 170,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
delBtn: this.validData(this.permission.flow_follow_delete, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowDel(row) {
|
||||
this.followBox = true;
|
||||
this.selectionId = row.id;
|
||||
this.processInstanceId = row.processInstanceId;
|
||||
},
|
||||
handleDelete() {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return deleteProcessInstance({
|
||||
deleteReason: this.deleteReason,
|
||||
processInstanceId: this.processInstanceId,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.followBox = false;
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
followList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.none-border {
|
||||
border: 0;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
371
src/views/flow/manager.vue
Normal file
371
src/views/flow/manager.vue
Normal file
@@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-radio-group v-model="mode">
|
||||
<el-radio-button label="1">通用流程</el-radio-button>
|
||||
<el-radio-button label="2">定制流程</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-refresh"
|
||||
v-if="permission.flow_manager_state"
|
||||
@click.stop="handleState(scope.row, scope.index)"
|
||||
>变更状态
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-search"
|
||||
v-if="permission.flow_manager_image"
|
||||
@click.stop="handleImage(scope.row, scope.index)"
|
||||
>流程图
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.flow_manager_remove"
|
||||
@click.stop="handleSlotDelete(scope.row, scope.index)"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #tenantId="{ row }">
|
||||
<el-tag>{{ row.tenantId === '' ? '通用' : row.tenantId }}</el-tag>
|
||||
</template>
|
||||
<template #version="{ row }">
|
||||
<el-tag>v{{ row.version }}</el-tag>
|
||||
</template>
|
||||
<template #suspensionState="{ row }">
|
||||
<el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design
|
||||
v-if="this.website.design.designMode"
|
||||
is-dialog
|
||||
v-model:is-display="flowBox"
|
||||
:process-definition-id="processDefinitionId"
|
||||
></flow-design>
|
||||
<el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程图"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">关 闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog title="流程变更" append-to-body v-model="stateBox" width="20%">
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form-item label="流程状态">
|
||||
<el-select v-model="flowState" placeholder="请选择" value="">
|
||||
<el-option
|
||||
v-for="item in stateOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="stateBox = false">关 闭</el-button>
|
||||
<el-button type="primary" @click="handleDoState">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { managerList, changeState, deleteDeployment } from '@/api/flow/flow';
|
||||
import { flowCategory } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
mode: '1',
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
processDefinitionId: '',
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
stateBox: false,
|
||||
flowState: '',
|
||||
stateOptions: [
|
||||
{
|
||||
value: 'active',
|
||||
label: '激活',
|
||||
},
|
||||
{
|
||||
value: 'suspend',
|
||||
label: '挂起',
|
||||
},
|
||||
],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 250,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '租户编号',
|
||||
prop: 'tenantId',
|
||||
slot: true,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
label: '流程主键',
|
||||
prop: 'id',
|
||||
},
|
||||
{
|
||||
label: '流程标识',
|
||||
prop: 'key',
|
||||
search: true,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '流程名称',
|
||||
prop: 'name',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '流程分类',
|
||||
type: 'select',
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'version',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'suspensionState',
|
||||
slot: true,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '部署时间',
|
||||
prop: 'deploymentTime',
|
||||
width: 170,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
mode() {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
delBtn: this.validData(this.permission.flow_manager_remove, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
deploymentIds() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.deploymentId);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return deleteDeployment(this.deploymentIds);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
handleSlotDelete(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return deleteDeployment(row.deploymentId);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
handleState(row) {
|
||||
this.stateBox = true;
|
||||
this.selectionId = row.id;
|
||||
},
|
||||
handleDoState() {
|
||||
if (!this.flowState) {
|
||||
this.$message({
|
||||
type: 'warn',
|
||||
message: '请先选择流程状态!',
|
||||
});
|
||||
return;
|
||||
}
|
||||
changeState({ processId: this.selectionId, state: this.flowState }).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: data.msg,
|
||||
});
|
||||
this.stateBox = false;
|
||||
this.onLoad(this.page);
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'warn',
|
||||
message: data.msg,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleImage(row) {
|
||||
if (this.website.design.designMode) {
|
||||
this.processDefinitionId = row.id;
|
||||
} else {
|
||||
this.flowUrl = `/blade-flow/process/resource-view?processDefinitionId=${row.id}`;
|
||||
}
|
||||
this.flowBox = true;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const values = {
|
||||
...params,
|
||||
category: params.category ? flowCategory(params.category) : null,
|
||||
mode: this.mode,
|
||||
};
|
||||
this.loading = true;
|
||||
managerList(page.currentPage, page.pageSize, Object.assign(values, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
432
src/views/flow/model.vue
Normal file
432
src/views/flow/model.vue
Normal file
@@ -0,0 +1,432 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="default"
|
||||
icon="el-icon-circle-plus"
|
||||
v-if="permission.flow_model_create"
|
||||
plain
|
||||
@click="handleCreate"
|
||||
>创 建
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="default"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.flow_model_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
size="default"
|
||||
icon="el-icon-setting"
|
||||
v-if="permission.flow_model_update"
|
||||
@click.stop="handleUpdate(scope.row, scope.index)"
|
||||
>配置
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
size="default"
|
||||
icon="el-icon-upload"
|
||||
v-if="permission.flow_model_deploy"
|
||||
@click.stop="handleDeploy(scope.row, scope.index)"
|
||||
>部署
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
size="default"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.flow_model_delete"
|
||||
@click.stop="handleSlotDelete(scope.row, scope.index)"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #version="{ row }">
|
||||
<el-tag>v{{ row.version }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design-step ref="bpmn" @loadData="onLoad"></flow-design-step>
|
||||
<el-dialog
|
||||
v-if="!this.website.design.designMode"
|
||||
title="流程配置"
|
||||
append-to-body
|
||||
v-model="flowBox"
|
||||
:fullscreen="true"
|
||||
>
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程设计器"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleRefresh">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog title="流程部署" append-to-body v-model="deployBox" width="20%">
|
||||
<avue-form ref="form" :option="optionDeploy" v-model="form" @submit="handleSubmit" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="deployBox = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleDoDeploy" :loading="deployLoading"
|
||||
>确 定</el-button
|
||||
>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { modelList, removeModel, deployModel, detail } from '@/api/flow/flow';
|
||||
import { flowCategory } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
optionDeploy: {
|
||||
menuBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '流程类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'categoryValue',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择流程类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '流程模式',
|
||||
prop: 'flowMode',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{
|
||||
label: '通用流程',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '定制流程',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
value: 1,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择流程模式',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '所属租户',
|
||||
prop: 'tenantId',
|
||||
type: 'tree',
|
||||
multiple: true,
|
||||
dicUrl: '/blade-system/tenant/select',
|
||||
props: {
|
||||
label: 'tenantName',
|
||||
value: 'tenantId',
|
||||
},
|
||||
display: false,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择所属租户',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
deployLoading: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
deployBox: false,
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 220,
|
||||
dialogClickModal: false,
|
||||
size: 'default',
|
||||
searchSize: 'default',
|
||||
column: [
|
||||
{
|
||||
label: '模型主键',
|
||||
prop: 'id',
|
||||
},
|
||||
{
|
||||
label: '模型标识',
|
||||
prop: 'modelKey',
|
||||
search: true,
|
||||
overHidden: true,
|
||||
},
|
||||
{
|
||||
label: '模型名称',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
overHidden: true,
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'version',
|
||||
slot: true,
|
||||
overHidden: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'created',
|
||||
overHidden: true,
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'lastUpdated',
|
||||
overHidden: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.flowMode'() {
|
||||
this.$refs.form.option.column.filter(item => {
|
||||
if (item.prop === 'tenantId') {
|
||||
item.display = this.form.flowMode === 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSubmit(form, done) {
|
||||
this.deployLoading = true;
|
||||
deployModel({
|
||||
modelId: this.selectionId,
|
||||
category: flowCategory(form.categoryValue),
|
||||
tenantIds: form.tenantId ? form.tenantId.join(',') : '',
|
||||
}).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: data.msg,
|
||||
});
|
||||
done();
|
||||
this.$refs.form.resetForm();
|
||||
this.deployBox = false;
|
||||
this.deployLoading = false;
|
||||
} else {
|
||||
done();
|
||||
this.deployLoading = false;
|
||||
this.$message({
|
||||
type: 'warn',
|
||||
message: data.msg,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeModel(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
handleCreate() {
|
||||
if (!this.website.design.designMode) {
|
||||
this.flowUrl = `${this.website.design.designUrl}/index.html`;
|
||||
this.flowBox = true;
|
||||
} else {
|
||||
this.$refs.bpmn.visible = true;
|
||||
}
|
||||
},
|
||||
handleRefresh() {
|
||||
this.flowBox = false;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
handleUpdate(row) {
|
||||
if (this.website.design.designMode) {
|
||||
detail({ id: row.id }).then(res => {
|
||||
const data = res.data.data;
|
||||
const { modelEditorXml } = data;
|
||||
this.$refs.bpmn.nutflowOption.step1.xml = modelEditorXml;
|
||||
this.$refs.bpmn.nutflowOption.process = data;
|
||||
this.$refs.bpmn.visible = true;
|
||||
});
|
||||
} else {
|
||||
this.flowUrl = `${this.website.design.designUrl}/index.html#/editor/${row.id}`;
|
||||
this.flowBox = true;
|
||||
}
|
||||
},
|
||||
handleDeploy(row) {
|
||||
this.deployBox = true;
|
||||
this.selectionId = row.id;
|
||||
},
|
||||
handleDoDeploy() {
|
||||
this.$refs.form.submit();
|
||||
},
|
||||
handleSlotDelete(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return removeModel(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
page = page || this.page;
|
||||
modelList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.nf-dialog {
|
||||
.el-dialog__body {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.avue-dialog__footer {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
295
src/views/job/jobinfo.vue
Normal file
295
src/views/job/jobinfo.vue
Normal file
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
v-model:search="search"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.jobinfo_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button type="info" plain icon="el-icon-sort" @click="handleSync"
|
||||
>数 据 同 步
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button type="primary" text icon="el-icon-video-play" @click="handleRun(scope.row)"
|
||||
>运 行
|
||||
</el-button>
|
||||
</template>
|
||||
<template #enable="{ row }">
|
||||
<el-switch
|
||||
v-model="row.enable"
|
||||
inline-prompt
|
||||
@change="slotChange(row)"
|
||||
active-text="启用"
|
||||
inactive-text="暂停"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove, change, run, sync } from '@/api/job/jobinfo';
|
||||
import option from '@/option/job/jobinfo';
|
||||
import { mapGetters } from 'vuex';
|
||||
import 'nprogress/nprogress.css';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
search: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: option,
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.jobinfo_add, false),
|
||||
viewBtn: this.validData(this.permission.jobinfo_view, false),
|
||||
delBtn: this.validData(this.permission.jobinfo_delete, false),
|
||||
editBtn: this.validData(this.permission.jobinfo_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
if (func.isArrayAndNotEmpty(row.lifecycle)) {
|
||||
const lifecycleStart = row.lifecycle[0];
|
||||
const lifecycleEnd = row.lifecycle[1];
|
||||
if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
|
||||
row.lifecycle = lifecycleStart + ',' + lifecycleEnd;
|
||||
}
|
||||
} else {
|
||||
row.lifecycle = '';
|
||||
}
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
if (func.isArrayAndNotEmpty(row.lifecycle)) {
|
||||
const lifecycleStart = row.lifecycle[0];
|
||||
const lifecycleEnd = row.lifecycle[1];
|
||||
if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
|
||||
row.lifecycle = lifecycleStart + ',' + lifecycleEnd;
|
||||
}
|
||||
} else {
|
||||
row.lifecycle = '';
|
||||
}
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleSync() {
|
||||
this.$confirm('确定进行数据双向同步?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return sync();
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRun(row) {
|
||||
this.$confirm('运行后将创建一个实例执行,是否继续?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return run(row);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '运行成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
slotChange(row) {
|
||||
if (!row.id) {
|
||||
return;
|
||||
}
|
||||
change(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page) {
|
||||
this.loading = true;
|
||||
|
||||
const { jobServerId, jobName } = this.query;
|
||||
|
||||
let values = {
|
||||
jobServerId_like: jobServerId,
|
||||
jobName_like: jobName,
|
||||
};
|
||||
|
||||
getList(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
236
src/views/job/jobserver.vue
Normal file
236
src/views/job/jobserver.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
v-model:search="search"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.jobserver_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button type="info" plain icon="el-icon-sort" @click="handleSync"
|
||||
>数 据 同 步
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button type="primary" text icon="el-icon-video-play" @click="handleLink(scope.row)"
|
||||
>服务跳转
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove, sync } from '@/api/job/jobserver';
|
||||
import option from '@/option/job/jobserver';
|
||||
import { mapGetters } from 'vuex';
|
||||
import 'nprogress/nprogress.css';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
search: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: option,
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.jobserver_add, false),
|
||||
viewBtn: this.validData(this.permission.jobserver_view, false),
|
||||
delBtn: this.validData(this.permission.jobserver_delete, false),
|
||||
editBtn: this.validData(this.permission.jobserver_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleLink(row) {
|
||||
window.open(func.formatUrl(row.jobServerUrl));
|
||||
},
|
||||
handleSync() {
|
||||
this.$confirm('确定进行数据双向同步?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return sync();
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page) {
|
||||
this.loading = true;
|
||||
|
||||
const { jobServerName, jobAppName } = this.query;
|
||||
|
||||
let values = {
|
||||
jobServerName_like: jobServerName,
|
||||
jobAppName_like: jobAppName,
|
||||
};
|
||||
|
||||
getList(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
162
src/views/monitor/log/api.vue
Normal file
162
src/views/monitor/log/api.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getApiList, getApiLogs } from '@/api/logs';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 120,
|
||||
dialogType: 'drawer',
|
||||
column: [
|
||||
{
|
||||
label: '服务id',
|
||||
prop: 'serviceId',
|
||||
width: '120',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '服务host',
|
||||
prop: 'serverHost',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '服务ip',
|
||||
prop: 'serverIp',
|
||||
width: '160',
|
||||
},
|
||||
{
|
||||
label: '软件环境',
|
||||
prop: 'env',
|
||||
width: '85',
|
||||
},
|
||||
{
|
||||
label: '日志名',
|
||||
prop: 'title',
|
||||
},
|
||||
{
|
||||
label: '请求方法',
|
||||
prop: 'method',
|
||||
width: '85',
|
||||
},
|
||||
{
|
||||
label: '请求接口',
|
||||
prop: 'requestUri',
|
||||
},
|
||||
{
|
||||
label: '日志时间',
|
||||
prop: 'createTime',
|
||||
width: '180',
|
||||
},
|
||||
{
|
||||
label: '用户代理',
|
||||
prop: 'userAgent',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '请求数据',
|
||||
prop: 'params',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
viewBtn: this.validData(this.permission.log_api_view, false),
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getApiLogs(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getApiList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
166
src/views/monitor/log/error.vue
Normal file
166
src/views/monitor/log/error.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getErrorList, getErrorLogs } from '@/api/logs';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 120,
|
||||
dialogType: 'drawer',
|
||||
column: [
|
||||
{
|
||||
label: '服务id',
|
||||
prop: 'serviceId',
|
||||
search: true,
|
||||
width: '120',
|
||||
},
|
||||
{
|
||||
label: '服务host',
|
||||
prop: 'serverHost',
|
||||
search: true,
|
||||
width: '150',
|
||||
},
|
||||
{
|
||||
label: '服务ip',
|
||||
prop: 'serverIp',
|
||||
width: '160',
|
||||
},
|
||||
{
|
||||
label: '软件环境',
|
||||
prop: 'env',
|
||||
width: '85',
|
||||
},
|
||||
{
|
||||
label: '请求方法',
|
||||
prop: 'method',
|
||||
width: '85',
|
||||
},
|
||||
{
|
||||
label: '请求接口',
|
||||
prop: 'requestUri',
|
||||
},
|
||||
{
|
||||
label: '日志时间',
|
||||
prop: 'createTime',
|
||||
width: '180',
|
||||
},
|
||||
{
|
||||
label: '用户代理',
|
||||
prop: 'userAgent',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '请求数据',
|
||||
prop: 'params',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '日志数据',
|
||||
prop: 'stackTrace',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 16,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
viewBtn: this.validData(this.permission.log_error_view, false),
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getErrorLogs(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getErrorList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
166
src/views/monitor/log/usual.vue
Normal file
166
src/views/monitor/log/usual.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
v-model:page="page"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUsualList, getUsualLogs } from '@/api/logs';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 120,
|
||||
dialogType: 'drawer',
|
||||
column: [
|
||||
{
|
||||
label: '服务id',
|
||||
prop: 'serviceId',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '服务host',
|
||||
prop: 'serverHost',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '服务ip',
|
||||
prop: 'serverIp',
|
||||
},
|
||||
{
|
||||
label: '软件环境',
|
||||
prop: 'env',
|
||||
width: '100',
|
||||
},
|
||||
{
|
||||
label: '日志级别',
|
||||
prop: 'logLevel',
|
||||
},
|
||||
{
|
||||
label: '日志id',
|
||||
prop: 'logId',
|
||||
},
|
||||
{
|
||||
label: '请求接口',
|
||||
prop: 'requestUri',
|
||||
},
|
||||
{
|
||||
label: '日志时间',
|
||||
prop: 'createTime',
|
||||
width: '180',
|
||||
},
|
||||
{
|
||||
label: '用户代理',
|
||||
prop: 'userAgent',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '日志数据',
|
||||
prop: 'logData',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '请求数据',
|
||||
prop: 'params',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
viewBtn: this.validData(this.permission.log_usual_view, false),
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getUsualLogs(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getUsualList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
210
src/views/report/reportlist.vue
Normal file
210
src/views/report/reportlist.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-edit"
|
||||
@click.stop="handleDesign(scope.row.name)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>设计
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
@click.stop="handlePreview(scope.row.name)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>预览
|
||||
</el-button>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<el-tag style="cursor: pointer" @click="handlePreview(row.name)">{{ row.name }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, remove } from '@/api/report/report';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '文件名',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
slot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: true,
|
||||
editBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handlePreview(name) {
|
||||
window.open(
|
||||
`${this.website.design.reportUrl}/preview?_u=blade-${name}&${
|
||||
this.website.tokenHeader
|
||||
}=bearer ${getToken()}`
|
||||
);
|
||||
},
|
||||
handleDesign(name) {
|
||||
window.open(
|
||||
`${this.website.design.reportUrl}/designer?_u=blade-${name}&${
|
||||
this.website.tokenHeader
|
||||
}=bearer ${getToken()}`
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
343
src/views/resource/attach.vue
Normal file
343
src/views/resource/attach.vue
Normal file
@@ -0,0 +1,343 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
v-if="permission.attach_upload"
|
||||
icon="el-icon-upload"
|
||||
@click="handleUpload"
|
||||
>上 传
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.attach_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-download"
|
||||
v-if="permission.attach_download"
|
||||
@click="handleDownload(scope.row)"
|
||||
>下载
|
||||
</el-button>
|
||||
</template>
|
||||
<template #attachSize="{ row }">
|
||||
<el-tag>{{ formatFileSize(row.attachSize) }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="附件管理" append-to-body v-model="attachBox" width="555px">
|
||||
<el-upload
|
||||
drag
|
||||
action="/api/blade-resource/oss/endpoint/put-file-attach"
|
||||
:show-file-list="false"
|
||||
:headers="uploadHeaders"
|
||||
:on-progress="handleUploadProgress"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">拖动文件上传 <em>或者点击上传</em></div>
|
||||
</el-upload>
|
||||
<el-progress
|
||||
v-if="uploading"
|
||||
:percentage="uploadPercent"
|
||||
:indeterminate="uploadPercent === 100"
|
||||
:format="progressFormat"
|
||||
style="margin-top: 16px"
|
||||
/>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, remove } from '@/api/resource/attach';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { UploadFilled } from '@element-plus/icons-vue';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UploadFilled,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
attachBox: false,
|
||||
uploading: false,
|
||||
uploadPercent: 0,
|
||||
selectionList: [],
|
||||
uploadHeaders: {
|
||||
'Blade-Auth': `bearer ${getToken()}`,
|
||||
'Blade-Requested-With': 'BladeHttpRequest',
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '附件地址',
|
||||
prop: 'link',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入附件地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附件域名',
|
||||
prop: 'domainUrl',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入附件域名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附件名称',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入附件名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附件原名',
|
||||
prop: 'originalName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入附件原名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附件拓展名',
|
||||
prop: 'extension',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入附件拓展名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附件大小',
|
||||
prop: 'attachSize',
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入附件大小',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
func() {
|
||||
return func;
|
||||
},
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: false,
|
||||
editBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: this.validData(this.permission.attach_delete, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatFileSize(bytes) {
|
||||
if (!bytes || bytes === 0) return '0 KB';
|
||||
const mb = bytes / (1024 * 1024);
|
||||
if (mb >= 1) {
|
||||
return `${mb.toFixed(2)} MB`;
|
||||
}
|
||||
const kb = bytes / 1024;
|
||||
return `${kb.toFixed(2)} KB`;
|
||||
},
|
||||
progressFormat(percentage) {
|
||||
return percentage === 100 ? '服务器处理中' : `${percentage}%`;
|
||||
},
|
||||
handleUpload() {
|
||||
this.attachBox = true;
|
||||
this.uploading = false;
|
||||
this.uploadPercent = 0;
|
||||
},
|
||||
handleUploadProgress(event) {
|
||||
this.uploading = true;
|
||||
this.uploadPercent = Math.round(event.percent) || 0;
|
||||
},
|
||||
handleUploadSuccess(response) {
|
||||
this.uploading = false;
|
||||
this.uploadPercent = 0;
|
||||
if (response.success) {
|
||||
this.$message.success('上传成功');
|
||||
this.attachBox = false;
|
||||
this.refreshChange();
|
||||
} else {
|
||||
this.$message.error(response.msg || '上传失败');
|
||||
}
|
||||
},
|
||||
handleUploadError() {
|
||||
this.uploading = false;
|
||||
this.uploadPercent = 0;
|
||||
this.$message.error('上传失败');
|
||||
},
|
||||
handleDownload(row) {
|
||||
window.open(`${row.link}`);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
469
src/views/resource/oss.vue
Normal file
469
src/views/resource/oss.vue
Normal file
@@ -0,0 +1,469 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.oss_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-video-play"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
@click="handleDebug(scope.row)"
|
||||
>调试
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-check"
|
||||
v-if="permission.oss_enable"
|
||||
@click.stop="handleEnable(scope.row)"
|
||||
>启用
|
||||
</el-button>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag>{{ row.statusName }}</el-tag>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="对象存储上传调试" append-to-body v-model="box" width="550px">
|
||||
<avue-form ref="form" :option="debugOption" v-model="debugForm" @submit="handleSubmit" />
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove, enable } from '@/api/resource/oss';
|
||||
import { mapGetters } from 'vuex';
|
||||
import func from '@/utils/func';
|
||||
import { sensitive } from '@/utils/sensitive';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
sensitiveManager: null,
|
||||
loading: true,
|
||||
box: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
menuWidth: 350,
|
||||
labelWidth: 100,
|
||||
dialogWidth: 880,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '分类',
|
||||
type: 'radio',
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 120,
|
||||
searchLabelWidth: 50,
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=oss',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择分类',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '资源编号',
|
||||
prop: 'ossCode',
|
||||
span: 24,
|
||||
width: 120,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入资源编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '资源地址',
|
||||
labelTip: '对象存储通用资源地址,可以是内网也可以是外网',
|
||||
prop: 'endpoint',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入资源地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '外网地址',
|
||||
labelTip: '资源地址设置为内网上传,则外部访问需要配置外网映射地址',
|
||||
prop: 'transformEndpoint',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '空间名',
|
||||
prop: 'bucketName',
|
||||
span: 24,
|
||||
width: 120,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入空间名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'accessKey',
|
||||
prop: 'accessKey',
|
||||
span: 24,
|
||||
search: true,
|
||||
width: 200,
|
||||
overHidden: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入accessKey',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'secretKey',
|
||||
prop: 'secretKey',
|
||||
span: 24,
|
||||
width: 200,
|
||||
hide: true,
|
||||
overHidden: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入secretKey',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'appId',
|
||||
prop: 'appId',
|
||||
span: 24,
|
||||
hide: true,
|
||||
display: false,
|
||||
},
|
||||
{
|
||||
label: 'region',
|
||||
prop: 'region',
|
||||
span: 24,
|
||||
hide: true,
|
||||
display: false,
|
||||
},
|
||||
{
|
||||
label: '是否启用',
|
||||
prop: 'status',
|
||||
span: 24,
|
||||
width: 85,
|
||||
align: 'center',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
debugForm: {
|
||||
code: '',
|
||||
},
|
||||
debugOption: {
|
||||
submitText: '提交',
|
||||
column: [
|
||||
{
|
||||
label: '资源编号',
|
||||
prop: 'code',
|
||||
disabled: true,
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '上传背景',
|
||||
prop: 'backgroundUrl',
|
||||
type: 'upload',
|
||||
listType: 'picture-img',
|
||||
dataType: 'string',
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
},
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.category'() {
|
||||
const category = func.toInt(this.form.category);
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'endpoint') {
|
||||
item.labelTip = category === 7 ? '本地服务器上传路径使用Nginx代理后的服务地址' : '';
|
||||
}
|
||||
if (item.prop === 'accessKey') {
|
||||
item.labelTip =
|
||||
category === 7
|
||||
? '本地服务器文件上传基础路径,注意必须提前创建好并分配目录读写权限'
|
||||
: '';
|
||||
}
|
||||
if (item.prop === 'secretKey') {
|
||||
item.display = category !== 7;
|
||||
}
|
||||
if (item.prop === 'appId') {
|
||||
item.display = category === 4;
|
||||
}
|
||||
if (item.prop === 'region') {
|
||||
item.display = category === 4 || category === 5;
|
||||
}
|
||||
});
|
||||
},
|
||||
'debugForm.code'() {
|
||||
const column = this.findColumn(this.debugOption.column, 'backgroundUrl');
|
||||
column.action = `/blade-resource/oss/endpoint/put-file?code=${this.debugForm.code}`;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.oss_add),
|
||||
viewBtn: this.validData(this.permission.oss_view),
|
||||
delBtn: this.validData(this.permission.oss_delete),
|
||||
editBtn: this.validData(this.permission.oss_edit),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// 创建脱敏工具实例
|
||||
this.sensitiveManager = sensitive.create({
|
||||
fields: ['accessKey', 'secretKey'], // 配置需要脱敏的字段
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
// 获取需要提交的数据
|
||||
const submitData = this.sensitiveManager.getSubmitData(row);
|
||||
update(submitData).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleEnable(row) {
|
||||
this.$confirm('是否确定启用这条配置?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return enable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleDebug(row) {
|
||||
this.box = true;
|
||||
this.debugForm.code = row.ossCode;
|
||||
this.debugForm.backgroundUrl = '';
|
||||
},
|
||||
handleSubmit(form, done) {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: `获取到图片地址:[${form.backgroundUrl}]`,
|
||||
});
|
||||
done();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
// 保存初始脱敏数据
|
||||
this.sensitiveManager.saveInitialData(this.form);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
501
src/views/resource/sms.vue
Normal file
501
src/views/resource/sms.vue
Normal file
@@ -0,0 +1,501 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.sms_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-video-play"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
@click="handleDebug(scope.row)"
|
||||
>调试
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-check"
|
||||
v-if="permission.sms_enable"
|
||||
@click.stop="handleEnable(scope.row)"
|
||||
>启用
|
||||
</el-button>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag>{{ row.statusName }}</el-tag>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="手机短信发送调试" append-to-body v-model="box" width="550px">
|
||||
<avue-form :option="debugOption" v-model="debugForm" @submit="handleSend" />
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove, enable, send } from '@/api/resource/sms';
|
||||
import { mapGetters } from 'vuex';
|
||||
import func from '@/utils/func';
|
||||
import { sensitive } from '@/utils/sensitive';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
sensitiveManager: null,
|
||||
loading: true,
|
||||
box: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
menuWidth: 350,
|
||||
labelWidth: 100,
|
||||
dialogWidth: 880,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '分类',
|
||||
type: 'radio',
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 100,
|
||||
searchLabelWidth: 50,
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=sms',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择分类',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '资源编号',
|
||||
prop: 'smsCode',
|
||||
span: 24,
|
||||
width: 200,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入资源编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '模版ID',
|
||||
prop: 'templateId',
|
||||
span: 24,
|
||||
width: 200,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入模版ID',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'SDKAppID',
|
||||
prop: 'appId',
|
||||
span: 24,
|
||||
width: 200,
|
||||
hide: true,
|
||||
display: false,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入appId',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'accessKey',
|
||||
prop: 'accessKey',
|
||||
span: 24,
|
||||
overHidden: true,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入accessKey',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'secretKey',
|
||||
prop: 'secretKey',
|
||||
span: 24,
|
||||
overHidden: true,
|
||||
display: true,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入secretKey',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'regionId',
|
||||
prop: 'regionId',
|
||||
span: 24,
|
||||
hide: true,
|
||||
display: false,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入regionId',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '短信签名',
|
||||
prop: 'signName',
|
||||
span: 24,
|
||||
width: 200,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入短信签名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '是否启用',
|
||||
prop: 'status',
|
||||
span: 24,
|
||||
width: 85,
|
||||
align: 'center',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
debugForm: {
|
||||
code: '',
|
||||
},
|
||||
debugOption: {
|
||||
submitText: '发送',
|
||||
column: [
|
||||
{
|
||||
label: '资源编号',
|
||||
prop: 'code',
|
||||
disabled: true,
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '发送手机',
|
||||
prop: 'phones',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '发送参数',
|
||||
prop: 'params',
|
||||
span: 24,
|
||||
placeholder: "例: {'code':2333,'title':'通知标题'}",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.category'() {
|
||||
const category = func.toInt(this.form.category);
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'templateId') {
|
||||
if (category === 1) {
|
||||
item.label = '模版内容';
|
||||
} else {
|
||||
item.label = '模版ID';
|
||||
}
|
||||
}
|
||||
if (item.prop === 'accessKey') {
|
||||
if (category === 1) {
|
||||
item.label = 'apiKey';
|
||||
} else if (category === 4) {
|
||||
item.label = 'secretId';
|
||||
} else {
|
||||
item.label = 'accessKey';
|
||||
}
|
||||
}
|
||||
if (item.prop === 'secretKey') {
|
||||
item.display = category !== 1;
|
||||
if (category === 4) {
|
||||
item.label = 'secretKey';
|
||||
} else {
|
||||
item.label = 'secretKey';
|
||||
}
|
||||
}
|
||||
if (item.prop === 'regionId') {
|
||||
if (category === 3) {
|
||||
item.display = true;
|
||||
this.form.regionId = 'cn-hangzhou';
|
||||
} else if (category === 4) {
|
||||
item.display = true;
|
||||
this.form.regionId = 'ap-guangzhou';
|
||||
} else {
|
||||
item.display = false;
|
||||
}
|
||||
}
|
||||
if (item.prop === 'appId') {
|
||||
item.display = category === 4;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.sms_add, false),
|
||||
viewBtn: this.validData(this.permission.sms_view, false),
|
||||
delBtn: this.validData(this.permission.sms_delete, false),
|
||||
editBtn: this.validData(this.permission.sms_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// 创建脱敏工具实例
|
||||
this.sensitiveManager = sensitive.create({
|
||||
fields: ['accessKey', 'secretKey'], // 配置需要脱敏的字段
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
// 获取需要提交的数据
|
||||
const submitData = this.sensitiveManager.getSubmitData(row);
|
||||
update(submitData).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleEnable(row) {
|
||||
this.$confirm('是否确定启用这条配置?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return enable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleDebug(row) {
|
||||
this.box = true;
|
||||
this.debugForm.code = row.smsCode;
|
||||
},
|
||||
handleSend(form, done) {
|
||||
send(form.code, form.phones, form.params).then(
|
||||
res => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '发送成功!',
|
||||
});
|
||||
done();
|
||||
window.console.log(res);
|
||||
this.box = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
// 保存初始脱敏数据
|
||||
this.sensitiveManager.saveInitialData(this.form);
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
303
src/views/system/authlock.vue
Normal file
303
src/views/system/authlock.vue
Normal file
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<!-- 账号锁定配置抽屉 -->
|
||||
<el-drawer title="账号锁定配置" append-to-body v-model="authLockBox" size="60%">
|
||||
<avue-crud
|
||||
:option="authLockOption"
|
||||
:table-loading="authLockLoading"
|
||||
:data="authLockData"
|
||||
ref="authLockCrud"
|
||||
v-model="authLockForm"
|
||||
v-model:page="authLockPage"
|
||||
v-model:search="authLockSearch"
|
||||
:before-open="authLockBeforeOpen"
|
||||
@search-change="authLockSearchChange"
|
||||
@search-reset="authLockSearchReset"
|
||||
@current-change="authLockCurrentChange"
|
||||
@size-change="authLockSizeChange"
|
||||
@refresh-change="authLockRefreshChange"
|
||||
@on-load="authLockOnLoad"
|
||||
>
|
||||
<template #lockTarget="{ row }">
|
||||
<el-tag type="primary" size="small" effect="light">{{ row.lockTarget }}</el-tag>
|
||||
</template>
|
||||
<template #userName="{ row }">
|
||||
{{ row.userName || '-' }}
|
||||
</template>
|
||||
<template #remoteIp="{ row }">
|
||||
<el-tag type="primary" size="small" effect="light">{{ row.remoteIp || '暂无' }}</el-tag>
|
||||
</template>
|
||||
<template #lockType="{ row }">
|
||||
<el-tag :type="row.lockType === 'SYSTEM' ? 'info' : 'danger'" size="small">
|
||||
{{ row.lockTypeName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #lockStatus="{ row }">
|
||||
<el-tag
|
||||
:type="row.expired ? 'info' : row.lockStatus === 'LOCKED' ? 'danger' : row.lockStatus === 'PRE_LOCKED' ? 'warning' : 'success'"
|
||||
size="small"
|
||||
>
|
||||
{{ row.lockStatusName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #lockBeginTime="{ row }">
|
||||
{{ row.lockBeginTime || '立即生效' }}
|
||||
</template>
|
||||
<template #lockEndTime="{ row }">
|
||||
{{ row.lockEndTime || '永久' }}
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
icon="el-icon-view"
|
||||
@click.stop="$refs.authLockCrud.rowView(row)"
|
||||
>查看</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="(row.lockStatus === 'LOCKED' || row.lockStatus === 'PRE_LOCKED') && !row.expired"
|
||||
text
|
||||
type="primary"
|
||||
icon="el-icon-key"
|
||||
@click.stop="handleAuthLockUnlock(row)"
|
||||
>解锁</el-button
|
||||
>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 手动锁定用户 -->
|
||||
<el-dialog title="锁定用户" append-to-body v-model="lockUserBox" width="500px">
|
||||
<el-form :model="lockUserForm" label-width="100px" :rules="lockUserRules" ref="lockUserFormRef">
|
||||
<el-form-item label="用户账号">
|
||||
<el-input :model-value="lockUserForm.account" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="锁定原因" prop="lockReason">
|
||||
<el-input
|
||||
v-model="lockUserForm.lockReason"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入锁定原因"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="锁定开始" prop="lockBeginTime">
|
||||
<el-date-picker
|
||||
v-model="lockUserForm.lockBeginTime"
|
||||
type="datetime"
|
||||
placeholder="不填则立即生效"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="锁定结束" prop="lockEndTime">
|
||||
<el-date-picker
|
||||
v-model="lockUserForm.lockEndTime"
|
||||
type="datetime"
|
||||
placeholder="不填则永久锁定"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="lockUserBox = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="lockSubmitLoading" @click="submitLockUser">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 解锁原因确认 -->
|
||||
<el-dialog title="解锁确认" append-to-body v-model="unlockReasonBox" width="450px">
|
||||
<el-form :model="unlockReasonForm" label-width="80px">
|
||||
<el-form-item label="解锁原因">
|
||||
<el-input
|
||||
v-model="unlockReasonForm.unlockReason"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入解锁原因(可选)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="unlockReasonBox = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="unlockSubmitLoading" @click="submitUnlockReason">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getAuthLockPage,
|
||||
getAuthLockDetail,
|
||||
authLockUnlock,
|
||||
lockUser,
|
||||
} from '@/api/system/authlock';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
import { authLockOption } from '@/option/system/authlock';
|
||||
|
||||
export default {
|
||||
name: 'AuthLock',
|
||||
emits: ['lock-success'],
|
||||
data() {
|
||||
return {
|
||||
authLockOption,
|
||||
// 锁定日志抽屉
|
||||
authLockBox: false,
|
||||
authLockLoading: false,
|
||||
authLockData: [],
|
||||
authLockForm: {},
|
||||
authLockSearch: {},
|
||||
authLockPage: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
// 手动锁定用户
|
||||
lockUserBox: false,
|
||||
lockUserForm: {},
|
||||
lockUserRules: {
|
||||
lockReason: [{ required: true, message: '请输入锁定原因', trigger: 'blur' }],
|
||||
lockEndTime: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback();
|
||||
} else if (this.lockUserForm.lockBeginTime && value < this.lockUserForm.lockBeginTime) {
|
||||
callback(new Error('结束时间不能早于开始时间'));
|
||||
} else if (!this.lockUserForm.lockBeginTime && value < new Date().toISOString().slice(0, 19).replace('T', ' ')) {
|
||||
callback(new Error('结束时间不能早于当前时间'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
},
|
||||
// 解锁确认
|
||||
unlockReasonBox: false,
|
||||
unlockReasonForm: {},
|
||||
currentUnlockId: null,
|
||||
lockSubmitLoading: false,
|
||||
unlockSubmitLoading: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// ========== 对外暴露的入口方法 ==========
|
||||
|
||||
openLockLog() {
|
||||
this.authLockSearch = {};
|
||||
this.authLockPage.currentPage = 1;
|
||||
this.authLockBox = true;
|
||||
this.$nextTick(() => {
|
||||
this.authLockOnLoad(this.authLockPage);
|
||||
});
|
||||
},
|
||||
openLockUser(userId, account) {
|
||||
this.lockUserForm = {
|
||||
userId,
|
||||
account,
|
||||
lockReason: '',
|
||||
lockBeginTime: '',
|
||||
lockEndTime: '',
|
||||
};
|
||||
this.lockUserBox = true;
|
||||
},
|
||||
|
||||
// ========== 锁定日志列表 ==========
|
||||
|
||||
authLockBeforeOpen(done, type) {
|
||||
if (type === 'view') {
|
||||
getAuthLockDetail(this.authLockForm.id).then(res => {
|
||||
this.authLockForm = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
authLockSearchChange(params, done) {
|
||||
this.authLockSearch = params;
|
||||
this.authLockPage.currentPage = 1;
|
||||
this.authLockOnLoad(this.authLockPage, params);
|
||||
done();
|
||||
},
|
||||
authLockSearchReset() {
|
||||
this.authLockSearch = {};
|
||||
this.authLockPage.currentPage = 1;
|
||||
this.authLockOnLoad(this.authLockPage);
|
||||
},
|
||||
authLockSizeChange(pageSize) {
|
||||
this.authLockPage.pageSize = pageSize;
|
||||
},
|
||||
authLockCurrentChange(currentPage) {
|
||||
this.authLockPage.currentPage = currentPage;
|
||||
},
|
||||
authLockRefreshChange() {
|
||||
this.authLockOnLoad(this.authLockPage, this.authLockSearch);
|
||||
},
|
||||
authLockOnLoad(page, params = {}) {
|
||||
this.authLockLoading = true;
|
||||
const merged = Object.assign({}, params, this.authLockSearch);
|
||||
const query = Object.fromEntries(Object.entries(merged).filter(([, val]) => !validatenull(val)));
|
||||
getAuthLockPage(page.currentPage, page.pageSize, query)
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.authLockPage.total = data.total;
|
||||
this.authLockData = data.records;
|
||||
this.authLockLoading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.authLockLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 解锁操作 ==========
|
||||
|
||||
handleAuthLockUnlock(row) {
|
||||
this.currentUnlockId = row.id;
|
||||
this.unlockReasonForm = { unlockReason: '' };
|
||||
this.unlockReasonBox = true;
|
||||
},
|
||||
submitUnlockReason() {
|
||||
this.unlockSubmitLoading = true;
|
||||
authLockUnlock(this.currentUnlockId, this.unlockReasonForm.unlockReason)
|
||||
.then(() => {
|
||||
this.unlockSubmitLoading = false;
|
||||
this.unlockReasonBox = false;
|
||||
this.$message({ type: 'success', message: '解锁成功!' });
|
||||
this.authLockOnLoad(this.authLockPage, this.authLockSearch);
|
||||
})
|
||||
.catch(() => {
|
||||
this.unlockSubmitLoading = false;
|
||||
this.$message({ type: 'error', message: '解锁失败,请重试!' });
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 手动锁定用户 ==========
|
||||
|
||||
submitLockUser() {
|
||||
this.$refs.lockUserFormRef.validate(valid => {
|
||||
if (!valid) return;
|
||||
this.lockSubmitLoading = true;
|
||||
lockUser(
|
||||
this.lockUserForm.userId,
|
||||
this.lockUserForm.lockReason,
|
||||
this.lockUserForm.lockBeginTime,
|
||||
this.lockUserForm.lockEndTime
|
||||
)
|
||||
.then(() => {
|
||||
this.lockSubmitLoading = false;
|
||||
this.lockUserBox = false;
|
||||
this.$message({ type: 'success', message: '锁定成功!' });
|
||||
this.$emit('lock-success');
|
||||
})
|
||||
.catch(() => {
|
||||
this.lockSubmitLoading = false;
|
||||
this.$message({ type: 'error', message: '锁定失败,请重试!' });
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
155
src/views/system/authlog.vue
Normal file
155
src/views/system/authlog.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<!-- 认证日志抽屉 -->
|
||||
<el-drawer :title="authLogTitle" append-to-body v-model="authLogBox" size="60%">
|
||||
<avue-crud
|
||||
:option="authLogOption"
|
||||
:table-loading="authLogLoading"
|
||||
:data="authLogData"
|
||||
ref="authLogCrud"
|
||||
v-model="authLogForm"
|
||||
v-model:page="authLogPage"
|
||||
v-model:search="authLogSearch"
|
||||
:before-open="authLogBeforeOpen"
|
||||
@search-change="authLogSearchChange"
|
||||
@search-reset="authLogSearchReset"
|
||||
@current-change="authLogCurrentChange"
|
||||
@size-change="authLogSizeChange"
|
||||
@refresh-change="authLogRefreshChange"
|
||||
@on-load="authLogOnLoad"
|
||||
>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
icon="el-icon-view"
|
||||
@click.stop="$refs.authLogCrud.rowView(row)"
|
||||
>查看</el-button
|
||||
>
|
||||
</template>
|
||||
<template #account="{ row }">
|
||||
<el-tag size="small" effect="light">{{ row.account }}</el-tag>
|
||||
</template>
|
||||
<template #grantType="{ row }">
|
||||
<el-tag size="small" effect="light">{{ getGrantTypeName(row.grantType) }}</el-tag>
|
||||
</template>
|
||||
<template #remoteIp="{ row }">
|
||||
<el-tag size="small" effect="light">{{ row.remoteIp || '-' }}</el-tag>
|
||||
</template>
|
||||
<template #env="{ row }">
|
||||
<el-tag
|
||||
:type="row.env === 'prod' ? 'danger' : row.env === 'test' ? 'warning' : row.env === 'dev' ? 'success' : 'info'"
|
||||
size="small"
|
||||
>{{ row.env || '-' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAuthLogPage, getAuthLogDetail } from '@/api/system/authlog';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
import { authLogOption, GRANT_TYPE_DIC } from '@/option/system/authlog';
|
||||
|
||||
export default {
|
||||
name: 'AuthLog',
|
||||
data() {
|
||||
return {
|
||||
authLogOption,
|
||||
authLogTitle: '认证日志',
|
||||
// 抽屉状态
|
||||
authLogBox: false,
|
||||
authLogLoading: false,
|
||||
authLogData: [],
|
||||
authLogForm: {},
|
||||
authLogSearch: {},
|
||||
authLogPage: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
// 用户ID过滤
|
||||
filterUserId: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// ========== 对外暴露的入口方法 ==========
|
||||
|
||||
openAuthLog() {
|
||||
this.filterUserId = null;
|
||||
this.authLogTitle = '认证日志';
|
||||
this.authLogSearch = {};
|
||||
this.authLogPage.currentPage = 1;
|
||||
this.authLogBox = true;
|
||||
this.$nextTick(() => {
|
||||
this.authLogOnLoad(this.authLogPage);
|
||||
});
|
||||
},
|
||||
openUserAuthLog(userId, userName) {
|
||||
this.filterUserId = userId;
|
||||
this.authLogTitle = userName ? `认证日志 - ${userName}` : '认证日志';
|
||||
this.authLogSearch = {};
|
||||
this.authLogPage.currentPage = 1;
|
||||
this.authLogBox = true;
|
||||
this.$nextTick(() => {
|
||||
this.authLogOnLoad(this.authLogPage);
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 授权类型转译 ==========
|
||||
|
||||
getGrantTypeName(grantType) {
|
||||
const item = GRANT_TYPE_DIC.find(d => d.value === grantType);
|
||||
return item ? item.label : grantType || '-';
|
||||
},
|
||||
|
||||
// ========== 认证日志列表 ==========
|
||||
|
||||
authLogBeforeOpen(done, type) {
|
||||
if (type === 'view') {
|
||||
getAuthLogDetail(this.authLogForm.id).then(res => {
|
||||
this.authLogForm = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
authLogSearchChange(params, done) {
|
||||
this.authLogSearch = params;
|
||||
this.authLogPage.currentPage = 1;
|
||||
this.authLogOnLoad(this.authLogPage, params);
|
||||
done();
|
||||
},
|
||||
authLogSearchReset() {
|
||||
this.authLogSearch = {};
|
||||
this.authLogPage.currentPage = 1;
|
||||
this.authLogOnLoad(this.authLogPage);
|
||||
},
|
||||
authLogSizeChange(pageSize) {
|
||||
this.authLogPage.pageSize = pageSize;
|
||||
},
|
||||
authLogCurrentChange(currentPage) {
|
||||
this.authLogPage.currentPage = currentPage;
|
||||
},
|
||||
authLogRefreshChange() {
|
||||
this.authLogOnLoad(this.authLogPage, this.authLogSearch);
|
||||
},
|
||||
authLogOnLoad(page, params = {}) {
|
||||
this.authLogLoading = true;
|
||||
const merged = Object.assign({}, params, this.authLogSearch);
|
||||
if (this.filterUserId) {
|
||||
merged.userId = this.filterUserId;
|
||||
}
|
||||
const query = Object.fromEntries(Object.entries(merged).filter(([, val]) => !validatenull(val)));
|
||||
getAuthLogPage(page.currentPage, page.pageSize, query)
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.authLogPage.total = data.total;
|
||||
this.authLogData = data.records;
|
||||
this.authLogLoading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.authLogLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
385
src/views/system/client.vue
Normal file
385
src/views/system/client.vue
Normal file
@@ -0,0 +1,385 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
@row-del="rowDel"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
:permission="permissionList"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.client_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/system/client';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '应用id',
|
||||
prop: 'clientId',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入客户端id',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '应用密钥',
|
||||
prop: 'clientSecret',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入客户端密钥',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '授权类型',
|
||||
prop: 'authorizedGrantTypes',
|
||||
type: 'checkbox',
|
||||
value: 'refresh_token,password,authorization_code,captcha,social,sms_code,register',
|
||||
dicData: [
|
||||
{
|
||||
label: 'refresh_token',
|
||||
value: 'refresh_token',
|
||||
},
|
||||
{
|
||||
label: 'password',
|
||||
value: 'password',
|
||||
},
|
||||
{
|
||||
label: 'authorization_code',
|
||||
value: 'authorization_code',
|
||||
},
|
||||
{
|
||||
label: 'captcha',
|
||||
value: 'captcha',
|
||||
},
|
||||
{
|
||||
label: 'social',
|
||||
value: 'social',
|
||||
},
|
||||
{
|
||||
label: 'sms_code',
|
||||
value: 'sms_code',
|
||||
},
|
||||
{
|
||||
label: 'register',
|
||||
value: 'register',
|
||||
},
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入授权类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '授权范围',
|
||||
prop: 'scope',
|
||||
value: 'all',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入授权范围',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '令牌秒数',
|
||||
prop: 'accessTokenValidity',
|
||||
type: 'number',
|
||||
value: 3600,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入令牌过期秒数',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '刷新秒数',
|
||||
prop: 'refreshTokenValidity',
|
||||
type: 'number',
|
||||
value: 604800,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入刷新令牌过期秒数',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '回调地址',
|
||||
prop: 'webServerRedirectUri',
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入回调地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '自动授权',
|
||||
prop: 'autoapprove',
|
||||
hide: true,
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{
|
||||
label: '是',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: 'false',
|
||||
},
|
||||
],
|
||||
value: 'true',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入自动授权',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '资源集合',
|
||||
prop: 'resourceIds',
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
message: '请输入资源集合',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '权限',
|
||||
prop: 'authorities',
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
message: '请输入权限',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '附加说明',
|
||||
hide: true,
|
||||
prop: 'additionalInformation',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
message: '请输入附加说明',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.client_add),
|
||||
viewBtn: this.validData(this.permission.client_view),
|
||||
delBtn: this.validData(this.permission.client_delete),
|
||||
editBtn: this.validData(this.permission.client_edit),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
404
src/views/system/dept.vue
Normal file
404
src/views/system/dept.vue
Normal file
@@ -0,0 +1,404 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
:before-close="beforeClose"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
@tree-load="treeLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dept_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
<template #deptCategory="{ row }">
|
||||
<el-tag>{{ row.deptCategoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLazyList, remove, update, add, getDept, getDeptTree } from '@/api/system/dept';
|
||||
import { getLeaderList } from '@/api/system/user';
|
||||
import { mapGetters } from 'vuex';
|
||||
import website from '@/config/website';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
parentId: 0,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
lazy: true,
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
menuWidth: 320,
|
||||
dialogWidth: 800,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '机构名称',
|
||||
prop: 'deptName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入机构名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '所属租户',
|
||||
prop: 'tenantId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/tenant/select',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: website.tenantMode,
|
||||
span: 24,
|
||||
props: {
|
||||
label: 'tenantName',
|
||||
value: 'tenantId',
|
||||
},
|
||||
hide: !website.tenantMode,
|
||||
search: website.tenantMode,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入所属租户',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '机构全称',
|
||||
prop: 'fullName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入机构全称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '上级机构',
|
||||
prop: 'parentId',
|
||||
dicData: [],
|
||||
type: 'tree',
|
||||
hide: true,
|
||||
addDisabled: false,
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: '请选择上级机构',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '直属主管',
|
||||
prop: 'leaderId',
|
||||
type: 'tree',
|
||||
multiple: true,
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
props: {
|
||||
label: 'realName',
|
||||
value: 'id',
|
||||
},
|
||||
clearable: true,
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '机构类型',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=org_category',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
width: 120,
|
||||
prop: 'deptCategory',
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入机构类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
align: 'right',
|
||||
width: 80,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dept_add, false),
|
||||
viewBtn: this.validData(this.permission.dept_view, false),
|
||||
delBtn: this.validData(this.permission.dept_delete, false),
|
||||
editBtn: this.validData(this.permission.dept_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData(tenantId) {
|
||||
getDeptTree(tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
getLeaderList(tenantId).then(res => {
|
||||
const column = this.findColumn(this.option.column, 'leaderId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
handleAdd(row) {
|
||||
this.parentId = row.id;
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.value = row.id;
|
||||
column.addDisabled = true;
|
||||
this.$refs.crud.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
row.leaderId = func.join(row.leaderId);
|
||||
add(row).then(
|
||||
res => {
|
||||
// 获取新增数据的相关字段
|
||||
const data = res.data.data;
|
||||
row.id = data.id;
|
||||
row.deptCategoryName = data.deptCategoryName;
|
||||
row.tenantId = data.tenantId;
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
// 数据回调进行刷新
|
||||
done(row);
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
row.leaderId = func.join(row.leaderId);
|
||||
update(row).then(
|
||||
() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
// 数据回调进行刷新
|
||||
done(row);
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row, index, done) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
// 数据回调进行刷新
|
||||
done(row);
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
// 刷新表格数据并重载
|
||||
this.data = [];
|
||||
this.parentId = 0;
|
||||
this.$refs.crud.refreshTable();
|
||||
this.$refs.crud.toggleSelection();
|
||||
// 表格数据重载
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.parentId = 0;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.parentId = '';
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData(this.form.tenantId);
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDept(this.form.id).then(res => {
|
||||
this.form = Object.assign(res.data.data, {
|
||||
hasChildren: this.form.hasChildren,
|
||||
});
|
||||
if (this.form.parentId === '0') {
|
||||
this.form.parentId = '';
|
||||
}
|
||||
if (this.form.hasOwnProperty('leaderId')) {
|
||||
this.form.leaderId = func.split(this.form.leaderId);
|
||||
}
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeClose(done) {
|
||||
this.parentId = '';
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.value = '';
|
||||
column.addDisabled = false;
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getLazyList(this.parentId, Object.assign(params, this.query)).then(res => {
|
||||
this.data = res.data.data;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
treeLoad(tree, treeNode, resolve) {
|
||||
const parentId = tree.id;
|
||||
getLazyList(parentId).then(res => {
|
||||
resolve(res.data.data);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
450
src/views/system/dict.vue
Normal file
450
src/views/system/dict.vue
Normal file
@@ -0,0 +1,450 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="optionParent"
|
||||
:table-loading="loading"
|
||||
:data="dataParent"
|
||||
:page="pageParent"
|
||||
ref="crud"
|
||||
v-model="formParent"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-click="handleRowClick"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoadParent"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
@click.stop="handleRowClick(scope.row)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>字典配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #code="{ row }">
|
||||
<el-tag @click="handleRowClick(row)" style="cursor: pointer">{{ row.code }}</el-tag>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog :title="`[${dictValue}]字典配置`" append-to-body v-model="box" width="1000px">
|
||||
<avue-crud
|
||||
:option="optionChild"
|
||||
:table-loading="loadingChild"
|
||||
:data="dataChild"
|
||||
ref="crudChild"
|
||||
v-model="formChild"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpenChild"
|
||||
:before-close="beforeCloseChild"
|
||||
@row-del="rowDelChild"
|
||||
@row-update="rowUpdateChild"
|
||||
@row-save="rowSaveChild"
|
||||
@search-change="searchChangeChild"
|
||||
@search-reset="searchResetChild"
|
||||
@selection-change="selectionChangeChild"
|
||||
@current-change="currentChangeChild"
|
||||
@size-change="sizeChangeChild"
|
||||
@refresh-change="refreshChangeChild"
|
||||
@on-load="onLoadChild"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getParentList,
|
||||
getChildList,
|
||||
remove,
|
||||
update,
|
||||
add,
|
||||
getDict,
|
||||
getDictTree,
|
||||
} from '@/api/system/dict';
|
||||
import { optionParent, optionChild } from '@/option/system/dict';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dictValue: '暂无',
|
||||
parentId: -1,
|
||||
formParent: {},
|
||||
formChild: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
box: false,
|
||||
loading: true,
|
||||
loadingChild: true,
|
||||
pageParent: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
pageChild: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
dataParent: [],
|
||||
dataChild: [],
|
||||
optionParent: optionParent,
|
||||
optionChild: optionChild,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dict_add, false),
|
||||
delBtn: this.validData(this.permission.dict_delete, false),
|
||||
editBtn: this.validData(this.permission.dict_edit, false),
|
||||
viewBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initData();
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getDictTree().then(res => {
|
||||
const column = this.findColumn(this.optionChild.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
handleAdd(row) {
|
||||
this.formChild.dictValue = '';
|
||||
this.formChild.dictKey = '';
|
||||
this.formChild.sort = 0;
|
||||
this.formChild.isSealed = 0;
|
||||
this.formChild.remark = '';
|
||||
this.formChild.parentId = row.id;
|
||||
this.$refs.crudChild.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const form = {
|
||||
...row,
|
||||
dictKey: -1,
|
||||
};
|
||||
add(form).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.onLoadChild(this.pageChild);
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.query = {};
|
||||
this.parentId = row.id;
|
||||
this.dictValue = row.dictValue;
|
||||
|
||||
const code = this.findColumn(this.optionChild.column, 'code');
|
||||
code.value = row.code;
|
||||
const parentId = this.findColumn(this.optionChild.column, 'parentId');
|
||||
parentId.value = row.id;
|
||||
|
||||
this.formChild.code = row.code;
|
||||
this.formChild.parentId = row.id;
|
||||
|
||||
this.box = true;
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoadParent(this.pageParent);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.pageParent.currentPage = 1;
|
||||
this.onLoadParent(this.pageParent, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formParent.id).then(res => {
|
||||
this.formParent = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.pageParent.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.pageParent.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoadParent(this.pageParent, this.query);
|
||||
},
|
||||
rowSaveChild(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateChild(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelChild(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchResetChild() {
|
||||
this.query = {};
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchChangeChild(params, done) {
|
||||
this.query = params;
|
||||
this.pageChild.currentPage = 1;
|
||||
this.onLoadChild(this.pageChild, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeChild(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClearChild() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
},
|
||||
handleDeleteChild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenChild(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formChild.id).then(res => {
|
||||
this.formChild = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeCloseChild(done) {
|
||||
this.$refs.crudChild.modelValue.parentId = this.parentId;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = this.parentId;
|
||||
}
|
||||
});
|
||||
done();
|
||||
},
|
||||
currentChangeChild(currentPage) {
|
||||
this.pageChild.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeChild(pageSize) {
|
||||
this.pageChild.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeChild() {
|
||||
this.onLoadChild(this.pageChild, this.query);
|
||||
},
|
||||
onLoadParent(page, params = {}) {
|
||||
this.loading = true;
|
||||
getParentList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageParent.total = data.total;
|
||||
this.dataParent = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
}
|
||||
);
|
||||
},
|
||||
onLoadChild(page, params = {}) {
|
||||
this.loadingChild = true;
|
||||
getChildList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
this.parentId,
|
||||
Object.assign(params, this.query)
|
||||
).then(res => {
|
||||
this.dataChild = res.data.data;
|
||||
this.loadingChild = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
450
src/views/system/dictbiz.vue
Normal file
450
src/views/system/dictbiz.vue
Normal file
@@ -0,0 +1,450 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="optionParent"
|
||||
:table-loading="loading"
|
||||
:data="dataParent"
|
||||
:page="pageParent"
|
||||
ref="crud"
|
||||
v-model="formParent"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-click="handleRowClick"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoadParent"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dictbiz_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
@click.stop="handleRowClick(scope.row)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>字典配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #code="{ row }">
|
||||
<el-tag @click="handleRowClick(row)" style="cursor: pointer">{{ row.code }}</el-tag>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog :title="`[${dictValue}]字典配置`" append-to-body v-model="box" width="1000px">
|
||||
<avue-crud
|
||||
:option="optionChild"
|
||||
:table-loading="loadingChild"
|
||||
:data="dataChild"
|
||||
ref="crudChild"
|
||||
v-model="formChild"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpenChild"
|
||||
:before-close="beforeCloseChild"
|
||||
@row-del="rowDelChild"
|
||||
@row-update="rowUpdateChild"
|
||||
@row-save="rowSaveChild"
|
||||
@search-change="searchChangeChild"
|
||||
@search-reset="searchResetChild"
|
||||
@selection-change="selectionChangeChild"
|
||||
@current-change="currentChangeChild"
|
||||
@size-change="sizeChangeChild"
|
||||
@refresh-change="refreshChangeChild"
|
||||
@on-load="onLoadChild"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getParentList,
|
||||
getChildList,
|
||||
remove,
|
||||
update,
|
||||
add,
|
||||
getDict,
|
||||
getDictTree,
|
||||
} from '@/api/system/dictbiz';
|
||||
import { optionParent, optionChild } from '@/option/system/dictbiz';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dictValue: '暂无',
|
||||
parentId: -1,
|
||||
formParent: {},
|
||||
formChild: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
box: false,
|
||||
loading: true,
|
||||
loadingChild: true,
|
||||
pageParent: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
pageChild: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
dataParent: [],
|
||||
dataChild: [],
|
||||
optionParent: optionParent,
|
||||
optionChild: optionChild,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dictbiz_add, false),
|
||||
delBtn: this.validData(this.permission.dictbiz_delete, false),
|
||||
editBtn: this.validData(this.permission.dictbiz_edit, false),
|
||||
viewBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initData();
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getDictTree().then(res => {
|
||||
const column = this.findColumn(this.optionChild.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
handleAdd(row) {
|
||||
this.formChild.dictValue = '';
|
||||
this.formChild.dictKey = '';
|
||||
this.formChild.sort = 0;
|
||||
this.formChild.isSealed = 0;
|
||||
this.formChild.remark = '';
|
||||
this.formChild.parentId = row.id;
|
||||
this.$refs.crudChild.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const form = {
|
||||
...row,
|
||||
dictKey: -1,
|
||||
};
|
||||
add(form).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.onLoadChild(this.pageChild);
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.query = {};
|
||||
this.parentId = row.id;
|
||||
this.dictValue = row.dictValue;
|
||||
|
||||
const code = this.findColumn(this.optionChild.column, 'code');
|
||||
code.value = row.code;
|
||||
const parentId = this.findColumn(this.optionChild.column, 'parentId');
|
||||
parentId.value = row.id;
|
||||
|
||||
this.formChild.code = row.code;
|
||||
this.formChild.parentId = row.id;
|
||||
|
||||
this.box = true;
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoadParent(this.pageParent);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.pageParent.currentPage = 1;
|
||||
this.onLoadParent(this.pageParent, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formParent.id).then(res => {
|
||||
this.formParent = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.pageParent.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.pageParent.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoadParent(this.pageParent, this.query);
|
||||
},
|
||||
rowSaveChild(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateChild(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelChild(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchResetChild() {
|
||||
this.query = {};
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchChangeChild(params, done) {
|
||||
this.query = params;
|
||||
this.pageChild.currentPage = 1;
|
||||
this.onLoadChild(this.pageChild, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeChild(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClearChild() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
},
|
||||
handleDeleteChild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenChild(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formChild.id).then(res => {
|
||||
this.formChild = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeCloseChild(done) {
|
||||
this.$refs.crudChild.modelValue.parentId = this.parentId;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = this.parentId;
|
||||
}
|
||||
});
|
||||
done();
|
||||
},
|
||||
currentChangeChild(currentPage) {
|
||||
this.pageChild.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeChild(pageSize) {
|
||||
this.pageChild.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeChild() {
|
||||
this.onLoadChild(this.pageChild, this.query);
|
||||
},
|
||||
onLoadParent(page, params = {}) {
|
||||
this.loading = true;
|
||||
getParentList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageParent.total = data.total;
|
||||
this.dataParent = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
}
|
||||
);
|
||||
},
|
||||
onLoadChild(page, params = {}) {
|
||||
this.loadingChild = true;
|
||||
getChildList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
this.parentId,
|
||||
Object.assign(params, this.query)
|
||||
).then(res => {
|
||||
this.dataChild = res.data.data;
|
||||
this.loadingChild = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
438
src/views/system/menu.vue
Normal file
438
src/views/system/menu.vue
Normal file
@@ -0,0 +1,438 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
:before-close="beforeClose"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
@tree-load="treeLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.menu_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
v-if="userInfo.authority.includes('admin') && scope.row.category === 1"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<i :class="row.source" style="margin-right: 5px" />
|
||||
<span>{{ row.name }}</span>
|
||||
</template>
|
||||
<template #source="{ row }">
|
||||
<div style="text-align: center">
|
||||
<i :class="row.source" />
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getLazyList, remove, update, add, getMenu } from '@/api/system/menu';
|
||||
import { mapGetters } from 'vuex';
|
||||
import iconList from '@/config/iconList';
|
||||
import func from '@/utils/func';
|
||||
import { getMenuTree } from '@/api/system/menu';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
selectionList: [],
|
||||
parentId: 0,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
lazy: true,
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
dialogWidth: '60%',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
menuWidth: 320,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '菜单名称',
|
||||
prop: 'name',
|
||||
width: 300,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '路由地址',
|
||||
prop: 'path',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入路由地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '上级菜单',
|
||||
prop: 'parentId',
|
||||
type: 'tree',
|
||||
dicData: [],
|
||||
hide: true,
|
||||
addDisabled: false,
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: '请选择上级菜单',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单图标',
|
||||
prop: 'source',
|
||||
type: 'icon',
|
||||
slot: true,
|
||||
iconList: iconList,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单图标',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单类型',
|
||||
prop: 'category',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{
|
||||
label: '菜单',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '按钮',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择菜单类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单别名',
|
||||
prop: 'alias',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单别名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '是否缓存',
|
||||
prop: 'isOpen',
|
||||
type: 'radio',
|
||||
hide: true,
|
||||
dicData: [
|
||||
{
|
||||
label: '否',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '是',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
value: 1,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择是否缓存',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.category'() {
|
||||
const category = func.toInt(this.form.category);
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'path') {
|
||||
item.rules[0].required = category === 1;
|
||||
}
|
||||
if (item.prop === 'isOpen') {
|
||||
item.disabled = category === 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.menu_add, false),
|
||||
viewBtn: this.validData(this.permission.menu_view, false),
|
||||
delBtn: this.validData(this.permission.menu_delete, false),
|
||||
editBtn: this.validData(this.permission.menu_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getMenuTree().then(res => {
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
handleAdd(row) {
|
||||
this.parentId = row.id;
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.value = row.id;
|
||||
column.addDisabled = true;
|
||||
this.$refs.crud.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
res => {
|
||||
// 获取新增数据的相关字段
|
||||
const data = res.data.data;
|
||||
row.id = data.id;
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
// 数据回调进行刷新
|
||||
done(row);
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
// 数据回调进行刷新
|
||||
done(row);
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row, index, done) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
// 数据回调进行刷新
|
||||
done(row);
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
// 刷新表格数据并重载
|
||||
this.data = [];
|
||||
this.parentId = 0;
|
||||
this.$refs.crud.refreshTable();
|
||||
this.$refs.crud.toggleSelection();
|
||||
// 表格数据重载
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.parentId = 0;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.parentId = '';
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getMenu(this.form.id).then(res => {
|
||||
this.form = Object.assign(res.data.data, {
|
||||
hasChildren: this.form.hasChildren,
|
||||
});
|
||||
if (this.form.parentId === '0') {
|
||||
this.form.parentId = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeClose(done) {
|
||||
this.parentId = '';
|
||||
const column = this.findColumn(this.option.column, 'parentId');
|
||||
column.value = '';
|
||||
column.addDisabled = false;
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getLazyList(this.parentId, Object.assign(params, this.query)).then(res => {
|
||||
this.data = res.data.data;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
treeLoad(tree, treeNode, resolve) {
|
||||
const parentId = tree.id;
|
||||
getLazyList(parentId).then(res => {
|
||||
resolve(res.data.data);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
404
src/views/system/param.vue
Normal file
404
src/views/system/param.vue
Normal file
@@ -0,0 +1,404 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<div class="avue-crud">
|
||||
<el-row :hidden="!search" style="padding: 6px 18px">
|
||||
<!-- 查询模块 -->
|
||||
<el-form :inline="true" :model="query">
|
||||
<el-form-item label="参数名:">
|
||||
<el-input v-model="query.paramName" placeholder="请输入参数名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="参数键:">
|
||||
<el-input v-model="query.paramKey" placeholder="请输入参数键"></el-input>
|
||||
</el-form-item>
|
||||
<!-- 查询按钮 -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="searchChange">搜 索</el-button>
|
||||
<el-button icon="el-icon-delete" @click="searchReset()">清 空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="avue-crud__header">
|
||||
<!-- 头部左侧按钮模块 -->
|
||||
<div class="avue-crud__left">
|
||||
<el-button
|
||||
v-if="this.permissionList.addBtn"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="handleAdd"
|
||||
>新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="this.permissionList.delBtn"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete"
|
||||
plain
|
||||
>删 除
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 头部右侧按钮模块 -->
|
||||
<div class="avue-crud__right">
|
||||
<el-button icon="el-icon-refresh" @click="searchChange" circle></el-button>
|
||||
<el-button icon="el-icon-search" @click="searchHide" circle></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<!-- 列表模块 -->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
@selection-change="selectionChange"
|
||||
:data="data"
|
||||
style="width: 100%"
|
||||
:border="option.border"
|
||||
>
|
||||
<el-table-column
|
||||
type="selection"
|
||||
v-if="option.selection"
|
||||
width="55"
|
||||
align="center"
|
||||
></el-table-column>
|
||||
<el-table-column type="expand" v-if="option.expand" align="center"></el-table-column>
|
||||
<el-table-column v-if="option.index" label="#" type="index" width="50" align="center">
|
||||
</el-table-column>
|
||||
<template v-for="(item, index) in option.column">
|
||||
<!-- table字段 -->
|
||||
<el-table-column
|
||||
v-if="item.hide !== true"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:width="item.width"
|
||||
:key="index"
|
||||
>
|
||||
<template v-if="item.tip" #header>
|
||||
<span>{{ item.label }}</span>
|
||||
<el-tooltip :content="item.tip" placement="top">
|
||||
<el-icon style="margin-left: 4px; vertical-align: middle; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<!-- 操作栏模块 -->
|
||||
<el-table-column prop="menu" label="操作" :width="220" align="center">
|
||||
<template #="{ row }">
|
||||
<el-button
|
||||
v-if="this.permissionList.viewBtn"
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
@click="handleView(row)"
|
||||
>查看
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="this.permissionList.editBtn"
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-edit"
|
||||
@click="handleEdit(row)"
|
||||
>编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="this.permissionList.delBtn"
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-delete"
|
||||
@click="rowDel(row)"
|
||||
>删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="avue-crud__pagination" style="width: 100%">
|
||||
<!-- 分页模块 -->
|
||||
<el-pagination
|
||||
align="right"
|
||||
background
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
:current-page="page.currentPage"
|
||||
:page-sizes="[10, 20, 30, 40, 50, 100]"
|
||||
:page-size="page.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="page.total"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</el-row>
|
||||
<!-- 表单模块 -->
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="box"
|
||||
width="50%"
|
||||
:before-close="beforeClose"
|
||||
append-to-body
|
||||
>
|
||||
<el-form :disabled="view" ref="form" :model="form" label-width="80px">
|
||||
<!-- 表单字段 -->
|
||||
<el-form-item label="参数名:" prop="paramName">
|
||||
<el-input v-model="form.paramName" placeholder="请输入参数名" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="paramKey">
|
||||
<template #label>
|
||||
<el-tooltip content="参数键为系统内部唯一标识,请勿随意修改" placement="top">
|
||||
<el-icon style="margin-right: 4px; vertical-align: middle; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
<span>参数键:</span>
|
||||
</template>
|
||||
<el-input v-model="form.paramKey" placeholder="请输入参数键" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="paramValue">
|
||||
<template #label>
|
||||
<el-tooltip content="包含敏感关键词的配置不展示具体数值" placement="top">
|
||||
<el-icon style="margin-right: 4px; vertical-align: middle; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
<span>参数值:</span>
|
||||
</template>
|
||||
<el-input v-model="form.paramValue" placeholder="请输入参数值" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注:" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 表单按钮 -->
|
||||
<template #footer>
|
||||
<span v-if="!view" class="dialog-footer">
|
||||
<el-button type="primary" icon="el-icon-circle-check" @click="handleSubmit"
|
||||
>提 交</el-button
|
||||
>
|
||||
<el-button icon="el-icon-circle-close" @click="box = false">取 消</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/system/param';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { sensitive } from '@/utils/sensitive';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 弹框标题
|
||||
title: '',
|
||||
// 是否展示弹框
|
||||
box: false,
|
||||
// 是否显示查询
|
||||
search: true,
|
||||
// 加载中
|
||||
loading: true,
|
||||
// 是否为查看模式
|
||||
view: false,
|
||||
// 脱敏工具实例
|
||||
sensitiveManager: null,
|
||||
// 查询信息
|
||||
query: {},
|
||||
// 分页信息
|
||||
page: {
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 40,
|
||||
},
|
||||
// 表单数据
|
||||
form: {},
|
||||
// 选择行
|
||||
selectionList: [],
|
||||
// 表单配置
|
||||
option: {
|
||||
expand: false,
|
||||
index: true,
|
||||
border: true,
|
||||
selection: true,
|
||||
column: [
|
||||
{
|
||||
label: '参数名',
|
||||
prop: 'paramName',
|
||||
},
|
||||
{
|
||||
label: '参数键',
|
||||
prop: 'paramKey',
|
||||
tip: '参数键为系统内部唯一标识,请勿随意修改',
|
||||
},
|
||||
{
|
||||
label: '参数值',
|
||||
prop: 'paramValue',
|
||||
tip: '包含敏感关键词的配置不展示具体数值',
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
},
|
||||
],
|
||||
},
|
||||
// 表单列表
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 创建脱敏工具实例
|
||||
this.sensitiveManager = sensitive.create({
|
||||
fields: ['paramValue'], // 配置需要脱敏的字段
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.param_add, false),
|
||||
viewBtn: this.validData(this.permission.param_view, false),
|
||||
delBtn: this.validData(this.permission.param_delete, false),
|
||||
editBtn: this.validData(this.permission.param_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.height = this.setPx(document.body.clientHeight - 340);
|
||||
},
|
||||
searchHide() {
|
||||
this.search = !this.search;
|
||||
},
|
||||
searchChange() {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
handleSubmit() {
|
||||
if (!this.form.id) {
|
||||
add(this.form).then(() => {
|
||||
this.box = false;
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 获取需要提交的数据
|
||||
const submitData = this.sensitiveManager.getSubmitData(this.form);
|
||||
update(submitData).then(() => {
|
||||
this.box = false;
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
handleAdd() {
|
||||
this.title = '新增';
|
||||
this.form = {};
|
||||
this.box = true;
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.title = '编辑';
|
||||
this.box = true;
|
||||
getDetail(row.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
// 保存初始脱敏数据
|
||||
this.sensitiveManager.saveInitialData(this.form);
|
||||
});
|
||||
},
|
||||
handleView(row) {
|
||||
this.title = '查看';
|
||||
this.view = true;
|
||||
this.box = true;
|
||||
getDetail(row.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
// 保存初始脱敏数据
|
||||
this.sensitiveManager.saveInitialData(this.form);
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.selectionClear();
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeClose(done) {
|
||||
done();
|
||||
this.form = {};
|
||||
this.view = false;
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.table.clearSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
298
src/views/system/post.vue
Normal file
298
src/views/system/post.vue
Normal file
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.post_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/system/post';
|
||||
import { mapGetters } from 'vuex';
|
||||
import website from '@/config/website';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '所属租户',
|
||||
prop: 'tenantId',
|
||||
type: 'tree',
|
||||
dicUrl: '/blade-system/tenant/select',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: website.tenantMode,
|
||||
span: 24,
|
||||
props: {
|
||||
label: 'tenantName',
|
||||
value: 'tenantId',
|
||||
},
|
||||
hide: !website.tenantMode,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入所属租户',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '岗位类型',
|
||||
prop: 'category',
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/dict/dictionary?code=post_category',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择岗位类型',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '岗位编号',
|
||||
prop: 'postCode',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入岗位编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '岗位名称',
|
||||
prop: 'postName',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入岗位名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '岗位排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入岗位排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '岗位描述',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
minRows: 6,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.post_add, false),
|
||||
viewBtn: this.validData(this.permission.post_view, false),
|
||||
delBtn: this.validData(this.permission.post_delete, false),
|
||||
editBtn: this.validData(this.permission.post_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
811
src/views/system/tenant.vue
Normal file
811
src/views/system/tenant.vue
Normal file
@@ -0,0 +1,811 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
v-if="permission.tenant_add && !recycleMode"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="$refs.crud.rowAdd()"
|
||||
>新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.tenant_delete && !recycleMode"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-refresh"
|
||||
v-if="userInfo.authority.includes('administrator') && !recycleMode"
|
||||
@click="handleRecycle"
|
||||
>回收站
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-check"
|
||||
v-if="userInfo.authority.includes('administrator') && recycleMode"
|
||||
@click="handleRecyclePass"
|
||||
>恢 复
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-close"
|
||||
v-if="userInfo.authority.includes('administrator') && recycleMode"
|
||||
@click="handleRecycleRemove"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-refresh-left"
|
||||
v-if="userInfo.authority.includes('administrator') && recycleMode"
|
||||
@click="handleRecycleBack"
|
||||
>返 回
|
||||
</el-button>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="给租户配置账号额度、过期时间等授权信息"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
plain
|
||||
type="info"
|
||||
v-if="userInfo.authority.includes('administrator') && !recycleMode"
|
||||
icon="el-icon-setting"
|
||||
@click="handleSetting"
|
||||
>批量授权配置
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #menu-right>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="将自定义的数据源定制为租户绑定的独立数据源"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
plain
|
||||
v-if="userInfo.authority.includes('administrator') && !recycleMode"
|
||||
icon="el-icon-message-box"
|
||||
@click="handleDatasourceSetting"
|
||||
>数据源管理
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="将自定义的菜单集合定制为租户绑定的菜单产品包"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
plain
|
||||
v-if="userInfo.authority.includes('administrator') && !recycleMode"
|
||||
icon="el-icon-set-up"
|
||||
@click="handlePackageSetting"
|
||||
>产品包管理
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
v-if="userInfo.authority.includes('administrator') && !recycleMode"
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-coin"
|
||||
@click.stop="handleRowDatasource(scope.row)"
|
||||
>数据源
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="userInfo.authority.includes('administrator') && !recycleMode"
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-notebook"
|
||||
@click.stop="handleRowPackage(scope.row)"
|
||||
>产品包
|
||||
</el-button>
|
||||
</template>
|
||||
<template #accountNumber="{ row }">
|
||||
<el-tag>{{ row.accountNumber > 0 ? row.accountNumber : '不限制' }}</el-tag>
|
||||
</template>
|
||||
<template #expireTime="{ row }">
|
||||
<el-tag>{{ row.expireTime ? row.expireTime : '不限制' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="租户授权配置" append-to-body v-model="box" width="450px">
|
||||
<avue-form :option="settingOption" v-model="settingForm" @submit="handleSubmit" />
|
||||
</el-dialog>
|
||||
<el-dialog title="租户数据源配置" append-to-body v-model="datasourceBox" width="450px">
|
||||
<avue-form
|
||||
ref="formDatasource"
|
||||
:option="datasourceOption"
|
||||
v-model="datasourceForm"
|
||||
@submit="handleDatasourceSubmit"
|
||||
/>
|
||||
</el-dialog>
|
||||
<el-drawer
|
||||
title="租户数据源管理"
|
||||
append-to-body
|
||||
v-model="datasourceSettingBox"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
>
|
||||
<tenant-datasource></tenant-datasource>
|
||||
</el-drawer>
|
||||
<el-dialog title="租户产品包配置" append-to-body v-model="packageBox" width="450px">
|
||||
<avue-form
|
||||
ref="formPackage"
|
||||
:option="packageOption"
|
||||
v-model="packageForm"
|
||||
@submit="handlePackageSubmit"
|
||||
>
|
||||
<template #menuId>
|
||||
<el-tree
|
||||
ref="packageMenuTree"
|
||||
:data="packageMenuTreeData"
|
||||
:props="packageMenuTreeProps"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
/>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
<el-drawer
|
||||
title="租户产品包管理"
|
||||
append-to-body
|
||||
v-model="packageSettingBox"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
>
|
||||
<tenant-package></tenant-package>
|
||||
</el-drawer>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getList,
|
||||
getDetail,
|
||||
update,
|
||||
add,
|
||||
setting,
|
||||
datasource,
|
||||
packageInfo,
|
||||
packageSetting,
|
||||
recycle,
|
||||
pass,
|
||||
remove,
|
||||
} from '@/api/system/tenant';
|
||||
import { getDetail as packageDetail } from '@/api/system/tenantpackage';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getMenuTree } from '@/api/system/menu';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
box: false,
|
||||
datasourceBox: false,
|
||||
datasourceSettingBox: false,
|
||||
packageBox: false,
|
||||
packageSettingBox: false,
|
||||
recycleMode: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
addBtn: false,
|
||||
viewBtn: true,
|
||||
menuWidth: 380,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '租户ID',
|
||||
prop: 'tenantId',
|
||||
width: 100,
|
||||
search: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入租户ID',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '租户名称',
|
||||
prop: 'tenantName',
|
||||
search: true,
|
||||
width: 180,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入参数名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '联系人',
|
||||
prop: 'linkman',
|
||||
width: 100,
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入联系人',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '联系电话',
|
||||
prop: 'contactNumber',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
label: '联系地址',
|
||||
prop: 'address',
|
||||
span: 24,
|
||||
minRows: 2,
|
||||
type: 'textarea',
|
||||
hide: true,
|
||||
},
|
||||
{
|
||||
label: '账号额度',
|
||||
prop: 'accountNumber',
|
||||
width: 90,
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '过期时间',
|
||||
prop: 'expireTime',
|
||||
width: 180,
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '绑定域名',
|
||||
prop: 'domainUrl',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '系统背景',
|
||||
prop: 'backgroundUrl',
|
||||
type: 'upload',
|
||||
listType: 'picture-img',
|
||||
dataType: 'string',
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
},
|
||||
hide: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
settingForm: {},
|
||||
settingOption: {
|
||||
column: [
|
||||
{
|
||||
label: '账号额度',
|
||||
labelTip: '代表租户可创建的最大额度,若不限制则默认为-1',
|
||||
prop: 'accountNumber',
|
||||
type: 'number',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '过期时间',
|
||||
labelTip: '代表租户可使用的最后日期,若不限制则默认为空',
|
||||
prop: 'expireTime',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
datasourceForm: {},
|
||||
datasourceOption: {
|
||||
column: [
|
||||
{
|
||||
label: '数据源',
|
||||
prop: 'datasourceId',
|
||||
search: true,
|
||||
span: 24,
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/tenant-datasource/select',
|
||||
props: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择数据源',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
packageForm: {},
|
||||
packageMenuTreeData: [],
|
||||
packageMenuTreeProps: {
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
children: 'children',
|
||||
},
|
||||
packageOption: {
|
||||
column: [
|
||||
{
|
||||
label: '产品包',
|
||||
prop: 'packageId',
|
||||
search: true,
|
||||
span: 24,
|
||||
type: 'select',
|
||||
dicUrl: '/blade-system/tenant-package/select',
|
||||
props: {
|
||||
label: 'packageName',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '菜单预览',
|
||||
prop: 'menuId',
|
||||
span: 24,
|
||||
formslot: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'packageForm.packageId'() {
|
||||
if (!validatenull(this.packageForm.packageId)) {
|
||||
packageDetail(this.packageForm.packageId).then(res => {
|
||||
const menuId = res.data.data.menuId;
|
||||
this.packageForm.menuId = menuId;
|
||||
|
||||
// 解析选中的菜单ID列表
|
||||
const selectedIds = this.parseMenuIds(menuId);
|
||||
|
||||
// 先加载完整菜单树,再根据选中ID筛选
|
||||
getMenuTree().then(treeRes => {
|
||||
const fullTreeData = treeRes.data.data;
|
||||
const selectedIdSet = new Set(selectedIds);
|
||||
// 筛选树:只保留选中节点及其所有祖先节点
|
||||
this.packageMenuTreeData = this.filterTreeBySelectedIds(fullTreeData, selectedIdSet);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.tenant_add, false),
|
||||
viewBtn: this.validData(this.permission.tenant_view, false),
|
||||
delBtn: this.validData(this.permission.tenant_delete, false),
|
||||
editBtn: this.validData(this.permission.tenant_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
tenantId() {
|
||||
return this.selectionList[0].tenantId;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getMenuTree().then(res => {
|
||||
this.packageMenuTreeData = res.data.data;
|
||||
});
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('删除后所选租户将进入回收站,是否继续?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return recycle(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
const data = res.data.data;
|
||||
if (!(data.accountNumber > 0)) {
|
||||
data.accountNumber = '不限制';
|
||||
}
|
||||
if (!data.expireTime) {
|
||||
data.expireTime = '不限制';
|
||||
}
|
||||
this.form = data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('删除后所选租户将进入回收站,是否继续?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return recycle(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleRecycle() {
|
||||
this.recycleMode = true;
|
||||
this.$refs.crud.option.editBtn = false;
|
||||
this.$refs.crud.option.delBtn = false;
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
handleRecyclePass() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将所选租户进行恢复?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return pass(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleRecycleRemove() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将所选租户永久删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleRecycleBack() {
|
||||
this.recycleMode = false;
|
||||
this.$refs.crud.option.editBtn = true;
|
||||
this.$refs.crud.option.delBtn = true;
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
handleSetting() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
if (this.selectionList.length === 1) {
|
||||
getDetail(this.selectionList[0].id).then(res => {
|
||||
const data = res.data.data;
|
||||
this.settingForm.accountNumber = data.accountNumber;
|
||||
this.settingForm.expireTime = data.expireTime;
|
||||
});
|
||||
} else {
|
||||
this.settingForm.accountNumber = -1;
|
||||
this.settingForm.expireTime = '';
|
||||
}
|
||||
this.box = true;
|
||||
},
|
||||
handleRowDatasource(row) {
|
||||
getDetail(row.id).then(res => {
|
||||
const data = res.data.data;
|
||||
this.datasourceForm.tenantId = row.tenantId;
|
||||
this.datasourceForm.datasourceId = data.datasourceId;
|
||||
});
|
||||
this.datasourceBox = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.formDatasource.updateDic('datasourceId');
|
||||
});
|
||||
},
|
||||
handleDatasource() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
if (this.selectionList.length !== 1) {
|
||||
this.$message.warning('只能选择一条数据');
|
||||
return;
|
||||
}
|
||||
getDetail(this.selectionList[0].id).then(res => {
|
||||
const data = res.data.data;
|
||||
this.datasourceForm.tenantId = this.selectionList[0].tenantId;
|
||||
this.datasourceForm.datasourceId = data.datasourceId;
|
||||
});
|
||||
this.datasourceBox = true;
|
||||
},
|
||||
handleRowPackage(row) {
|
||||
// 重置数据
|
||||
this.packageMenuTreeData = [];
|
||||
packageInfo(row.id).then(res => {
|
||||
const data = res.data.data;
|
||||
this.packageForm.tenantId = row.tenantId;
|
||||
this.packageForm.packageId = data.id;
|
||||
this.packageForm.menuId = data.menuId;
|
||||
// 加载菜单树数据
|
||||
this.initData();
|
||||
});
|
||||
this.packageBox = true;
|
||||
//更新字典远程数据
|
||||
setTimeout(() => {
|
||||
const form = this.$refs.formPackage;
|
||||
form.updateDic('packageId');
|
||||
}, 10);
|
||||
},
|
||||
handleDatasourceSetting() {
|
||||
this.datasourceSettingBox = true;
|
||||
},
|
||||
handlePackageSetting() {
|
||||
this.packageSettingBox = true;
|
||||
},
|
||||
handleSubmit(form, done) {
|
||||
setting(this.ids, form).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '配置成功!',
|
||||
});
|
||||
done();
|
||||
this.box = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
handleDatasourceSubmit(form, done) {
|
||||
datasource(form.tenantId, form.datasourceId).then(
|
||||
() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '配置成功!',
|
||||
});
|
||||
done();
|
||||
this.datasourceBox = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
handlePackageSubmit(form, done) {
|
||||
packageSetting(form.tenantId, form.packageId).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '配置成功!',
|
||||
});
|
||||
done();
|
||||
this.packageBox = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
Object.assign(params, this.query, {
|
||||
status_equal: this.recycleMode ? -1 : 1,
|
||||
})
|
||||
).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 解析菜单ID为字符串数组
|
||||
* @param {string|Array} menuId - 菜单ID,可能是逗号分隔的字符串或数组
|
||||
* @returns {string[]} - 菜单ID字符串数组
|
||||
*/
|
||||
parseMenuIds(menuId) {
|
||||
if (!menuId) return [];
|
||||
if (Array.isArray(menuId)) {
|
||||
return menuId.map(id => String(id).trim());
|
||||
}
|
||||
return menuId
|
||||
.split(',')
|
||||
.map(id => id.trim())
|
||||
.filter(Boolean);
|
||||
},
|
||||
/**
|
||||
* 递归筛选树节点,只保留选中的节点及其所有祖先节点
|
||||
* @param {Array} tree - 原始树数据
|
||||
* @param {Set<string>} selectedIdSet - 选中的节点ID集合
|
||||
* @returns {Array} - 筛选后的树数据
|
||||
*/
|
||||
filterTreeBySelectedIds(tree, selectedIdSet) {
|
||||
if (!tree || !tree.length) return [];
|
||||
|
||||
return tree.reduce((result, node) => {
|
||||
// 递归处理子节点
|
||||
const filteredChildren = this.filterTreeBySelectedIds(node.children, selectedIdSet);
|
||||
|
||||
// 判断当前节点是否需要保留:自身被选中 或 有子节点被保留(作为祖先节点)
|
||||
const nodeId = String(node.id);
|
||||
const isSelected = selectedIdSet.has(nodeId);
|
||||
const hasRetainedChildren = filteredChildren.length > 0;
|
||||
|
||||
if (isSelected || hasRetainedChildren) {
|
||||
result.push({
|
||||
...node,
|
||||
children: hasRetainedChildren ? filteredChildren : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
408
src/views/system/tenantdatasource.vue
Normal file
408
src/views/system/tenantdatasource.vue
Normal file
@@ -0,0 +1,408 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #shardingConfig-form="{}">
|
||||
<code-editor v-model="form.shardingConfig" height="300px" />
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/system/tenantdatasource';
|
||||
import { mapGetters } from 'vuex';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 900,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'name',
|
||||
width: 120,
|
||||
span: 24,
|
||||
search: true,
|
||||
gridRow: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据源名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据类型',
|
||||
type: 'radio',
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 120,
|
||||
searchLabelWidth: 100,
|
||||
row: true,
|
||||
gridRow: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=datasource_category',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
prop: 'category',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择分类',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'YAML',
|
||||
prop: 'shardingConfig',
|
||||
span: 24,
|
||||
minRows: 5,
|
||||
hide: true,
|
||||
display: false,
|
||||
gridRow: true,
|
||||
slot: true,
|
||||
type: 'textarea',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入YAML配置',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '驱动类',
|
||||
prop: 'driverClass',
|
||||
type: 'select',
|
||||
span: 24,
|
||||
gridRow: true,
|
||||
dicData: [
|
||||
{
|
||||
label: 'com.mysql.cj.jdbc.Driver',
|
||||
value: 'com.mysql.cj.jdbc.Driver',
|
||||
},
|
||||
{
|
||||
label: 'org.postgresql.Driver',
|
||||
value: 'org.postgresql.Driver',
|
||||
},
|
||||
{
|
||||
label: 'oracle.jdbc.OracleDriver',
|
||||
value: 'oracle.jdbc.OracleDriver',
|
||||
},
|
||||
{
|
||||
label: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
|
||||
value: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
|
||||
},
|
||||
{
|
||||
label: 'dm.jdbc.driver.DmDriver',
|
||||
value: 'dm.jdbc.driver.DmDriver',
|
||||
},
|
||||
{
|
||||
label: 'com.yashandb.jdbc.Driver',
|
||||
value: 'com.yashandb.jdbc.Driver',
|
||||
},
|
||||
{
|
||||
label: 'com.kingbase8.Driver',
|
||||
value: 'com.kingbase8.Driver',
|
||||
},
|
||||
],
|
||||
width: 200,
|
||||
display: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入驱动类',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '连接地址',
|
||||
prop: 'url',
|
||||
span: 24,
|
||||
display: true,
|
||||
gridRow: true,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入连接地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '用户名',
|
||||
prop: 'username',
|
||||
width: 120,
|
||||
display: true,
|
||||
gridRow: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入用户名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
prop: 'password',
|
||||
hide: true,
|
||||
display: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
minRows: 3,
|
||||
hide: true,
|
||||
type: 'textarea',
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
driverUrlTemplates: {
|
||||
'com.mysql.cj.jdbc.Driver':
|
||||
'jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true',
|
||||
'org.postgresql.Driver': 'jdbc:postgresql://127.0.0.1:5432/bladex',
|
||||
'oracle.jdbc.OracleDriver': 'jdbc:oracle:thin:@//127.0.0.1:1521/ORCLPDB',
|
||||
'com.microsoft.sqlserver.jdbc.SQLServerDriver':
|
||||
'jdbc:sqlserver://127.0.0.1:1433;DatabaseName=bladex',
|
||||
'dm.jdbc.driver.DmDriver':
|
||||
'jdbc:dm://127.0.0.1:5236/bladex?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8',
|
||||
'com.yashandb.jdbc.Driver': 'jdbc:yasdb://127.0.0.1:1688/bladex',
|
||||
'com.kingbase8.Driver': 'jdbc:kingbase8://localhost:4321/bladex',
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.category'() {
|
||||
const category = func.toInt(this.form.category);
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'driverClass') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'url') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'username') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'password') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'shardingConfig') {
|
||||
item.display = category === 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
'form.driverClass'(newDriverClass) {
|
||||
// 当驱动类发生变化时,自动设置对应的默认URL
|
||||
if (newDriverClass && this.driverUrlTemplates[newDriverClass]) {
|
||||
// 只在URL为空或者是其他驱动的默认URL时才自动填充
|
||||
const isDefaultUrl =
|
||||
!this.form.url || Object.values(this.driverUrlTemplates).includes(this.form.url);
|
||||
if (isDefaultUrl) {
|
||||
this.form.url = this.driverUrlTemplates[newDriverClass];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: true,
|
||||
viewBtn: true,
|
||||
delBtn: true,
|
||||
editBtn: true,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
368
src/views/system/tenantpackage.vue
Normal file
368
src/views/system/tenantpackage.vue
Normal file
@@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDelete">删 除</el-button>
|
||||
</template>
|
||||
<template #menuId-form>
|
||||
<el-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||
>
|
||||
<span style="display: inline-flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="menuLinked"
|
||||
active-text="节点联动"
|
||||
size="small"
|
||||
@change="handleLinkedChange"
|
||||
/>
|
||||
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
|
||||
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button size="small" plain @click="handleSelectAll">全选</el-button>
|
||||
<el-button size="small" plain @click="handleInvertSelect">反选</el-button>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<el-tree
|
||||
ref="menuTree"
|
||||
:data="menuTreeData"
|
||||
:props="menuTreeProps"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:default-checked-keys="checkedMenuKeys"
|
||||
:default-expand-all="false"
|
||||
:check-strictly="!menuLinked"
|
||||
@check="handleMenuCheck"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/system/tenantpackage';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getMenuTree } from '@/api/system/menu';
|
||||
|
||||
export default {
|
||||
name: 'tenantPackage',
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
menuTreeData: [],
|
||||
checkedMenuKeys: [],
|
||||
menuLinked: false,
|
||||
menuTreeProps: {
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
children: 'children',
|
||||
},
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
dialogWidth: 600,
|
||||
column: [
|
||||
{
|
||||
label: '产品包名',
|
||||
prop: 'packageName',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入产品包名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单列表',
|
||||
prop: 'menuId',
|
||||
span: 24,
|
||||
hide: true,
|
||||
formslot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择菜单',
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: true,
|
||||
viewBtn: false,
|
||||
delBtn: true,
|
||||
editBtn: true,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getMenuTree().then(res => {
|
||||
this.menuTreeData = res.data.data;
|
||||
this.$nextTick(() => this.applyCheckedKeys());
|
||||
});
|
||||
},
|
||||
// el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放,
|
||||
// 复用的树实例需在打开表单时整体覆盖勾选状态,避免残留上一次配置的勾选
|
||||
applyCheckedKeys() {
|
||||
const tree = this.$refs.menuTree;
|
||||
if (!tree) return;
|
||||
tree.setCheckedKeys(this.checkedMenuKeys);
|
||||
},
|
||||
handleMenuCheck(node, data) {
|
||||
// 只保存全选的节点,不保存半选的节点
|
||||
// 恢复时半选状态会根据子节点自动计算
|
||||
this.form.menuId = data.checkedKeys;
|
||||
},
|
||||
getAllNodeKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
keys.push(node.id);
|
||||
if (node.children && node.children.length > 0) {
|
||||
keys = keys.concat(this.getAllNodeKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
getLeafKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
keys.push(node.id);
|
||||
} else {
|
||||
keys = keys.concat(this.getLeafKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
handleSelectAll() {
|
||||
const tree = this.$refs.menuTree;
|
||||
if (!tree) return;
|
||||
const allKeys = this.getAllNodeKeys(this.menuTreeData);
|
||||
tree.setCheckedKeys(allKeys);
|
||||
this.form.menuId = tree.getCheckedKeys();
|
||||
},
|
||||
handleInvertSelect() {
|
||||
const tree = this.$refs.menuTree;
|
||||
if (!tree) return;
|
||||
const checkedKeys = new Set(tree.getCheckedKeys());
|
||||
if (this.menuLinked) {
|
||||
const leafKeys = this.getLeafKeys(this.menuTreeData);
|
||||
const invertedKeys = leafKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
} else {
|
||||
const allKeys = this.getAllNodeKeys(this.menuTreeData);
|
||||
const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
}
|
||||
this.form.menuId = tree.getCheckedKeys();
|
||||
},
|
||||
handleLinkedChange() {
|
||||
const tree = this.$refs.menuTree;
|
||||
if (!tree) return;
|
||||
const checkedKeys = tree.getCheckedKeys();
|
||||
const halfCheckedKeys = tree.getHalfCheckedKeys();
|
||||
this.$nextTick(() => {
|
||||
tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
|
||||
this.form.menuId = tree.getCheckedKeys();
|
||||
});
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
// 将 menuId 数组转为逗号分隔的字符串
|
||||
if (Array.isArray(row.menuId)) {
|
||||
row.menuId = row.menuId.join(',');
|
||||
}
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
// 将 menuId 数组转为逗号分隔的字符串
|
||||
if (Array.isArray(row.menuId)) {
|
||||
row.menuId = row.menuId.join(',');
|
||||
}
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
// 重置选中状态和树数据
|
||||
this.checkedMenuKeys = [];
|
||||
this.menuTreeData = [];
|
||||
this.menuLinked = false;
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
// 设置选中的菜单
|
||||
if (this.form.menuId) {
|
||||
this.checkedMenuKeys = Array.isArray(this.form.menuId)
|
||||
? this.form.menuId
|
||||
: this.form.menuId.split(',').map(id => id.trim());
|
||||
}
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
531
src/views/system/topmenu.vue
Normal file
531
src/views/system/topmenu.vue
Normal file
@@ -0,0 +1,531 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.topmenu_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-setting"
|
||||
@click="handleMenuSetting"
|
||||
v-if="permission.topmenu_setting"
|
||||
plain
|
||||
>菜单配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
v-if="permission.topmenu_setting"
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
@click.stop="handleRowMenuSetting(scope.row)"
|
||||
>配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #name="{ row }">
|
||||
<i :class="row.source" style="margin-right: 5px" />
|
||||
<span>{{ row.name }}</span>
|
||||
</template>
|
||||
<template #source="{ row }">
|
||||
<div style="text-align: center">
|
||||
<i :class="row.source"></i>
|
||||
</div>
|
||||
</template>
|
||||
<template #sort="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.sort"
|
||||
@change="sortChange(row)"
|
||||
:min="1"
|
||||
:max="100"
|
||||
></el-input-number>
|
||||
</template>
|
||||
<template #isMain="{ row }">
|
||||
<el-switch
|
||||
v-model="row.isMain"
|
||||
inline-prompt
|
||||
:before-change="() => isMainChange(row)"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog
|
||||
title="下级菜单配置"
|
||||
append-to-body
|
||||
v-model="box"
|
||||
width="345px"
|
||||
@closed="handleDialogClose"
|
||||
>
|
||||
<el-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||
>
|
||||
<span style="display: inline-flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="menuLinked"
|
||||
active-text="节点联动"
|
||||
size="small"
|
||||
@change="handleLinkedChange"
|
||||
/>
|
||||
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
|
||||
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button size="small" plain @click="handleSelectAll">全选</el-button>
|
||||
<el-button size="small" plain @click="handleInvertSelect">反选</el-button>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<el-tree
|
||||
:data="menuGrantList"
|
||||
show-checkbox
|
||||
:check-strictly="!menuLinked"
|
||||
node-key="id"
|
||||
ref="treeMenu"
|
||||
:default-checked-keys="menuTreeObj"
|
||||
:props="props"
|
||||
>
|
||||
</el-tree>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="box = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getList,
|
||||
getDetail,
|
||||
add,
|
||||
update,
|
||||
remove,
|
||||
grant,
|
||||
grantTree,
|
||||
getTopTree,
|
||||
enable,
|
||||
} from '@/api/system/topmenu';
|
||||
import { mapGetters } from 'vuex';
|
||||
import iconList from '@/config/iconList';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
box: false,
|
||||
query: {},
|
||||
loading: true,
|
||||
currentMenuIds: [],
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
},
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
menuGrantList: [],
|
||||
menuTreeObj: [],
|
||||
menuLinked: false,
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
menuWidth: 300,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '菜单名',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单图标',
|
||||
prop: 'source',
|
||||
type: 'icon',
|
||||
slot: true,
|
||||
iconList: iconList,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单图标',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '是否主页',
|
||||
prop: 'isMain',
|
||||
slot: true,
|
||||
align: 'center',
|
||||
width: 100,
|
||||
value: 0,
|
||||
display: false,
|
||||
dicData: [
|
||||
{ label: '否', value: 0 },
|
||||
{ label: '是', value: 1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单路由',
|
||||
prop: 'path',
|
||||
span: 24,
|
||||
hide: true,
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: '请输入菜单路由',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.topmenu_add, false),
|
||||
viewBtn: this.validData(this.permission.topmenu_view, false),
|
||||
delBtn: this.validData(this.permission.topmenu_delete, false),
|
||||
editBtn: this.validData(this.permission.topmenu_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
idsArray() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getAllNodeKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
keys.push(node.id);
|
||||
if (node.children && node.children.length > 0) {
|
||||
keys = keys.concat(this.getAllNodeKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
getLeafKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
keys.push(node.id);
|
||||
} else {
|
||||
keys = keys.concat(this.getLeafKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
handleSelectAll() {
|
||||
const tree = this.$refs.treeMenu;
|
||||
if (!tree) return;
|
||||
const allKeys = this.getAllNodeKeys(this.menuGrantList);
|
||||
tree.setCheckedKeys(allKeys);
|
||||
},
|
||||
handleInvertSelect() {
|
||||
const tree = this.$refs.treeMenu;
|
||||
if (!tree) return;
|
||||
const checkedKeys = new Set(tree.getCheckedKeys());
|
||||
if (this.menuLinked) {
|
||||
const leafKeys = this.getLeafKeys(this.menuGrantList);
|
||||
const invertedKeys = leafKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
} else {
|
||||
const allKeys = this.getAllNodeKeys(this.menuGrantList);
|
||||
const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
}
|
||||
},
|
||||
handleLinkedChange() {
|
||||
const tree = this.$refs.treeMenu;
|
||||
if (!tree) return;
|
||||
const checkedKeys = tree.getCheckedKeys();
|
||||
const halfCheckedKeys = tree.getHalfCheckedKeys();
|
||||
this.$nextTick(() => {
|
||||
tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
|
||||
});
|
||||
},
|
||||
// el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放,
|
||||
// 复用的树实例需在打开配置时整体覆盖勾选状态,避免残留上一次配置的勾选
|
||||
applyCheckedKeys() {
|
||||
const tree = this.$refs.treeMenu;
|
||||
if (!tree) return;
|
||||
tree.setCheckedKeys(this.menuTreeObj);
|
||||
},
|
||||
isMainChange(row) {
|
||||
const newValue = row.isMain === 1 ? 0 : 1;
|
||||
const request = newValue === 1 ? enable(row.id) : update({ ...row, isMain: newValue });
|
||||
return request.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
return true;
|
||||
}).catch(error => {
|
||||
window.console.log(error);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
submit() {
|
||||
const menuList = this.$refs.treeMenu.getCheckedKeys();
|
||||
grant(this.currentMenuIds, menuList).then(() => {
|
||||
this.box = false;
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleMenuSetting() {
|
||||
if (this.selectionList.length !== 1) {
|
||||
this.$message.warning('只能选择一条数据');
|
||||
return;
|
||||
}
|
||||
this.currentMenuIds = this.idsArray;
|
||||
this.menuTreeObj = [];
|
||||
grantTree().then(res => {
|
||||
this.menuGrantList = res.data.data.menu;
|
||||
getTopTree(this.ids).then(res => {
|
||||
this.menuTreeObj = res.data.data.menu;
|
||||
this.box = true;
|
||||
this.$nextTick(() => this.applyCheckedKeys());
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowMenuSetting(row) {
|
||||
this.currentMenuIds = [row.id];
|
||||
this.menuTreeObj = [];
|
||||
grantTree().then(res => {
|
||||
this.menuGrantList = res.data.data.menu;
|
||||
getTopTree(row.id).then(res => {
|
||||
this.menuTreeObj = res.data.data.menu;
|
||||
this.box = true;
|
||||
this.$nextTick(() => this.applyCheckedKeys());
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDialogClose() {
|
||||
this.currentMenuIds = [];
|
||||
this.menuLinked = false;
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
sortChange(row) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.none-border {
|
||||
border: 0;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
863
src/views/system/user.vue
Normal file
863
src/views/system/user.vue
Normal file
@@ -0,0 +1,863 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="5">
|
||||
<div class="box">
|
||||
<el-scrollbar>
|
||||
<basic-container>
|
||||
<avue-tree :option="treeOption" :data="treeData" @node-click="nodeClick" />
|
||||
</basic-container>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="userOption"
|
||||
v-model:search="search"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
:before-open="beforeOpen"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
v-if="permission.user_add && !auditMode"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="$refs.crud.rowAdd()"
|
||||
>新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.user_delete && !auditMode"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-operation"
|
||||
v-if="userInfo.authority.includes('admin') && !auditMode"
|
||||
@click="handleAudit"
|
||||
>审 核
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="el-icon-check"
|
||||
v-if="userInfo.authority.includes('admin') && auditMode"
|
||||
@click="handleAuditPass"
|
||||
>通 过
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="el-icon-close"
|
||||
v-if="userInfo.authority.includes('admin') && auditMode"
|
||||
@click="handleAuditRefuse"
|
||||
>拒 绝
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="el-icon-refresh-left"
|
||||
v-if="userInfo.authority.includes('admin') && auditMode"
|
||||
@click="handleAuditBack"
|
||||
>返 回
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
plain
|
||||
v-if="permission.user_role && !auditMode"
|
||||
icon="el-icon-user"
|
||||
@click="handleGrant"
|
||||
>角色配置
|
||||
</el-button>
|
||||
<el-dropdown
|
||||
v-if="userInfo.authority.includes('admin') && !auditMode"
|
||||
@command="handleDataManageCommand"
|
||||
style="margin-left: 10px"
|
||||
>
|
||||
<el-button type="primary" plain icon="el-icon-files">
|
||||
数据管理<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="import">
|
||||
<el-icon><upload /></el-icon>
|
||||
导入数据
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="export">
|
||||
<el-icon><download /></el-icon>
|
||||
导出数据
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<template #menu-right>
|
||||
<el-button
|
||||
v-if="userInfo.authority.includes('admin') && !auditMode"
|
||||
icon="el-icon-document"
|
||||
@click="$refs.authLog.openAuthLog()"
|
||||
>认证日志
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="userInfo.authority.includes('admin') && !auditMode"
|
||||
icon="el-icon-lock"
|
||||
@click="$refs.authLock.openLockLog()"
|
||||
>锁定管理
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="{ row, index, size }">
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
icon="el-icon-view"
|
||||
v-if="this.permission.user_view"
|
||||
@click.stop="$refs.crud.rowView(row, index)"
|
||||
>查 看
|
||||
</el-button>
|
||||
<el-button
|
||||
text
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
v-if="this.permission.user_edit"
|
||||
@click.stop="$refs.crud.rowEdit(row, index)"
|
||||
>编 辑
|
||||
</el-button>
|
||||
<el-dropdown @command="command => handleDropdownCommand(command, row, index)">
|
||||
<el-button text type="primary" icon="el-icon-setting">更 多 </el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="setLeader" v-if="userInfo.authority.includes('admin')">
|
||||
<el-icon><circle-close v-if="row.isLeader === 1" /><coordinate v-else /></el-icon>
|
||||
{{ row.isLeader === 1 ? '取消主管' : '设置主管' }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="grantRole" v-if="permission.user_role">
|
||||
<el-icon><user /></el-icon>
|
||||
角色配置
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="platformConfig"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>
|
||||
<el-icon><setting /></el-icon>
|
||||
平台配置
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="authLog" v-if="userInfo.authority.includes('admin')">
|
||||
<el-icon><document /></el-icon>
|
||||
认证日志
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="lockUser" v-if="userInfo.authority.includes('admin')">
|
||||
<el-icon><lock /></el-icon>
|
||||
锁定账号
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="resetPassword" v-if="permission.user_reset">
|
||||
<el-icon><refresh /></el-icon>
|
||||
密码重置
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="delete" v-if="this.permission.user_delete">
|
||||
<el-icon><delete /></el-icon>
|
||||
删除用户
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<template #tenantName="{ row }">
|
||||
<el-tag>{{ row.tenantName }}</el-tag>
|
||||
</template>
|
||||
<template #roleName="{ row }">
|
||||
<el-tag>{{ row.roleName }}</el-tag>
|
||||
</template>
|
||||
<template #deptName="{ row }">
|
||||
<el-tag>{{ row.deptName }}</el-tag>
|
||||
</template>
|
||||
<template #userTypeName="{ row }">
|
||||
<el-tag>{{ row.userTypeName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<!-- 用户角色配置 -->
|
||||
<el-dialog title="用户角色配置" append-to-body v-model="roleBox" width="345px">
|
||||
<el-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
style="margin-bottom: 12px; background: #f5f7fa; padding: 6px 10px; border-radius: 4px"
|
||||
>
|
||||
<span style="display: inline-flex; align-items: center">
|
||||
<el-switch
|
||||
v-model="roleLinked"
|
||||
active-text="节点联动"
|
||||
size="small"
|
||||
@change="handleLinkedChange('treeRole')"
|
||||
/>
|
||||
<el-tooltip content="开启后勾选父节点会自动勾选所有子节点,关闭则可独立勾选任意节点" placement="top">
|
||||
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-button-group>
|
||||
<el-button size="small" plain @click="handleSelectAll('treeRole', roleGrantList)"
|
||||
>全选</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
plain
|
||||
@click="handleInvertSelect('treeRole', roleGrantList, roleLinked)"
|
||||
>反选</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
</el-row>
|
||||
<el-tree
|
||||
:data="roleGrantList"
|
||||
show-checkbox
|
||||
:check-strictly="!roleLinked"
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
ref="treeRole"
|
||||
:default-checked-keys="roleTreeObj"
|
||||
:props="props"
|
||||
>
|
||||
</el-tree>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="roleBox = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitRole">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 用户数据导入 -->
|
||||
<el-dialog title="用户数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 用户平台配置(行级别) -->
|
||||
<el-dialog title="用户平台配置" append-to-body v-model="platformBox" width="600px">
|
||||
<avue-form ref="platformFormRef" :option="platformFormOption" v-model="platformForm" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="platformBox = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitPlatformConfig">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 认证日志组件 -->
|
||||
<auth-log ref="authLog" />
|
||||
<!-- 认证锁定配置组件 -->
|
||||
<auth-lock ref="authLock" @lock-success="onLoad(page)" />
|
||||
</basic-container>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getList,
|
||||
getUser,
|
||||
getUserPlatform,
|
||||
remove,
|
||||
update,
|
||||
updatePlatform,
|
||||
add,
|
||||
grant,
|
||||
resetPassword,
|
||||
auditPass,
|
||||
auditRefuse,
|
||||
setLeader,
|
||||
getLeaderList,
|
||||
} from '@/api/system/user';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { getRoleTree } from '@/api/system/role';
|
||||
import { getPostList } from '@/api/system/post';
|
||||
import { mapGetters } from 'vuex';
|
||||
import website from '@/config/website';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import func from '@/utils/func';
|
||||
import { sensitive } from '@/utils/sensitive';
|
||||
import { excelOption, platformFormOption, treeOption, userOption } from '@/option/system/user';
|
||||
import AuthLog from './authlog.vue';
|
||||
import AuthLock from './authlock.vue';
|
||||
|
||||
export default {
|
||||
components: { AuthLog, AuthLock },
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
search: {},
|
||||
sensitiveManager: null,
|
||||
roleBox: false,
|
||||
roleLinked: false,
|
||||
grantUserId: '',
|
||||
excelBox: false,
|
||||
platformBox: false,
|
||||
initFlag: true,
|
||||
auditMode: false,
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
},
|
||||
roleGrantList: [],
|
||||
roleTreeObj: [],
|
||||
treeDeptId: '',
|
||||
treeData: [],
|
||||
treeOption: treeOption,
|
||||
userOption: userOption(this),
|
||||
platformFormOption: platformFormOption,
|
||||
data: [],
|
||||
platformForm: {},
|
||||
excelForm: {},
|
||||
excelOption: excelOption,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.tenantId'() {
|
||||
if (this.form.tenantId !== '' && this.initFlag) {
|
||||
this.initData(this.form.tenantId);
|
||||
}
|
||||
},
|
||||
'excelForm.isCovered'() {
|
||||
if (this.excelForm.isCovered !== '') {
|
||||
const column = this.findColumn(this.excelOption.column, 'excelFile');
|
||||
column.action = `/blade-system/user/import-user?isCovered=${this.excelForm.isCovered}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.user_add, false),
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
editBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 非租户模式默认加载管理组数据
|
||||
if (!website.tenantMode) {
|
||||
this.initData(website.tenantId);
|
||||
} else {
|
||||
this.initData();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 创建脱敏工具实例
|
||||
this.sensitiveManager = sensitive.create({
|
||||
fields: ['phone', 'email'], // 配置需要脱敏的字段
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
nodeClick(data) {
|
||||
this.treeDeptId = data.id;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
handleDropdownCommand(command, row, index) {
|
||||
if (command === 'setLeader') {
|
||||
this.handleSetLeader(row);
|
||||
} else if (command === 'grantRole') {
|
||||
this.handleGrantForRow(row);
|
||||
} else if (command === 'authLog') {
|
||||
this.$refs.authLog.openUserAuthLog(row.id, row.name);
|
||||
} else if (command === 'lockUser') {
|
||||
this.$refs.authLock.openLockUser(row.id, row.account);
|
||||
} else if (command === 'platformConfig') {
|
||||
this.handlePlatformForRow(row);
|
||||
} else if (command === 'resetPassword') {
|
||||
this.handleResetForRow(row);
|
||||
} else if (command === 'delete') {
|
||||
this.$refs.crud.rowDel(row, index);
|
||||
}
|
||||
},
|
||||
handleResetForRow(row) {
|
||||
this.$confirm(`确定将账号【${row.account}】的密码重置为初始密码?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return resetPassword(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '密码重置成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDataManageCommand(command) {
|
||||
if (command === 'import') {
|
||||
this.handleImport();
|
||||
} else if (command === 'export') {
|
||||
this.handleExport();
|
||||
}
|
||||
},
|
||||
handleSetLeader(row) {
|
||||
const tip = row.isLeader === 1 ? '确定取消用户的主管职务?' : '确定设置用户为主管职务?';
|
||||
const message = row.isLeader === 1 ? '取消主管成功!' : '设置主管成功!';
|
||||
this.$confirm(tip, {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return setLeader(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: message,
|
||||
});
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
initData(tenantId) {
|
||||
getRoleTree(tenantId).then(res => {
|
||||
const column = this.findColumn(this.userOption.group, 'roleId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
getDeptTree(tenantId).then(res => {
|
||||
const column = this.findColumn(this.userOption.group, 'deptId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
getPostList(tenantId).then(res => {
|
||||
const column = this.findColumn(this.userOption.group, 'postId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
getLeaderList(tenantId).then(res => {
|
||||
const column = this.findColumn(this.userOption.group, 'leaderId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
submitRole() {
|
||||
const roleList = this.$refs.treeRole.getCheckedKeys().join(',');
|
||||
grant(this.grantUserId, roleList).then(() => {
|
||||
this.roleBox = false;
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.onLoad(this.page);
|
||||
});
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
row.deptId = func.join(row.deptId);
|
||||
row.roleId = func.join(row.roleId);
|
||||
row.postId = func.join(row.postId);
|
||||
row.leaderId = func.join(row.leaderId);
|
||||
add(row).then(
|
||||
() => {
|
||||
this.initFlag = false;
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
row.deptId = func.join(row.deptId);
|
||||
row.roleId = func.join(row.roleId);
|
||||
row.postId = func.join(row.postId);
|
||||
row.leaderId = func.join(row.leaderId);
|
||||
// 获取需要提交的数据
|
||||
const submitData = this.sensitiveManager.getSubmitData(row);
|
||||
update(submitData).then(
|
||||
() => {
|
||||
this.initFlag = false;
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.treeDeptId = '';
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleAudit() {
|
||||
this.auditMode = true;
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
handleAuditPass() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据通过审核?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return auditPass(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleAuditRefuse() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据拒绝审核?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return auditRefuse(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleAuditBack() {
|
||||
this.auditMode = false;
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
handleReset() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择账号密码重置为初始密码?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return resetPassword(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleGrant() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.roleTreeObj = [];
|
||||
this.roleLinked = false;
|
||||
this.grantUserId = this.ids;
|
||||
if (this.selectionList.length === 1) {
|
||||
this.roleTreeObj = this.selectionList[0].roleId.split(',');
|
||||
}
|
||||
getRoleTree().then(res => {
|
||||
this.roleGrantList = res.data.data;
|
||||
this.roleBox = true;
|
||||
this.$nextTick(() => this.applyCheckedKeys('treeRole', this.roleTreeObj));
|
||||
});
|
||||
},
|
||||
handleGrantForRow(row) {
|
||||
this.roleTreeObj = row.roleId ? row.roleId.split(',') : [];
|
||||
this.roleLinked = false;
|
||||
this.grantUserId = row.id;
|
||||
getRoleTree().then(res => {
|
||||
this.roleGrantList = res.data.data;
|
||||
this.roleBox = true;
|
||||
this.$nextTick(() => this.applyCheckedKeys('treeRole', this.roleTreeObj));
|
||||
});
|
||||
},
|
||||
getAllNodeKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
keys.push(node.id);
|
||||
if (node.children && node.children.length > 0) {
|
||||
keys = keys.concat(this.getAllNodeKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
getLeafKeys(nodes) {
|
||||
let keys = [];
|
||||
nodes.forEach(node => {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
keys.push(node.id);
|
||||
} else {
|
||||
keys = keys.concat(this.getLeafKeys(node.children));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
},
|
||||
handleSelectAll(treeRef, dataList) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
const allKeys = this.getAllNodeKeys(dataList);
|
||||
tree.setCheckedKeys(allKeys);
|
||||
},
|
||||
handleInvertSelect(treeRef, dataList, isLinked) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
const checkedKeys = new Set(tree.getCheckedKeys());
|
||||
if (isLinked) {
|
||||
const leafKeys = this.getLeafKeys(dataList);
|
||||
const invertedKeys = leafKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
} else {
|
||||
const allKeys = this.getAllNodeKeys(dataList);
|
||||
const invertedKeys = allKeys.filter(key => !checkedKeys.has(key));
|
||||
tree.setCheckedKeys(invertedKeys);
|
||||
}
|
||||
},
|
||||
handleLinkedChange(treeRef) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
const checkedKeys = tree.getCheckedKeys();
|
||||
const halfCheckedKeys = tree.getHalfCheckedKeys();
|
||||
this.$nextTick(() => {
|
||||
tree.setCheckedKeys([...checkedKeys, ...halfCheckedKeys]);
|
||||
});
|
||||
},
|
||||
// el-tree 会把 setCheckedKeys 的入参记录为默认勾选集并在树数据重载时回放,
|
||||
// 复用的树实例需在打开配置时整体覆盖勾选状态,避免残留上一次配置的勾选
|
||||
applyCheckedKeys(treeRef, keys) {
|
||||
const tree = this.$refs[treeRef];
|
||||
if (!tree) return;
|
||||
tree.setCheckedKeys(keys);
|
||||
},
|
||||
// ========== 平台配置(行级别) ==========
|
||||
handlePlatformForRow(row) {
|
||||
getUserPlatform(row.id).then(res => {
|
||||
this.platformForm = { ...res.data.data, account: row.account };
|
||||
this.platformBox = true;
|
||||
});
|
||||
},
|
||||
submitPlatformConfig() {
|
||||
updatePlatform(
|
||||
this.platformForm.id,
|
||||
this.platformForm.userType,
|
||||
this.platformForm.userExt
|
||||
).then(() => {
|
||||
this.platformBox = false;
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done, loading, column) {
|
||||
window.console.log(column);
|
||||
this.excelBox = false;
|
||||
this.refreshChange();
|
||||
done();
|
||||
},
|
||||
handleExport() {
|
||||
const account = func.toStr(this.search.account);
|
||||
const realName = func.toStr(this.search.realName);
|
||||
this.$confirm('是否导出用户数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob(
|
||||
`/blade-system/user/export-user?${
|
||||
this.website.tokenHeader
|
||||
}=${getToken()}&account=${account}&realName=${realName}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, `用户数据表${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(
|
||||
`/blade-system/user/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '用户数据模板.xlsx');
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getUser(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
if (this.form.hasOwnProperty('deptId')) {
|
||||
this.form.deptId = func.split(this.form.deptId);
|
||||
}
|
||||
if (this.form.hasOwnProperty('roleId')) {
|
||||
this.form.roleId = func.split(this.form.roleId);
|
||||
}
|
||||
if (this.form.hasOwnProperty('postId')) {
|
||||
this.form.postId = func.split(this.form.postId);
|
||||
}
|
||||
if (this.form.hasOwnProperty('leaderId')) {
|
||||
this.form.leaderId = func.split(this.form.leaderId);
|
||||
}
|
||||
if (this.form.sex === -1) {
|
||||
this.form.sex = '';
|
||||
}
|
||||
// 保存初始脱敏数据
|
||||
this.sensitiveManager.saveInitialData(this.form);
|
||||
});
|
||||
}
|
||||
this.initFlag = true;
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
Object.assign(params, this.query, { status: this.auditMode ? 0 : 1 }),
|
||||
this.treeDeptId
|
||||
).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.box {
|
||||
height: 800px;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.box .el-scrollbar__wrap {
|
||||
overflow: scroll;
|
||||
}
|
||||
</style>
|
||||
485
src/views/system/userinfo.vue
Normal file
485
src/views/system/userinfo.vue
Normal file
@@ -0,0 +1,485 @@
|
||||
<template>
|
||||
<div>
|
||||
<basic-container>
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<!-- 个人信息 Tab -->
|
||||
<el-tab-pane label="个人信息" name="info">
|
||||
<el-form ref="infoForm" :model="infoForm" label-width="100px">
|
||||
<el-form-item label="头像">
|
||||
<div class="avatar-upload-container">
|
||||
<div class="avatar-wrapper" v-if="infoForm.avatar">
|
||||
<img :src="infoForm.avatar" alt="用户头像" class="avatar" />
|
||||
<div class="avatar-overlay">
|
||||
<el-icon class="avatar-action" @click="previewAvatar"><View /></el-icon>
|
||||
<el-icon class="avatar-action" @click="deleteAvatar"><Delete /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
v-else
|
||||
class="avatar-uploader"
|
||||
action="/api/blade-resource/oss/endpoint/put-file"
|
||||
:headers="uploadHeaders"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
accept=".jpg,.jpeg,.png,.gif"
|
||||
>
|
||||
<el-icon class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
<div class="el-upload__tip">支持 JPG/PNG/GIF 格式,文件大小不超过 1MB</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="infoForm.realName" placeholder="请输入姓名">
|
||||
<template #prefix>
|
||||
<el-icon><Bell /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="infoForm.name" placeholder="请输入用户名">
|
||||
<template #prefix>
|
||||
<el-icon><User /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="infoForm.phone" placeholder="请输入手机号">
|
||||
<template #prefix>
|
||||
<el-icon><Iphone /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="infoForm.email" placeholder="请输入邮箱">
|
||||
<template #prefix>
|
||||
<el-icon><Message /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="button-group">
|
||||
<el-button type="primary" @click="submitInfo" :loading="loading">
|
||||
<el-icon><Check /></el-icon>
|
||||
<span>提交</span>
|
||||
</el-button>
|
||||
<el-button @click="resetForm">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
<span>清空</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 修改密码 Tab -->
|
||||
<el-tab-pane label="修改密码" name="password">
|
||||
<el-form ref="passwordForm" :model="passwordForm" label-width="100px">
|
||||
<el-form-item label="原密码">
|
||||
<el-input
|
||||
v-model="passwordForm.oldPassword"
|
||||
type="password"
|
||||
placeholder="请输入原密码"
|
||||
show-password
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Lock /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码">
|
||||
<el-input
|
||||
v-model="passwordForm.newPassword"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
show-password
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Lock /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码">
|
||||
<el-input
|
||||
v-model="passwordForm.newPassword1"
|
||||
type="password"
|
||||
placeholder="请确认新密码"
|
||||
show-password
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Lock /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="button-group">
|
||||
<el-button type="primary" @click="submitPassword" :loading="loading">
|
||||
<el-icon><Check /></el-icon>
|
||||
<span>提交</span>
|
||||
</el-button>
|
||||
<el-button @click="resetForm">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
<span>清空</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</basic-container>
|
||||
<!-- 头像预览组件 -->
|
||||
<el-image-viewer
|
||||
v-if="showAvatarPreview"
|
||||
:url-list="avatarPreviewList"
|
||||
:initial-index="0"
|
||||
:hide-on-click-modal="true"
|
||||
:teleported="true"
|
||||
@close="showAvatarPreview = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUserInfo, updateInfo, updatePassword } from '@/api/system/user';
|
||||
import md5 from 'js-md5';
|
||||
import { sensitive } from '@/utils/sensitive';
|
||||
import {
|
||||
Plus,
|
||||
Bell,
|
||||
User,
|
||||
Iphone,
|
||||
Message,
|
||||
Lock,
|
||||
Check,
|
||||
RefreshRight,
|
||||
View,
|
||||
Delete,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { ElImageViewer } from 'element-plus';
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ElImageViewer,
|
||||
Plus,
|
||||
Bell,
|
||||
User,
|
||||
Iphone,
|
||||
Message,
|
||||
Lock,
|
||||
Check,
|
||||
RefreshRight,
|
||||
View,
|
||||
Delete,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'info',
|
||||
loading: false,
|
||||
infoForm: {
|
||||
id: '',
|
||||
avatar: '',
|
||||
name: '',
|
||||
realName: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
},
|
||||
passwordForm: {
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword1: '',
|
||||
},
|
||||
sensitiveManager: null,
|
||||
showAvatarPreview: false,
|
||||
avatarPreviewList: [],
|
||||
uploadHeaders: {
|
||||
'Blade-Auth': `bearer ${getToken()}`,
|
||||
'Blade-Requested-With': 'BladeHttpRequest',
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化脱敏工具
|
||||
this.sensitiveManager = sensitive.create({
|
||||
fields: ['phone', 'email'],
|
||||
});
|
||||
// 初始化时根据默认tab加载数据
|
||||
if (this.activeTab === 'info') {
|
||||
this.loadUserInfo();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Tab切换处理
|
||||
handleTabChange(name) {
|
||||
if (name === 'info') {
|
||||
this.loadUserInfo();
|
||||
}
|
||||
},
|
||||
// 加载用户信息
|
||||
loadUserInfo() {
|
||||
getUserInfo().then(res => {
|
||||
const user = res.data.data;
|
||||
this.infoForm = {
|
||||
id: user.id,
|
||||
avatar: user.avatar,
|
||||
name: user.name,
|
||||
realName: user.realName,
|
||||
phone: user.phone,
|
||||
email: user.email,
|
||||
};
|
||||
// 保存初始脱敏数据
|
||||
this.sensitiveManager.saveInitialData(this.infoForm);
|
||||
});
|
||||
},
|
||||
// 提交个人信息
|
||||
submitInfo() {
|
||||
this.loading = true;
|
||||
const submitData = this.sensitiveManager.getSubmitData(this.infoForm);
|
||||
updateInfo(submitData).then(
|
||||
res => {
|
||||
if (res.data.success) {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '修改信息成功!',
|
||||
});
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'error',
|
||||
message: res.data.msg,
|
||||
});
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
// 提交密码修改
|
||||
submitPassword() {
|
||||
this.loading = true;
|
||||
updatePassword(
|
||||
md5(this.passwordForm.oldPassword),
|
||||
md5(this.passwordForm.newPassword),
|
||||
md5(this.passwordForm.newPassword1)
|
||||
).then(
|
||||
res => {
|
||||
if (res.data.success) {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '修改密码成功!',
|
||||
});
|
||||
// 清空密码表单
|
||||
this.passwordForm = {
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword1: '',
|
||||
};
|
||||
} else {
|
||||
this.$message({
|
||||
type: 'error',
|
||||
message: res.data.msg,
|
||||
});
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
// 头像上传成功处理
|
||||
handleAvatarSuccess(response) {
|
||||
if (response && response.data) {
|
||||
this.infoForm.avatar = response.data.link || response.data;
|
||||
}
|
||||
},
|
||||
// 头像上传前验证
|
||||
beforeAvatarUpload(file) {
|
||||
const isValidType = ['image/jpeg', 'image/png', 'image/gif'].includes(file.type);
|
||||
const isLt1M = file.size / 1024 < 1024;
|
||||
|
||||
if (!isValidType) {
|
||||
this.$message.error('上传头像只支持 JPG/PNG/GIF 格式!');
|
||||
}
|
||||
if (!isLt1M) {
|
||||
this.$message.error('上传头像大小不能超过 1MB!');
|
||||
}
|
||||
return isValidType && isLt1M;
|
||||
},
|
||||
// 重置表单
|
||||
resetForm() {
|
||||
if (this.activeTab === 'info') {
|
||||
// 清空个人信息表单,但保留id
|
||||
const id = this.infoForm.id;
|
||||
this.infoForm = {
|
||||
id: id,
|
||||
avatar: '',
|
||||
name: '',
|
||||
realName: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
};
|
||||
} else if (this.activeTab === 'password') {
|
||||
// 清空密码表单
|
||||
this.passwordForm = {
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
newPassword1: '',
|
||||
};
|
||||
}
|
||||
},
|
||||
// 预览头像
|
||||
previewAvatar() {
|
||||
if (this.infoForm.avatar) {
|
||||
this.avatarPreviewList = [this.infoForm.avatar];
|
||||
this.showAvatarPreview = true;
|
||||
} else {
|
||||
this.$message.warning('暂无头像可预览');
|
||||
}
|
||||
},
|
||||
// 删除头像
|
||||
deleteAvatar() {
|
||||
this.$confirm('确定要删除头像吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
this.infoForm.avatar = '';
|
||||
this.$message.success('头像已删除');
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.avatar-upload-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-wrapper:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.avatar-wrapper .avatar {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: block;
|
||||
border-radius: 12px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.6) 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.avatar-wrapper:hover .avatar-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.avatar-action {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avatar-action:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.avatar-uploader :deep(.el-upload) {
|
||||
border: 2px dashed #d9d9d9;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background-color: #fafafa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avatar-uploader :deep(.el-upload:hover) {
|
||||
border-color: var(--el-color-primary);
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 40px;
|
||||
color: #8c939d;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-uploader :deep(.el-upload:hover) .avatar-uploader-icon {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
font-size: 12px;
|
||||
color: #969799;
|
||||
margin-top: 10px;
|
||||
line-height: 1.5;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
9
src/views/test.vue
Normal file
9
src/views/test.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<basic-container>测试页</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
588
src/views/tool/code.vue
Normal file
588
src/views/tool/code.vue
Normal file
@@ -0,0 +1,588 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
v-model:page="page"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.code_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="将选中的多重配置集合批量生成代码"
|
||||
placement="top"
|
||||
>
|
||||
<el-button type="primary" plain icon="el-icon-refresh" @click="handleBuild"
|
||||
>代码批量生成
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="不通过多重配置直接生成最简化的CRUD代码"
|
||||
placement="top"
|
||||
>
|
||||
<el-button type="info" plain icon="el-icon-cpu" @click="handleCodeGen"
|
||||
>代码快速生成
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #menu-right>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="通过可视化表单设计器快速生成单表模块"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
plain
|
||||
v-if="userInfo.authority.includes('administrator')"
|
||||
:loading="!componentLoaded"
|
||||
icon="el-icon-connection"
|
||||
@click="handleFormSetting"
|
||||
>可视化表单设计器
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="配置代码生成的默认参数后自动绑定"
|
||||
placement="top"
|
||||
>
|
||||
<el-button
|
||||
plain
|
||||
v-if="userInfo.authority.includes('administrator')"
|
||||
icon="el-icon-message-box"
|
||||
@click="handleCodeSetting"
|
||||
>默认配置管理
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-document-copy"
|
||||
v-if="permission.code_edit"
|
||||
class="none-border"
|
||||
@click.stop="handleCopy(scope.row)"
|
||||
>复制
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-refresh"
|
||||
v-if="userInfo.authority.includes('administrator')"
|
||||
class="none-border"
|
||||
@click.stop="handleBuildSingle(scope.row)"
|
||||
>生成
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-drawer title="代码快速生成" append-to-body v-model="codeGenBox" direction="rtl" size="50%">
|
||||
<avue-form :option="genOption" v-model="genForm" @submit="handleCodeGenSubmit" />
|
||||
</el-drawer>
|
||||
<el-drawer
|
||||
title="可视化表单管理"
|
||||
append-to-body
|
||||
v-model="formSettingBox"
|
||||
direction="rtl"
|
||||
size="1000px"
|
||||
>
|
||||
<form-setting></form-setting>
|
||||
</el-drawer>
|
||||
<el-drawer
|
||||
title="默认配置管理"
|
||||
append-to-body
|
||||
v-model="codeSettingBox"
|
||||
direction="rtl"
|
||||
size="1000px"
|
||||
>
|
||||
<code-setting></code-setting>
|
||||
</el-drawer>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getCode, build, remove, add, update, copy, buildFast } from '@/api/tool/code';
|
||||
import { getEnableDetail, getTableForm } from '@/api/tool/codesetting';
|
||||
import {
|
||||
getDetail as modelDetail,
|
||||
getTableInfoByName,
|
||||
getTableList,
|
||||
prototypeDetail,
|
||||
} from '@/api/tool/model';
|
||||
import { codeOption, genOption } from '@/option/tool/code';
|
||||
import { validatejson, validatenull } from '@/utils/validate';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getMenuTree } from '@/api/system/menu';
|
||||
import { loadFormDesignModule, loadFormModule } from '@/utils/module';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
genForm: {},
|
||||
selectionList: [],
|
||||
componentLoaded: false,
|
||||
loading: true,
|
||||
loadingOption: {
|
||||
lock: true,
|
||||
text: '物理表读取中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
},
|
||||
addMode: false,
|
||||
codeGenBox: false,
|
||||
codeSettingBox: false,
|
||||
codeSetting: {},
|
||||
formSettingBox: false,
|
||||
query: {},
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: codeOption,
|
||||
genOption: genOption,
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 懒加载表单设计器模块
|
||||
Promise.all([loadFormModule(this.$app), loadFormDesignModule(this.$app)]).then(() => {
|
||||
this.componentLoaded = true;
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
'form.modelId'() {
|
||||
if (!validatenull(this.form.modelId) && this.addMode) {
|
||||
// 获取数据模型信息
|
||||
modelDetail(this.form.modelId).then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
const { modelName, modelTable, modelCode } = result.data;
|
||||
const lowerModelCode = modelCode.toLowerCase();
|
||||
//if (validatenull(this.form.tablePrefix)) {
|
||||
this.form.tablePrefix = modelTable.split('_')[0] + '_';
|
||||
//}
|
||||
//if (validatenull(this.form.tableName)) {
|
||||
this.form.tableName = modelTable;
|
||||
//}
|
||||
//if (validatenull(this.form.codeName)) {
|
||||
this.form.codeName = modelName;
|
||||
//}
|
||||
if (validatenull(this.form.serviceName)) {
|
||||
this.form.serviceName = `blade-${lowerModelCode}`;
|
||||
}
|
||||
//if (validatenull(this.form.pkName)) {
|
||||
this.form.pkName = 'id';
|
||||
//}
|
||||
if (validatenull(this.form.packageName)) {
|
||||
this.form.packageName = `org.springblade.${lowerModelCode}`;
|
||||
}
|
||||
//if (validatenull(this.form.subFkId) && !validatenull(this.form.tablePrefix)) {
|
||||
this.form.subFkId = modelTable.replace(this.form.tablePrefix, '') + '_id';
|
||||
//}
|
||||
|
||||
// 获取数据原型信息
|
||||
prototypeDetail(this.form.modelId).then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
const columnTreeId = this.findColumn(this.option.group, 'treeId');
|
||||
const columnTreePid = this.findColumn(this.option.group, 'treePid');
|
||||
const columnTreeName = this.findColumn(this.option.group, 'treeName');
|
||||
columnTreeId.dicData = result.data;
|
||||
columnTreePid.dicData = result.data;
|
||||
columnTreeName.dicData = result.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
'form.templateType'() {
|
||||
// 模版类型
|
||||
const type = this.form.templateType;
|
||||
|
||||
// 主子表字段显隐
|
||||
const columnSubModelId = this.findColumn(this.option.group, 'subModelId');
|
||||
const columnSubFkId = this.findColumn(this.option.group, 'subFkId');
|
||||
columnSubModelId.display = type === 'sub';
|
||||
columnSubFkId.display = type === 'sub';
|
||||
|
||||
// 树表字段显隐
|
||||
const columnTreeId = this.findColumn(this.option.group, 'treeId');
|
||||
const columnTreePid = this.findColumn(this.option.group, 'treePid');
|
||||
const columnTreeName = this.findColumn(this.option.group, 'treeName');
|
||||
columnTreeId.display = type === 'tree';
|
||||
columnTreePid.display = type === 'tree';
|
||||
columnTreeName.display = type === 'tree';
|
||||
},
|
||||
'genForm.datasourceId'() {
|
||||
if (!validatenull(this.genForm.datasourceId)) {
|
||||
const fullLoading = this.$loading(this.loadingOption);
|
||||
getTableList(this.genForm.datasourceId)
|
||||
.then(res => {
|
||||
const column = this.findColumn(this.genOption.column, 'modelTable');
|
||||
column.dicData = res.data.data;
|
||||
fullLoading.close();
|
||||
})
|
||||
.catch(() => {
|
||||
fullLoading.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
'genForm.modelTable'() {
|
||||
if (!validatenull(this.genForm.modelTable)) {
|
||||
const fullLoading = this.$loading(this.loadingOption);
|
||||
getTableInfoByName(this.genForm.modelTable, this.genForm.datasourceId)
|
||||
.then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
// 赋默认值
|
||||
const { comment, entityName } = result.data;
|
||||
this.genForm.modelClass = entityName;
|
||||
this.genForm.modelCode = entityName.replace(/^\S/, s => s.toLowerCase());
|
||||
const lowerModelCode = this.genForm.modelCode.toLowerCase();
|
||||
this.genForm.tablePrefix = this.genForm.modelTable.split('_')[0] + '_';
|
||||
this.genForm.tableName = this.genForm.modelTable;
|
||||
this.genForm.codeName = comment;
|
||||
this.genForm.pkName = 'id';
|
||||
if (validatenull(this.genForm.serviceName)) {
|
||||
this.genForm.serviceName = `blade-${lowerModelCode}`;
|
||||
}
|
||||
if (validatenull(this.genForm.packageName)) {
|
||||
this.genForm.packageName = `org.springblade.${lowerModelCode}`;
|
||||
}
|
||||
// 字段显隐
|
||||
const columnModelForm = this.findColumn(this.genOption.column, 'modelForm');
|
||||
const columnModelClass = this.findColumn(this.genOption.column, 'modelClass');
|
||||
const columnModelCode = this.findColumn(this.genOption.column, 'modelCode');
|
||||
const columnTablePrefix = this.findColumn(this.genOption.column, 'tablePrefix');
|
||||
const columnTableName = this.findColumn(this.genOption.column, 'tableName');
|
||||
const columnCodeName = this.findColumn(this.genOption.column, 'codeName');
|
||||
const columnPkName = this.findColumn(this.genOption.column, 'pkName');
|
||||
this.genForm.modelForm = '';
|
||||
getTableForm(this.genForm.modelTable).then(res => {
|
||||
columnModelForm.dicData = res.data.data;
|
||||
});
|
||||
columnModelForm.display = true;
|
||||
columnModelClass.display = true;
|
||||
columnModelCode.display = true;
|
||||
columnTablePrefix.display = true;
|
||||
columnTableName.display = true;
|
||||
columnCodeName.display = true;
|
||||
columnPkName.display = true;
|
||||
fullLoading.close();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
fullLoading.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.code_add, false),
|
||||
viewBtn: this.validData(this.permission.code_view, false),
|
||||
delBtn: this.validData(this.permission.code_delete, false),
|
||||
editBtn: this.validData(this.permission.code_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
return getMenuTree().then(res => {
|
||||
const column = this.findColumn(this.option.group, 'menuId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
initGenData() {
|
||||
getMenuTree().then(res => {
|
||||
const column = this.findColumn(this.genOption.column, 'menuId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
getEnableDetail().then(res => {
|
||||
if (validatejson(res.data.data.settings)) {
|
||||
this.codeSetting = JSON.parse(res.data.data.settings);
|
||||
this.genForm = {
|
||||
menuId: this.codeSetting.menuId,
|
||||
serviceName: this.codeSetting.serviceName,
|
||||
packageName: this.codeSetting.packageName,
|
||||
baseMode: this.codeSetting.baseMode,
|
||||
wrapMode: this.codeSetting.wrapMode,
|
||||
feignMode: this.codeSetting.feignMode,
|
||||
codeStyle: this.codeSetting.codeStyle,
|
||||
apiPath: this.codeSetting.apiPath,
|
||||
webPath: this.codeSetting.webPath,
|
||||
};
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '默认配置加载成功',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
initSetting() {
|
||||
return getEnableDetail().then(res => {
|
||||
if (validatejson(res.data.data.settings)) {
|
||||
this.codeSetting = JSON.parse(res.data.data.settings);
|
||||
this.form = {
|
||||
menuId: this.codeSetting.menuId,
|
||||
serviceName: this.codeSetting.serviceName,
|
||||
packageName: this.codeSetting.packageName,
|
||||
baseMode: this.codeSetting.baseMode,
|
||||
wrapMode: this.codeSetting.wrapMode,
|
||||
feignMode: this.codeSetting.feignMode,
|
||||
codeStyle: this.codeSetting.codeStyle,
|
||||
apiPath: this.codeSetting.apiPath,
|
||||
webPath: this.codeSetting.webPath,
|
||||
};
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '默认配置加载成功',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
initCode(id) {
|
||||
return getCode(id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleCodeGen() {
|
||||
this.initGenData();
|
||||
this.codeGenBox = true;
|
||||
},
|
||||
handleFormSetting() {
|
||||
this.formSettingBox = true;
|
||||
},
|
||||
handleCodeSetting() {
|
||||
this.codeSettingBox = true;
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleBuild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('是否生成选中模块的代码?', {
|
||||
title: '代码生成确认',
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
return build(this.ids).then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleBuildSingle(row) {
|
||||
this.$confirm(`是否生成选中模块 [${row.codeName}] 的代码?`, {
|
||||
title: '代码生成确认',
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
return build(row.id).then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleCodeGenSubmit(form, done) {
|
||||
buildFast(form).then(
|
||||
() => {
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '代码生成成功!',
|
||||
});
|
||||
done();
|
||||
this.codeGenBox = false;
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
handleCopy(row) {
|
||||
copy(row.id).then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '复制成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
async beforeOpen(done, type) {
|
||||
const tasks = [];
|
||||
|
||||
if (['add'].includes(type)) {
|
||||
this.addMode = true;
|
||||
tasks.push(this.initSetting());
|
||||
}
|
||||
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
tasks.push(this.initData());
|
||||
}
|
||||
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
this.addMode = false;
|
||||
tasks.push(this.initCode(this.form.id));
|
||||
}
|
||||
|
||||
await Promise.all(tasks);
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
281
src/views/tool/codesetting.vue
Normal file
281
src/views/tool/codesetting.vue
Normal file
@@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
v-model:search="search"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-check"
|
||||
@click.stop="handleEnable(scope.row)"
|
||||
>启 用
|
||||
</el-button>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag>{{ row.status === 1 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove, enable } from '@/api/tool/codesetting';
|
||||
import { getMenuTree } from '@/api/system/menu';
|
||||
import option from '@/option/tool/codesetting';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
search: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: option,
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: true,
|
||||
viewBtn: true,
|
||||
delBtn: true,
|
||||
editBtn: true,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getMenuTree().then(res => {
|
||||
const column = this.findColumn(this.option.column, 'menuId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
rowSettings(row) {
|
||||
return JSON.stringify({
|
||||
serviceName: row.serviceName,
|
||||
packageName: row.packageName,
|
||||
menuId: row.menuId,
|
||||
baseMode: row.baseMode,
|
||||
wrapMode: row.wrapMode,
|
||||
feignMode: row.feignMode,
|
||||
codeStyle: row.codeStyle,
|
||||
apiPath: row.apiPath,
|
||||
webPath: row.webPath,
|
||||
});
|
||||
},
|
||||
rowSettingsForm(data) {
|
||||
const row = JSON.parse(data);
|
||||
return {
|
||||
serviceName: row.serviceName,
|
||||
packageName: row.packageName,
|
||||
menuId: row.menuId,
|
||||
baseMode: row.baseMode,
|
||||
wrapMode: row.wrapMode,
|
||||
feignMode: row.feignMode,
|
||||
codeStyle: row.codeStyle,
|
||||
apiPath: row.apiPath,
|
||||
webPath: row.webPath,
|
||||
};
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const data = {
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
settings: this.rowSettings(row),
|
||||
};
|
||||
add(data).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const data = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
settings: this.rowSettings(row),
|
||||
};
|
||||
update(data).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleEnable(row) {
|
||||
this.$confirm('是否确定启用这条配置?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return enable(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = {
|
||||
...this.form,
|
||||
...this.rowSettingsForm(res.data.data.settings),
|
||||
};
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
|
||||
let values = {
|
||||
...params,
|
||||
...this.query,
|
||||
category: 1,
|
||||
};
|
||||
|
||||
getList(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
409
src/views/tool/datasource.vue
Normal file
409
src/views/tool/datasource.vue
Normal file
@@ -0,0 +1,409 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.datasource_delete"
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove } from '@/api/tool/datasource';
|
||||
import { mapGetters } from 'vuex';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 900,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '名称',
|
||||
prop: 'name',
|
||||
width: 120,
|
||||
span: 24,
|
||||
search: true,
|
||||
gridRow: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入数据源名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据类型',
|
||||
type: 'radio',
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 120,
|
||||
searchLabelWidth: 100,
|
||||
row: true,
|
||||
gridRow: true,
|
||||
display: false,
|
||||
dicData: [
|
||||
{
|
||||
label: 'jdbc',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
dataType: 'number',
|
||||
prop: 'category',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择分类',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'YAML',
|
||||
prop: 'shardingConfig',
|
||||
span: 24,
|
||||
minRows: 5,
|
||||
hide: true,
|
||||
display: false,
|
||||
gridRow: true,
|
||||
type: 'textarea',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入YAML配置',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '驱动类',
|
||||
prop: 'driverClass',
|
||||
type: 'select',
|
||||
span: 24,
|
||||
gridRow: true,
|
||||
dicData: [
|
||||
{
|
||||
label: 'com.mysql.cj.jdbc.Driver',
|
||||
value: 'com.mysql.cj.jdbc.Driver',
|
||||
},
|
||||
{
|
||||
label: 'org.postgresql.Driver',
|
||||
value: 'org.postgresql.Driver',
|
||||
},
|
||||
{
|
||||
label: 'oracle.jdbc.OracleDriver',
|
||||
value: 'oracle.jdbc.OracleDriver',
|
||||
},
|
||||
{
|
||||
label: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
|
||||
value: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
|
||||
},
|
||||
{
|
||||
label: 'dm.jdbc.driver.DmDriver',
|
||||
value: 'dm.jdbc.driver.DmDriver',
|
||||
},
|
||||
{
|
||||
label: 'com.yashandb.jdbc.Driver',
|
||||
value: 'com.yashandb.jdbc.Driver',
|
||||
},
|
||||
{
|
||||
label: 'com.kingbase8.Driver',
|
||||
value: 'com.kingbase8.Driver',
|
||||
},
|
||||
],
|
||||
width: 200,
|
||||
display: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入驱动类',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '连接地址',
|
||||
prop: 'url',
|
||||
span: 24,
|
||||
display: true,
|
||||
gridRow: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入连接地址',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '用户名',
|
||||
prop: 'username',
|
||||
width: 120,
|
||||
display: true,
|
||||
gridRow: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入用户名',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
prop: 'password',
|
||||
hide: true,
|
||||
display: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
span: 24,
|
||||
minRows: 3,
|
||||
hide: true,
|
||||
type: 'textarea',
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
driverUrlTemplates: {
|
||||
'com.mysql.cj.jdbc.Driver':
|
||||
'jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true',
|
||||
'org.postgresql.Driver': 'jdbc:postgresql://127.0.0.1:5432/bladex',
|
||||
'oracle.jdbc.OracleDriver': 'jdbc:oracle:thin:@//127.0.0.1:1521/ORCLPDB',
|
||||
'com.microsoft.sqlserver.jdbc.SQLServerDriver':
|
||||
'jdbc:sqlserver://127.0.0.1:1433;DatabaseName=bladex',
|
||||
'dm.jdbc.driver.DmDriver':
|
||||
'jdbc:dm://127.0.0.1:5236/bladex?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8',
|
||||
'com.yashandb.jdbc.Driver': 'jdbc:yasdb://127.0.0.1:1688/bladex',
|
||||
'com.kingbase8.Driver': 'jdbc:kingbase8://localhost:4321/bladex',
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.category'() {
|
||||
const category = func.toInt(this.form.category);
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'driverClass') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'url') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'username') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'password') {
|
||||
item.display = category === 1;
|
||||
}
|
||||
if (item.prop === 'shardingConfig') {
|
||||
item.display = category === 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
'form.driverClass'(newDriverClass) {
|
||||
// 当驱动类发生变化时,自动设置对应的默认URL
|
||||
if (newDriverClass && this.driverUrlTemplates[newDriverClass]) {
|
||||
// 只在URL为空或者是其他驱动的默认URL时才自动填充
|
||||
const isDefaultUrl =
|
||||
!this.form.url || Object.values(this.driverUrlTemplates).includes(this.form.url);
|
||||
if (isDefaultUrl) {
|
||||
this.form.url = this.driverUrlTemplates[newDriverClass];
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.datasource_add, false),
|
||||
viewBtn: this.validData(this.permission.datasource_view, false),
|
||||
delBtn: this.validData(this.permission.datasource_delete, false),
|
||||
editBtn: this.validData(this.permission.datasource_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
419
src/views/tool/formsetting.vue
Normal file
419
src/views/tool/formsetting.vue
Normal file
@@ -0,0 +1,419 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
v-model:search="search"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button type="primary" text icon="el-icon-setting" @click.stop="handleDesign(scope.row)"
|
||||
>设 计
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog
|
||||
title="可视化表单设计"
|
||||
v-model="designBox"
|
||||
:fullscreen="true"
|
||||
:before-close="handleDesignClose"
|
||||
append-to-body
|
||||
>
|
||||
<nf-form-design
|
||||
ref="formDesign"
|
||||
style="height: 90vh"
|
||||
:options="options"
|
||||
:toolbar="['clear', 'preview', 'import', 'generate']"
|
||||
:includeFields="[
|
||||
'group', // 分组
|
||||
'dynamic', // 子表单
|
||||
'title', // 标题
|
||||
'table', // 表格
|
||||
'input', // 输入框
|
||||
'password', // 密码
|
||||
'textarea', // 文本域
|
||||
'number', // 数字
|
||||
'ueditor', // 富文本
|
||||
'map', // 地图选择器
|
||||
'radio', // 单选
|
||||
'checkbox', // 多选
|
||||
'select', // 下拉选择
|
||||
'tree', // 树形选择
|
||||
'cascader', // 级联选择
|
||||
'table-select', // 表格选择
|
||||
'upload', // 上传
|
||||
'year', // 年
|
||||
'month', // 月
|
||||
'week', // 周
|
||||
'date', // 日期
|
||||
'time', // 时间
|
||||
'datetime', // 日期时间
|
||||
'daterange', // 日期范围
|
||||
'datetimerange', // 日期时间范围
|
||||
'timerange', // 时间范围
|
||||
'sign', // 签名
|
||||
'switch', // 开关
|
||||
'rate', // 评价
|
||||
'color', // 颜色
|
||||
'icon', // 图标
|
||||
'slider', // 滑块
|
||||
]"
|
||||
:is-crud="isCrud"
|
||||
>
|
||||
<template #toolbar>
|
||||
<el-button
|
||||
style="padding: 0"
|
||||
text
|
||||
type="primary"
|
||||
size="default"
|
||||
icon="el-icon-download"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</nf-form-design>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, getDetail, add, update, remove, getTablePrototype } from '@/api/tool/codesetting';
|
||||
import option from '@/option/tool/formsetting';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
import { getTableInfoByName, getTableList } from '@/api/tool/model';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
search: {},
|
||||
loading: true,
|
||||
loadingOption: {
|
||||
lock: true,
|
||||
text: '物理表读取中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
},
|
||||
designId: '',
|
||||
designBox: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: option,
|
||||
data: [],
|
||||
modelTable: '',
|
||||
datasourceId: '',
|
||||
isCrud: true,
|
||||
options: {},
|
||||
// 默认不需要显示的字段名
|
||||
hideFields: [
|
||||
'id',
|
||||
'tenant_id',
|
||||
'create_user',
|
||||
'create_dept',
|
||||
'create_time',
|
||||
'update_user',
|
||||
'update_time',
|
||||
'status',
|
||||
'is_deleted',
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: true,
|
||||
viewBtn: true,
|
||||
delBtn: true,
|
||||
editBtn: true,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'form.datasourceId'() {
|
||||
if (!validatenull(this.form.datasourceId)) {
|
||||
this.datasourceId = this.form.datasourceId;
|
||||
const fullLoading = this.$loading(this.loadingOption);
|
||||
getTableList(this.form.datasourceId)
|
||||
.then(res => {
|
||||
const column = this.findColumn(this.option.column, 'modelTable');
|
||||
column.dicData = res.data.data;
|
||||
fullLoading.close();
|
||||
})
|
||||
.catch(() => {
|
||||
fullLoading.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
'form.modelTable'() {
|
||||
if (!validatenull(this.form.modelTable)) {
|
||||
this.modelTable = this.form.modelTable;
|
||||
const fullLoading = this.$loading(this.loadingOption);
|
||||
getTableInfoByName(this.form.modelTable, this.form.datasourceId)
|
||||
.then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
// 赋默认值
|
||||
const { comment } = result.data;
|
||||
this.form.name = comment;
|
||||
this.form.code = this.form.modelTable;
|
||||
fullLoading.close();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
fullLoading.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
const data = {
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
category: 2,
|
||||
settings: row.settings,
|
||||
};
|
||||
add(data).then(
|
||||
res => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
let design = {
|
||||
id: res.data.data.id,
|
||||
name: data.name,
|
||||
code: data.code,
|
||||
};
|
||||
this.handleDesign(design);
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const data = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
settings: row.settings,
|
||||
};
|
||||
update(data).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
handleDesign(design) {
|
||||
this.designBox = true;
|
||||
this.designId = design.id;
|
||||
getDetail(design.id).then(res => {
|
||||
const settings = res.data.data.settings;
|
||||
if (!validatenull(settings)) {
|
||||
this.options = JSON.parse(settings);
|
||||
} else if (!validatenull(this.modelTable) && !validatenull(this.datasourceId)) {
|
||||
getTablePrototype(this.modelTable, this.datasourceId).then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
const column = result.data
|
||||
.filter(field => {
|
||||
return !validatenull(field.name) && !this.hideFields.includes(field.name);
|
||||
})
|
||||
.map(field => {
|
||||
return {
|
||||
type: 'input',
|
||||
label: field.comment,
|
||||
display: true,
|
||||
prop: func.camelCaseString(field.name),
|
||||
};
|
||||
});
|
||||
this.options = {
|
||||
column: column,
|
||||
};
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDesignClose() {
|
||||
this.modelTable = '';
|
||||
this.datasourceId = '';
|
||||
this.options = {};
|
||||
this.designBox = false;
|
||||
},
|
||||
// 可视化表单提交
|
||||
handleSubmit() {
|
||||
// json, string, app
|
||||
this.$refs.formDesign.getData('json').then(data => {
|
||||
// 表单/表格option
|
||||
console.log(data);
|
||||
const row = {
|
||||
id: this.designId,
|
||||
settings: JSON.stringify(data),
|
||||
};
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.designBox = false;
|
||||
},
|
||||
error => {
|
||||
console.log(error);
|
||||
}
|
||||
);
|
||||
this.$refs.formDesign.getChangeList().then(changeList => {
|
||||
// 字段prop, type, label修改记录
|
||||
console.log(changeList);
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = {
|
||||
...this.form,
|
||||
};
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
|
||||
let values = {
|
||||
...params,
|
||||
...this.query,
|
||||
category: 2,
|
||||
};
|
||||
|
||||
getList(page.currentPage, page.pageSize, values).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
393
src/views/tool/model.vue
Normal file
393
src/views/tool/model.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
:page="page"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
v-model="form"
|
||||
v-loading.fullscreen.lock="fullscreenLoading"
|
||||
ref="crud"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button type="danger" icon="el-icon-delete" plain @click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-setting"
|
||||
plain
|
||||
class="none-border"
|
||||
@click.stop="handleModel(row)"
|
||||
>模型配置
|
||||
</el-button>
|
||||
</template>
|
||||
<template #modelTable="{ row }">
|
||||
<el-tag>{{ row.modelTable }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="数据库模型配置" v-model="modelBox" :fullscreen="true" append-to-body>
|
||||
<avue-crud
|
||||
ref="crudModel"
|
||||
:option="optionModel"
|
||||
:table-loading="loading"
|
||||
:data="fields"
|
||||
></avue-crud>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="danger" @click="modelBox = false">关 闭</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">提 交</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getList,
|
||||
getDetail,
|
||||
add,
|
||||
update,
|
||||
remove,
|
||||
getTableList,
|
||||
getTableInfoByName,
|
||||
getModelPrototype,
|
||||
submitModelPrototype,
|
||||
} from '@/api/tool/model';
|
||||
import { entityDic, option, optionModel } from '@/const/tool/model';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
loadingOption: {
|
||||
lock: true,
|
||||
text: '物理表读取中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
},
|
||||
fullscreenLoading: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
modelBox: false,
|
||||
modelId: 0,
|
||||
datasourceId: 1,
|
||||
tableInfo: {},
|
||||
active: 0,
|
||||
stepStart: 0,
|
||||
stepEnd: 4,
|
||||
data: [],
|
||||
option: option,
|
||||
optionModel: optionModel,
|
||||
formStep: {},
|
||||
fields: [],
|
||||
selectionModelList: [],
|
||||
// 默认不需要显示的字段名
|
||||
hideFields: [
|
||||
'id',
|
||||
'tenant_id',
|
||||
'create_user',
|
||||
'create_dept',
|
||||
'create_time',
|
||||
'update_user',
|
||||
'update_time',
|
||||
'status',
|
||||
'is_deleted',
|
||||
],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'form.datasourceId'() {
|
||||
if (!validatenull(this.form.datasourceId)) {
|
||||
const fullLoading = this.$loading(this.loadingOption);
|
||||
getTableList(this.form.datasourceId)
|
||||
.then(res => {
|
||||
const column = this.findColumn(this.option.column, 'modelTable');
|
||||
column.dicData = res.data.data;
|
||||
fullLoading.close();
|
||||
})
|
||||
.catch(() => {
|
||||
fullLoading.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
'form.modelTable'() {
|
||||
if (!validatenull(this.form.modelTable)) {
|
||||
const fullLoading = this.$loading(this.loadingOption);
|
||||
getTableInfoByName(this.form.modelTable, this.form.datasourceId)
|
||||
.then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
const { comment, entityName } = result.data;
|
||||
//if (validatenull(this.form.modelClass)) {
|
||||
this.form.modelClass = entityName;
|
||||
//}
|
||||
//if (validatenull(this.form.modelName)) {
|
||||
this.form.modelName = comment;
|
||||
//}
|
||||
//if (validatenull(this.form.modelCode)) {
|
||||
this.form.modelCode = entityName.replace(/^\S/, s => s.toLowerCase());
|
||||
//}
|
||||
fullLoading.close();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
fullLoading.close();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: true,
|
||||
delBtn: true,
|
||||
editBtn: true,
|
||||
viewBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
res => {
|
||||
done();
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$confirm('是否进行模型配置?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
let model = {
|
||||
id: res.data.data.id,
|
||||
datasourceId: res.data.data.datasourceId,
|
||||
};
|
||||
this.handleModel(model);
|
||||
});
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
done();
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
},
|
||||
error => {
|
||||
loading();
|
||||
window.console.log(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionModelChange(list) {
|
||||
this.selectionModelList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
handleModel(row) {
|
||||
this.fields = [];
|
||||
this.modelBox = true;
|
||||
this.loading = true;
|
||||
this.modelId = row.id;
|
||||
this.datasourceId = row.datasourceId;
|
||||
getModelPrototype(this.modelId, this.datasourceId).then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
this.fields = result.data;
|
||||
this.fields.forEach(item => {
|
||||
item.$cellEdit = true;
|
||||
item.modelId = this.modelId;
|
||||
// 根据字段物理类型自动适配实体类型
|
||||
if (!validatenull(item.name)) {
|
||||
item.jdbcName = item.name;
|
||||
item.jdbcType = item.propertyType;
|
||||
item.jdbcComment = item.comment;
|
||||
if (item.propertyType === 'LocalDateTime') {
|
||||
item.propertyType = 'Date';
|
||||
item.propertyEntity = 'java.util.Date';
|
||||
} else {
|
||||
entityDic.forEach(d => {
|
||||
if (d.label === item.propertyType) {
|
||||
item.propertyType = d.label;
|
||||
item.propertyEntity = d.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// 首次加载配置默认值
|
||||
if (validatenull(item.id)) {
|
||||
item.isList = 1;
|
||||
item.isForm = 1;
|
||||
item.isRow = 0;
|
||||
item.isRequired = 0;
|
||||
item.isQuery = 0;
|
||||
item.componentType = 'input';
|
||||
// 默认不需要显示的字段名配置
|
||||
if (this.hideFields.includes(item.jdbcName)) {
|
||||
item.isList = 0;
|
||||
item.isForm = 0;
|
||||
item.isRequired = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
console.log(this.fields);
|
||||
this.$confirm('确定提交模型配置?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
this.fields.forEach(item => {
|
||||
entityDic.forEach(d => {
|
||||
if (d.value === item.propertyEntity) {
|
||||
item.propertyType = d.label;
|
||||
}
|
||||
});
|
||||
});
|
||||
submitModelPrototype(this.fields).then(res => {
|
||||
const result = res.data;
|
||||
if (result.success) {
|
||||
this.$message.success(result.msg);
|
||||
this.modelBox = false;
|
||||
} else {
|
||||
this.$message.error(result.msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.none-border {
|
||||
border: 0;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.step-div {
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
45
src/views/util/affix.vue
Normal file
45
src/views/util/affix.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="affix">
|
||||
<avue-affix id="avue-view">
|
||||
<span class="affix-affix">固定在最顶部</span>
|
||||
</avue-affix>
|
||||
<div class="affix-line"></div>
|
||||
<avue-affix id="avue-view" :offset-top="50">
|
||||
<span class="affix-affix">固定在距离顶部 50px 的位置</span>
|
||||
</avue-affix>
|
||||
<div class="affix-line"></div>
|
||||
<avue-affix id="avue-view" :offset-top="100">
|
||||
<span class="affix-affix">固定在距离顶部 100px 的位置</span>
|
||||
</avue-affix>
|
||||
<div class="affix-line"></div>
|
||||
<avue-affix id="avue-view" :offset-top="150">
|
||||
<span class="affix-affix">固定在距离顶部 150px 的位置</span>
|
||||
</avue-affix>
|
||||
<div style="height: 2000px">
|
||||
<div style="padding: 15px 20px; font-size: 18px">往下拉就会出现图钉</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.affix {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
&-affix {
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
padding: 10px 30px;
|
||||
text-align: center;
|
||||
background: rgba(0, 153, 229, 0.9);
|
||||
}
|
||||
|
||||
&-line {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
26
src/views/util/cache.vue
Normal file
26
src/views/util/cache.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>这个页面会被 keep-alive</h3>
|
||||
<el-tag
|
||||
>在下面的输入框输入任意字符后,切换到其它页面,再回到此页时输入框文字保留,证明被缓存
|
||||
</el-tag>
|
||||
<br />
|
||||
<br />
|
||||
<el-tag>同时滚动下拉,返回时还会保持滚动条所在的位置</el-tag>
|
||||
<br />
|
||||
<br />
|
||||
<el-input v-model="value" placeholder="input here" />
|
||||
|
||||
<div style="height: 1000px"></div>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
52
src/views/util/crud-form.vue
Normal file
52
src/views/util/crud-form.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>点击新增或编辑跳转到新的页面</h3>
|
||||
<avue-crud :option="option" :data="data">
|
||||
<template #menu-left>
|
||||
<el-button type="primary" @click="handleForm()" icon="el-icon-plus">add</el-button>
|
||||
</template>
|
||||
<template #menu="{ row }">
|
||||
<el-button type="primary" text @click="handleForm(row.id)" icon="el-icon-edit"
|
||||
>edit
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
option: {
|
||||
addBtn: false,
|
||||
editBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'small',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleForm(id) {
|
||||
this.$router.push({
|
||||
path: '/form-detail/index',
|
||||
query: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
198
src/views/util/data.vue
Normal file
198
src/views/util/data.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>数据展示</h3>
|
||||
<avue-data-pay :option="option"></avue-data-pay>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
option: {
|
||||
span: 8,
|
||||
data: [
|
||||
{
|
||||
title: '后台模版',
|
||||
src: '/img/bg/vip1.png',
|
||||
money: '299',
|
||||
dismoney: '199',
|
||||
tip: '/永久',
|
||||
color: '#808695',
|
||||
subtext: '购买',
|
||||
click: () => {
|
||||
this.box = true;
|
||||
},
|
||||
list: [
|
||||
{
|
||||
title: '点击体验',
|
||||
href: 'https://cli1.avue.top',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '面向全屏幕尺寸的响应式适配能力',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '支持IE9+等系列浏览器',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '全新的前端错误日志监控机制',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '基于最新的avuex底层开发',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '前端路由动态服务端加载',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '灵活的多款主题自由配置',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '模块的可拆卸化,达到开箱即用',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '免费的私人git私服',
|
||||
},
|
||||
{
|
||||
title: '专属会员群',
|
||||
},
|
||||
{
|
||||
title: '前端最新干货分享',
|
||||
},
|
||||
{
|
||||
title: '赠送 Avue-cli脚手架文档(价值¥59.99)',
|
||||
href: 'https://www.kancloud.cn/smallwei/avue',
|
||||
},
|
||||
{
|
||||
title: '赠送 Avue 修仙系列视频教程',
|
||||
href: 'https://www.bilibili.com/video/av24644922',
|
||||
check: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Avuex源码',
|
||||
src: '/img/bg/vip2.png',
|
||||
color: '#ffa820',
|
||||
money: '999',
|
||||
dismoney: '399',
|
||||
tip: '/永久',
|
||||
subtext: '购买',
|
||||
click: () => {
|
||||
this.box = true;
|
||||
},
|
||||
list: [
|
||||
{
|
||||
title: '一键集成表格的导出excel,打印,等功能',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '底层代码可重用轻松对接多个UI框架',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '底层更加完善的开发错误调试机制',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '一套代码多个终端自适应',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '一键集成表格的导出excel,打印,等常用功能',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '表格的批量操作,表单的级联操作更加便捷',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '新增大量常用组件(搜索,选项卡)',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '新增大量全新可配置的骚属性',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '丰富的数据展示模版组件包',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '专属的开发者文档,助你快速掌握',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '赠送 Avue-cli脚手架文档(价值¥59.99)',
|
||||
href: 'https://www.kancloud.cn/smallwei/avue',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '赠送 Avue 修仙系列视频教程',
|
||||
href: 'https://www.bilibili.com/video/av24644922',
|
||||
check: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '全家桶',
|
||||
src: '/img/bg/vip3.png',
|
||||
color: '#ef4868',
|
||||
money: '999.99',
|
||||
dismoney: '399.99',
|
||||
tip: '/永久',
|
||||
subtext: '购买',
|
||||
click: () => {
|
||||
this.box = true;
|
||||
},
|
||||
list: [
|
||||
{
|
||||
title: '授权商业化开发,永久更新授权使用',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '后期更新和新产品将全部免费',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '拥有avuex系列的全部特权和全部源码',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '免费的私人git私服',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '专属会员群',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '前端最新干货分享',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '赠送 Avue-cli脚手架文档(价值¥59.99)',
|
||||
href: 'https://www.kancloud.cn/smallwei/avue',
|
||||
check: true,
|
||||
},
|
||||
{
|
||||
title: '赠送 Avue 修仙系列视频教程',
|
||||
href: 'https://www.bilibili.com/video/av24644922',
|
||||
check: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
352
src/views/util/demo/dict-classic.vue
Normal file
352
src/views/util/demo/dict-classic.vue
Normal file
@@ -0,0 +1,352 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
:before-close="beforeClose"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, remove, update, add, getDict, getDictTree } from '@/api/system/dict';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
option: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
menuWidth: 300,
|
||||
dialogWidth: 880,
|
||||
column: [
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典名称',
|
||||
prop: 'dictValue',
|
||||
search: true,
|
||||
align: 'center',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '上级字典',
|
||||
prop: 'parentId',
|
||||
type: 'tree',
|
||||
dicData: [],
|
||||
hide: true,
|
||||
props: {
|
||||
label: 'title',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: false,
|
||||
message: '请选择上级字典',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典键值',
|
||||
prop: 'dictKey',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典键值',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '封存',
|
||||
prop: 'isSealed',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择封存',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典备注',
|
||||
prop: 'remark',
|
||||
search: true,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dict_add, false),
|
||||
viewBtn: this.validData(this.permission.dict_view, false),
|
||||
delBtn: this.validData(this.permission.dict_delete, false),
|
||||
editBtn: this.validData(this.permission.dict_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
getDictTree().then(res => {
|
||||
const column = this.findColumn(this.optionChild.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
handleAdd(row) {
|
||||
this.$refs.crud.value.code = row.code;
|
||||
this.$refs.crud.value.parentId = row.id;
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'code') {
|
||||
item.value = row.code;
|
||||
item.addDisabled = true;
|
||||
}
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = row.id;
|
||||
item.addDisabled = true;
|
||||
}
|
||||
});
|
||||
this.$refs.crud.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeClose(done) {
|
||||
this.$refs.crud.tableForm = {};
|
||||
this.$refs.crud.value.code = '';
|
||||
this.$refs.crud.value.parentId = '';
|
||||
this.$refs.crud.value.addDisabled = false;
|
||||
this.$refs.crud.option.column.filter(item => {
|
||||
if (item.prop === 'code') {
|
||||
item.value = '';
|
||||
item.addDisabled = false;
|
||||
}
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = '';
|
||||
item.addDisabled = false;
|
||||
}
|
||||
});
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
|
||||
this.data = res.data.data;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
469
src/views/util/demo/dict-horizontal.vue
Normal file
469
src/views/util/demo/dict-horizontal.vue
Normal file
@@ -0,0 +1,469 @@
|
||||
<template>
|
||||
<div>
|
||||
<basic-container>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="dataParent"
|
||||
:page="pageParent"
|
||||
ref="crud"
|
||||
v-model="formParent"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-click="handleRowClick"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoadParent"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="dataChild"
|
||||
:page="pageChild"
|
||||
ref="crudChild"
|
||||
v-model="formChild"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpenChild"
|
||||
@row-del="rowDelChild"
|
||||
@row-update="rowUpdateChild"
|
||||
@row-save="rowSaveChild"
|
||||
@search-change="searchChangeChild"
|
||||
@search-reset="searchResetChild"
|
||||
@selection-change="selectionChangeChild"
|
||||
@current-change="currentChangeChild"
|
||||
@size-change="sizeChangeChild"
|
||||
@refresh-change="refreshChangeChild"
|
||||
@on-load="onLoadChild"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</basic-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getParentList, getChildList, remove, update, add, getDict } from '@/api/system/dict';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
parentId: 0,
|
||||
formParent: {},
|
||||
formChild: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
pageParent: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
pageChild: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
dataParent: [],
|
||||
dataChild: [],
|
||||
option: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
menuWidth: 300,
|
||||
dialogWidth: 880,
|
||||
column: [
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典键值',
|
||||
prop: 'dictKey',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典键值',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典名称',
|
||||
prop: 'dictValue',
|
||||
search: true,
|
||||
align: 'center',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '封存',
|
||||
prop: 'isSealed',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择封存',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典备注',
|
||||
prop: 'remark',
|
||||
search: true,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dict_add, false),
|
||||
viewBtn: this.validData(this.permission.dict_view, false),
|
||||
delBtn: this.validData(this.permission.dict_delete, false),
|
||||
editBtn: this.validData(this.permission.dict_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.parentId = row.id;
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoadParent(this.pageParent);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.pageParent.currentPage = 1;
|
||||
this.onLoadParent(this.pageParent, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formParent.id).then(res => {
|
||||
this.formParent = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.pageParent.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.pageParent.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoadParent(this.page, this.query);
|
||||
},
|
||||
rowSaveChild(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateChild(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelChild(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchResetChild() {
|
||||
this.query = {};
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchChangeChild(params, done) {
|
||||
this.query = params;
|
||||
this.pageChild.currentPage = 1;
|
||||
this.onLoadChild(this.pageChild, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeChild(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClearChild() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
},
|
||||
handleDeleteChild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenChild(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formChild.id).then(res => {
|
||||
this.formChild = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChangeChild(currentPage) {
|
||||
this.pageChild.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeChild(pageSize) {
|
||||
this.pageChild.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeChild() {
|
||||
this.onLoadChild(this.pageChild, this.query);
|
||||
},
|
||||
onLoadParent(page, params = {}) {
|
||||
this.loading = true;
|
||||
getParentList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageParent.total = data.total;
|
||||
this.dataParent = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
}
|
||||
);
|
||||
},
|
||||
onLoadChild(page, params = {}) {
|
||||
this.loading = true;
|
||||
getChildList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
this.parentId,
|
||||
Object.assign(params, this.query)
|
||||
).then(res => {
|
||||
const data = res.data.data;
|
||||
this.pageChild.total = data.total;
|
||||
this.dataChild = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
474
src/views/util/demo/dict-vertical.vue
Normal file
474
src/views/util/demo/dict-vertical.vue
Normal file
@@ -0,0 +1,474 @@
|
||||
<template>
|
||||
<div>
|
||||
<basic-container>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="dataParent"
|
||||
:page="pageParent"
|
||||
ref="crud"
|
||||
v-model="formParent"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-click="handleRowClick"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoadParent"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</basic-container>
|
||||
<basic-container>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="dataChild"
|
||||
:page="pageChild"
|
||||
ref="crudChild"
|
||||
v-model="formChild"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpenChild"
|
||||
@row-del="rowDelChild"
|
||||
@row-update="rowUpdateChild"
|
||||
@row-save="rowSaveChild"
|
||||
@search-change="searchChangeChild"
|
||||
@search-reset="searchResetChild"
|
||||
@selection-change="selectionChangeChild"
|
||||
@current-change="currentChangeChild"
|
||||
@size-change="sizeChangeChild"
|
||||
@refresh-change="refreshChangeChild"
|
||||
@on-load="onLoadChild"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</basic-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getParentList, getChildList, remove, update, add, getDict } from '@/api/system/dict';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
parentId: 0,
|
||||
formParent: {},
|
||||
formChild: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
pageParent: {
|
||||
pageSize: 5,
|
||||
pageSizes: [5, 10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
pageChild: {
|
||||
pageSize: 5,
|
||||
pageSizes: [5, 10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
dataParent: [],
|
||||
dataChild: [],
|
||||
option: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
height: 300,
|
||||
menuWidth: 300,
|
||||
dialogWidth: 880,
|
||||
column: [
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典编号',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典键值',
|
||||
prop: 'dictKey',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典键值',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典名称',
|
||||
prop: 'dictValue',
|
||||
search: true,
|
||||
align: 'center',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典排序',
|
||||
prop: 'sort',
|
||||
type: 'number',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入字典排序',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '封存',
|
||||
prop: 'isSealed',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{
|
||||
label: '否',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '是',
|
||||
value: 1,
|
||||
},
|
||||
],
|
||||
slot: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择封存',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '字典备注',
|
||||
prop: 'remark',
|
||||
search: true,
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dict_add, false),
|
||||
viewBtn: this.validData(this.permission.dict_view, false),
|
||||
delBtn: this.validData(this.permission.dict_delete, false),
|
||||
editBtn: this.validData(this.permission.dict_edit, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.parentId = row.id;
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoadParent(this.pageParent);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.pageParent.currentPage = 1;
|
||||
this.onLoadParent(this.pageParent, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formParent.id).then(res => {
|
||||
this.formParent = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.pageParent.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.pageParent.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoadParent(this.page, this.query);
|
||||
},
|
||||
rowSaveChild(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateChild(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelChild(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchResetChild() {
|
||||
this.query = {};
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchChangeChild(params, done) {
|
||||
this.query = params;
|
||||
this.pageChild.currentPage = 1;
|
||||
this.onLoadChild(this.pageChild, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeChild(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClearChild() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
},
|
||||
handleDeleteChild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenChild(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formChild.id).then(res => {
|
||||
this.formChild = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChangeChild(currentPage) {
|
||||
this.pageChild.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeChild(pageSize) {
|
||||
this.pageChild.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeChild() {
|
||||
this.onLoadChild(this.pageChild, this.query);
|
||||
},
|
||||
onLoadParent(page, params = {}) {
|
||||
this.loading = true;
|
||||
getParentList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageParent.total = data.total;
|
||||
this.dataParent = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
}
|
||||
);
|
||||
},
|
||||
onLoadChild(page, params = {}) {
|
||||
this.loading = true;
|
||||
getChildList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
this.parentId,
|
||||
Object.assign(params, this.query)
|
||||
).then(res => {
|
||||
const data = res.data.data;
|
||||
this.pageChild.total = data.total;
|
||||
this.dataChild = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
465
src/views/util/demo/dict.vue
Normal file
465
src/views/util/demo/dict.vue
Normal file
@@ -0,0 +1,465 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="11">
|
||||
<basic-container>
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>字典列表</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="clearfix">
|
||||
<avue-crud
|
||||
:option="optionParent"
|
||||
:table-loading="loading"
|
||||
:data="dataParent"
|
||||
:page="pageParent"
|
||||
ref="crud"
|
||||
v-model="formParent"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-click="handleRowClick"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoadParent"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
</el-card>
|
||||
</basic-container>
|
||||
</el-col>
|
||||
<el-col :span="13">
|
||||
<basic-container>
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>[{{ dictValue }}] 字典详情</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="clearfix">
|
||||
<avue-crud
|
||||
:option="optionChild"
|
||||
:table-loading="loadingChild"
|
||||
:data="dataChild"
|
||||
ref="crudChild"
|
||||
v-model="formChild"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpenChild"
|
||||
:before-close="beforeCloseChild"
|
||||
@row-del="rowDelChild"
|
||||
@row-update="rowUpdateChild"
|
||||
@row-save="rowSaveChild"
|
||||
@search-change="searchChangeChild"
|
||||
@search-reset="searchResetChild"
|
||||
@selection-change="selectionChangeChild"
|
||||
@current-change="currentChangeChild"
|
||||
@size-change="sizeChangeChild"
|
||||
@refresh-change="refreshChangeChild"
|
||||
@on-load="onLoadChild"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
</el-card>
|
||||
</basic-container>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getParentList,
|
||||
getChildList,
|
||||
remove,
|
||||
update,
|
||||
add,
|
||||
getDict,
|
||||
getDictTree,
|
||||
} from '@/api/system/dict';
|
||||
import { optionParent, optionChild } from '@/option/system/dict';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dictValue: '暂无',
|
||||
parentId: -1,
|
||||
formParent: {},
|
||||
formChild: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
loadingChild: true,
|
||||
pageParent: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
pageChild: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
dataParent: [],
|
||||
dataChild: [],
|
||||
optionParent: optionParent,
|
||||
optionChild: optionChild,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dict_add, false),
|
||||
delBtn: this.validData(this.permission.dict_delete, false),
|
||||
editBtn: this.validData(this.permission.dict_edit, false),
|
||||
viewBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initData();
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getDictTree().then(res => {
|
||||
const column = this.findColumn(this.optionChild.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
handleAdd(row) {
|
||||
this.$refs.crudChild.value.dictValue = '';
|
||||
this.$refs.crudChild.value.dictKey = '';
|
||||
this.$refs.crudChild.value.sort = 0;
|
||||
this.$refs.crudChild.value.isSealed = 0;
|
||||
this.$refs.crudChild.value.remark = '';
|
||||
this.$refs.crudChild.modelValue.parentId = row.id;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = row.id;
|
||||
}
|
||||
});
|
||||
this.$refs.crudChild.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const form = {
|
||||
...row,
|
||||
dictKey: -1,
|
||||
};
|
||||
add(form).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.onLoadChild(this.pageChild);
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.query = {};
|
||||
this.parentId = row.id;
|
||||
this.dictValue = row.dictValue;
|
||||
this.$refs.crudChild.value.code = row.code;
|
||||
this.$refs.crudChild.modelValue.parentId = row.id;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'code') {
|
||||
item.value = row.code;
|
||||
}
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = row.id;
|
||||
}
|
||||
});
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoadParent(this.pageParent);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.pageParent.currentPage = 1;
|
||||
this.onLoadParent(this.pageParent, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formParent.id).then(res => {
|
||||
this.formParent = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.pageParent.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.pageParent.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoadParent(this.pageParent, this.query);
|
||||
},
|
||||
rowSaveChild(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateChild(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelChild(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchResetChild() {
|
||||
this.query = {};
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchChangeChild(params, done) {
|
||||
this.query = params;
|
||||
this.pageChild.currentPage = 1;
|
||||
this.onLoadChild(this.pageChild, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeChild(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClearChild() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
},
|
||||
handleDeleteChild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenChild(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formChild.id).then(res => {
|
||||
this.formChild = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeCloseChild(done) {
|
||||
this.$refs.crudChild.modelValue.parentId = this.parentId;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = this.parentId;
|
||||
}
|
||||
});
|
||||
done();
|
||||
},
|
||||
currentChangeChild(currentPage) {
|
||||
this.pageChild.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeChild(pageSize) {
|
||||
this.pageChild.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeChild() {
|
||||
this.onLoadChild(this.pageChild, this.query);
|
||||
},
|
||||
onLoadParent(page, params = {}) {
|
||||
this.loading = true;
|
||||
getParentList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageParent.total = data.total;
|
||||
this.dataParent = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
}
|
||||
);
|
||||
},
|
||||
onLoadChild(page, params = {}) {
|
||||
this.loadingChild = true;
|
||||
getChildList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
this.parentId,
|
||||
Object.assign(params, this.query)
|
||||
).then(res => {
|
||||
this.dataChild = res.data.data;
|
||||
this.loadingChild = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
459
src/views/util/demo/dictbiz.vue
Normal file
459
src/views/util/demo/dictbiz.vue
Normal file
@@ -0,0 +1,459 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="11">
|
||||
<basic-container>
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>业务字典列表</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="clearfix">
|
||||
<avue-crud
|
||||
:option="optionParent"
|
||||
:table-loading="loading"
|
||||
:data="dataParent"
|
||||
:page="pageParent"
|
||||
ref="crud"
|
||||
v-model="formParent"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-del="rowDel"
|
||||
@row-update="rowUpdate"
|
||||
@row-save="rowSave"
|
||||
@row-click="handleRowClick"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoadParent"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
</el-card>
|
||||
</basic-container>
|
||||
</el-col>
|
||||
<el-col :span="13">
|
||||
<basic-container>
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<span>[{{ dictValue }}] 业务字典详情</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="clearfix">
|
||||
<avue-crud
|
||||
:option="optionChild"
|
||||
:table-loading="loadingChild"
|
||||
:data="dataChild"
|
||||
ref="crudChild"
|
||||
v-model="formChild"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpenChild"
|
||||
:before-close="beforeCloseChild"
|
||||
@row-del="rowDelChild"
|
||||
@row-update="rowUpdateChild"
|
||||
@row-save="rowSaveChild"
|
||||
@search-change="searchChangeChild"
|
||||
@search-reset="searchResetChild"
|
||||
@selection-change="selectionChangeChild"
|
||||
@current-change="currentChangeChild"
|
||||
@size-change="sizeChangeChild"
|
||||
@refresh-change="refreshChangeChild"
|
||||
@on-load="onLoadChild"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.dict_delete"
|
||||
plain
|
||||
@click="handleDelete"
|
||||
>删 除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-circle-plus"
|
||||
@click.stop="handleAdd(scope.row, scope.index)"
|
||||
v-if="userInfo.authority.includes('admin')"
|
||||
>新增子项
|
||||
</el-button>
|
||||
</template>
|
||||
<template #isSealed="{ row }">
|
||||
<el-tag>{{ row.isSealed === 0 ? '否' : '是' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
</el-card>
|
||||
</basic-container>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getParentList,
|
||||
getChildList,
|
||||
remove,
|
||||
update,
|
||||
add,
|
||||
getDict,
|
||||
getDictTree,
|
||||
} from '@/api/system/dictbiz';
|
||||
import { optionParent, optionChild } from '@/option/system/dictbiz';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dictValue: '暂无',
|
||||
parentId: -1,
|
||||
formParent: {},
|
||||
formChild: {},
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
loadingChild: true,
|
||||
pageParent: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
pageChild: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 30, 50, 100, 200],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
dataParent: [],
|
||||
dataChild: [],
|
||||
optionParent: optionParent,
|
||||
optionChild: optionChild,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.dictbiz_add, false),
|
||||
delBtn: this.validData(this.permission.dictbiz_delete, false),
|
||||
editBtn: this.validData(this.permission.dictbiz_edit, false),
|
||||
viewBtn: false,
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initData();
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
getDictTree().then(res => {
|
||||
const column = this.findColumn(this.optionChild.column, 'parentId');
|
||||
column.dicData = res.data.data;
|
||||
});
|
||||
},
|
||||
handleAdd(row) {
|
||||
this.$refs.crudChild.modelValue.parentId = row.id;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = row.id;
|
||||
}
|
||||
});
|
||||
this.$refs.crudChild.rowAdd();
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const form = {
|
||||
...row,
|
||||
dictKey: -1,
|
||||
};
|
||||
add(form).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.query = {};
|
||||
this.parentId = row.id;
|
||||
this.dictValue = row.dictValue;
|
||||
this.$refs.crudChild.value.code = row.code;
|
||||
this.$refs.crudChild.modelValue.parentId = row.id;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'code') {
|
||||
item.value = row.code;
|
||||
}
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = row.id;
|
||||
}
|
||||
});
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoadParent(this.pageParent);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.pageParent.currentPage = 1;
|
||||
this.onLoadParent(this.pageParent, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadParent(this.pageParent);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formParent.id).then(res => {
|
||||
this.formParent = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.pageParent.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.pageParent.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoadParent(this.pageParent, this.query);
|
||||
},
|
||||
rowSaveChild(row, done, loading) {
|
||||
add(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdateChild(row, index, done, loading) {
|
||||
update(row).then(
|
||||
() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDelChild(row) {
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(row.id);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
searchResetChild() {
|
||||
this.query = {};
|
||||
this.onLoadChild(this.pageChild);
|
||||
},
|
||||
searchChangeChild(params, done) {
|
||||
this.query = params;
|
||||
this.pageChild.currentPage = 1;
|
||||
this.onLoadChild(this.pageChild, params);
|
||||
done();
|
||||
},
|
||||
selectionChangeChild(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClearChild() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
},
|
||||
handleDeleteChild() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定将选择数据删除?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return remove(this.ids);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoadChild(this.pageChild);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
this.$refs.crudChild.toggleSelection();
|
||||
});
|
||||
},
|
||||
beforeOpenChild(done, type) {
|
||||
if (['add', 'edit'].includes(type)) {
|
||||
this.initData();
|
||||
}
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDict(this.formChild.id).then(res => {
|
||||
this.formChild = res.data.data;
|
||||
});
|
||||
}
|
||||
done();
|
||||
},
|
||||
beforeCloseChild(done) {
|
||||
this.$refs.crudChild.modelValue.parentId = this.parentId;
|
||||
this.$refs.crudChild.option.column.filter(item => {
|
||||
if (item.prop === 'parentId') {
|
||||
item.value = this.parentId;
|
||||
}
|
||||
});
|
||||
done();
|
||||
},
|
||||
currentChangeChild(currentPage) {
|
||||
this.pageChild.currentPage = currentPage;
|
||||
},
|
||||
sizeChangeChild(pageSize) {
|
||||
this.pageChild.pageSize = pageSize;
|
||||
},
|
||||
refreshChangeChild() {
|
||||
this.onLoadChild(this.pageChild, this.query);
|
||||
},
|
||||
onLoadParent(page, params = {}) {
|
||||
this.loading = true;
|
||||
getParentList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(
|
||||
res => {
|
||||
const data = res.data.data;
|
||||
this.pageParent.total = data.total;
|
||||
this.dataParent = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
}
|
||||
);
|
||||
},
|
||||
onLoadChild(page, params = {}) {
|
||||
this.loadingChild = true;
|
||||
getChildList(
|
||||
page.currentPage,
|
||||
page.pageSize,
|
||||
this.parentId,
|
||||
Object.assign(params, this.query)
|
||||
).then(res => {
|
||||
this.dataChild = res.data.data;
|
||||
this.loadingChild = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
37
src/views/util/form-detail.vue
Normal file
37
src/views/util/form-detail.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>{{ $route.query.id ? '编辑' : '新增' }}</h3>
|
||||
<avue-form :option="option" v-model="form">
|
||||
<template #menu-form>
|
||||
<el-button icon="el-icon-back" @click="handleBack()">返 回</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
option: {
|
||||
labelWidth: 110,
|
||||
column: [
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleBack() {
|
||||
this.$router.$avueRouter.closeTag();
|
||||
this.$router.back();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
53
src/views/util/form.vue
Normal file
53
src/views/util/form.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>表单例子</h3>
|
||||
<avue-form :option="option" v-model="form"></avue-form>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
option: {
|
||||
labelWidth: 110,
|
||||
column: [
|
||||
{
|
||||
label: '用户名',
|
||||
prop: 'username',
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
prop: 'password',
|
||||
type: 'password',
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '再次输入密码',
|
||||
prop: 'password',
|
||||
type: 'newpassword',
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '申请日期',
|
||||
prop: 'date',
|
||||
type: 'date',
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '个性签名',
|
||||
prop: 'textarea',
|
||||
type: 'textarea',
|
||||
minRows: 8,
|
||||
row: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
16
src/views/util/logs.vue
Normal file
16
src/views/util/logs.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>请打开浏览器控制台,然后点击下面的按钮</h3>
|
||||
<el-button type="danger" @click="handleNewError">触发一个错误</el-button>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleNewError() {
|
||||
window.console.log(a); // eslint-disable-line
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
81
src/views/util/permission.vue
Normal file
81
src/views/util/permission.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<basic-container>
|
||||
<h3>表格权限控制</h3>
|
||||
<avue-crud ref="crud" :permission="permission" :option="option" :data="data">
|
||||
<template #expand="scope">
|
||||
{{ scope }}
|
||||
</template>
|
||||
</avue-crud>
|
||||
</basic-container>
|
||||
<basic-container>
|
||||
权限开关
|
||||
<el-switch
|
||||
:active-value="false"
|
||||
:inactive-value="true"
|
||||
v-model="text"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
>
|
||||
</el-switch>
|
||||
<p>
|
||||
具体参考<a href="https://avuex.avue.top/#/doc/crud-permission"
|
||||
>https://avuex.avue.top/#/doc/crud-permission</a
|
||||
>
|
||||
</p>
|
||||
</basic-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
text: false,
|
||||
permission: {},
|
||||
option: {
|
||||
expand: true,
|
||||
column: [
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
},
|
||||
{
|
||||
label: '年龄',
|
||||
prop: 'sex',
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
name: '张三',
|
||||
sex: 12,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '李四',
|
||||
sex: 20,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
text() {
|
||||
if (this.text === true) {
|
||||
this.permission = {
|
||||
delBtn: false,
|
||||
addBtn: false,
|
||||
};
|
||||
} else {
|
||||
this.permission = {
|
||||
delBtn: true,
|
||||
addBtn: true,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
95
src/views/util/store.vue
Normal file
95
src/views/util/store.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>存储</h3>
|
||||
<el-tag class="title">基本读写删(持久化存储)</el-tag>
|
||||
<div class="box">
|
||||
<el-button type="primary" @click="setItem({ name: 'username', value: 'avuex' })"
|
||||
>set('username', 'avuex')
|
||||
</el-button>
|
||||
|
||||
<el-button type="success" @click="getItem({ name: 'username' })">get('username')</el-button>
|
||||
|
||||
<el-button type="danger" @click="delItem({ name: 'username' })"
|
||||
>remove('username')
|
||||
</el-button>
|
||||
</div>
|
||||
<el-tag class="title">设置session(session存储)</el-tag>
|
||||
<div class="box">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="setItem({ name: 'username', value: 'avuex', type: 'session' })"
|
||||
>set('username', 'avuex')
|
||||
</el-button>
|
||||
|
||||
<el-button type="success" @click="getItem({ name: 'username', type: 'session' })"
|
||||
>get('username')
|
||||
</el-button>
|
||||
|
||||
<el-button type="danger" @click="delItem({ name: 'username', type: 'session' })"
|
||||
>remove('username')
|
||||
</el-button>
|
||||
</div>
|
||||
<el-tag class="title">获取所有可以获得的数据</el-tag>
|
||||
<div class="box">
|
||||
<el-button type="success" @click="getAll()">getAll(持久化存储)</el-button>
|
||||
<el-button type="success" @click="getAll({ type: 'session' })"
|
||||
>getAll(session存储)
|
||||
</el-button>
|
||||
<el-button type="danger" @click="clearAll()">delAll(持久化存储)</el-button>
|
||||
<el-button type="danger" @click="clearAll({ type: 'session' })"
|
||||
>delAll(session存储)
|
||||
</el-button>
|
||||
</div>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setStore, getStore, removeStore, clearStore, getAllStore } from '@/utils/store';
|
||||
|
||||
export default {
|
||||
name: 'store',
|
||||
methods: {
|
||||
setItem(params = {}) {
|
||||
const { name, value, type } = params;
|
||||
setStore({
|
||||
name: name,
|
||||
content: value,
|
||||
type: type,
|
||||
});
|
||||
this.$message(`设置数据 ${name} = ${value}`);
|
||||
},
|
||||
getItem(params = {}) {
|
||||
const { name, type } = params;
|
||||
const content = getStore({
|
||||
name: name,
|
||||
type: type,
|
||||
});
|
||||
this.$message(`获取数据 ${name} = ${content}`);
|
||||
},
|
||||
delItem(params = {}) {
|
||||
const { name, type } = params;
|
||||
removeStore({ name, type });
|
||||
this.$message(`删除数据 ${name}`);
|
||||
},
|
||||
clearAll(params = {}) {
|
||||
clearStore(params);
|
||||
this.$message(`清除全部数据完成`);
|
||||
},
|
||||
getAll(params = {}) {
|
||||
const list = getAllStore(params);
|
||||
window.console.log(list);
|
||||
this.$message(`结果已经打印到控制台`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
126
src/views/util/table.vue
Normal file
126
src/views/util/table.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>表格例子</h3>
|
||||
<avue-crud :option="option" v-model:page="page" :data="data" />
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
page: {
|
||||
total: 122,
|
||||
},
|
||||
data: [
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
{
|
||||
username: 'smallwei',
|
||||
name: 'avue',
|
||||
password: '123456',
|
||||
newpassword: '123456',
|
||||
date: '2019-01-01',
|
||||
textarea: '这是一条很长很长很长很长很长很长很长很长的个性签名',
|
||||
},
|
||||
],
|
||||
option: {
|
||||
column: [
|
||||
{
|
||||
label: '用户名',
|
||||
prop: 'username',
|
||||
span: 14,
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
prop: 'name',
|
||||
span: 14,
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
prop: 'password',
|
||||
type: 'password',
|
||||
span: 14,
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '确认密码',
|
||||
prop: 'newpassword',
|
||||
type: 'password',
|
||||
hide: true,
|
||||
span: 14,
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '申请日期',
|
||||
prop: 'date',
|
||||
type: 'date',
|
||||
span: 14,
|
||||
row: true,
|
||||
},
|
||||
{
|
||||
label: '个性签名',
|
||||
prop: 'textarea',
|
||||
type: 'textarea',
|
||||
minRows: 8,
|
||||
span: 24,
|
||||
overHidden: true,
|
||||
row: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
18
src/views/util/tags.vue
Normal file
18
src/views/util/tags.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<h3>标签</h3>
|
||||
<el-button type="primary" @click="$router.push('/test/index')">打开一个页面</el-button>
|
||||
<el-button type="primary" @click="$router.$avueRouter.closeTag('/test/index')"
|
||||
>关闭打开的页面
|
||||
</el-button>
|
||||
<el-button type="primary" @click="$router.$avueRouter.closeTag()">关闭本标签</el-button>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
19
src/views/util/test.vue
Normal file
19
src/views/util/test.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<basic-container>测试页</basic-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wel',
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
computed: {},
|
||||
created() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped="scoped" lang="scss"></style>
|
||||
26
src/views/util/top.vue
Normal file
26
src/views/util/top.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="height: 2000px; background-color: #fff">
|
||||
<div style="padding: 15px 20px; font-size: 18px">往下拉就会出现返回菜单</div>
|
||||
</div>
|
||||
<avue-back-top id="avue-view"></avue-back-top>
|
||||
<avue-back-top id="avue-view" :height="100" :bottom="200">
|
||||
<div class="top">返回顶端</div>
|
||||
</avue-back-top>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.top {
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
background: rgba(0, 153, 229, 0.7);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
196
src/views/wel/dashboard.vue
Normal file
196
src/views/wel/dashboard.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<div class="wel">
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-platform-eleme"
|
||||
text="开始菜单1"
|
||||
time="1"
|
||||
background="/img/bg/bg3.jpg"
|
||||
color="#d56259"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-eleme"
|
||||
text="开始菜单2"
|
||||
time="2"
|
||||
background="/img/bg/bg2.jpg"
|
||||
color="#419ce7"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-delete-solid"
|
||||
text="开始菜单3"
|
||||
time="3"
|
||||
color="#56b69b"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-delete"
|
||||
text="开始菜单4"
|
||||
time="4"
|
||||
color="#d44858"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-tools"
|
||||
text="开始菜单5"
|
||||
time="5"
|
||||
color="#3a1f7e"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="410"
|
||||
:height="height"
|
||||
icon="el-icon-setting"
|
||||
text="开始菜单6"
|
||||
time="6"
|
||||
background="/img/bg/bg1.jpg"
|
||||
dept="这是一段很长的很长很长很长的描述这是一段很长的很长很长很长的描述"
|
||||
color="#422829"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-user-solid"
|
||||
text="开始菜单7"
|
||||
time="7"
|
||||
color="#613cbd"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-star-off"
|
||||
text="开始菜单8"
|
||||
time="8"
|
||||
color="#da542e"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-goods"
|
||||
text="开始菜单9"
|
||||
time="9"
|
||||
color="#2e8aef"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-circle-check"
|
||||
text="开始菜单10"
|
||||
time="10"
|
||||
color="#3d17b8"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-platform"
|
||||
text="开始菜单11"
|
||||
time="11"
|
||||
color="#e31462"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-fold"
|
||||
text="开始菜单12"
|
||||
time="12"
|
||||
color="#d9532d"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="410"
|
||||
:height="height"
|
||||
icon="el-icon-s-open"
|
||||
text="开始菜单13"
|
||||
time="13"
|
||||
dept="这是一段很长的很长很长很长的描述这是一段很长的很长很长很长的描述"
|
||||
color="#b72147"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-flag"
|
||||
text="开始菜单14"
|
||||
time="14"
|
||||
color="#01a100"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-data"
|
||||
text="开始菜单15"
|
||||
time="15"
|
||||
color="#0c56bf"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-grid"
|
||||
text="开始菜单16"
|
||||
time="16"
|
||||
color="#0098a9"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-release"
|
||||
text="开始菜单17"
|
||||
time="17"
|
||||
background="/img/bg/bg2.jpg"
|
||||
color="#209bdf"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="width"
|
||||
:height="height"
|
||||
icon="el-icon-s-home"
|
||||
text="开始菜单18"
|
||||
time="18"
|
||||
background="/img/bg/bg3.jpg"
|
||||
color="#603bbc"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="515"
|
||||
:height="height"
|
||||
icon="el-icon-s-promotion"
|
||||
text="开始菜单19"
|
||||
time="19"
|
||||
dept="这是一段很长的很长很长很长的描述这是一段很长的很长很长很长的描述"
|
||||
color="#009bad"
|
||||
></basic-block>
|
||||
<basic-block
|
||||
:width="515"
|
||||
:height="height"
|
||||
icon="el-icon-s-custom"
|
||||
text="开始菜单20"
|
||||
time="20"
|
||||
background="/img/bg/bg4.jpg"
|
||||
dept="这是一段很长的很长很长很长的描述这是一段很长的很长很长很长的描述"
|
||||
color="#d74e2a"
|
||||
></basic-block>
|
||||
</div>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
width: 200,
|
||||
height: 120,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.wel {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
1524
src/views/wel/index.vue
Normal file
1524
src/views/wel/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
242
src/views/work/claim.vue
Normal file
242
src/views/work/claim.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-download"
|
||||
v-if="permission.work_claim_sign"
|
||||
@click.stop="handleClaim(scope.row)"
|
||||
>签收
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
v-if="permission.work_claim_detail"
|
||||
@click.stop="handleDetail(scope.row)"
|
||||
>详情
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-search"
|
||||
v-if="permission.work_claim_follow"
|
||||
@click.stop="handleImage(scope.row, scope.index)"
|
||||
>流程图
|
||||
</el-button>
|
||||
</template>
|
||||
<template #processDefinitionVersion="{ row }">
|
||||
<el-tag>v{{ row.processDefinitionVersion }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design
|
||||
v-if="this.website.design.designMode"
|
||||
is-dialog
|
||||
v-model:is-display="flowBox"
|
||||
:process-instance-id="processInstanceId"
|
||||
></flow-design>
|
||||
<el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程图"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">关 闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { claimList, claimTask } from '@/api/work/work';
|
||||
import { flowCategory, flowRoute } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
processInstanceId: '',
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
workBox: false,
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 250,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '流程分类',
|
||||
type: 'select',
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
hide: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '流程名称',
|
||||
prop: 'processDefinitionName',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '当前步骤',
|
||||
prop: 'taskName',
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'processDefinitionVersion',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '申请时间',
|
||||
prop: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'flowRoutes']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleClaim(row) {
|
||||
this.$confirm('确定签收此任务?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
return claimTask(row.taskId);
|
||||
})
|
||||
.then(() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: 'success',
|
||||
message: '操作成功!',
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDetail(row) {
|
||||
this.$router.push({
|
||||
path: `/work/process/${flowRoute(this.flowRoutes, row.category)}/detail/${
|
||||
row.processInstanceId
|
||||
}/${row.businessId}`,
|
||||
});
|
||||
},
|
||||
handleImage(row) {
|
||||
if (this.website.design.designMode) {
|
||||
this.processInstanceId = row.processInstanceId;
|
||||
} else {
|
||||
this.flowUrl = `/api/blade-flow/process/diagram-view?processInstanceId=${row.processInstanceId}`;
|
||||
}
|
||||
this.flowBox = true;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const query = {
|
||||
...this.query,
|
||||
category: params.category ? flowCategory(params.category) : null,
|
||||
};
|
||||
this.loading = true;
|
||||
claimList(page.currentPage, page.pageSize, Object.assign(params, query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
217
src/views/work/done.vue
Normal file
217
src/views/work/done.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
v-if="permission.work_done_detail"
|
||||
@click.stop="handleDetail(scope.row)"
|
||||
>详情
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-search"
|
||||
v-if="permission.work_done_follow"
|
||||
@click.stop="handleImage(scope.row, scope.index)"
|
||||
>流程图
|
||||
</el-button>
|
||||
</template>
|
||||
<template #processDefinitionVersion="{ row }">
|
||||
<el-tag>v{{ row.processDefinitionVersion }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design
|
||||
v-if="this.website.design.designMode"
|
||||
is-dialog
|
||||
v-model:is-display="flowBox"
|
||||
:process-instance-id="processInstanceId"
|
||||
></flow-design>
|
||||
<el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程图"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">关 闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { doneList } from '@/api/work/work';
|
||||
import { flowCategory, flowRoute } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
processInstanceId: '',
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
workBox: false,
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 200,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '流程分类',
|
||||
type: 'select',
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
hide: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '流程名称',
|
||||
prop: 'processDefinitionName',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '当前步骤',
|
||||
prop: 'taskName',
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'processDefinitionVersion',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '申请时间',
|
||||
prop: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'flowRoutes']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDetail(row) {
|
||||
this.$router.push({
|
||||
path: `/work/process/${flowRoute(this.flowRoutes, row.category)}/detail/${
|
||||
row.processInstanceId
|
||||
}/${row.businessId}`,
|
||||
});
|
||||
},
|
||||
handleImage(row) {
|
||||
if (this.website.design.designMode) {
|
||||
this.processInstanceId = row.processInstanceId;
|
||||
} else {
|
||||
this.flowUrl = `/api/blade-flow/process/diagram-view?processInstanceId=${row.processInstanceId}`;
|
||||
}
|
||||
this.flowBox = true;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const query = {
|
||||
...this.query,
|
||||
category: params.category ? flowCategory(params.category) : null,
|
||||
};
|
||||
this.loading = true;
|
||||
doneList(page.currentPage, page.pageSize, Object.assign(params, query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
144
src/views/work/process/leave/detail.vue
Normal file
144
src/views/work/process/leave/detail.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<el-form ref="form" :model="form" label-width="80px">
|
||||
<el-row type="flex" class="row-bg" justify="end">
|
||||
<el-form-item>
|
||||
<el-button @click="handleCancel">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>审批信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form-item label="申请人">
|
||||
<el-input :disabled="true" v-model="form.flow.assigneeName" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间">
|
||||
<el-input :disabled="true" v-model="form.startTime" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间">
|
||||
<el-input :disabled="true" v-model="form.endTime" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="请假理由">
|
||||
<el-input :disabled="true" type="textarea" v-model="form.reason" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>流程信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-row type="flex" class="row-bg">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
:key="flow.id"
|
||||
:timestamp="flow.createTime"
|
||||
v-for="flow in flowList"
|
||||
placement="top"
|
||||
>
|
||||
<el-card shadow="hover">
|
||||
<p>
|
||||
{{ flow.assigneeName }} 在 [{{ flow.createTime }}] 开始处理 [{{
|
||||
flow.historyActivityName
|
||||
}}] 环节
|
||||
</p>
|
||||
<p v-if="flow.historyActivityDurationTime !== ''">
|
||||
任务历时 [{{ flow.historyActivityDurationTime }}]
|
||||
</p>
|
||||
<p v-if="flow.comment !== ''">批复意见: [{{ flow.comment }}]</p>
|
||||
<p v-if="flow.endTime !== ''">结束时间: [{{ flow.endTime }}]</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>流程跟踪</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-row class="row-bg">
|
||||
<flow-design
|
||||
style="width: 100%"
|
||||
:is-display="true"
|
||||
:process-instance-id="processInstanceId"
|
||||
></flow-design>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { historyFlowList, leaveDetail } from '@/api/work/process';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
businessId: '',
|
||||
processInstanceId: '',
|
||||
src: '',
|
||||
flowList: [],
|
||||
form: {
|
||||
flow: {
|
||||
assigneeName: '',
|
||||
},
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
reason: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
if (to.path !== from.path) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (!this.website.design.designMode) {
|
||||
this.src = `/blade-flow/process/diagram-view?processInstanceId=${
|
||||
this.$route.params.processInstanceId
|
||||
}&t=${new Date().getTime()}`;
|
||||
}
|
||||
this.processInstanceId = this.$route.params.processInstanceId;
|
||||
this.businessId = this.$route.params.businessId;
|
||||
if (func.isEmpty(this.processInstanceId) || func.isEmpty(this.businessId)) {
|
||||
return;
|
||||
}
|
||||
historyFlowList(this.processInstanceId).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.flowList = data.data;
|
||||
}
|
||||
});
|
||||
leaveDetail(this.businessId).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.form = data.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCancel() {
|
||||
this.$router.$avueRouter.closeTag();
|
||||
this.$router.push({ path: `/work/start` });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
114
src/views/work/process/leave/form.vue
Normal file
114
src/views/work/process/leave/form.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<basic-container>
|
||||
<avue-form :option="option" v-model="form" @submit="handleSubmit" />
|
||||
</basic-container>
|
||||
<basic-container v-if="this.website.design.designMode">
|
||||
<flow-design :is-display="true" :process-definition-id="processDefinitionId"></flow-design>
|
||||
</basic-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { leaveProcess } from '@/api/work/process';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
processDefinitionId: '',
|
||||
form: {},
|
||||
option: {
|
||||
group: [
|
||||
{
|
||||
icon: 'el-icon-view',
|
||||
label: '请假基础信息',
|
||||
prop: 'group1',
|
||||
column: [
|
||||
{
|
||||
label: '审批人员',
|
||||
prop: 'taskUser',
|
||||
type: 'select',
|
||||
dicUrl: `/blade-system/user/user-list`,
|
||||
props: {
|
||||
label: 'account',
|
||||
value: 'id',
|
||||
},
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择审批人员',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '开始时间',
|
||||
prop: 'startTime',
|
||||
type: 'datetime',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择开始时间',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '结束时间',
|
||||
prop: 'endTime',
|
||||
type: 'datetime',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择结束时间',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '请假理由',
|
||||
prop: 'reason',
|
||||
type: 'textarea',
|
||||
span: 24,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入请假理由',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.website.design.designMode) {
|
||||
this.processDefinitionId = this.$route.params.processDefinitionId;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmit() {
|
||||
const params = {
|
||||
processDefinitionId: this.$route.params.processDefinitionId,
|
||||
...this.form,
|
||||
};
|
||||
leaveProcess(params).then(resp => {
|
||||
const data = resp.data;
|
||||
if (data.success) {
|
||||
this.$message.success(data.msg);
|
||||
this.$router.$avueRouter.closeTag();
|
||||
this.$router.push({ path: `/work/start` });
|
||||
} else {
|
||||
this.$message.error(data.msg || '提交失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
203
src/views/work/process/leave/handle.vue
Normal file
203
src/views/work/process/leave/handle.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-row type="flex" class="row-bg" justify="end">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleAgree">同意</el-button>
|
||||
<el-button type="danger" @click="handleDisagree">驳回</el-button>
|
||||
<el-button @click="handleCancel">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>审批信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form-item label="申请人">
|
||||
<el-input :disabled="true" v-model="form.flow.assigneeName" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间">
|
||||
<el-input :disabled="true" v-model="form.startTime" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间">
|
||||
<el-input :disabled="true" v-model="form.endTime" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="请假理由">
|
||||
<el-input :disabled="true" type="textarea" v-model="form.reason" />
|
||||
</el-form-item>
|
||||
<el-form-item label="批复意见">
|
||||
<el-input type="textarea" v-model="form.comment" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>流程信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-row type="flex" class="row-bg">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
:key="flow.id"
|
||||
:timestamp="flow.createTime"
|
||||
v-for="flow in flowList"
|
||||
placement="top"
|
||||
>
|
||||
<el-card shadow="hover">
|
||||
<p>
|
||||
{{ flow.assigneeName }} 在 [{{ flow.createTime }}] 开始处理 [{{
|
||||
flow.historyActivityName
|
||||
}}] 环节
|
||||
</p>
|
||||
<p v-if="flow.historyActivityDurationTime !== ''">
|
||||
任务历时 [{{ flow.historyActivityDurationTime }}]
|
||||
</p>
|
||||
<p v-if="flow.comment !== ''">批复意见: [{{ flow.comment }}]</p>
|
||||
<p v-if="flow.endTime !== ''">结束时间: [{{ flow.endTime }}]</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div>
|
||||
<span>流程跟踪</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-row v-if="this.website.design.designMode">
|
||||
<flow-design
|
||||
style="width: 100%"
|
||||
:is-display="true"
|
||||
:process-instance-id="processInstanceId"
|
||||
></flow-design>
|
||||
</el-row>
|
||||
<el-row v-else type="flex" class="row-bg">
|
||||
<img :src="src" alt="design" />
|
||||
</el-row>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { historyFlowList, leaveDetail } from '@/api/work/process';
|
||||
import { completeTask } from '@/api/work/work';
|
||||
import func from '@/utils/func';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
taskId: '',
|
||||
businessId: '',
|
||||
processInstanceId: '',
|
||||
src: '',
|
||||
flowList: [],
|
||||
form: {
|
||||
flow: {
|
||||
assigneeName: '',
|
||||
},
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
reason: '',
|
||||
comment: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
if (to.path !== from.path) {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (!this.website.design.designMode) {
|
||||
this.src = `/blade-flow/process/diagram-view?processInstanceId=${
|
||||
this.$route.params.processInstanceId
|
||||
}&t=${new Date().getTime()}`;
|
||||
}
|
||||
this.taskId = this.$route.params.taskId;
|
||||
this.processInstanceId = this.$route.params.processInstanceId;
|
||||
this.businessId = this.$route.params.businessId;
|
||||
if (
|
||||
func.isEmpty(this.taskId) ||
|
||||
func.isEmpty(this.processInstanceId) ||
|
||||
func.isEmpty(this.businessId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
historyFlowList(this.processInstanceId).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.flowList = data.data;
|
||||
}
|
||||
});
|
||||
leaveDetail(this.businessId).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.form = data.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleAgree() {
|
||||
if (!this.form.comment) {
|
||||
this.$message.warning('请先填写批复意见');
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
taskId: this.taskId,
|
||||
processInstanceId: this.processInstanceId,
|
||||
flag: 'ok',
|
||||
comment: this.form.comment,
|
||||
};
|
||||
completeTask(params).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.$message.success(data.msg);
|
||||
this.$router.$avueRouter.closeTag();
|
||||
this.$router.push({ path: `/work/start` });
|
||||
} else {
|
||||
this.$message.error(data.msg || '提交失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDisagree() {
|
||||
if (!this.form.comment) {
|
||||
this.$message.warning('请先填写批复意见');
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
taskId: this.taskId,
|
||||
processInstanceId: this.processInstanceId,
|
||||
comment: this.form.comment,
|
||||
};
|
||||
completeTask(params).then(res => {
|
||||
const data = res.data;
|
||||
if (data.success) {
|
||||
this.$message.success(data.msg);
|
||||
this.$router.$avueRouter.closeTag();
|
||||
this.$router.push({ path: `/work/start` });
|
||||
} else {
|
||||
this.$message.error(data.msg || '提交失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCancel() {
|
||||
this.$router.$avueRouter.closeTag();
|
||||
this.$router.push({ path: `/work/start` });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
226
src/views/work/send.vue
Normal file
226
src/views/work/send.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
v-model:page="page"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
v-if="permission.work_send_detail"
|
||||
@click.stop="handleDetail(scope.row)"
|
||||
>详情
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-search"
|
||||
v-if="permission.work_send_follow"
|
||||
@click.stop="handleImage(scope.row, scope.index)"
|
||||
>流程图
|
||||
</el-button>
|
||||
</template>
|
||||
<template #processDefinitionVersion="{ row }">
|
||||
<el-tag>v{{ row.processDefinitionVersion }}</el-tag>
|
||||
</template>
|
||||
<template #processIsFinished="{ row }">
|
||||
<el-tag>{{ row.processIsFinished === 'finished' ? '已完成' : '未完成' }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design
|
||||
v-if="this.website.design.designMode"
|
||||
is-dialog
|
||||
v-model:is-display="flowBox"
|
||||
:process-instance-id="processInstanceId"
|
||||
></flow-design>
|
||||
<el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程图"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">关 闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { sendList } from '@/api/work/work';
|
||||
import { flowCategory, flowRoute } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
processInstanceId: '',
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
workBox: false,
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 200,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '流程分类',
|
||||
type: 'select',
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
hide: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '流程名称',
|
||||
prop: 'processDefinitionName',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '当前步骤',
|
||||
prop: 'taskName',
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'processDefinitionVersion',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '流程进度',
|
||||
prop: 'processIsFinished',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '申请时间',
|
||||
prop: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'flowRoutes']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleDetail(row) {
|
||||
this.$router.push({
|
||||
path: `/work/process/${flowRoute(this.flowRoutes, row.category)}/detail/${
|
||||
row.processInstanceId
|
||||
}/${row.businessId}`,
|
||||
});
|
||||
},
|
||||
handleImage(row) {
|
||||
if (this.website.design.designMode) {
|
||||
this.processInstanceId = row.processInstanceId;
|
||||
} else {
|
||||
this.flowUrl = `/api/blade-flow/process/diagram-view?processInstanceId=${row.processInstanceId}`;
|
||||
}
|
||||
this.flowBox = true;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const query = {
|
||||
...this.query,
|
||||
category: params.category ? flowCategory(params.category) : null,
|
||||
};
|
||||
this.loading = true;
|
||||
sendList(page.currentPage, page.pageSize, Object.assign(params, query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
248
src/views/work/start.vue
Normal file
248
src/views/work/start.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-radio-group v-model="mode">
|
||||
<el-radio-button label="1">通用流程</el-radio-button>
|
||||
<el-radio-button label="2">定制流程</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-video-play"
|
||||
v-if="permission.work_start_flow"
|
||||
@click.stop="handleStart(scope.row)"
|
||||
>发起
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-search"
|
||||
v-if="permission.work_start_image"
|
||||
@click.stop="handleImage(scope.row, scope.index)"
|
||||
>流程图
|
||||
</el-button>
|
||||
</template>
|
||||
<template #tenantId="{ row }">
|
||||
<el-tag>{{ row.tenantId === '' ? '通用' : row.tenantId }}</el-tag>
|
||||
</template>
|
||||
<template #version="{ row }">
|
||||
<el-tag>v{{ row.version }}</el-tag>
|
||||
</template>
|
||||
<template #suspensionState="{ row }">
|
||||
<el-tag>{{ row.suspensionState === 1 ? '激活' : '挂起' }}</el-tag>
|
||||
</template>
|
||||
<template #category="{ row }">
|
||||
<el-tag>{{ row.categoryName }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design
|
||||
v-if="this.website.design.designMode"
|
||||
is-dialog
|
||||
v-model:is-display="flowBox"
|
||||
:process-definition-id="processDefinitionId"
|
||||
></flow-design>
|
||||
<el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程图"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">关 闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { startList } from '@/api/work/work';
|
||||
import { flowCategory, flowRoute } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
mode: '1',
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
processDefinitionId: '',
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
workBox: false,
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 200,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '租户编号',
|
||||
prop: 'tenantId',
|
||||
slot: true,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
label: '流程分类',
|
||||
type: 'select',
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: '流程标识',
|
||||
prop: 'key',
|
||||
},
|
||||
{
|
||||
label: '流程名称',
|
||||
prop: 'name',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'version',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'suspensionState',
|
||||
slot: true,
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
label: '部署时间',
|
||||
prop: 'deploymentTime',
|
||||
width: 170,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
mode() {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'flowRoutes']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleStart(row) {
|
||||
this.$router.push({
|
||||
path: `/work/process/${flowRoute(this.flowRoutes, row.category)}/form/${row.id}`,
|
||||
});
|
||||
},
|
||||
handleImage(row) {
|
||||
if (this.website.design.designMode) {
|
||||
this.processDefinitionId = row.id;
|
||||
} else {
|
||||
this.flowUrl = `/api/blade-flow/process/resource-view?processDefinitionId=${row.id}`;
|
||||
}
|
||||
this.flowBox = true;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const query = {
|
||||
...this.query,
|
||||
category: params.category ? flowCategory(params.category) : null,
|
||||
mode: this.mode,
|
||||
};
|
||||
this.loading = true;
|
||||
startList(page.currentPage, page.pageSize, Object.assign(params, query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
232
src/views/work/todo.vue
Normal file
232
src/views/work/todo.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<template>
|
||||
<basic-container>
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
ref="crud"
|
||||
v-model="form"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-video-play"
|
||||
v-if="permission.work_todo_handle"
|
||||
@click.stop="handleWork(scope.row)"
|
||||
>处理
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
v-if="permission.work_todo_detail"
|
||||
@click.stop="handleDetail(scope.row)"
|
||||
>详情
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-search"
|
||||
v-if="permission.work_todo_follow"
|
||||
@click.stop="handleImage(scope.row, scope.index)"
|
||||
>流程图
|
||||
</el-button>
|
||||
</template>
|
||||
<template #processDefinitionVersion="{ row }">
|
||||
<el-tag>v{{ row.processDefinitionVersion }}</el-tag>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<flow-design
|
||||
v-if="this.website.design.designMode"
|
||||
is-dialog
|
||||
v-model:is-display="flowBox"
|
||||
:process-instance-id="processInstanceId"
|
||||
></flow-design>
|
||||
<el-dialog v-else title="流程图" append-to-body v-model="flowBox" :fullscreen="true">
|
||||
<iframe
|
||||
:src="flowUrl"
|
||||
width="100%"
|
||||
height="700"
|
||||
title="流程图"
|
||||
frameBorder="no"
|
||||
border="0"
|
||||
marginWidth="0"
|
||||
marginHeight="0"
|
||||
scrolling="no"
|
||||
allowTransparency="yes"
|
||||
>
|
||||
</iframe>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="flowBox = false">关 闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { todoList } from '@/api/work/work';
|
||||
import { flowCategory, flowRoute } from '@/utils/flow';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
selectionId: '',
|
||||
selectionList: [],
|
||||
query: {},
|
||||
loading: true,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
processInstanceId: '',
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
workBox: false,
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
editBtn: false,
|
||||
addBtn: false,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
dialogWidth: 900,
|
||||
menuWidth: 250,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
{
|
||||
label: '流程分类',
|
||||
type: 'select',
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=flow',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
dataType: 'number',
|
||||
slot: true,
|
||||
prop: 'category',
|
||||
search: true,
|
||||
hide: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '流程名称',
|
||||
prop: 'processDefinitionName',
|
||||
search: true,
|
||||
},
|
||||
{
|
||||
label: '当前步骤',
|
||||
prop: 'taskName',
|
||||
},
|
||||
{
|
||||
label: '流程版本',
|
||||
prop: 'processDefinitionVersion',
|
||||
slot: true,
|
||||
width: 85,
|
||||
},
|
||||
{
|
||||
label: '申请时间',
|
||||
prop: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'flowRoutes']),
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
ids.push(ele.id);
|
||||
});
|
||||
return ids.join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
handleWork(row) {
|
||||
this.$router.push({
|
||||
path: `/work/process/${flowRoute(this.flowRoutes, row.category)}/handle/${row.taskId}/${
|
||||
row.processInstanceId
|
||||
}/${row.businessId}`,
|
||||
});
|
||||
},
|
||||
handleDetail(row) {
|
||||
this.$router.push({
|
||||
path: `/work/process/${flowRoute(this.flowRoutes, row.category)}/detail/${
|
||||
row.processInstanceId
|
||||
}/${row.businessId}`,
|
||||
});
|
||||
},
|
||||
handleImage(row) {
|
||||
if (this.website.design.designMode) {
|
||||
this.processInstanceId = row.processInstanceId;
|
||||
} else {
|
||||
this.flowUrl = `/api/blade-flow/process/diagram-view?processInstanceId=${row.processInstanceId}`;
|
||||
}
|
||||
this.flowBox = true;
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
const query = {
|
||||
...this.query,
|
||||
category: params.category ? flowCategory(params.category) : null,
|
||||
};
|
||||
this.loading = true;
|
||||
todoList(page.currentPage, page.pageSize, Object.assign(params, query)).then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records;
|
||||
this.loading = false;
|
||||
this.selectionClear();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user