This commit is contained in:
陈红丽 2024-07-11 15:17:54 +08:00
parent 15a3bc00c7
commit 676a9411a6
9 changed files with 663 additions and 423 deletions

128
src/api/data/dictionary.ts Normal file
View File

@ -0,0 +1,128 @@
import { http } from '@/utils/http/axios';
/**
* @description:
*/
export function getDictList(params?) {
return http.request({
url: '/dict/page',
method: 'GET',
params,
});
}
/**
* @description: ID获取详情
*/
export function getDictDetail(id) {
return http.request({
url: '/dict/detail/'+id,
method: 'get',
});
}
/**
* @description:
*/
export function refreshCache() {
return http.request({
url: '/dict/refreshCache',
method: 'get',
});
}
/**
* @description:
*/
export function dictAdd(data:any) {
return http.request({
url: '/dict/add',
method: 'POST',
data,
});
}
/**
* @description:
*/
export function dictUpdate(data:any) {
return http.request({
url: '/dict/update',
method: 'PUT',
data
});
}
/**
* @description:
*/
export function dictDelete(id) {
return http.request({
url: '/dict/delete/'+id,
method: 'DELETE',
});
}
/**
* @description:
*/
export function dictBatchDelete(data:any) {
return http.request({
url: '/dict/batchDelete',
method: 'DELETE',
data
});
}
/**
* @description:
*/
export function getDictItemList(params?) {
return http.request({
url: '/dict/item/page',
method: 'GET',
params,
});
}
/**
* @description: ID获取详情
*/
export function getDictItemDetail(id) {
return http.request({
url: '/dict/item/detail/'+id,
method: 'get',
});
}
/**
* @description:
*/
export function dictItemAdd(data:any) {
return http.request({
url: '/dict/item/add',
method: 'POST',
data,
});
}
/**
* @description:
*/
export function dictItemUpdate(data:any) {
return http.request({
url: '/dict/item/update',
method: 'PUT',
data
});
}
/**
* @description:
*/
export function dictItemDelete(id) {
return http.request({
url: '/dict/item/delete/'+id,
method: 'DELETE',
});
}
/**
* @description:
*/
export function dictItemBatchDelete(data:any) {
return http.request({
url: '/dict/item/batchDelete',
method: 'DELETE',
data
});
}

View File

@ -0,0 +1,13 @@
export const columns = [
{
type: 'selection',
},
{
label: '字典名称',
prop: 'name',
},
{
label: '字典编码',
prop: 'code',
},
];

View File

@ -4,18 +4,14 @@ export const columns = [
},
{
label: '字典名称',
prop: 'label',
prop: 'name',
},
{
label: '字典值',
label: '字典值',
prop: 'value',
},
{
label: '排序',
prop: 'order',
},
{
label: '创建时间',
prop: 'create_date',
prop: 'sort',
},
];

View File

