Merge remote-tracking branch 'origin/master'
This commit is contained in:
17
web/src/views/system/columns/api.ts
Normal file
17
web/src/views/system/columns/api.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { PageQuery } from './types'
|
||||
|
||||
export function getRoleList(query: PageQuery) {
|
||||
return request({
|
||||
url: '/api/system/role/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getModelList() {
|
||||
return request({
|
||||
url: '/api/system/column/get_models/',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
115
web/src/views/system/columns/components/ColumnsFormCom/index.vue
Normal file
115
web/src/views/system/columns/components/ColumnsFormCom/index.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="columns-form-com">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
|
||||
<el-form-item label="字段名" prop="field_name">
|
||||
<el-input v-model="formData.field_name" placeholder="请输入字段名" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="列名" prop="title">
|
||||
<el-input v-model="formData.title" placeholder="请输入列名" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="创建显示">
|
||||
<el-switch v-model="formData.is_create" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="编辑显示">
|
||||
<el-switch v-model="formData.is_update" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="查询显示">
|
||||
<el-switch v-model="formData.is_query" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="btnLoading"> 确定 </el-button>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { addColumnsData, updateColumnsData } from '../ColumnsTableCom/api';
|
||||
import { successNotification } from '/@/utils/message';
|
||||
import { CurrentInfoType, ColumnsFormDataType } from '../../types';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
currentInfo: {
|
||||
type: Object as () => CurrentInfoType,
|
||||
required: true,
|
||||
default: () => {},
|
||||
},
|
||||
initFormData: {
|
||||
type: Object as () => Partial<ColumnsFormDataType>,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['drawerClose']);
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const formRules = reactive({
|
||||
field_name: [{ required: true, message: '请输入字段名!', trigger: 'blur' }],
|
||||
title: [{ required: true, message: '请输入列名!', trigger: 'blur' }],
|
||||
});
|
||||
|
||||
let formData = reactive<ColumnsFormDataType>({
|
||||
field_name: '',
|
||||
title: '',
|
||||
is_create: true,
|
||||
is_update: true,
|
||||
is_query: true,
|
||||
});
|
||||
let btnLoading = ref(false);
|
||||
|
||||
const setMenuFormData = () => {
|
||||
if (props.initFormData?.id) {
|
||||
formData.id = props.initFormData?.id || '';
|
||||
formData.field_name = props.initFormData.field_name || '';
|
||||
formData.title = props.initFormData.title || '';
|
||||
formData.is_create = !!props.initFormData.is_create;
|
||||
formData.is_update = !!props.initFormData.is_update;
|
||||
formData.is_query = !!props.initFormData.is_query;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
formRef.value?.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
try {
|
||||
btnLoading.value = true;
|
||||
let res;
|
||||
if (formData.id) {
|
||||
res = await updateColumnsData({ ...formData, ...props.currentInfo });
|
||||
} else {
|
||||
res = await addColumnsData({ ...formData, ...props.currentInfo });
|
||||
}
|
||||
if (res?.code === 2000) {
|
||||
successNotification(res.msg as string);
|
||||
handleClose('submit');
|
||||
}
|
||||
} finally {
|
||||
btnLoading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = (type: string = '') => {
|
||||
emit('drawerClose', type);
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
setMenuFormData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.columns-form-com {
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
import { request } from '/@/utils/service';
|
||||
import { CurrentInfoType, AddColumnsDataType } from '../../types'
|
||||
|
||||
export function getColumnsData(query: any) {
|
||||
return request({
|
||||
url: '/api/system/column/',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function automatchColumnsData(data: CurrentInfoType) {
|
||||
return request({
|
||||
url: '/api/system/column/auto_match_fields/',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function addColumnsData(data: AddColumnsDataType) {
|
||||
return request({
|
||||
url: '/api/system/column/',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteColumnsData(id: number) {
|
||||
return request({
|
||||
url: `/api/system/column/${id}/`,
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
||||
export function updateColumnsData(data: AddColumnsDataType) {
|
||||
return request({
|
||||
url: `/api/system/column/${data.id}/`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="columns-table-com">
|
||||
<p class="ctc-title">字段权限</p>
|
||||
|
||||
<div class="ctc-head">
|
||||
<el-button type="primary" @click="handleUpdateColumn('create')">新增</el-button>
|
||||
<el-button type="primary" @click="handleAutomatch">自动匹配</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="state.data" border v-loading="state.loading" class="ctc-table">
|
||||
<el-table-column prop="field_name" label="字段名" />
|
||||
<el-table-column prop="title" label="列名" />
|
||||
<el-table-column prop="is_create" label="创建显示">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.is_create" @change="handleChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="is_update" label="编辑显示">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.is_update" @change="handleChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="is_query" label="查询显示">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.is_query" @change="handleChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" @click="handleUpdateColumn('update', scope.row)">编辑</el-button>
|
||||
<el-button type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="ctc-pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="searchParams.page"
|
||||
v-model:page-size="searchParams.limit"
|
||||
:page-sizes="[5, 10, 20, 50]"
|
||||
:total="state.total"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-drawer v-model="drawerVisible" title="字段权限" direction="rtl" size="500px" :close-on-click-modal="false" :before-close="handleDrawerClose">
|
||||
<ColumnsFormCom v-if="drawerVisible" :currentInfo="props.currentInfo" :initFormData="drawerFormData" @drawerClose="handleDrawerClose" />
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import ColumnsFormCom from '../ColumnsFormCom/index.vue';
|
||||
import { getColumnsData, automatchColumnsData, deleteColumnsData, updateColumnsData } from './api';
|
||||
import { successNotification, warningNotification } from '/@/utils/message';
|
||||
import { CurrentInfoType, ColumnsFormDataType, AddColumnsDataType } from '../../types';
|
||||
|
||||
const props = defineProps({
|
||||
currentInfo: {
|
||||
type: Object as () => CurrentInfoType,
|
||||
required: true,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
let searchParams = reactive({
|
||||
page: 1,
|
||||
limit: 20,
|
||||
});
|
||||
let state = reactive({
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
});
|
||||
let drawerVisible = ref(false);
|
||||
let drawerFormData = ref<Partial<ColumnsFormDataType>>({});
|
||||
|
||||
const fetchData = async (query: CurrentInfoType = props.currentInfo) => {
|
||||
try {
|
||||
state.loading = true;
|
||||
const res = await getColumnsData({ ...searchParams, ...query });
|
||||
if (res?.code === 2000) {
|
||||
state.data = res.data;
|
||||
state.total = res.total;
|
||||
}
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 自动匹配列
|
||||
*/
|
||||
const handleAutomatch = async () => {
|
||||
if (props.currentInfo?.role && props.currentInfo?.model && props.currentInfo?.app) {
|
||||
const res = await automatchColumnsData(props.currentInfo);
|
||||
if (res?.code === 2000) {
|
||||
successNotification('匹配成功');
|
||||
fetchData();
|
||||
}
|
||||
return;
|
||||
}
|
||||
warningNotification('请选择角色和模型表!');
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增 or 编辑
|
||||
*/
|
||||
const handleUpdateColumn = (type: string, record?: ColumnsFormDataType) => {
|
||||
if (props.currentInfo?.role && props.currentInfo?.model && props.currentInfo?.app) {
|
||||
if (type === 'update' && record) {
|
||||
drawerFormData.value = record;
|
||||
}
|
||||
drawerVisible.value = true;
|
||||
return;
|
||||
}
|
||||
warningNotification('请选择角色和模型表!');
|
||||
};
|
||||
const handleDrawerClose = (type?: string) => {
|
||||
if (type === 'submit') {
|
||||
fetchData();
|
||||
}
|
||||
drawerVisible.value = false;
|
||||
drawerFormData.value = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除 deleteColumnsData
|
||||
*/
|
||||
const handleDelete = ({ id }: { id: number }) => {
|
||||
ElMessageBox.confirm('确定删除该字段吗?', '提示', {
|
||||
type: 'error',
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
})
|
||||
.then(async () => {
|
||||
const res = await deleteColumnsData(id);
|
||||
if (res?.code === 2000) {
|
||||
successNotification('删除成功');
|
||||
fetchData();
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const handleChange = (record: AddColumnsDataType) => {
|
||||
updateColumnsData(record);
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
const handleSizeChange = (limit: number) => {
|
||||
searchParams.limit = limit;
|
||||
fetchData();
|
||||
};
|
||||
const handleCurrentChange = (page: number) => {
|
||||
searchParams.page = page;
|
||||
fetchData();
|
||||
};
|
||||
|
||||
defineExpose({ fetchData });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.columns-table-com {
|
||||
height: 100%;
|
||||
.ctc-title {
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
.ctc-head {
|
||||
height: 35px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.ctc-table {
|
||||
width: 100%;
|
||||
height: calc(100% - 135px);
|
||||
margin: 10px 0;
|
||||
}
|
||||
.ctc-pagination {
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
138
web/src/views/system/columns/components/ItemCom/index.vue
Normal file
138
web/src/views/system/columns/components/ItemCom/index.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="item-com">
|
||||
<p class="item-com-title">{{ props.title }}</p>
|
||||
<ul class="item-com-list" :style="{ height: showPagination ? 'calc(100% - 75px)' : 'calc(100% - 45px)' }">
|
||||
<li
|
||||
v-for="item in state.data"
|
||||
:key="item[props.value]"
|
||||
@click="handleClick(item)"
|
||||
:class="state.current === item[props.value] ? 'item-com-item active' : 'item-com-item'"
|
||||
>
|
||||
{{ item[props.label] }}
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="showPagination" class="item-com-pagination">
|
||||
<el-pagination
|
||||
background
|
||||
small
|
||||
hide-on-single-page
|
||||
v-model:current-page="state.page"
|
||||
v-model:page-size="state.limit"
|
||||
layout="prev, pager, next"
|
||||
:pager-count="5"
|
||||
:total="state.total"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { RoleInfoStateType } from './types';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'role',
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: 'name',
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
},
|
||||
showPagination: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['fetchData', 'itemClick']);
|
||||
|
||||
const state = reactive<RoleInfoStateType>({
|
||||
current: '',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
data: [],
|
||||
total: 10,
|
||||
});
|
||||
|
||||
const fetchData = () => {
|
||||
emit(
|
||||
'fetchData',
|
||||
{
|
||||
page: state.page,
|
||||
limit: state.limit,
|
||||
},
|
||||
(res: { code: number; data: any[]; total: number }) => {
|
||||
if (res?.code === 2000) {
|
||||
state.data = res.data;
|
||||
state.total = res?.total || 10;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleClick = (record: any) => {
|
||||
state.current = record[props.value];
|
||||
emit('itemClick', props.type, record);
|
||||
};
|
||||
|
||||
const handleCurrentChange = (page: number) => {
|
||||
state.page = page;
|
||||
fetchData();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item-com {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.item-com-title {
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
.item-com-list {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
.item-com-item {
|
||||
padding: 10px 16px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 500ms;
|
||||
}
|
||||
.active {
|
||||
color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
transition: all 500ms;
|
||||
}
|
||||
.item-com-item:hover {
|
||||
color: var(--el-color-primary);
|
||||
transition: all 500ms;
|
||||
}
|
||||
}
|
||||
.item-com-pagination {
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
29
web/src/views/system/columns/components/ItemCom/types.ts
Normal file
29
web/src/views/system/columns/components/ItemCom/types.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export interface PageQuery {
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface RoleItemType {
|
||||
id: number | string;
|
||||
modifier_name: string;
|
||||
creator_name: string;
|
||||
create_datetime: string;
|
||||
update_datetime: string;
|
||||
description: string;
|
||||
modifier: string;
|
||||
dept_belong_id: number | string | null,
|
||||
name: string;
|
||||
key: string;
|
||||
sort: number;
|
||||
status: boolean;
|
||||
admin: boolean;
|
||||
creator: string;
|
||||
}
|
||||
|
||||
export interface RoleInfoStateType {
|
||||
current: string;
|
||||
page: number;
|
||||
limit: number;
|
||||
data: any[],
|
||||
total: number;
|
||||
}
|
||||
99
web/src/views/system/columns/index.vue
Normal file
99
web/src/views/system/columns/index.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<fs-page class="columns">
|
||||
<el-row class="columns-el-row">
|
||||
<el-col :span="4">
|
||||
<div class="columns-box columns-left">
|
||||
<ItemCom title="角色" type="role" showPagination @fetchData="fetchRoleData" @itemClick="handleClick" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="columns-box columns-center">
|
||||
<ItemCom title="模型表" type="model" label="showText" value="key" @fetchData="fetchModelData" @itemClick="handleClick" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="columns-box columns-right">
|
||||
<ColumnsTableCom ref="columnsTableRef" :currentInfo="currentInfo" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import ItemCom from './components/ItemCom/index.vue';
|
||||
import ColumnsTableCom from './components/ColumnsTableCom/index.vue';
|
||||
import { getRoleList, getModelList } from './api';
|
||||
import { PageQuery, CurrentInfoType, ModelItemType } from './types';
|
||||
|
||||
const columnsTableRef = ref<InstanceType<typeof ColumnsTableCom> | null>(null);
|
||||
let currentInfo = reactive<CurrentInfoType>({
|
||||
role: '',
|
||||
model: '',
|
||||
app: '',
|
||||
});
|
||||
|
||||
const fetchRoleData = async (query: PageQuery, callback: Function) => {
|
||||
const res = await getRoleList(query);
|
||||
callback(res);
|
||||
};
|
||||
|
||||
const fetchModelData = async (query: PageQuery, callback: Function) => {
|
||||
const res = await getModelList();
|
||||
res.data.forEach((item: ModelItemType) => {
|
||||
item.showText = `${item.app}-${item.title}(${item.key})`;
|
||||
});
|
||||
callback(res);
|
||||
};
|
||||
|
||||
const fetchTableData = () => {
|
||||
if (currentInfo.role && currentInfo.model && currentInfo.app) {
|
||||
columnsTableRef.value?.fetchData(currentInfo);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = (type: string, record: any) => {
|
||||
if (type === 'role') {
|
||||
currentInfo.role = record.id;
|
||||
}
|
||||
if (type === 'model') {
|
||||
currentInfo.model = record.key;
|
||||
currentInfo.app = record.app;
|
||||
}
|
||||
fetchTableData();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.columns {
|
||||
.columns-el-row {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.el-col {
|
||||
height: 100%;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
.columns-box {
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.columns-left {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
.columns-center {
|
||||
margin: 0 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.columns-right {
|
||||
position: relative;
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
35
web/src/views/system/columns/types.ts
Normal file
35
web/src/views/system/columns/types.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export interface PageQuery {
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface CurrentInfoType {
|
||||
role: string;
|
||||
model: string;
|
||||
app: string;
|
||||
}
|
||||
|
||||
export interface ModelItemType {
|
||||
app: string;
|
||||
key: string;
|
||||
title: string;
|
||||
showText?: string;
|
||||
}
|
||||
|
||||
export interface AddColumnsDataType extends CurrentInfoType {
|
||||
id?: number | string;
|
||||
field_name: string;
|
||||
title: string;
|
||||
is_query: boolean;
|
||||
is_create: boolean;
|
||||
is_update: boolean;
|
||||
}
|
||||
|
||||
export interface ColumnsFormDataType {
|
||||
id?: number | string;
|
||||
field_name: string;
|
||||
title: string;
|
||||
is_create: boolean;
|
||||
is_update: boolean;
|
||||
is_query: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user