@ -0,0 +1,127 @@
<template>
<PageWrapper>
<BasicTable :columns="columns" :request="loadDataTable" :row-key="(row) => row.id" ref="tableRef"
:actionColumn="actionColumn" @selection-change="onSelectionChange">
<template #tableTitle>
<el-space>
<el-input type="text" v-model="params.name" placeholder="请输入字典项名称">
<template #prefix>
<el-icon class="el-input__icon">
<SearchOutlined />
</el-icon>
</template>
</el-input>
<el-button type="primary" @click="reloadTable"> 查询 </el-button>
<el-button type="primary" @click="handleAdd">新建</el-button>
<el-button type="danger" :disabled="!selectionData.length"@click="handleDelete()">删除</el-button>
</el-space>
</template>
</BasicTable>
<editDialog v-if="editVisible" :dictId="dictId" :dictItemId="dictItemId" v-model:visible="editVisible" @success="reloadTable">
</editDialog>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, reactive, h, nextTick, watch, defineAsyncComponent } from 'vue';
import { SearchOutlined } from '@vicons/antd';
import { BasicTable, TableAction } from '@/components/Table';
import { getDictItemList, dictItemDelete, dictItemBatchDelete } from '@/api/data/dictionary';
import { columns } from './columnsItem';
import { message, confirm } from "@/utils/auth";
const editDialog = defineAsyncComponent(() =>
import('./editItem.vue')
)
const tableRef = ref();
const editVisible = ref(false)
const selectionData = ref([])
const dictItemId = ref(0)
const params = ref({
name: '',
});
const props = defineProps({
dictId: {
type: Number,
required: true,
default: 0
}
});
watch(
() => props.dictId,
async (value) => {
if(value){
await nextTick()
reloadTable()
}
}
)
const actionColumn = reactive({
lable: 150,
title: '操作',
prop: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'text',
actions: [
{
label: '编辑',
type: 'warning',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
type: 'danger',
onClick: handleDelete.bind(null, record),
}
],
});
},
});
//
function reloadTable() {
tableRef.value.reload({ pageNo: 1 });
}
//
const loadDataTable = async (res) => {
const result = await getDictItemList({ ...params.value, dictId:props.dictId, ...res });
return result;
};
//
const handleAdd = async () => {
dictItemId.value=0
await nextTick();
editVisible.value=true
};
//
const handleEdit = async (record: Recordable) => {
dictItemId.value=record.row.id
await nextTick();
editVisible.value=true
};
//
async function handleDelete(record: Recordable) {
let ids = []
if (!record) {
ids = selectionData.value.map(({ id }) => id);
}
await confirm('确定要删除?');
record ? await dictItemDelete(record.row.id) : await dictItemBatchDelete(ids);
message("删除成功");
reloadTable()
}
function onSelectionChange(value) {
selectionData.value = value
}
</script>
<style lang="scss" scoped>
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<el-dialog
v-model="props.visible"
:title="props.dictId?'编辑':'新增'"
:append-to-body="true"
width="500"
:close-on-click-modal="false"
:before-close="dialogClose"
>
<el-form
class="ls-form"
ref="formRef"
:model="formData"
label-width="80px"
>
<el-form-item
label="字典名称"
prop="name"
:rules="{ required: true, message: '请输入字典名称', trigger: 'blur' }"
>
<el-input
class="ls-input"
v-model="formData.name"
placeholder="请输入字典名称"
clearable
/>
</el-form-item>
<el-form-item
label="字典编码"
prop="code"
:rules="{ required: true, message: '请输入字典编码', trigger: 'blur' }"
>
<el-input
class="ls-input"
v-model="formData.code"
placeholder="请输入字典编码"
clearable
/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort"/>
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="formData.note" type="textarea" placeholder="请输入备注" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogClose">取消</el-button>
<el-button :loading="subLoading" type="primary" @click="submit">
确定
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import type {FormInstance} from "element-plus";
import {dictAdd,dictUpdate,getDictDetail} from "@/api/data/dictionary";
import {onMounted, reactive, shallowRef} from "vue";
import {message} from "@/utils/auth";
import {useLockFn} from "@/utils/useLockFn";
const emit = defineEmits(["success", "update:visible"]);
const formRef = shallowRef<FormInstance>();
const formData = reactive({
id: "",
name: "",
code: "",
sort: 0,
note:'',
});
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false
},
dictId: {
type: Number,
required: true,
default: 0
}
});
const handleSubmit = async () => {
await formRef.value?.validate();
props.dictId ? await dictUpdate(formData) : await dictAdd(formData);
message("操作成功");
emit("update:visible", false);
emit("success");
};
const dialogClose = () => {
emit("update:visible", false);
};
const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
const setFormData = async () => {
const data = await getDictDetail(props.dictId);
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key];
}
}
};
onMounted(() => {
if (props.dictId) {
setFormData();
}
});
</script>

View File

@ -0,0 +1,123 @@
<template>
<el-dialog
v-model="props.visible"
:title="props.dictItemId?'编辑字典项':'新增字典项'"
:append-to-body="true"
width="500"
:close-on-click-modal="false"
:before-close="dialogClose"
>
<el-form
class="ls-form"
ref="formRef"
:model="formData"
label-width="80px"
>
<el-form-item
label="名称"
prop="name"
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
>
<el-input
class="ls-input"
v-model="formData.name"
placeholder="请输入名称"
clearable
/>
</el-form-item>
<el-form-item
label="值"
prop="value"
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
>
<el-input
class="ls-input"
v-model="formData.value"
placeholder="请输入名称"
clearable
/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort"/>
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="formData.note" type="textarea" placeholder="请输入备注" clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogClose">取消</el-button>
<el-button :loading="subLoading" type="primary" @click="submit">
确定
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import type {FormInstance} from "element-plus";
import {getDictItemDetail,dictItemAdd,dictItemUpdate} from "@/api/data/dictionary";
import {onMounted, reactive, shallowRef} from "vue";
import {message} from "@/utils/auth";
import {useLockFn} from "@/utils/useLockFn";
const emit = defineEmits(["success", "update:visible"]);
const formRef = shallowRef<FormInstance>();
const formData = reactive({
id: "",
name: "",
value: "",
sort: 0,
note:''
});
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false
},
dictId: {
type: Number,
required: true,
default: 0
},
dictItemId: {
type: Number,
required: true,
default: 0
}
});
const handleSubmit = async () => {
await formRef.value?.validate();
props.dictItemId ? await dictItemUpdate({...formData,dictId:props.dictId}) : await dictItemAdd({...formData,dictId:props.dictId});
message("操作成功");
emit("update:visible", false);
emit("success");
};
const dialogClose = () => {
emit("update:visible", false);
};
const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
const setFormData = async () => {
const data = await getDictItemDetail(props.dictItemId);
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key];
}
}
};
onMounted(() => {
if (props.dictItemId) {
setFormData();
}
});
</script>

View File

@ -0,0 +1,151 @@
<template>
<PageWrapper>
<el-row :gutter="10" class="mt-3">
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8" class="mb-4">
<el-card shadow="hover" class="border-0">
<template #header>
<el-row>
<el-col :span="20">
<el-input type="text" v-model="params.name" clearable placeholder="请输入字典名称">
<template #prefix>
<el-icon class="el-input__icon">
<SearchOutlined />
</el-icon>
</template>
</el-input>
</el-col>
<el-col :span="4" style="text-align: right;">
<el-button type="primary" @click="reloadTable"> 查询 </el-button>
</el-col>
</el-row>
<div style="margin-top:15px;">
<el-button type="primary" @click="dictRefresh">刷新缓存</el-button>
<el-button type="primary" @click="addDict">新建</el-button>
<el-button type="danger" :disabled="!selectionData.length" @click="handleDelete()">删除</el-button>
</div>
</template>
<BasicTable :columns="columns" :showTableSetting="false" :request="loadDataTable" :row-key="(row) => row.id"
ref="tableRef" :actionColumn="actionColumn" @selection-change="onSelectionChange" highlight-current-row
@row-click="onCheckedRow" />
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16" class="mb-4">
<el-card shadow="hover" class="mb-4 border-0 proCard">
<dictItem :dictId="dictId" v-if="dictItemShow"></dictItem>
</el-card>
</el-col>
</el-row>
<editDialog v-if="editVisible" :dictId="dictId" v-model:visible="editVisible" @success="reloadTable">
</editDialog>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, reactive, h, nextTick, defineAsyncComponent } from 'vue';
import { SearchOutlined } from '@vicons/antd';
import { BasicTable, TableAction } from '@/components/Table';
import { getDictList, refreshCache, dictDelete, dictBatchDelete } from '@/api/data/dictionary';
import { columns } from './columns';
import dictItem from './dictItem.vue';
import { message, confirm } from "@/utils/auth";
const editDialog = defineAsyncComponent(() =>
import('./edit.vue')
)
const dictId = ref(0)
const dictItemShow = ref(false)
const tableRef = ref();
const selectedKey = ref('');
const rowKeysName = ref('');
const currentRow = ref();
const editVisible = ref(false)
const selectionData = ref([])
const params = ref({
name: '',
});
const actionColumn = reactive({
lable: 150,
title: '操作',
prop: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'text',
actions: [
{
label: '编辑',
type: 'warning',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
type: 'danger',
onClick: handleDelete.bind(null, record),
}
],
});
},
});
//
const addDict = async () => {
dictId.value=0
await nextTick();
editVisible.value=true
};
//
const handleEdit = async (record: Recordable) => {
dictId.value=record.row.id
await nextTick();
editVisible.value=true
};
//
function onCheckedRow(row) {
dictId.value = row.id
}
//
function reloadTable() {
tableRef.value.reload({ pageNo: 1 });
}
//;
const loadDataTable = async (res) => {
const result = await getDictList({ ...params.value, ...res });
dictId.value = result?.records[0]?.id
dictItemShow.value = true
nextTick(() => {
const tables = tableRef.value.getTableRef();
tables.setCurrentRow(result?.records[0])
})
return result;
};
//
async function dictRefresh() {
await refreshCache();
message("刷新成功");
}
//
async function handleDelete(record: Recordable) {
let ids = []
if (!record) {
ids = selectionData.value.map(({ id }) => id);
}
await confirm('确定要删除?');
record ? await dictDelete(record.row.id) : await dictBatchDelete(ids);
message("删除成功");
reloadTable()
}
function onSelectionChange(value) {
selectionData.value = value
}
</script>
<style lang="scss" scoped>
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@ -1,139 +0,0 @@
<template>
<basicModal @register="modalRegister" ref="modalRef" @ok="okModal" @close="onClose">
<template #default>
<BasicForm @register="registerForm">
<template #passwordSlot="{ model, field }">
<Password ref="passwordRef" v-model="model[field]" :required="false" block />
</template>
<template #rePasswordSlot="{ model, field }">
<Password ref="passwordRef" v-model="model[field]" :required="false" block />
</template>
</BasicForm>
</template>
</basicModal>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { BasicForm, useForm } from '@/components/Form/index';
import { basicModal, useModal } from '@/components/Modal';
import { Password } from '@/components/Password';
import { ElMessage } from 'element-plus';
const props = defineProps({
title: {
type: String,
default: '添加字典',
},
isEdit: {
type: Boolean,
default: false,
},
createType: {
type: Number,
default: 1,
},
});
const typeName = computed(() => {
return props.createType === 1 ? '' : '项';
});
//
const schemas = computed(() => [
{
field: 'label',
component: 'Input',
label: `字典${typeName.value}名称`,
componentProps: {
placeholder: `请输入字典${typeName.value}名称`,
},
rules: [
{
required: true,
message: `请输入字典${typeName.value}名称`,
trigger: ['blur'],
},
],
},
{
field: 'value',
component: 'Input',
label: `字典${typeName.value}`,
componentProps: {
placeholder: `请输入字典${typeName.value}`,
},
rules: [
{
required: true,
message: `请输入字典${typeName.value}`,
trigger: ['blur'],
},
],
},
{
field: 'order',
component: 'InputNumber',
label: '排序',
componentProps: {
placeholder: '请输入排序',
},
rules: [{ required: true, type: 'number', message: '请输入排序', trigger: ['blur'] }],
},
{
field: 'remark',
component: 'Input',
label: '备注',
componentProps: {
placeholder: '请输入备注',
type: 'textarea',
},
},
]);
const title = ref(props.title);
//form
const [registerForm, { submit, setFieldsValue, resetFields }] = useForm({
gridProps: { cols: 1 },
collapsed: false,
labelWidth: 100,
layout: 'horizontal',
submitButtonText: '确定',
showActionButtonGroup: false,
schemas,
});
//
const [modalRegister, { openModal, closeModal, setSubLoading, setProps }] = useModal({
title,
subBtuText: '确定',
width: 450,
});
//
async function okModal() {
const formRes = await submit();
if (formRes) {
// const { isEdit } = props;
ElMessage.error('抱歉,您没有操作权限');
//ElMessage.success(isEdit ? '' : '');
closeModal();
} else {
ElMessage.error('验证失败,请填写完整信息');
setSubLoading(false);
}
}
function onClose() {
resetFields();
}
//
defineExpose({
openModal,
closeModal,
setFieldsValue,
setProps,
});
</script>

View File

@ -1,277 +0,0 @@
<template>
<PageWrapper
title="字典管理"
content="可代替后台管理系统,设置的大量枚举值和配置,统一标准化管理,随时修改或增加"
>
<el-row :gutter="10" class="mt-3">
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6" class="mb-4">
<el-card shadow="hover" class="border-0">
<template #header>
<div class="card-header">
<div>
<el-space>
<el-button type="primary" @click="addDictionary">新建</el-button>
<el-button type="primary" @click="editDictionary" :disabled="!selectedKey"
>编辑</el-button
>
<el-button type="danger" @click="removeDictionary" :disabled="!selectedKey"
>删除</el-button
>
</el-space>
</div>
<el-input type="text" v-model="pattern" placeholder="请输入">
<template #prefix>
<el-icon class="el-input__icon">
<SearchOutlined />
</el-icon>
</template>
</el-input>
</div>
</template>
<template #header-extra> </template>
<el-table :data="tableDataLeft" highlight-current-row @current-change="selectedRow">
<el-table-column type="index" width="50" />
<el-table-column prop="id" label="ID" width="60" />
<el-table-column prop="label" label="字典名称" />
</el-table>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :md="18" :lg="18" :xl="18" class="mb-4">
<el-card shadow="hover" class="mb-4 border-0 proCard">
<BasicTable
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="tableRef"
:actionColumn="actionColumn"
@checked-row-change="onCheckedRow"
>
<template #tableTitle>
<el-space>
<el-input type="text" v-model="params.keywords" placeholder="请输入">
<template #prefix>
<el-icon class="el-input__icon">
<SearchOutlined />
</el-icon>
</template>
</el-input>
<el-button type="primary" @click="reloadTable"> 查询 </el-button>
<el-button type="primary" :disabled="!selectedKey" @click="addDictionaryValue"
>新建</el-button
>
<el-button type="danger" :disabled="!rowKeys.length" @click="removeDictionaryValues"
>删除</el-button
>
</el-space>
</template>
</BasicTable>
</el-card>
</el-col>
</el-row>
<basicModal @register="lightModalRegister" ref="modalRef" @ok="removeOkModal">
<template #default>
<p class="text-gray-600" style="padding-left: 35px"
>您确认要删除<span strong>{{ rowKeysName }} ?</span></p
>
</template>
</basicModal>
<CreateModal ref="createModalRef" :createType="createType" :isEdit="isEdit" />
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, reactive, h, onMounted, nextTick, unref } from 'vue';
import { SearchOutlined } from '@vicons/antd';
import { BasicTable, TableAction } from '@/components/Table';
import { getDictionary, getDictionaryInfo } from '@/api/system/dictionary';
import { basicModal, useModal } from '@/components/Modal';
import CreateModal from './CreateModal.vue';
import { columns } from './columns';
import { getTreeItem } from '@/utils';
import { ElMessage } from 'element-plus';
const createModalRef = ref();
const tableRef = ref();
const loading = ref(true);
const selectedKey = ref('');
const tableDataLeft = ref<any>([]);
const dictionaryValues = ref<any>([]);
const rowKeys = ref<any>([]);
const rowKeysName = ref('');
const createType = ref(1);
const isEdit = ref(false);
const pattern = ref('');
const currentRow = ref();
const params = ref({
pageSize: 10,
name: 'xiaoMa',
keywords: '',
});
const actionColumn = reactive({
lable: 150,
title: '操作',
prop: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'text',
actions: [
{
label: '删除',
onClick: removeDictionaryValueOne.bind(null, record),
},
{
label: '编辑',
onClick: editDictionaryValue.bind(null, record),
},
],
});
},
});
//
const [
lightModalRegister,
{ openModal: lightOpenModal, closeModal: lightCloseModal, setSubLoading: lightSetSubLoading },
] = useModal({
title: '删除确认',
showIcon: true,
type: 'warning',
closable: false,
maskClosable: true,
width: 380,
});
//
function selectedRow(val) {
selectedKey.value = val.key;
rowKeysName.value = val.label;
currentRow.value = val;
reloadTable();
}
//
function addDictionary() {
createType.value = 1;
createModalRef.value.setProps({ title: '添加字典' });
createModalRef.value.openModal();
}
//
function addDictionaryValue() {
createType.value = 2;
createModalRef.value.setProps({ title: '添加字典项' });
createModalRef.value.openModal();
}
//
function removeOkModal() {
lightCloseModal();
lightSetSubLoading();
ElMessage.error('抱歉,您没有操作权限');
}
//
function onCheckedRow(keys) {
rowKeys.value = keys.value.map((item) => {
return item.id;
});
}
//
function reloadTable() {
tableRef.value.reload();
}
//
function openRemoveModal() {
lightOpenModal();
}
//
const loadDataTable = async (res) => {
const key = selectedKey.value ? selectedKey.value : '';
const result = await getDictionaryInfo({ ...params.value, key, ...res });
dictionaryValues.value = result.list;
return result;
};
//
function removeDictionaryValues() {
//
rowKeysName.value = dictionaryValues.value
.filter((item) => {
return rowKeys.value.includes(item.id as number);
})
.map((item) => {
return item.label;
})
.join('');
openRemoveModal();
}
//
function removeDictionaryValueOne(record: Recordable) {
console.log('点击了删除', record);
rowKeysName.value = record.row.label;
openRemoveModal();
}
//
function removeDictionary() {
//
if (!selectedKey.value.length) return;
const treeItem = getTreeItem(tableDataLeft.value, unref(selectedKey)[0]);
rowKeysName.value = treeItem.label;
openRemoveModal();
}
//
function editDictionaryValue(record: Recordable) {
console.log('点击了编辑', record);
createModalRef.value.openModal();
nextTick(() => {
isEdit.value = true;
createModalRef.value.setProps({ title: '编辑字典项' });
createModalRef.value.setFieldsValue({
...unref(record.row),
});
});
}
//
function editDictionary() {
if (!selectedKey.value) return;
const treeItem = getTreeItem(tableDataLeft.value, selectedKey.value);
createModalRef.value.openModal();
nextTick(() => {
isEdit.value = true;
createModalRef.value.setProps({ title: '编辑字典' });
createModalRef.value.setFieldsValue({
...unref(treeItem),
});
});
}
//
async function init() {
const res = await getDictionary();
tableDataLeft.value = res;
loading.value = false;
}
onMounted(() => {
init();
});
</script>
<style lang="scss" scoped>
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>