update
init
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
export enum Api {
|
||||
list = '/sys/user/queryByOrgCodeForAddressList',
|
||||
positionList = '/sys/position/list',
|
||||
queryDepartTreeSync = '/sys/sysDepart/queryDepartTreeSync',
|
||||
}
|
||||
/**
|
||||
* 获取部门树列表
|
||||
*/
|
||||
export const queryDepartTreeSync = (params?) => defHttp.get({ url: Api.queryDepartTreeSync, params });
|
||||
/**
|
||||
* 部门用户信息
|
||||
*/
|
||||
export const list = (params?) => defHttp.get({ url: Api.list, params });
|
||||
/**
|
||||
* 职务list
|
||||
*/
|
||||
export const positionList = (params?) => defHttp.get({ url: Api.positionList, params });
|
||||
@@ -0,0 +1,51 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'realname',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '员工编号', // 原来为工号
|
||||
dataIndex: 'workNo',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
dataIndex: 'departName',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '职务',
|
||||
dataIndex: 'post',
|
||||
width: 150,
|
||||
slots: { customRender: 'post' },
|
||||
},
|
||||
{
|
||||
title: '手机',
|
||||
width: 150,
|
||||
dataIndex: 'telephone',
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
width: 150,
|
||||
dataIndex: 'email',
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '姓名',
|
||||
field: 'realname',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '员工编号', // 原来为工号
|
||||
field: 'workNo',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<a-card :bordered="false" style="height: 100%">
|
||||
<a-spin :spinning="loading">
|
||||
<a-input-search placeholder="按部门名称搜索…" style="margin-bottom: 10px" @search="onSearch" allowClear />
|
||||
<!--组织机构树-->
|
||||
<template v-if="treeData.length > 0">
|
||||
<a-tree
|
||||
v-if="!treeReloading"
|
||||
showLine
|
||||
:clickRowToExpand="false"
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
:load-data="loadChildrenTreeData"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
@select="onSelect"
|
||||
/>
|
||||
</template>
|
||||
<a-empty v-else description="暂无数据" />
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, nextTick, ref, unref, defineExpose } from 'vue';
|
||||
import { queryDepartTreeSync } from '../address.api';
|
||||
import { searchByKeywords } from '/@/views/system/departUser/depart.user.api';
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const emit = defineEmits(['select', 'rootTreeData']);
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
// 部门树列表数据
|
||||
const treeData = ref<any[]>([]);
|
||||
// 当前展开的项
|
||||
const expandedKeys = ref<any[]>([]);
|
||||
// 当前选中的项
|
||||
const selectedKeys = ref<any[]>([]);
|
||||
// 树组件重新加载
|
||||
const treeReloading = ref<boolean>(false);
|
||||
// 当前选中的部门
|
||||
const currentDepart = ref<any>(null);
|
||||
// 搜索关键字
|
||||
const searchKeyword = ref('');
|
||||
|
||||
// 加载顶级部门信息
|
||||
async function loadRootTreeData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
const result = await queryDepartTreeSync();
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
}
|
||||
if (expandedKeys.value.length === 0) {
|
||||
autoExpandParentNode();
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
loadRootTreeData();
|
||||
|
||||
// 加载子级部门信息
|
||||
async function loadChildrenTreeData(treeNode) {
|
||||
try {
|
||||
const result = await queryDepartTreeSync({
|
||||
pid: treeNode.dataRef.id,
|
||||
});
|
||||
if (result.length == 0) {
|
||||
treeNode.dataRef.isLeaf = true;
|
||||
} else {
|
||||
treeNode.dataRef.children = result;
|
||||
if (expandedKeys.value.length > 0) {
|
||||
// 判断获取的子级是否有当前展开的项
|
||||
let subKeys: any[] = [];
|
||||
for (let key of expandedKeys.value) {
|
||||
if (result.findIndex((item) => item.id === key) !== -1) {
|
||||
subKeys.push(key);
|
||||
}
|
||||
}
|
||||
if (subKeys.length > 0) {
|
||||
expandedKeys.value = [...expandedKeys.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
treeData.value = [...treeData.value];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 自动展开父节点,只展开一级
|
||||
function autoExpandParentNode() {
|
||||
let item = treeData.value[0];
|
||||
if (item) {
|
||||
if (!item.isLeaf) {
|
||||
expandedKeys.value = [item.key];
|
||||
}
|
||||
reloadTree();
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载树组件,防止无法默认展开数据
|
||||
async function reloadTree() {
|
||||
await nextTick();
|
||||
treeReloading.value = true;
|
||||
await nextTick();
|
||||
treeReloading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中的行
|
||||
*/
|
||||
function setSelectedKey(key: string, data?: object) {
|
||||
selectedKeys.value = [key];
|
||||
if (data) {
|
||||
currentDepart.value = data;
|
||||
emit('select', data);
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
async function onSearch(value: string) {
|
||||
if (value) {
|
||||
try {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
let result = await searchByKeywords({ keyWord: value });
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
}
|
||||
autoExpandParentNode();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
loadRootTreeData();
|
||||
}
|
||||
searchKeyword.value = value;
|
||||
}
|
||||
|
||||
// 树选择事件
|
||||
function onSelect(selKeys, event) {
|
||||
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
|
||||
setSelectedKey(selKeys[0], event.selectedNodes[0]);
|
||||
} else {
|
||||
// 这样可以防止用户取消选择
|
||||
setSelectedKey(selectedKeys.value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
loadRootTreeData,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-depart-manage';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&--box {
|
||||
.ant-tabs-nav {
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<a-row :class="['p-4', `${prefixCls}--box`]" type="flex" :gutter="10" style="max-height: 800px">
|
||||
<a-col :xl="6" :lg="24" :md="24" style="margin-bottom: 10px">
|
||||
<DepartLeftTree ref="leftTree" @select="onTreeSelect" />
|
||||
</a-col>
|
||||
<a-col :xl="18" :lg="24" :md="24" style="margin-bottom: 10px">
|
||||
<div style="height: 100%" class="address-book">
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable">
|
||||
<template #post="{ text }">
|
||||
{{
|
||||
(text || '')
|
||||
.split(',')
|
||||
.map((t) => (positionInfo[t] ? positionInfo[t] : t))
|
||||
.join(',')
|
||||
}}
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { provide, ref, unref } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import DepartLeftTree from './components/DepartLeftTree.vue';
|
||||
import { BasicTable } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { columns, searchFormSchema } from './address.data';
|
||||
import { list, positionList } from './address.api';
|
||||
|
||||
const { prefixCls } = useDesign('address-list');
|
||||
provide('prefixCls', prefixCls);
|
||||
|
||||
// 给子组件定义一个ref变量
|
||||
const leftTree = ref();
|
||||
|
||||
// 当前选中的部门code
|
||||
const orgCode = ref('');
|
||||
const positionInfo = ref({});
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { tableContext } = useListPage({
|
||||
tableProps: {
|
||||
api: list,
|
||||
columns,
|
||||
//update-begin---author:wangshuai ---date:20220629 for:[VUEN-1485]进入系统管理--通讯录页面后,网页命令行报错------------
|
||||
rowKey: 'userId',
|
||||
//update-end---author:wangshuai ---date:20220629 for:[VUEN-1485]进入系统管理--通讯录页面后,网页命令行报错--------------
|
||||
showIndexColumn: true,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
canResize: false,
|
||||
actionColumn: null,
|
||||
showTableSetting: false,
|
||||
// 请求之前对参数做处理
|
||||
beforeFetch(params) {
|
||||
params.orgCode = orgCode.value;
|
||||
},
|
||||
},
|
||||
});
|
||||
//注册table数据
|
||||
const [registerTable, { reload }] = tableContext;
|
||||
|
||||
// 左侧树选择后触发
|
||||
function onTreeSelect(data) {
|
||||
orgCode.value = data.orgCode;
|
||||
reload();
|
||||
}
|
||||
|
||||
// 查询职务信息
|
||||
async function queryPositionInfo() {
|
||||
const result = await positionList({ pageSize: 99999 });
|
||||
if (result) {
|
||||
let obj = {};
|
||||
result.records.forEach((position) => {
|
||||
obj[position['code']] = position['name'];
|
||||
});
|
||||
positionInfo.value = obj;
|
||||
}
|
||||
}
|
||||
queryPositionInfo();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import './index.less';
|
||||
|
||||
/*begin 兼容暗夜模式*/
|
||||
.address-book {
|
||||
background-color: @component-background !important;
|
||||
}
|
||||
/*end 兼容暗夜模式*/
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/category/rootList',
|
||||
save = '/sys/category/add',
|
||||
edit = '/sys/category/edit',
|
||||
deleteCategory = '/sys/category/delete',
|
||||
deleteBatch = '/sys/category/deleteBatch',
|
||||
importExcel = '/sys/category/importExcel',
|
||||
exportXls = '/sys/category/exportXls',
|
||||
loadTreeData = '/sys/category/loadTreeRoot',
|
||||
getChildList = '/sys/category/childList',
|
||||
getChildListBatch = '/sys/category/getChildListBatch',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
* @param params
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
export const deleteCategory = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteCategory, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteCategory = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateDict = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 查询全部树形节点数据
|
||||
* @param params
|
||||
*/
|
||||
export const loadTreeData = (params) => defHttp.get({ url: Api.loadTreeData, params });
|
||||
/**
|
||||
* 查询子节点数据
|
||||
* @param params
|
||||
*/
|
||||
export const getChildList = (params) => defHttp.get({ url: Api.getChildList, params });
|
||||
/**
|
||||
* 批量查询子节点数据
|
||||
* @param params
|
||||
*/
|
||||
export const getChildListBatch = (params) => defHttp.get({ url: Api.getChildListBatch, params }, { isTransformResponse: false });
|
||||
@@ -0,0 +1,65 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '分类名称',
|
||||
dataIndex: 'name',
|
||||
width: 350,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '分类编码',
|
||||
dataIndex: 'code',
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '名称',
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '编码',
|
||||
field: 'code',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '父级节点',
|
||||
field: 'pid',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
replaceFields: {
|
||||
value: 'key',
|
||||
},
|
||||
dropdownStyle: {
|
||||
maxHeight: '50vh',
|
||||
},
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
show: ({ values }) => {
|
||||
return values.pid !== '0';
|
||||
},
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '分类名称',
|
||||
field: 'name',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { formSchema } from '../category.data';
|
||||
import { loadTreeData, saveOrUpdateDict } from '../category.api';
|
||||
// 获取emit
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
const expandedRowKeys = ref([]);
|
||||
const treeData = ref([]);
|
||||
const isSubAdd = ref(false);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 4 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 18 },
|
||||
},
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
expandedRowKeys.value = [];
|
||||
setModalProps({ confirmLoading: false, minHeight: 80 });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
isSubAdd.value = !!!data?.isUpdate && data.record.id;
|
||||
if (data?.record) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
//父级节点树信息
|
||||
treeData.value = await loadTreeData({ async: false, pcode: '' });
|
||||
updateSchema({
|
||||
field: 'pid',
|
||||
componentProps: { treeData },
|
||||
});
|
||||
});
|
||||
//设置标题
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增字典' : '编辑字典'));
|
||||
|
||||
/**
|
||||
* 根据pid获取展开的节点
|
||||
* @param pid
|
||||
* @param arr
|
||||
*/
|
||||
function getExpandKeysByPid(pid, arr) {
|
||||
if (pid && arr && arr.length > 0) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].key == pid && unref(expandedRowKeys).indexOf(pid) < 0) {
|
||||
expandedRowKeys.value.push(arr[i].key);
|
||||
getExpandKeysByPid(arr[i]['parentId'], unref(treeData));
|
||||
} else {
|
||||
getExpandKeysByPid(pid, arr[i].children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateDict(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//展开的节点信息
|
||||
await getExpandKeysByPid(values['pid'], unref(treeData));
|
||||
//刷新列表(isUpdate:是否编辑;values:表单信息;expandedArr:展开的节点信息)
|
||||
emit('success', {
|
||||
isUpdate: unref(isUpdate),
|
||||
isSubAdd: unref(isSubAdd),
|
||||
values: { ...values },
|
||||
expandedArr: unref(expandedRowKeys).reverse(),
|
||||
});
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable
|
||||
@register="registerTable"
|
||||
:rowSelection="rowSelection"
|
||||
:expandedRowKeys="expandedRowKeys"
|
||||
@expand="handleExpand"
|
||||
@fetch-success="onFetchSuccess"
|
||||
>
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleCreate"> 新增</a-button>
|
||||
<a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button preIcon="ant-design:import-outlined" type="primary" @click="onImportXls">导入</j-upload-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--字典弹窗-->
|
||||
<CategoryModal @register="registerModal" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-category" setup>
|
||||
//ts语法
|
||||
import { ref, computed, unref, toRaw, nextTick } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import CategoryModal from './components/CategoryModal.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMethods } from '/@/hooks/system/useMethods';
|
||||
import { columns, searchFormSchema } from './category.data';
|
||||
import { list, deleteCategory, batchDeleteCategory, getExportUrl, getImportUrl, getChildList, getChildListBatch } from './category.api';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
const expandedRowKeys = ref([]);
|
||||
const { handleExportXls, handleImportXls } = useMethods();
|
||||
//字典model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, onExportXls, onImportXls, tableContext } = useListPage({
|
||||
designScope: 'category-template',
|
||||
tableProps: {
|
||||
title: '分类字典',
|
||||
api: list,
|
||||
columns: columns,
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
},
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '分类字典列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
|
||||
tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
async function handleEdit(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
async function handleDetail(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
hideFooter: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteCategory({ id: record.id }, importSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
const ids = selectedRowKeys.value.filter((item) => !item.includes('loading'));
|
||||
await batchDeleteCategory({ ids: ids }, importSuccess);
|
||||
}
|
||||
/**
|
||||
* 导入
|
||||
*/
|
||||
function importSuccess() {
|
||||
//update-begin---author:wangshuai ---date:20220530 for:[issues/54]树字典,勾选,然后批量删除,系统错误------------
|
||||
(selectedRowKeys.value = []) && reload();
|
||||
//update-end---author:wangshuai ---date:20220530 for:[issues/54]树字典,勾选,然后批量删除,系统错误--------------
|
||||
}
|
||||
/**
|
||||
* 添加下级
|
||||
*/
|
||||
function handleAddSub(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
async function handleSuccess({ isUpdate, isSubAdd, values, expandedArr }) {
|
||||
if (isUpdate) {
|
||||
//编辑回调
|
||||
updateTableDataRecord(values.id, values);
|
||||
} else {
|
||||
if (!values['pid']) {
|
||||
//新增根节点
|
||||
reload();
|
||||
} else {
|
||||
//新增子集
|
||||
//update-begin-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长---
|
||||
if (isSubAdd) {
|
||||
await expandTreeNode(values.pid);
|
||||
//update-end-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长---
|
||||
} else {
|
||||
expandedRowKeys.value = [];
|
||||
for (let key of unref(expandedArr)) {
|
||||
await expandTreeNode(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口请求成功后回调
|
||||
*/
|
||||
function onFetchSuccess(result) {
|
||||
getDataByResult(result.items) && loadDataByExpandedRows();
|
||||
}
|
||||
/**
|
||||
* 根据已展开的行查询数据(用于保存后刷新时异步加载子级的数据)
|
||||
*/
|
||||
async function loadDataByExpandedRows() {
|
||||
if (unref(expandedRowKeys).length > 0) {
|
||||
const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',') });
|
||||
if (res.success && res.result.records.length > 0) {
|
||||
//已展开的数据批量子节点
|
||||
let records = res.result.records;
|
||||
const listMap = new Map();
|
||||
for (let item of records) {
|
||||
let pid = item['pid'];
|
||||
if (unref(expandedRowKeys).includes(pid)) {
|
||||
let mapList = listMap.get(pid);
|
||||
if (mapList == null) {
|
||||
mapList = [];
|
||||
}
|
||||
mapList.push(item);
|
||||
listMap.set(pid, mapList);
|
||||
}
|
||||
}
|
||||
let childrenMap = listMap;
|
||||
let fn = (list) => {
|
||||
if (list) {
|
||||
list.forEach((data) => {
|
||||
if (unref(expandedRowKeys).includes(data.id)) {
|
||||
data.children = getDataByResult(childrenMap.get(data.id));
|
||||
fn(data.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
fn(getDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 处理数据集
|
||||
*/
|
||||
function getDataByResult(result) {
|
||||
if (result && result.length > 0) {
|
||||
return result.map((item) => {
|
||||
//判断是否标记了带有子节点
|
||||
if (item['hasChild'] == '1') {
|
||||
let loadChild = { id: item.id + '_loadChild', name: 'loading...', isLoading: true };
|
||||
item.children = [loadChild];
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
*树节点展开合并
|
||||
* */
|
||||
async function handleExpand(expanded, record) {
|
||||
// 判断是否是展开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
|
||||
if (expanded) {
|
||||
expandedRowKeys.value.push(record.id);
|
||||
if (record.children.length > 0 && !!record.children[0].isLoading) {
|
||||
let result = await getChildList({ pid: record.id });
|
||||
if (result && result.length > 0) {
|
||||
record.children = getDataByResult(result);
|
||||
} else {
|
||||
record.children = null;
|
||||
record.hasChild = '0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let keyIndex = expandedRowKeys.value.indexOf(record.id);
|
||||
if (keyIndex >= 0) {
|
||||
expandedRowKeys.value.splice(keyIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
*操作表格后处理树节点展开合并
|
||||
* */
|
||||
async function expandTreeNode(key) {
|
||||
let record: any = findTableDataRecord(key);
|
||||
//update-begin-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长,显示“接口请求超时,请刷新页面重试!”---
|
||||
if (!expandedRowKeys.value.includes(key)) {
|
||||
expandedRowKeys.value.push(key);
|
||||
}
|
||||
//update-end-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长,显示“接口请求超时,请刷新页面重试!”---
|
||||
let result = await getChildList({ pid: key });
|
||||
if (result && result.length > 0) {
|
||||
record.children = getDataByResult(result);
|
||||
} else {
|
||||
record.children = null;
|
||||
record.hasChild = '0';
|
||||
}
|
||||
updateTableDataRecord(key, record);
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '添加下级',
|
||||
onClick: handleAddSub.bind(null, { pid: record.id }),
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" @ok="handleSubmit" :title="title" :width="1200" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
|
||||
<a-tabs v-model:activeKey="activeKey" animated>
|
||||
<a-tab-pane tab="局部规则" key="1" :forceRender="true">
|
||||
<JVxeTable ref="vTable1" toolbar rowNumber dragSort rowSelection :maxHeight="580" :dataSource="dataSource1" :columns="columns1">
|
||||
<template #toolbarAfter>
|
||||
<a-alert type="info" showIcon message="局部规则按照你输入的位数有序的校验" style="margin-bottom: 8px" />
|
||||
</template>
|
||||
</JVxeTable>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="全局规则" key="2" :forceRender="true">
|
||||
<JVxeTable
|
||||
ref="vTable2"
|
||||
toolbar
|
||||
rowNumber
|
||||
dragSort
|
||||
rowSelection
|
||||
:maxHeight="580"
|
||||
:dataSource="dataSource2"
|
||||
:addSetActive="false"
|
||||
:columns="columns2"
|
||||
>
|
||||
<template #toolbarAfter>
|
||||
<a-alert
|
||||
type="info"
|
||||
showIcon
|
||||
message="全局规则可校验用户输入的所有字符;全局规则的优先级比局部规则的要高。"
|
||||
style="margin-bottom: 8px"
|
||||
/>
|
||||
</template>
|
||||
</JVxeTable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { computed, ref, unref } from 'vue';
|
||||
import { formSchema } from './check.rule.data';
|
||||
import { saveCheckRule, updateCheckRule } from './check.rule.api';
|
||||
import { JVxeTypes, JVxeColumn, JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
//设置标题
|
||||
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }] = useForm({
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const activeKey = ref('1');
|
||||
let arr1: any[] = [];
|
||||
let dataSource1 = ref(arr1);
|
||||
let arr2: any[] = [];
|
||||
let dataSource2 = ref(arr2);
|
||||
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
activeKey.value = '1';
|
||||
dataSource1.value = [];
|
||||
dataSource2.value = [];
|
||||
if (unref(isUpdate)) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
|
||||
let ruleJson = data.record.ruleJson;
|
||||
if (ruleJson) {
|
||||
let ruleList = JSON.parse(ruleJson);
|
||||
// 筛选出全局规则和局部规则
|
||||
let global: any[] = [],
|
||||
design: any[] = [],
|
||||
priority = '1';
|
||||
ruleList.forEach((rule) => {
|
||||
if (rule.digits === '*') {
|
||||
global.push(Object.assign(rule, { priority }));
|
||||
} else {
|
||||
priority = '0';
|
||||
design.push(rule);
|
||||
}
|
||||
});
|
||||
dataSource1.value = design;
|
||||
dataSource2.value = global;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const vTable1 = ref<JVxeTableInstance>();
|
||||
const vTable2 = ref<JVxeTableInstance>();
|
||||
|
||||
// 验证表格 返回表格数据
|
||||
function validateMyTable(tableRef, key) {
|
||||
return new Promise((resolve, reject) => {
|
||||
tableRef.value!.validateTable().then((errMap) => {
|
||||
if (errMap) {
|
||||
activeKey.value = key;
|
||||
reject();
|
||||
} else {
|
||||
const values = tableRef.value!.getTableData();
|
||||
resolve(values);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
let mainData;
|
||||
let globalValues = [];
|
||||
let designValues = [];
|
||||
validate()
|
||||
.then((formValue) => {
|
||||
mainData = formValue;
|
||||
return validateMyTable(vTable1, '1');
|
||||
})
|
||||
.then((tableData1: []) => {
|
||||
if (tableData1 && tableData1.length > 0) {
|
||||
designValues = tableData1;
|
||||
}
|
||||
return validateMyTable(vTable2, '2');
|
||||
})
|
||||
.then((tableData2: []) => {
|
||||
if (tableData2 && tableData2.length > 0) {
|
||||
globalValues = tableData2;
|
||||
}
|
||||
// 整合两个子表的数据
|
||||
let firstGlobal: any[] = [],
|
||||
afterGlobal: any[] = [];
|
||||
for (let i = 0; i < globalValues.length; i++) {
|
||||
let v: any = globalValues[i];
|
||||
v.digits = '*';
|
||||
if (v.priority === '1') {
|
||||
firstGlobal.push(v);
|
||||
} else {
|
||||
afterGlobal.push(v);
|
||||
}
|
||||
}
|
||||
let concatValues = firstGlobal.concat(designValues).concat(afterGlobal);
|
||||
let subValues = concatValues.map((i) => pick(i, 'digits', 'pattern', 'message'));
|
||||
// 生成 formData,用于传入后台
|
||||
let ruleJson = JSON.stringify(subValues);
|
||||
let formData = Object.assign({}, mainData, { ruleJson });
|
||||
saveOrUpdateFormData(formData);
|
||||
})
|
||||
.catch(() => {
|
||||
setModalProps({ confirmLoading: false });
|
||||
console.error('验证未通过!');
|
||||
});
|
||||
}
|
||||
|
||||
// 表单提交请求
|
||||
async function saveOrUpdateFormData(formData) {
|
||||
try {
|
||||
console.log('表单提交数据', formData);
|
||||
setModalProps({ confirmLoading: true });
|
||||
if (isUpdate.value) {
|
||||
await updateCheckRule(formData);
|
||||
} else {
|
||||
await saveCheckRule(formData);
|
||||
}
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验
|
||||
* @param cellValue
|
||||
* @param callback
|
||||
*/
|
||||
const validatePatternHandler = ({ cellValue }, callback) => {
|
||||
try {
|
||||
new RegExp(cellValue);
|
||||
callback(true);
|
||||
} catch (e) {
|
||||
callback(false, '请输入正确的正则表达式');
|
||||
}
|
||||
};
|
||||
|
||||
const columns1 = ref<JVxeColumn[]>([
|
||||
{
|
||||
title: '位数',
|
||||
key: 'digits',
|
||||
type: JVxeTypes.inputNumber,
|
||||
minWidth: 180,
|
||||
validateRules: [
|
||||
{ required: true, message: '${title}不能为空' },
|
||||
{ pattern: /^[1-9]\d*$/, message: '请输入零以上的正整数' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '规则(正则表达式)',
|
||||
key: 'pattern',
|
||||
minWidth: 320,
|
||||
type: JVxeTypes.input,
|
||||
validateRules: [{ required: true, message: '规则不能为空' }, { handler: validatePatternHandler }],
|
||||
},
|
||||
{
|
||||
title: '提示文本',
|
||||
key: 'message',
|
||||
minWidth: 180,
|
||||
type: JVxeTypes.input,
|
||||
validateRules: [{ required: true, message: '${title}不能为空' }],
|
||||
},
|
||||
]);
|
||||
|
||||
const columns2 = ref<JVxeColumn[]>([
|
||||
{
|
||||
title: '优先级',
|
||||
key: 'priority',
|
||||
type: JVxeTypes.select,
|
||||
defaultValue: '1',
|
||||
options: [
|
||||
{ title: '优先运行', value: '1' },
|
||||
{ title: '最后运行', value: '0' },
|
||||
],
|
||||
validateRules: [],
|
||||
},
|
||||
{
|
||||
title: '规则(正则表达式)',
|
||||
key: 'pattern',
|
||||
width: '40%',
|
||||
type: JVxeTypes.input,
|
||||
validateRules: [{ required: true, message: '规则不能为空' }, { handler: validatePatternHandler }],
|
||||
},
|
||||
{
|
||||
title: '提示文本',
|
||||
key: 'message',
|
||||
width: '20%',
|
||||
type: JVxeTypes.input,
|
||||
validateRules: [{ required: true, message: '${title}不能为空' }],
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" :okButtonProps="okButtonProps" @register="registerModal" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
<div style="display: flex; flex-flow: row wrap">
|
||||
<div style="padding: 0 4px" v-for="(str, index) of realTestValue" :key="index">
|
||||
<a-row>
|
||||
<a-col style="text-align: center">
|
||||
<a-input :value="str" style="text-align: center; width: 40px" />
|
||||
</a-col>
|
||||
<a-col style="text-align: center">{{ index + 1 }}</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { checkRuleInput } from '/@/views/system/checkRule/check.rule.data';
|
||||
import { ref } from 'vue';
|
||||
let realTestValue = ref('');
|
||||
const okButtonProps = {
|
||||
style: { display: 'none' },
|
||||
};
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }] = useForm({
|
||||
schemas: checkRuleInput,
|
||||
showActionButtonGroup: false,
|
||||
labelCol: {
|
||||
span: 24,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 24,
|
||||
},
|
||||
});
|
||||
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
realTestValue.value = '';
|
||||
setModalProps({
|
||||
confirmLoading: false,
|
||||
cancelText: '关闭',
|
||||
title: '功能测试',
|
||||
width: '1000px',
|
||||
});
|
||||
await setFieldsValue({
|
||||
ruleCode: data.ruleCode,
|
||||
testValue: realTestValue,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,86 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/checkRule/list',
|
||||
delete = '/sys/checkRule/delete',
|
||||
deleteBatch = '/sys/checkRule/deleteBatch',
|
||||
exportXls = 'sys/checkRule/exportXls',
|
||||
importXls = 'sys/checkRule/importExcel',
|
||||
checkByCode = '/sys/checkRule/checkByCode',
|
||||
save = '/sys/checkRule/add',
|
||||
edit = '/sys/checkRule/edit',
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出地址
|
||||
*/
|
||||
export const exportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入地址
|
||||
*/
|
||||
export const importUrl = Api.importXls;
|
||||
|
||||
/**
|
||||
* 列表查询
|
||||
* @param params
|
||||
*/
|
||||
export const getCheckRuleList = (params) => {
|
||||
return defHttp.get({ url: Api.list, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param params
|
||||
* @param handleSuccess
|
||||
*/
|
||||
export const deleteCheckRule = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.delete, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteCheckRule = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据编码校验规则code,校验传入的值是否合法
|
||||
* @param ruleCode
|
||||
* @param value
|
||||
*/
|
||||
export const validateCheckRule = (ruleCode, value) => {
|
||||
value = encodeURIComponent(value);
|
||||
const params = { ruleCode, value };
|
||||
return defHttp.get({ url: Api.checkByCode, params }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存
|
||||
* @param params
|
||||
*/
|
||||
export const saveCheckRule = (params) => {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param params
|
||||
*/
|
||||
export const updateCheckRule = (params) => {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
import { duplicateCheck } from '/@/views/system/user/user.api';
|
||||
import { validateCheckRule } from '/@/views/system/checkRule/check.rule.api';
|
||||
import { array } from 'vue-types';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '规则名称',
|
||||
dataIndex: 'ruleName',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '规则编码',
|
||||
dataIndex: 'ruleCode',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '规则描述',
|
||||
dataIndex: 'ruleDescription',
|
||||
width: 300,
|
||||
align: 'center',
|
||||
customRender: function ({ text }) {
|
||||
return render.renderTip(text, 30);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'ruleName',
|
||||
label: '规则名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
field: 'ruleCode',
|
||||
label: '规则编码',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'ruleName',
|
||||
label: '规则名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
{
|
||||
field: 'ruleCode',
|
||||
label: '规则编码',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
dynamicRules: ({ model }) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!value) {
|
||||
return reject('请输入规则编码!');
|
||||
}
|
||||
const params = {
|
||||
tableName: 'sys_check_rule',
|
||||
fieldName: 'rule_code',
|
||||
fieldVal: value,
|
||||
dataId: model.id,
|
||||
};
|
||||
duplicateCheck(params)
|
||||
.then((res) => {
|
||||
res.success ? resolve() : reject('规则编码已存在!');
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '校验失败');
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ruleDescription',
|
||||
label: '规则描述',
|
||||
colProps: { span: 24 },
|
||||
component: 'InputTextArea',
|
||||
componentProps: {
|
||||
placeholder: '请输入规则描述',
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const checkRuleInput: FormSchema[] = [
|
||||
{
|
||||
label: '123',
|
||||
field: 'ruleCode',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'testValue',
|
||||
label: '需要测试的值:',
|
||||
component: 'Input',
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
onChange: (e) => {
|
||||
formModel.testValue = e.target.value;
|
||||
},
|
||||
};
|
||||
},
|
||||
dynamicRules: ({ model }) => {
|
||||
const { ruleCode } = model;
|
||||
return [
|
||||
{
|
||||
required: false,
|
||||
validator: (_, value) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (ruleCode && value) {
|
||||
/*console.log({ruleCode,value})*/
|
||||
validateCheckRule(ruleCode, value)
|
||||
.then((res) => {
|
||||
//console.log(1233, res)
|
||||
res['success'] ? resolve() : reject(res['message']);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || err);
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd">新增</a-button>
|
||||
<a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button preIcon="ant-design:import-outlined" type="primary" @click="onImportXls">导入</j-upload-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作</span>
|
||||
<Icon icon="mdi:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<CheckRuleTestModal @register="testModal" />
|
||||
<CheckRuleModal @register="registerModal" @success="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script name="system-fillrule" lang="ts" setup>
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import {
|
||||
getCheckRuleList,
|
||||
exportUrl,
|
||||
importUrl,
|
||||
deleteCheckRule,
|
||||
batchDeleteCheckRule,
|
||||
validateCheckRule,
|
||||
} from '/@/views/system/checkRule/check.rule.api';
|
||||
import { columns, searchFormSchema } from '/@/views/system/checkRule/check.rule.data';
|
||||
import { ActionItem } from '/@/components/Table';
|
||||
|
||||
import CheckRuleTestModal from '/@/views/system/checkRule/CheckRuleTestModal.vue';
|
||||
const [testModal, { openModal: openTestModal }] = useModal();
|
||||
|
||||
import CheckRuleModal from '/@/views/system/checkRule/CheckRuleModal.vue';
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, createMessage, onExportXls, onImportXls, createSuccessModal } = useListPage({
|
||||
designScope: 'check-rule',
|
||||
tableProps: {
|
||||
title: '编码校验规则管理页面',
|
||||
api: getCheckRuleList,
|
||||
columns: columns,
|
||||
showIndexColumn: true,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
url: exportUrl,
|
||||
name: '编码校验规则列表',
|
||||
},
|
||||
importConfig: {
|
||||
url: importUrl,
|
||||
success: () => reload(),
|
||||
},
|
||||
});
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
console.log('record....', record);
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
console.log(12345, record);
|
||||
await deleteCheckRule({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteCheckRule({ ids: selectedRowKeys.value }, () => {
|
||||
selectedRowKeys.value = [];
|
||||
reload();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能测试
|
||||
*/
|
||||
function testRule(record) {
|
||||
openTestModal(true, { ruleCode: record.ruleCode });
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [{ label: '编辑', onClick: handleEdit.bind(null, record) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{ label: '功能测试', onClick: testRule.bind(null, record) },
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<BasicDrawer title="数据规则/按钮权限配置" :width="365" @close="onClose" @register="registerDrawer">
|
||||
<a-spin :spinning="loading">
|
||||
<a-tabs defaultActiveKey="1">
|
||||
<a-tab-pane tab="数据规则" key="1">
|
||||
<a-checkbox-group v-model:value="dataRuleChecked" v-if="dataRuleList.length > 0">
|
||||
<a-row>
|
||||
<a-col :span="24" v-for="(item, index) in dataRuleList" :key="'dr' + index">
|
||||
<a-checkbox :value="item.id">{{ item.ruleName }}</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<div style="width: 100%; margin-top: 15px">
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:size="'small'"
|
||||
preIcon="ant-design:save-filled"
|
||||
@click="saveDataRuleForRole"
|
||||
>
|
||||
<span>点击保存</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-checkbox-group>
|
||||
<a-empty v-else description="无配置信息" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
|
||||
import { queryDepartDataRule, saveDepartDataRule } from '../depart.api';
|
||||
|
||||
defineEmits(['register']);
|
||||
const loading = ref<boolean>(false);
|
||||
const departId = ref('');
|
||||
const functionId = ref('');
|
||||
const dataRuleList = ref<Array<any>>([]);
|
||||
const dataRuleChecked = ref<Array<any>>([]);
|
||||
|
||||
// 注册抽屉组件
|
||||
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
|
||||
departId.value = unref(data.departId);
|
||||
functionId.value = unref(data.functionId);
|
||||
loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { datarule, drChecked } = await queryDepartDataRule(functionId, departId);
|
||||
dataRuleList.value = datarule;
|
||||
if (drChecked) {
|
||||
dataRuleChecked.value = drChecked.split(',');
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function saveDataRuleForRole() {
|
||||
let params = {
|
||||
departId: departId.value,
|
||||
permissionId: functionId.value,
|
||||
dataRuleIds: dataRuleChecked.value.join(','),
|
||||
};
|
||||
saveDepartDataRule(params);
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
doReset();
|
||||
}
|
||||
|
||||
function doReset() {
|
||||
functionId.value = '';
|
||||
dataRuleList.value = [];
|
||||
dataRuleChecked.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<BasicModal :title="title" :width="800" v-bind="$attrs" @ok="handleOk" @register="registerModal">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #addressInfo="{ model }">
|
||||
<a-input style="width: calc(100% - 100px)" v-model:value="model['address']" :disabled="true" />
|
||||
<a-button style="margin-left: 10px" @click="viewMap"> 查看地图</a-button>
|
||||
</template>
|
||||
</BasicForm>
|
||||
</BasicModal>
|
||||
|
||||
<Map @register="registerMap" :state="state" ref="map" @get-position="getPosition" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
|
||||
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
|
||||
|
||||
import { saveOrUpdateDepart } from '../depart.api';
|
||||
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
|
||||
import Map from '/@/views/consult/resource/components/Map.vue';
|
||||
|
||||
const [registerMap, { openModal }] = useModal();
|
||||
const state = ref({});
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const props = defineProps({
|
||||
rootTreeData: { type: Array, default: () => [] },
|
||||
});
|
||||
const prefixCls = inject('prefixCls');
|
||||
// 当前是否是更新模式
|
||||
const isUpdate = ref<boolean>(false);
|
||||
// 当前的弹窗数据
|
||||
const model = ref<object>({});
|
||||
const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
|
||||
|
||||
//注册表单
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema, getFieldsValue }] = useForm({
|
||||
schemas: useBasicFormSchema().basicFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
// 打开地图弹窗
|
||||
function viewMap() {
|
||||
openModal(true, {
|
||||
record: { ...getFieldsValue(), ...state.value },
|
||||
});
|
||||
}
|
||||
|
||||
async function getPosition(val) {
|
||||
let { pname, cityname, adname, address, name } = val.handleItem || '';
|
||||
let nameList = [pname, cityname, adname, address, name];
|
||||
let str = '';
|
||||
nameList.map((item) => {
|
||||
if (item !== undefined) {
|
||||
str += item;
|
||||
}
|
||||
});
|
||||
await setFieldsValue({
|
||||
lat: val.lat,
|
||||
lng: val.lng,
|
||||
address: str,
|
||||
});
|
||||
state.value = {
|
||||
...state.value,
|
||||
latitude: val.lat,
|
||||
longitude: val.lng,
|
||||
address: str,
|
||||
};
|
||||
}
|
||||
// 注册弹窗
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
isUpdate.value = unref(data?.isUpdate);
|
||||
// 当前是否为添加子级
|
||||
let isChild = unref(data?.isChild);
|
||||
let categoryOptions = isChild ? orgCategoryOptions.child : orgCategoryOptions.root;
|
||||
// 隐藏不需要展示的字段
|
||||
updateSchema([
|
||||
{
|
||||
field: 'parentId',
|
||||
show: isChild,
|
||||
componentProps: {
|
||||
// 如果是添加子部门,就禁用该字段
|
||||
disabled: isChild,
|
||||
treeData: props.rootTreeData,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCode',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'orgCategory',
|
||||
componentProps: { options: categoryOptions },
|
||||
},
|
||||
]);
|
||||
|
||||
let record = unref(data?.record);
|
||||
if (typeof record !== 'object') {
|
||||
record = {};
|
||||
}
|
||||
// 赋默认值
|
||||
record = Object.assign(
|
||||
{
|
||||
departOrder: 0,
|
||||
orgCategory: categoryOptions[0].value,
|
||||
},
|
||||
record
|
||||
);
|
||||
model.value = record;
|
||||
await setFieldsValue({ ...record });
|
||||
});
|
||||
|
||||
// 提交事件
|
||||
async function handleOk() {
|
||||
try {
|
||||
setModalProps({ confirmLoading: true });
|
||||
let values = await validate();
|
||||
//提交表单
|
||||
await saveOrUpdateDepart(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #addressInfo="{ model }">
|
||||
<a-input style="width: calc(100% - 100px)" v-model:value="model['address']" :disabled="true" />
|
||||
<a-button style="margin-left: 10px" @click="viewMap" :disabled="!hasPer"> 查看地图</a-button>
|
||||
</template>
|
||||
</BasicForm>
|
||||
<div class="j-box-bottom-button offset-20" style="margin-top: 30px" v-if="hasPer">
|
||||
<div class="j-box-bottom-button-float">
|
||||
<a-button preIcon="ant-design:sync-outlined" @click="onReset">重置</a-button>
|
||||
<a-button preIcon="ant-design:save-filled" type="primary" @click="onSubmit">保存</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<Map @register="registerMap" :state="state" ref="map" @get-position="getPosition" />
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { saveOrUpdateDepart } from '../depart.api';
|
||||
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
|
||||
import Map from '/@/views/consult/resource/components/Map.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { orgManageApi } from '/@/utils/auth/buttonAuth/system';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const [registerMap, { openModal }] = useModal();
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
rootTreeData: { type: Array, default: () => [] },
|
||||
});
|
||||
const { userInfo } = useUserStore();
|
||||
const prefixCls = inject('prefixCls');
|
||||
const loading = ref<boolean>(false);
|
||||
// 当前是否是更新模式
|
||||
const isUpdate = ref<boolean>(true);
|
||||
// 当前的弹窗数据
|
||||
const model = ref<object>({});
|
||||
const state = ref({});
|
||||
//注册表单
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema, getFieldsValue, clearValidate, setProps }] = useForm({
|
||||
labelWidth: 130,
|
||||
schemas: useBasicFormSchema().basicFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const categoryOptions = computed(() => {
|
||||
if (!!props?.data?.parentId) {
|
||||
return orgCategoryOptions.child;
|
||||
} else {
|
||||
return orgCategoryOptions.root;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
// 禁用字段
|
||||
updateSchema([
|
||||
{ field: 'parentId', componentProps: { disabled: true } },
|
||||
{ field: 'orgCode', componentProps: { disabled: true } },
|
||||
]);
|
||||
// data 变化,重填表单
|
||||
watch(
|
||||
() => props.data,
|
||||
async () => {
|
||||
let record = unref(props.data);
|
||||
if (typeof record !== 'object') {
|
||||
record = {};
|
||||
}
|
||||
model.value = record;
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...record });
|
||||
updateState(record);
|
||||
|
||||
await setProps({ disabled: !hasPer.value });
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
// 更新 父部门 选项
|
||||
watch(
|
||||
() => props.rootTreeData,
|
||||
async () => {
|
||||
updateSchema([
|
||||
{
|
||||
field: 'parentId',
|
||||
componentProps: { treeData: props.rootTreeData },
|
||||
},
|
||||
]);
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
// 监听并更改 orgCategory options
|
||||
watch(
|
||||
categoryOptions,
|
||||
async () => {
|
||||
updateSchema([
|
||||
{
|
||||
field: 'orgCategory',
|
||||
componentProps: { options: categoryOptions.value },
|
||||
},
|
||||
]);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
await clearValidate();
|
||||
});
|
||||
|
||||
const hasPer = computed(() => {
|
||||
// 修改此字段
|
||||
return (
|
||||
(!!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system'))) ||
|
||||
(hasPermission(orgManageApi.departManageList) && props.data.manager)
|
||||
);
|
||||
});
|
||||
|
||||
function updateState(val) {
|
||||
state.value = {
|
||||
latitude: val.lat,
|
||||
longitude: val.lng,
|
||||
address: val.address,
|
||||
};
|
||||
}
|
||||
|
||||
// 打开地图弹窗
|
||||
function viewMap() {
|
||||
openModal(true, {
|
||||
record: { ...getFieldsValue(), ...state.value },
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭地图
|
||||
async function getPosition(val) {
|
||||
let { pname, cityname, adname, address, name } = val.handleItem || '';
|
||||
let nameList = [pname, cityname, adname, address, name];
|
||||
let str = '';
|
||||
nameList.map((item) => {
|
||||
if (item !== undefined) {
|
||||
str += item;
|
||||
}
|
||||
});
|
||||
await setFieldsValue({
|
||||
lat: val.lat,
|
||||
lng: val.lng,
|
||||
address: str,
|
||||
});
|
||||
state.value = {
|
||||
...state.value,
|
||||
latitude: val.lat,
|
||||
longitude: val.lng,
|
||||
address: str,
|
||||
};
|
||||
}
|
||||
// 重置表单
|
||||
async function onReset() {
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...model.value });
|
||||
}
|
||||
|
||||
// 提交事件
|
||||
async function onSubmit() {
|
||||
try {
|
||||
loading.value = true;
|
||||
let values = await validate();
|
||||
values = Object.assign({}, model.value, values);
|
||||
//提交表单
|
||||
await saveOrUpdateDepart(values, isUpdate.value);
|
||||
//刷新列表
|
||||
emit('success');
|
||||
Object.assign(model.value, values);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/*begin 兼容暗夜模式*/
|
||||
.j-box-bottom-button-float {
|
||||
background-color: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
}
|
||||
/*end 兼容暗夜模式*/
|
||||
</style>
|
||||
@@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<a-card :bordered="false" style="height: 100%">
|
||||
<div class="j-table-operator" style="width: 100%">
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" v-auth="'system:depart:add'" @click="onAddDepart" v-if="hasPer"
|
||||
>新增</a-button
|
||||
>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" v-auth="'system:depart:add'" @click="onAddChildDepart()">添加下级</a-button>
|
||||
<!-- 2024年3月15日09:26:36 暂时注掉导入导出-->
|
||||
<!-- <a-upload name="file" :showUploadList="false" :customRequest="onImportXls">-->
|
||||
<!-- <a-button type="primary" preIcon="ant-design:import-outlined">导入</a-button>-->
|
||||
<!-- </a-upload>-->
|
||||
<!-- <a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls">导出</a-button>-->
|
||||
<!-- <a-button type="primary" preIcon="ant-design:sync-outlined">同步企微?</a-button>-->
|
||||
<!-- <a-button type="primary" preIcon="ant-design:sync-outlined">同步钉钉?</a-button>-->
|
||||
<template v-if="checkedKeys.length > 0">
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteBatch">
|
||||
<icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作 </span>
|
||||
<icon icon="akar-icons:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
<a-alert type="info" show-icon class="alert" style="margin-bottom: 8px">
|
||||
<template #message>
|
||||
<template v-if="checkedKeys.length > 0">
|
||||
<span>已选中 {{ checkedKeys.length }} 条记录</span>
|
||||
<a-divider type="vertical" />
|
||||
<a @click="checkedKeys = []">清空</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>未选中任何数据</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-spin :spinning="loading" class="spin-tree">
|
||||
<a-input-search placeholder="按部门名称搜索…" style="margin-bottom: 10px" @search="onSearch" />
|
||||
<!--组织机构树-->
|
||||
|
||||
<!-- checkable-->
|
||||
<template v-if="treeData.length > 0">
|
||||
<a-tree
|
||||
v-if="!treeReloading"
|
||||
:clickRowToExpand="false"
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
:checkStrictly="checkStrictly"
|
||||
:load-data="loadChildrenTreeData"
|
||||
:checkedKeys="checkedKeys"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
@check="onCheck"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ key: treeKey, title, dataRef }">
|
||||
<a-dropdown :trigger="['contextmenu']">
|
||||
<Popconfirm
|
||||
:visible="visibleTreeKey === treeKey"
|
||||
title="确定要删除吗?"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
placement="rightTop"
|
||||
@confirm="onDelete(dataRef)"
|
||||
@visibleChange="onVisibleChange"
|
||||
>
|
||||
<span>{{ title }}</span>
|
||||
</Popconfirm>
|
||||
|
||||
<template
|
||||
#overlay
|
||||
v-if="
|
||||
dataRef?.manager ||
|
||||
(!!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system')))
|
||||
"
|
||||
>
|
||||
<a-menu @click="">
|
||||
<a-menu-item key="1" @click="onAddChildDepart(dataRef)">添加子级</a-menu-item>
|
||||
<a-menu-item key="2" @click="visibleTreeKey = treeKey">
|
||||
<span style="color: red">删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-tree>
|
||||
</template>
|
||||
<a-empty v-else description="暂无数据" />
|
||||
</a-spin>
|
||||
<DepartFormModal :rootTreeData="treeData" @register="registerModal" @success="loadRootTreeData" />
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineExpose, inject, nextTick, ref, unref, computed } from 'vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useMethods } from '/@/hooks/system/useMethods';
|
||||
import { Api, deleteBatchDepart, queryDepartTreeSync } from '../depart.api';
|
||||
import { searchByKeywords } from '/@/views/system/departUser/depart.user.api';
|
||||
import DepartFormModal from '/@/views/system/depart/components/DepartFormModal.vue';
|
||||
import { message, Popconfirm } from 'ant-design-vue';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const emit = defineEmits(['select', 'rootTreeData']);
|
||||
const { createMessage } = useMessage();
|
||||
const { handleImportXls, handleExportXls } = useMethods();
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { orgManageApi } from '/@/utils/auth/buttonAuth/system';
|
||||
const { userInfo } = useUserStore();
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
// 部门树列表数据
|
||||
const treeData = ref<any[]>([]);
|
||||
// 当前选中的项
|
||||
const checkedKeys = ref<any[]>([]);
|
||||
// 当前展开的项
|
||||
const expandedKeys = ref<any[]>([]);
|
||||
// 当前选中的项
|
||||
const selectedKeys = ref<any[]>([]);
|
||||
// 树组件重新加载
|
||||
const treeReloading = ref<boolean>(false);
|
||||
// 树父子是否关联
|
||||
const checkStrictly = ref<boolean>(true);
|
||||
// 当前选中的部门
|
||||
const currentDepart = ref<any>(null);
|
||||
// 控制确认删除提示框是否显示
|
||||
const visibleTreeKey = ref<any>(null);
|
||||
// 搜索关键字
|
||||
const searchKeyword = ref('');
|
||||
|
||||
// 注册 modal
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
// 加载顶级部门信息
|
||||
async function loadRootTreeData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
const result = await queryDepartTreeSync();
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
}
|
||||
if (expandedKeys.value.length === 0) {
|
||||
autoExpandParentNode();
|
||||
} else {
|
||||
if (selectedKeys.value.length === 0) {
|
||||
let item = treeData.value[0];
|
||||
if (item) {
|
||||
// 默认选中第一个
|
||||
setSelectedKey(item.id, item);
|
||||
}
|
||||
} else {
|
||||
emit('select', currentDepart.value);
|
||||
}
|
||||
}
|
||||
emit('rootTreeData', treeData.value);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
loadRootTreeData();
|
||||
|
||||
// 加载子级部门信息
|
||||
async function loadChildrenTreeData(treeNode) {
|
||||
try {
|
||||
const result = await queryDepartTreeSync({
|
||||
pid: treeNode.dataRef.id,
|
||||
});
|
||||
if (result && result.length == 0) {
|
||||
treeNode.dataRef.isLeaf = true;
|
||||
} else {
|
||||
treeNode.dataRef.children = result;
|
||||
if (expandedKeys.value.length > 0) {
|
||||
// 判断获取的子级是否有当前展开的项
|
||||
let subKeys: any[] = [];
|
||||
for (let key of expandedKeys.value) {
|
||||
if (result.findIndex((item) => item.id === key) !== -1) {
|
||||
subKeys.push(key);
|
||||
}
|
||||
}
|
||||
if (subKeys.length > 0) {
|
||||
expandedKeys.value = [...expandedKeys.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
treeData.value = [...treeData.value];
|
||||
emit('rootTreeData', treeData.value);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 自动展开父节点,只展开一级
|
||||
function autoExpandParentNode() {
|
||||
let item = treeData.value[0];
|
||||
if (item) {
|
||||
if (!item.isLeaf) {
|
||||
expandedKeys.value = [item.key];
|
||||
}
|
||||
// 默认选中第一个
|
||||
setSelectedKey(item.id, item);
|
||||
reloadTree();
|
||||
} else {
|
||||
emit('select', null);
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载树组件,防止无法默认展开数据
|
||||
async function reloadTree() {
|
||||
await nextTick();
|
||||
treeReloading.value = true;
|
||||
await nextTick();
|
||||
treeReloading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中的行
|
||||
*/
|
||||
function setSelectedKey(key: string, data?: object) {
|
||||
selectedKeys.value = [key];
|
||||
if (data) {
|
||||
currentDepart.value = data;
|
||||
emit('select', data);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加一级部门
|
||||
function onAddDepart() {
|
||||
if (!hasPer.value) return message.info('无权限');
|
||||
openModal(true, { isUpdate: false, isChild: false });
|
||||
}
|
||||
|
||||
// 添加子级部门
|
||||
function onAddChildDepart(data = currentDepart.value) {
|
||||
if (!hasPer.value) return message.info('无权限');
|
||||
if (data == null) {
|
||||
createMessage.warning('请先选择一个部门');
|
||||
return;
|
||||
}
|
||||
const record = { parentId: data.id };
|
||||
openModal(true, { isUpdate: false, isChild: true, record });
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
async function onSearch(value: string) {
|
||||
if (value) {
|
||||
try {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
let result = await searchByKeywords({ keyWord: value });
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
}
|
||||
autoExpandParentNode();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
loadRootTreeData();
|
||||
}
|
||||
searchKeyword.value = value;
|
||||
}
|
||||
|
||||
// 树复选框选择事件
|
||||
function onCheck(e) {
|
||||
if (Array.isArray(e)) {
|
||||
checkedKeys.value = e;
|
||||
} else {
|
||||
checkedKeys.value = e.checked;
|
||||
}
|
||||
}
|
||||
|
||||
// 树选择事件
|
||||
function onSelect(selKeys, event) {
|
||||
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
|
||||
setSelectedKey(selKeys[0], event.selectedNodes[0]);
|
||||
} else {
|
||||
// 这样可以防止用户取消选择
|
||||
setSelectedKey(selectedKeys.value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ids 删除部门
|
||||
* @param idListRef array
|
||||
* @param confirm 是否显示确认提示框
|
||||
*/
|
||||
async function doDeleteDepart(idListRef, confirm = true) {
|
||||
const idList = unref(idListRef);
|
||||
if (idList.length > 0) {
|
||||
try {
|
||||
loading.value = true;
|
||||
await deleteBatchDepart({ ids: idList.join(',') }, confirm);
|
||||
await loadRootTreeData();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除单个部门
|
||||
async function onDelete(data) {
|
||||
if (!hasPer.value) return message.info('无权限');
|
||||
if (data) {
|
||||
onVisibleChange(false);
|
||||
doDeleteDepart([data.id], false);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量删除部门
|
||||
async function onDeleteBatch() {
|
||||
try {
|
||||
if (!hasPer.value) return message.info('无权限');
|
||||
await doDeleteDepart(checkedKeys);
|
||||
checkedKeys.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
function onVisibleChange(visible) {
|
||||
if (!visible) {
|
||||
visibleTreeKey.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onImportXls(d) {
|
||||
if (!hasPer.value) return message.info('无权限');
|
||||
handleImportXls(d, Api.importExcelUrl, () => {
|
||||
loadRootTreeData();
|
||||
});
|
||||
}
|
||||
|
||||
function onExportXls() {
|
||||
if (!hasPer.value) return message.info('无权限');
|
||||
handleExportXls('部门信息', Api.exportXlsUrl);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
loadRootTreeData,
|
||||
});
|
||||
|
||||
const hasPer = computed(() => {
|
||||
// 修改此字段
|
||||
return (
|
||||
(!!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system'))) ||
|
||||
(hasPermission(orgManageApi.departManageList) && currentDepart.value?.manager)
|
||||
);
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<template v-if="treeData.length > 0">
|
||||
<BasicTree
|
||||
ref="basicTree"
|
||||
class="depart-rule-tree"
|
||||
checkable
|
||||
:treeData="treeData"
|
||||
:checkedKeys="checkedKeys"
|
||||
:selectedKeys="selectedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:checkStrictly="checkStrictly"
|
||||
style="height: 500px; overflow: auto"
|
||||
@check="onCheck"
|
||||
@expand="onExpand"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ slotTitle, ruleFlag }">
|
||||
<span>{{ slotTitle }}</span>
|
||||
<Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red" />
|
||||
</template>
|
||||
</BasicTree>
|
||||
</template>
|
||||
<a-empty v-else description="无可配置部门权限" />
|
||||
|
||||
<div class="j-box-bottom-button offset-20" style="margin-top: 30px">
|
||||
<div class="j-box-bottom-button-float">
|
||||
<a-dropdown :trigger="['click']" placement="top">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="3" @click="toggleCheckALL(true)">全部勾选</a-menu-item>
|
||||
<a-menu-item key="4" @click="toggleCheckALL(false)">取消全选</a-menu-item>
|
||||
<a-menu-item key="5" @click="toggleExpandAll(true)">展开所有</a-menu-item>
|
||||
<a-menu-item key="6" @click="toggleExpandAll(false)">收起所有</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button style="float: left">
|
||||
树操作
|
||||
<Icon icon="ant-design:up-outlined" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<a-button preIcon="ant-design:save-filled" type="primary" @click="onSubmit">保存</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
<DepartDataRuleDrawer @register="registerDataRuleDrawer" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, ref, nextTick } from 'vue';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { BasicTree } from '/@/components/Tree/index';
|
||||
import DepartDataRuleDrawer from './DepartDataRuleDrawer.vue';
|
||||
import { queryRoleTreeList, queryDepartPermission, saveDepartPermission } from '../depart.api';
|
||||
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
});
|
||||
// 当前选中的部门ID,可能会为空,代表未选择部门
|
||||
const departId = computed(() => props.data?.id);
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const basicTree = ref();
|
||||
const loading = ref<boolean>(false);
|
||||
const treeData = ref<any[]>([]);
|
||||
const expandedKeys = ref<Array<any>>([]);
|
||||
const selectedKeys = ref<Array<any>>([]);
|
||||
const checkedKeys = ref<Array<any>>([]);
|
||||
const lastCheckedKeys = ref<Array<any>>([]);
|
||||
const checkStrictly = ref(true);
|
||||
|
||||
// 注册数据规则授权弹窗抽屉
|
||||
const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer();
|
||||
|
||||
// onCreated
|
||||
loadData();
|
||||
watch(departId, () => loadDepartPermission(), { immediate: true });
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
let { treeList } = await queryRoleTreeList();
|
||||
treeData.value = treeList;
|
||||
await nextTick();
|
||||
toggleExpandAll(true);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDepartPermission() {
|
||||
if (departId.value) {
|
||||
try {
|
||||
loading.value = true;
|
||||
let keys = await queryDepartPermission({ departId: departId.value });
|
||||
checkedKeys.value = keys;
|
||||
lastCheckedKeys.value = [...keys];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
loading.value = true;
|
||||
await saveDepartPermission({
|
||||
departId: departId.value,
|
||||
permissionIds: checkedKeys.value.join(','),
|
||||
lastpermissionIds: lastCheckedKeys.value.join(','),
|
||||
});
|
||||
await loadData();
|
||||
await loadDepartPermission();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// tree勾选复选框事件
|
||||
function onCheck(event) {
|
||||
if (!Array.isArray(event)) {
|
||||
checkedKeys.value = event.checked;
|
||||
} else {
|
||||
checkedKeys.value = event;
|
||||
}
|
||||
}
|
||||
|
||||
// tree展开事件
|
||||
function onExpand($expandedKeys) {
|
||||
expandedKeys.value = $expandedKeys;
|
||||
}
|
||||
|
||||
// tree选中事件
|
||||
function onSelect($selectedKeys, { selectedNodes }) {
|
||||
if (selectedNodes[0]?.ruleFlag) {
|
||||
let functionId = $selectedKeys[0];
|
||||
dataRuleDrawer.openDrawer(true, { departId, functionId });
|
||||
}
|
||||
selectedKeys.value = [];
|
||||
}
|
||||
|
||||
// 切换父子关联
|
||||
async function toggleCheckStrictly(flag) {
|
||||
checkStrictly.value = flag;
|
||||
await nextTick();
|
||||
checkedKeys.value = basicTree.value.getCheckedKeys();
|
||||
}
|
||||
|
||||
// 切换展开收起
|
||||
async function toggleExpandAll(flag) {
|
||||
basicTree.value.expandAll(flag);
|
||||
await nextTick();
|
||||
expandedKeys.value = basicTree.value.getExpandedKeys();
|
||||
}
|
||||
|
||||
// 切换全选
|
||||
async function toggleCheckALL(flag) {
|
||||
basicTree.value.checkAll(flag);
|
||||
await nextTick();
|
||||
checkedKeys.value = basicTree.value.getCheckedKeys();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// 【VUEN-188】解决滚动条不灵敏的问题
|
||||
.depart-rule-tree :deep(.scrollbar__bar) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*begin 兼容暗夜模式*/
|
||||
.j-box-bottom-button-float {
|
||||
background-color: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
}
|
||||
/*end 兼容暗夜模式*/
|
||||
</style>
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<BasicDrawer width="800px" :title="isUpdate ? '修改部门管理员' : '添加部门管理员'" @register="registerModal" destroyOnClose @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #roles="{ model, field }">
|
||||
<a-input v-model:value="model[field]" readonly style="cursor: pointer" @click="clickShowTransfer" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
<transfer-modal @register="registerTransferModal" :orgInfo="{ orgCode: props.orgCode, departId: props.departId }" @success="handleSuccess" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import BasicForm from '/@/components/Form/src/BasicForm.vue';
|
||||
import { useForm } from '/@/components/Form';
|
||||
import { ref } from 'vue';
|
||||
import { formSchema } from '/@/views/system/depart/components/departManage/departManageList.data';
|
||||
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue';
|
||||
import { useDrawerInner } from '/@/components/Drawer';
|
||||
import { addUserEndowDepartManager } from '/@/views/system/depart/components/departManage/departManageList.api';
|
||||
import TransferModal from '/@/views/system/depart/components/departManage/components/transferModal.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { dealPassword } from '/@/views/system/user/user.data';
|
||||
|
||||
const props = defineProps({
|
||||
orgCode: { type: String, default: () => '' },
|
||||
departId: { type: String, default: () => '' },
|
||||
});
|
||||
|
||||
const [registerTransferModal, { openModal }] = useModal();
|
||||
|
||||
const roleCodes = ref<string>('');
|
||||
const listData = ref<Array<any>>([]);
|
||||
|
||||
const isUpdate = ref<boolean>(false);
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
//labelWidth: 150,
|
||||
schemas: formSchema(isUpdate.value),
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { span: 24 },
|
||||
});
|
||||
|
||||
const [registerModal, { closeDrawer, setDrawerProps }] = useDrawerInner(async (data) => {
|
||||
await resetFields();
|
||||
roleCodes.value = '';
|
||||
listData.value = [];
|
||||
setDrawerProps({ confirmLoading: false, showFooter: data.showFooter, showOkBtn: data.showFooter, showCancelBtn: data.showFooter });
|
||||
// listData.value = [];
|
||||
// isUpdate.value = data.isUpdate;
|
||||
//
|
||||
// if (unref(isUpdate)) {
|
||||
// await setFieldsValue({
|
||||
// ...data.record,
|
||||
// });
|
||||
// await clearValidate();
|
||||
// }
|
||||
});
|
||||
|
||||
function clickShowTransfer() {
|
||||
openModal(true, {
|
||||
list: listData.value,
|
||||
});
|
||||
}
|
||||
|
||||
function preSubmit(values) {
|
||||
return {
|
||||
...values,
|
||||
extension: values,
|
||||
};
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
// if (!values.birthday) {
|
||||
// values.birthday = values.idCard.substring(6, 10) + '-' + values.idCard.substring(10, 12) + '-' + values.idCard.substring(12, 14);
|
||||
// }
|
||||
// values['orgCode'] = props.orgCode;
|
||||
// setDrawerProps({ confirmLoading: true });
|
||||
// //提交表单
|
||||
values['roleCodes'] = roleCodes.value;
|
||||
values['orgCode'] = props.orgCode;
|
||||
values['password'] = dealPassword(values['password']);
|
||||
await addUserEndowDepartManager(values);
|
||||
closeDrawer();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
function handleSuccess(list) {
|
||||
listData.value = list;
|
||||
let ids = '';
|
||||
let names = '';
|
||||
list.map((item, index) => {
|
||||
item = JSON.parse(item);
|
||||
if (index === 0) {
|
||||
ids += item.id;
|
||||
names += item.roleCode;
|
||||
} else {
|
||||
ids += ',' + item.id;
|
||||
names += ',' + item.roleCode;
|
||||
}
|
||||
});
|
||||
|
||||
setFieldsValue({
|
||||
roles: names,
|
||||
});
|
||||
roleCodes.value = ids;
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
:deep(.ant-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
.question-choose {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
&:before {
|
||||
content: '';
|
||||
width: 36%;
|
||||
height: 1px;
|
||||
background-color: #000000;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
width: 36%;
|
||||
height: 1px;
|
||||
background-color: #000000;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
}
|
||||
}
|
||||
:deep(.ant-col-sm-18) {
|
||||
max-width: 100%;
|
||||
}
|
||||
:deep(.input-number .ant-input-number-input-wrap input) {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<BasicDrawer width="50%" title="选择用户" @register="registerModal" destroyOnClose @close="handleClose">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<transfer-modal @register="registerTransferModal" :orgInfo="{ orgCode: props.orgCode, departId: props.departId }" @success="handleSuccess" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import BasicTable from '/@/components/Table/src/BasicTable.vue';
|
||||
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { noManagerUsers } from '/@/views/system/depart/components/departManage/departManageList.api';
|
||||
import { selectColumns, searchFormSchemaSelect } from '/@/views/system/depart/components/departManage/departManageList.data';
|
||||
import { useDrawerInner } from '/@/components/Drawer';
|
||||
import { TableAction } from '/@/components/Table';
|
||||
import TransferModal from '/@/views/system/depart/components/departManage/components/transferModal.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
|
||||
const props = defineProps({
|
||||
orgCode: { type: String, default: () => '' },
|
||||
departId: { type: String, default: () => '' },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['reloadTable']);
|
||||
|
||||
const [registerTransferModal, { openModal }] = useModal();
|
||||
|
||||
//注册table数据
|
||||
const { tableContext } = useListPage({
|
||||
tableProps: {
|
||||
title: '员工',
|
||||
api: noManagerUsers,
|
||||
columns: selectColumns,
|
||||
canResize: false,
|
||||
clickToRowSelect: false,
|
||||
beforeFetch: (params) => {
|
||||
if (params['orgCode2']) {
|
||||
params['orgCode'] = params['orgCode2'];
|
||||
} else {
|
||||
params['orgCode'] = params['orgCode1'];
|
||||
}
|
||||
return params;
|
||||
},
|
||||
formConfig: {
|
||||
//labelWidth: 120,
|
||||
schemas: searchFormSchemaSelect,
|
||||
autoSubmitOnEnter: true,
|
||||
showAdvancedButton: false,
|
||||
fieldMapToNumber: [],
|
||||
fieldMapToTime: [],
|
||||
labelWidth: 100,
|
||||
baseColProps: {
|
||||
xs: 12,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 8,
|
||||
xl: 8,
|
||||
xxl: 8,
|
||||
},
|
||||
actionColOptions: {
|
||||
style: {
|
||||
paddingLeft: '104px',
|
||||
},
|
||||
span: 24,
|
||||
offset: 0,
|
||||
xs: 12,
|
||||
sm: 12,
|
||||
md: 8,
|
||||
lg: 8,
|
||||
xl: 8,
|
||||
xxl: 8,
|
||||
},
|
||||
},
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
function getTableAction(record: Recordable) {
|
||||
return [
|
||||
{
|
||||
label: record.departId && record.departId.includes(props.departId) ? '修改' : '绑定',
|
||||
onClick: handleBind.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const [registerModal, { closeDrawer }] = useDrawerInner(async () => {
|
||||
selectedRowKeys.value = [];
|
||||
});
|
||||
|
||||
function handleBind(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
});
|
||||
// if (!props.orgCode) {
|
||||
// return message.error('请选择需要管理的部门');
|
||||
// }
|
||||
//
|
||||
// relevanceDepartManager(props.orgCode, data).then(() => {
|
||||
// closeDrawer();
|
||||
// emit('reloadTable');
|
||||
// });
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
emit('reloadTable');
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
:deep(.ant-form-item-control-input-content) {
|
||||
//display: flex;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<BasicModal @register="transferModal" destroyOnClose @ok="handleSubmit" zIndex="1002" width="700px" title="选择角色">
|
||||
<div style="display: flex; align-items: center; justify-content: center">
|
||||
<a-transfer
|
||||
v-model:target-keys="targetKeys"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:data-source="mockData"
|
||||
show-search
|
||||
:list-style="{
|
||||
width: '250px',
|
||||
height: '500px',
|
||||
}"
|
||||
:filter-option="filterOption"
|
||||
:titles="['无权限', '已有权限']"
|
||||
:render="(item) => item.roleName"
|
||||
@change="handleChange"
|
||||
@select-change="handleSelectChange"
|
||||
@scroll="handleScroll"
|
||||
/>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
|
||||
import { ref } from 'vue';
|
||||
import { addManagerDepartAndEndowRole, listCustom, userRoles } from '/@/views/system/depart/components/departManage/departManageList.api';
|
||||
import { useModalInner } from '/@/components/Modal';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
orgInfo: propTypes.object.def({ orgCode: '', departId: '' }),
|
||||
});
|
||||
|
||||
const userId = ref<string>('');
|
||||
const targetKeys = ref<Array<any>>([]);
|
||||
const selectedKeys = ref<Array<any>>([]);
|
||||
const mockData = ref<Array<any>>([]);
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const [transferModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
selectedKeys.value = [];
|
||||
mockData.value = [];
|
||||
userId.value = data?.record?.userId;
|
||||
targetKeys.value = userId.value ? [] : data.list;
|
||||
const { records } = await listCustom({ pageNo: 1, pageSize: 9999, systemFlag: false });
|
||||
let haveList = [];
|
||||
if (userId.value) {
|
||||
haveList = await userRoles({ userId: data.record.userId, pageNo: 1, pageSize: 9999 });
|
||||
}
|
||||
mockData.value = records.map((item: any) => {
|
||||
// targetKeys.value?.push(item.roleCode);
|
||||
if (haveList.includes(item.roleCode)) {
|
||||
targetKeys.value?.push(item.id);
|
||||
}
|
||||
// selectedKeys.value?.push(item.id);
|
||||
return { ...{ key: userId.value ? item.id : JSON.stringify({ id: item.id, roleCode: item.roleName }) }, ...item };
|
||||
});
|
||||
});
|
||||
|
||||
function handleChange() {}
|
||||
function handleSelectChange() {}
|
||||
function handleScroll() {}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
if (userId.value) {
|
||||
let params = {
|
||||
...props.orgInfo,
|
||||
userId: userId.value,
|
||||
roleIds: targetKeys.value,
|
||||
};
|
||||
setModalProps({ confirmLoading: true });
|
||||
await addManagerDepartAndEndowRole(params);
|
||||
closeModal();
|
||||
emit('success');
|
||||
} else {
|
||||
closeModal();
|
||||
emit('success', targetKeys.value);
|
||||
}
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
function filterOption(inputValue: string, option) {
|
||||
return option.roleName.indexOf(inputValue) > -1;
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less"></style>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
list = '/health-system/sys/sysDepart/departManagers',
|
||||
addDepartManager = '/health-system/sys/healthUserEmployeeEx/addDepartManager',
|
||||
// noManagerUsers = '/health-system/sys/user/noManagerUsers',
|
||||
noManagerUsers = '/health-system/sys/user/listUser',
|
||||
listCustom = '/health-system/sys/role/listCustom',
|
||||
userRoles = '/health-system/sys/user/userRoles',
|
||||
relevanceDepartManager = '/health-system/sys/sysDepart/relevanceDepartManager',
|
||||
cancelDepartManager = '/health-system/sys/sysDepart/cancelDepartManager',
|
||||
addManagerDepartAndEndowRole = '/health-system/sys/role/addManagerDepartAndEndowRole',
|
||||
addUserEndowDepartManager = '/health-system/sys/user/addUserEndowDepartManager',
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
export const noManagerUsers = (params) => defHttp.get({ url: Api.noManagerUsers, params });
|
||||
export const listCustom = (params) => defHttp.get({ url: Api.listCustom, params });
|
||||
export const userRoles = (params) => defHttp.get({ url: Api.userRoles, params });
|
||||
export const addManagerDepartAndEndowRole = (params) => defHttp.post({ url: Api.addManagerDepartAndEndowRole, params });
|
||||
export const addUserEndowDepartManager = (params) => defHttp.post({ url: Api.addUserEndowDepartManager, params });
|
||||
export const addDepartManager = (params) => defHttp.post({ url: Api.addDepartManager, params });
|
||||
export const relevanceDepartManager = (orgCode, params) => defHttp.post({ url: Api.relevanceDepartManager + '?orgCode=' + orgCode, params });
|
||||
export const cancelDepartManager = (orgCode, params) => defHttp.post({ url: Api.cancelDepartManager + '?orgCode=' + orgCode, params });
|
||||
@@ -0,0 +1,250 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { getSecondaryDepartmentList, getThirdDepartmentList } from '/@/views/system/user/user.api';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { checkPassword } from '/@/hooks/checkPassword/checkPassword';
|
||||
//列表数据
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '员工账号',
|
||||
align: 'center',
|
||||
dataIndex: 'userName',
|
||||
},
|
||||
{
|
||||
title: '员工姓名',
|
||||
align: 'center',
|
||||
dataIndex: 'realName',
|
||||
},
|
||||
// {
|
||||
// title: '员工编号',
|
||||
// align: 'center',
|
||||
// dataIndex: 'workNo',
|
||||
// },
|
||||
{
|
||||
title: '单位名称',
|
||||
align: 'center',
|
||||
dataIndex: 'secondDepart',
|
||||
},
|
||||
{
|
||||
title: '部门名称',
|
||||
align: 'center',
|
||||
dataIndex: 'depart',
|
||||
},
|
||||
];
|
||||
export const selectColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '员工账号',
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
dataIndex: 'userName',
|
||||
},
|
||||
{
|
||||
title: '员工姓名',
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
dataIndex: 'realName',
|
||||
},
|
||||
// {
|
||||
// title: '员工编号',
|
||||
// align: 'center',
|
||||
// dataIndex: 'workNo',
|
||||
// },
|
||||
{
|
||||
title: '单位名称',
|
||||
align: 'center',
|
||||
dataIndex: 'secondDepart',
|
||||
},
|
||||
{
|
||||
title: '部门名称',
|
||||
align: 'center',
|
||||
dataIndex: 'depart',
|
||||
},
|
||||
];
|
||||
|
||||
//查询数据
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '员工姓名',
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
export const searchFormSchemaSelect: FormSchema[] = [
|
||||
{
|
||||
label: '单位',
|
||||
field: 'orgCode1',
|
||||
component: 'ApiSelect',
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
api: getSecondaryDepartmentList,
|
||||
resultField: 'result',
|
||||
labelField: 'departName',
|
||||
valueField: 'orgCode',
|
||||
immediate: true,
|
||||
onChange: (_value, option) => {
|
||||
formModel.secondOrgId = option?.id || '';
|
||||
formModel.secondOrgCode = option?.orgCode || '';
|
||||
if (formModel?.thirdOrgId) {
|
||||
formModel.thirdOrgId = '';
|
||||
formModel.thirdOrgCode = '';
|
||||
}
|
||||
},
|
||||
onDeselect: () => {
|
||||
formModel.thirdOrgId = '';
|
||||
formModel.thirdOrgCode = '';
|
||||
},
|
||||
showSearch: true,
|
||||
filterOption: (input: string, option: any): boolean => {
|
||||
const str: string = input.toLowerCase();
|
||||
return option.label.toLowerCase().indexOf(str) >= 0;
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '部门',
|
||||
field: 'orgCode2',
|
||||
component: 'ApiSelect',
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
api: getThirdDepartmentList,
|
||||
resultField: 'list',
|
||||
labelField: 'departName',
|
||||
params: {
|
||||
secondDepartId: formModel?.secondOrgId || 'defghjkl',
|
||||
},
|
||||
valueField: 'orgCode',
|
||||
immediate: false,
|
||||
onChange: (_value, option) => {
|
||||
formModel.thirdOrgId = option?.id || '';
|
||||
formModel.thirdOrgCode = option?.orgCode || '';
|
||||
},
|
||||
onDeselect: () => {
|
||||
formModel.thirdOrgId = '';
|
||||
formModel.thirdOrgCode = '';
|
||||
},
|
||||
onFocus: () => {
|
||||
if (!formModel.secondOrgId) {
|
||||
return message.warn('请先选择单位!');
|
||||
}
|
||||
},
|
||||
showSearch: true,
|
||||
filterOption: (input: string, option: any): boolean => {
|
||||
const str: string = input.toLowerCase();
|
||||
return option.label.toLowerCase().indexOf(str) >= 0;
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '员工姓名',
|
||||
field: 'realName',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '员工编号',
|
||||
field: 'workNo',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
//表单数据
|
||||
export const schema: FormSchema[] = [
|
||||
{
|
||||
label: '员工姓名',
|
||||
field: 'name',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: (isUpdate: boolean) => FormSchema[] = (isUpdate) => {
|
||||
return [
|
||||
{
|
||||
label: '用户账号',
|
||||
field: 'username',
|
||||
component: 'Input',
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
onInput: () => {
|
||||
formModel.username = formModel.username.replace(/[^a-zA-Z0-9_]/g, '');
|
||||
},
|
||||
autocomplete: 'off',
|
||||
};
|
||||
},
|
||||
required: true,
|
||||
dynamicDisabled: isUpdate,
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
field: 'password',
|
||||
component: 'StrengthMeter',
|
||||
componentProps: {
|
||||
autocomplete: 'new-password',
|
||||
},
|
||||
dynamicRules: () => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
let { message } = checkPassword(value);
|
||||
if (message === 'ok') {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(message);
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
];
|
||||
},
|
||||
required: !isUpdate,
|
||||
show: !isUpdate,
|
||||
},
|
||||
{
|
||||
label: '姓名',
|
||||
field: 'realname',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '员工编号', // 原来为工号
|
||||
field: 'workNo',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
ifShow: false,
|
||||
},
|
||||
{
|
||||
label: '角色',
|
||||
field: 'roles',
|
||||
component: 'Input',
|
||||
slot: 'roles',
|
||||
required: true,
|
||||
},
|
||||
// {
|
||||
// label: '部门',
|
||||
// field: 'orgCode',
|
||||
// component: 'JSelectDeptStaff',
|
||||
// componentProps: ({ formModel }) => {
|
||||
// return {
|
||||
// sync: true,
|
||||
// checkStrictly: true,
|
||||
// defaultExpandLevel: 1,
|
||||
// initOptions: isUpdate
|
||||
// ? [
|
||||
// {
|
||||
// value: formModel.departId,
|
||||
// label: ' ' + formModel.secondDepartName + ' - ' + formModel.departName + ' ',
|
||||
// },
|
||||
// ]
|
||||
// : [],
|
||||
// };
|
||||
// },
|
||||
// required: true,
|
||||
// },
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable :rowSelection="rowSelection" @register="registerTable">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" v-auth="'system:depart-manage-list:add'" @click="handleAdd">
|
||||
新增
|
||||
</a-button>
|
||||
<a-button type="primary" @click="selectUser">选择用户</a-button>
|
||||
<a-button type="primary" @click="largeUnbind">批量解绑</a-button>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 表单区域 -->
|
||||
<depart-manage-list-drawer @register="registerModal" @success="handleSuccess" :orgCode="props.data?.orgCode" />
|
||||
<select-no-manager @register="registerModal1" @reloadTable="handleSuccess" :orgCode="props.data?.orgCode" :departId="props.data?.id" />
|
||||
<RestPass @register="restPassModal" />
|
||||
|
||||
<transfer-modal
|
||||
@register="registerTransferModal"
|
||||
:orgInfo="{ orgCode: props.data?.orgCode, departId: props.data?.id }"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { columns, searchFormSchema } from './departManageList.data';
|
||||
import { list, cancelDepartManager } from './departManageList.api';
|
||||
import DepartManageListDrawer from '/@/views/system/depart/components/departManage/components/departManageListDrawer.vue';
|
||||
import SelectNoManager from '/@/views/system/depart/components/departManage/components/selectNoManager.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import RestPass from '/@/views/system/user/restPass/RestPass.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import TransferModal from '/@/views/system/depart/components/departManage/components/transferModal.vue';
|
||||
const [registerModal, { openDrawer }] = useDrawer();
|
||||
const [registerModal1, { openDrawer: openDrawer1 }] = useDrawer();
|
||||
const [restPassModal, { openModal: restPassModel }] = useModal();
|
||||
const [registerTransferModal, { openModal: openModal1 }] = useModal();
|
||||
const showFooter = ref(true);
|
||||
|
||||
function selectUser() {
|
||||
openDrawer1(true);
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
});
|
||||
watch(
|
||||
() => props.data?.orgCode,
|
||||
() => {
|
||||
reload({ page: 1 });
|
||||
}
|
||||
);
|
||||
//注册table数据
|
||||
const { tableContext } = useListPage({
|
||||
tableProps: {
|
||||
title: '员工',
|
||||
api: list,
|
||||
rowKey: 'userId',
|
||||
columns,
|
||||
canResize: false,
|
||||
clickToRowSelect: false,
|
||||
beforeFetch: (params) => {
|
||||
params['orgCode'] = props.data?.orgCode;
|
||||
return params;
|
||||
},
|
||||
formConfig: {
|
||||
labelWidth: 100,
|
||||
schemas: searchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
showAdvancedButton: false,
|
||||
fieldMapToNumber: [],
|
||||
fieldMapToTime: [],
|
||||
baseColProps: {
|
||||
xs: 8, // <576px
|
||||
sm: 8, // ≥576px
|
||||
md: 8, // ≥768px
|
||||
lg: 8, // ≥992px
|
||||
xl: 12, // ≥1200px
|
||||
xxl: 12, // ≥1600px
|
||||
},
|
||||
actionColOptions: {
|
||||
offset: 0,
|
||||
span: 8,
|
||||
xs: 8, // <576px
|
||||
sm: 8, // ≥576px
|
||||
md: 8, // ≥768px
|
||||
lg: 8, // ≥992px
|
||||
xl: 12, // ≥1200px
|
||||
xxl: 12, // ≥1600px
|
||||
},
|
||||
},
|
||||
actionColumn: {
|
||||
width: 170,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
type: '新增',
|
||||
});
|
||||
}
|
||||
function handleUnBind(d) {
|
||||
let orgCode = props.data?.orgCode;
|
||||
if (!orgCode) {
|
||||
return message.error('请选择需要管理的部门');
|
||||
}
|
||||
cancelDepartManager(orgCode, d).then(() => {
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function largeUnbind() {
|
||||
if (selectedRowKeys.value.length === 0) return message.info('请至少选择一条数据');
|
||||
handleUnBind(selectedRowKeys.value);
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
(selectedRowKeys.value = []) && reload();
|
||||
}
|
||||
// 修改密码弹窗
|
||||
function restPass(record) {
|
||||
restPassModel(true, {
|
||||
record: record.userId,
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '修改',
|
||||
onClick: handleEditBind.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '解绑',
|
||||
onClick: handleUnBind.bind(null, [record.userId]),
|
||||
},
|
||||
{
|
||||
label: '重置密码',
|
||||
onClick: restPass.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function handleEditBind(record: Recordable) {
|
||||
openModal1(true, {
|
||||
record,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,95 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
enum Api {
|
||||
list = '/health-system/sys/sysDepart/departUsers',
|
||||
save = '/sys/healthUserEmployeeEx/add',
|
||||
edit = '/sys/healthUserEmployeeEx/edit',
|
||||
deleteOne = '/sys/healthUserEmployeeEx/delete',
|
||||
deleteBatch = '/sys/healthUserEmployeeEx/deleteBatch',
|
||||
importExcel = '/sys/healthUserEmployeeEx/importExcel',
|
||||
exportXls = '/sys/healthUserEmployeeEx/exportXls',
|
||||
queryById = '/sys/healthUserEmployeeEx/queryById',
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
|
||||
export const queryByIdUrl = Api.queryById;
|
||||
|
||||
/**
|
||||
* 请求编辑数据
|
||||
*/
|
||||
export const resEditData = async (params) => {
|
||||
return await defHttp.get({ url: Api.queryById, params }, { joinParamsToUrl: true });
|
||||
};
|
||||
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 删除单个
|
||||
*/
|
||||
export const deleteOne = (params, handleSuccess) => {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认删除',
|
||||
content: '是否确认删除',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp
|
||||
.delete(
|
||||
{
|
||||
url: Api.deleteOne,
|
||||
data: params,
|
||||
},
|
||||
{ joinParamsToUrl: true }
|
||||
)
|
||||
.then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
*/
|
||||
export const batchDelete = (params, handleSuccess) => {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更新
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { h } from 'vue';
|
||||
import { Image, message } from 'ant-design-vue';
|
||||
import { getDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
|
||||
import { getSecondaryDepartmentList, getThirdDepartmentList } from '/@/views/system/user/user.api';
|
||||
//列表数据
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '用户账号',
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
dataIndex: 'realname',
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
align: 'center',
|
||||
dataIndex: 'sex_dictText',
|
||||
width: 64,
|
||||
},
|
||||
{
|
||||
title: '身份证号',
|
||||
align: 'center',
|
||||
dataIndex: 'idCard',
|
||||
width: 160,
|
||||
},
|
||||
];
|
||||
|
||||
//查询数据
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '用户账号',
|
||||
field: 'username',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '用户姓名',
|
||||
field: 'realname',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
//表单数据
|
||||
@@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable">
|
||||
<!--插槽:table标题-->
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 表单区域 -->
|
||||
<!-- 重置密码 -->
|
||||
<RestPass @register="restPassModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { columns, searchFormSchema } from './departUserList.data';
|
||||
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list, resEditData } from './departUserList.api';
|
||||
import { message } from 'ant-design-vue';
|
||||
import RestPass from '/@/views/system/user/restPass/RestPass.vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { lockUser } from '/@/views/system/user/hospitalDoctor/HospitalDoctor.api';
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [restPassModal, { openModal: restPaddModel }] = useModal();
|
||||
const showFooter = ref(true);
|
||||
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
});
|
||||
watch(
|
||||
() => props.data?.orgCode,
|
||||
() => {
|
||||
reload();
|
||||
}
|
||||
);
|
||||
//注册table数据
|
||||
const { tableContext } = useListPage({
|
||||
tableProps: {
|
||||
title: '员工',
|
||||
api: list,
|
||||
columns,
|
||||
canResize: false,
|
||||
clickToRowSelect: false,
|
||||
formConfig: {
|
||||
//labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
showAdvancedButton: false,
|
||||
fieldMapToNumber: [],
|
||||
fieldMapToTime: [],
|
||||
actionColOptions: {
|
||||
offset: 0,
|
||||
span: 8,
|
||||
xs: 8, // <576px
|
||||
sm: 8, // ≥576px
|
||||
md: 8, // ≥768px
|
||||
lg: 8, // ≥992px
|
||||
xl: 8, // ≥1200px
|
||||
xxl: 8, // ≥1600px
|
||||
},
|
||||
},
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '员工',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
success: handleSuccess,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable, { reload }, { selectedRowKeys }] = tableContext;
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
type: '新增',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function handleEdit(record: Recordable, type) {
|
||||
showFooter.value = true;
|
||||
getEdit(record, type);
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
function handleDetail(record: Recordable, type) {
|
||||
showFooter.value = false;
|
||||
getEdit(record, type);
|
||||
}
|
||||
/**
|
||||
* 获取编辑数据
|
||||
*/
|
||||
function getEdit(record, type) {
|
||||
resEditData({ id: record.id }).then((res) => {
|
||||
openDrawer(true, {
|
||||
record: {
|
||||
...res.extension,
|
||||
...res,
|
||||
orgCode: res?.depart?.id,
|
||||
departId: res?.depart?.id,
|
||||
departName: res?.depart?.departName,
|
||||
secondDepartName: res?.secondDepart?.departName,
|
||||
},
|
||||
type,
|
||||
isUpdate: true,
|
||||
showFooter: unref(showFooter),
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteOne({ id: record.id }, handleSuccess);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
message.warning('未选中任何数据');
|
||||
return;
|
||||
}
|
||||
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
(selectedRowKeys.value = []) && reload();
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [];
|
||||
}
|
||||
// 重置密码
|
||||
function restPass(record) {
|
||||
restPaddModel(true, {
|
||||
record: record.id,
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
});
|
||||
}
|
||||
// 1 锁定用户 2 激活用户
|
||||
async function handleUserOpt(record, type) {
|
||||
const params = {
|
||||
userId: record.id,
|
||||
status: type,
|
||||
};
|
||||
await lockUser(params, type, handleSuccess);
|
||||
}
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
// {
|
||||
// label: '编辑',
|
||||
// onClick: handleEdit.bind(null, record, '编辑'),
|
||||
// auth: 'system:health_user_employee_ex:edit',
|
||||
// },
|
||||
// {
|
||||
// label: '详情',
|
||||
// onClick: handleDetail.bind(null, record, '详情'),
|
||||
// },
|
||||
// {
|
||||
// label: '删除',
|
||||
// onClick: handleDelete.bind(null, record),
|
||||
// auth: 'system:health_user_employee_ex:delete',
|
||||
// },
|
||||
{
|
||||
label: '重置密码',
|
||||
onClick: restPass.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '锁定用户',
|
||||
auth: 'system:user:frozen',
|
||||
onClick: handleUserOpt.bind(null, record, 2),
|
||||
},
|
||||
{
|
||||
label: '激活用户',
|
||||
auth: 'system:user:frozen',
|
||||
onClick: handleUserOpt.bind(null, record, 1),
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.ant-table-title) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,125 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
export enum Api {
|
||||
queryDepartTreeSync = '/sys/sysDepart/queryDepartTreeSync',
|
||||
save = '/sys/sysDepart/add',
|
||||
edit = '/sys/sysDepart/edit',
|
||||
delete = '/sys/sysDepart/delete',
|
||||
deleteBatch = '/sys/sysDepart/deleteBatch',
|
||||
exportXlsUrl = '/sys/sysDepart/exportXls',
|
||||
importExcelUrl = '/sys/sysDepart/importExcel',
|
||||
|
||||
roleQueryTreeList = '/sys/role/queryTreeList',
|
||||
queryDepartPermission = '/sys/permission/queryDepartPermission',
|
||||
saveDepartPermission = '/sys/permission/saveDepartPermission',
|
||||
|
||||
dataRule = '/sys/sysDepartPermission/datarule',
|
||||
|
||||
getCurrentUserDeparts = '/sys/user/getCurrentUserDeparts',
|
||||
selectDepart = '/sys/selectDepart',
|
||||
getUpdateDepartInfo = '/sys/user/getUpdateDepartInfo',
|
||||
doUpdateDepartInfo = '/sys/user/doUpdateDepartInfo',
|
||||
changeDepartChargePerson = '/sys/user/changeDepartChargePerson',
|
||||
departTree = '/sys/core/getDepartTree',
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门树列表
|
||||
*/
|
||||
export const queryDepartTreeSync = (params?) => defHttp.get({ url: Api.queryDepartTreeSync, params });
|
||||
|
||||
/**
|
||||
* 保存或者更新部门角色
|
||||
*/
|
||||
export const saveOrUpdateDepart = (params, isUpdate) => {
|
||||
if (isUpdate) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除部门角色
|
||||
*/
|
||||
export const deleteBatchDepart = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取权限树列表
|
||||
*/
|
||||
export const queryRoleTreeList = (params?) => defHttp.get({ url: Api.roleQueryTreeList, params });
|
||||
/**
|
||||
* 查询部门权限
|
||||
*/
|
||||
export const queryDepartPermission = (params?) => defHttp.get({ url: Api.queryDepartPermission, params });
|
||||
/**
|
||||
* 保存部门权限
|
||||
*/
|
||||
export const saveDepartPermission = (params) => defHttp.post({ url: Api.saveDepartPermission, params });
|
||||
|
||||
/**
|
||||
* 查询部门数据权限列表
|
||||
*/
|
||||
export const queryDepartDataRule = (functionId, departId, params?) => {
|
||||
const url = `${Api.dataRule}/${unref(functionId)}/${unref(departId)}`;
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
/**
|
||||
* 保存部门数据权限
|
||||
*/
|
||||
export const saveDepartDataRule = (params) => defHttp.post({ url: Api.dataRule, params });
|
||||
/**
|
||||
* 获取登录用户部门信息
|
||||
*/
|
||||
export const getUserDeparts = (params?) => defHttp.get({ url: Api.getCurrentUserDeparts, params });
|
||||
/**
|
||||
* 切换选择部门
|
||||
*/
|
||||
export const selectDepart = (params?) => defHttp.put({ url: Api.selectDepart, params });
|
||||
|
||||
/**
|
||||
* 编辑部门前获取部门相关信息
|
||||
* @param id
|
||||
*/
|
||||
export const getUpdateDepartInfo = (id) => defHttp.get({ url: Api.getUpdateDepartInfo, params: { id } });
|
||||
|
||||
/**
|
||||
* 编辑部门
|
||||
* @param params
|
||||
*/
|
||||
export const doUpdateDepartInfo = (params) => defHttp.put({ url: Api.doUpdateDepartInfo, params });
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
* @param id
|
||||
*/
|
||||
export const deleteDepart = (id) => defHttp.delete({ url: Api.delete, params: { id } }, { joinParamsToUrl: true });
|
||||
|
||||
/**
|
||||
* 设置负责人 取消负责人
|
||||
* @param params
|
||||
*/
|
||||
export const changeDepartChargePerson = (params) => defHttp.put({ url: Api.changeDepartChargePerson, params });
|
||||
|
||||
export const departTree = (params) => defHttp.get({ url: Api.departTree, params });
|
||||
@@ -0,0 +1,219 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import { getDictCache } from '/@/utils/dict';
|
||||
import { isQH } from '/@/utils/getEnv';
|
||||
|
||||
function checkPhone(_, value) {
|
||||
const rule = /^[0-9,]+$/;
|
||||
if (value && !rule.test(value)) {
|
||||
return Promise.reject('只能输入数字和英文逗号!');
|
||||
}
|
||||
if (value?.length > 11) {
|
||||
const reg = /^1[3456789]\d{9}$/;
|
||||
const strs = value.split(',');
|
||||
for (let i = 0; i < strs.length; i++) {
|
||||
if (!reg.test(strs[i])) {
|
||||
return Promise.reject('第' + (i + 1) + '个手机号码格式不正确!');
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 部门基础表单
|
||||
export function useBasicFormSchema() {
|
||||
const basicFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'departName',
|
||||
label: '机构名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入机构/部门名称',
|
||||
},
|
||||
rules: [{ required: true, message: '机构名称不能为空' }],
|
||||
},
|
||||
{
|
||||
field: 'parentId',
|
||||
label: '上级部门',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
treeData: [],
|
||||
placeholder: '无',
|
||||
dropdownStyle: { maxHeight: '200px', overflow: 'auto' },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCode',
|
||||
label: '机构编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入机构编码',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCategory',
|
||||
label: '机构类型',
|
||||
component: 'RadioButtonGroup',
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgClass',
|
||||
label: '机构分类',
|
||||
component: 'JDictSelectTag',
|
||||
defaultValue: '1',
|
||||
componentProps: () => {
|
||||
return {
|
||||
type: 'radio',
|
||||
dictCode: 'orgclass',
|
||||
allowClear: false,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'departOrder',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {},
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
label: '电话',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入电话',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'fax',
|
||||
label: '传真',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入传真',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '健康管理员姓名',
|
||||
field: 'healthManageUserName',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '健康管理员电话',
|
||||
field: 'healthManageUserMobile',
|
||||
component: 'Input',
|
||||
componentProps: () => {
|
||||
return {
|
||||
placeholder: '请输入健康管理员电话,多个电话请用英文逗号隔开',
|
||||
};
|
||||
},
|
||||
dynamicRules: () => {
|
||||
return [
|
||||
{
|
||||
validator: checkPhone,
|
||||
trigger: 'blur',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '健康管理员地址',
|
||||
field: 'healthManageUserAddress',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '职业体检管理员姓名',
|
||||
field: 'medialManageUserName',
|
||||
component: 'Input',
|
||||
show: !isQH(),
|
||||
},
|
||||
{
|
||||
label: '职业体检管理员电话',
|
||||
field: 'medialManageUserMobile',
|
||||
component: 'Input',
|
||||
show: !isQH(),
|
||||
componentProps: () => {
|
||||
return {
|
||||
placeholder: '请输入体检管理员电话,多个电话请用英文逗号隔开',
|
||||
};
|
||||
},
|
||||
dynamicRules: () => {
|
||||
return [
|
||||
{
|
||||
validator: checkPhone,
|
||||
trigger: 'blur',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '职业体检管理员地址',
|
||||
field: 'medialManageUserAddress',
|
||||
component: 'Input',
|
||||
show: !isQH(),
|
||||
},
|
||||
{
|
||||
label: '健康监测管理员姓名',
|
||||
field: 'monitorManageUserName',
|
||||
component: 'Input',
|
||||
show: !isQH(),
|
||||
},
|
||||
{
|
||||
label: '健康监测管理员电话',
|
||||
field: 'monitorManageUserMobile',
|
||||
component: 'Input',
|
||||
componentProps: () => {
|
||||
return {
|
||||
placeholder: '请输入健康监测管理员电话,多个电话请用英文逗号隔开',
|
||||
};
|
||||
},
|
||||
show: !isQH(),
|
||||
dynamicRules: () => {
|
||||
return [
|
||||
{
|
||||
validator: checkPhone,
|
||||
trigger: 'blur',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '详细地址',
|
||||
field: 'address',
|
||||
component: 'Input',
|
||||
slot: 'addressInfo',
|
||||
// required: true,
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
field: 'lng',
|
||||
component: 'InputNumber',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
field: 'lat',
|
||||
component: 'InputNumber',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'memo',
|
||||
label: '备注',
|
||||
component: 'InputTextArea',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
];
|
||||
return { basicFormSchema };
|
||||
}
|
||||
|
||||
// 机构类型选项
|
||||
export const orgCategoryOptions = {
|
||||
// 一级部门
|
||||
root: [{ value: '1', label: '公司' }],
|
||||
// 子级部门
|
||||
child: [
|
||||
{ value: '2', label: '部门' },
|
||||
{ value: '3', label: '岗位' },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-depart-manage';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&--box {
|
||||
.ant-tabs-nav {
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="outer">
|
||||
<div class="inner">
|
||||
<a-row :class="['p-4', `${prefixCls}--box`]" type="flex" :gutter="10" style="height: 100%">
|
||||
<a-col :xl="12" :lg="24" :md="24" class="col-l">
|
||||
<DepartLeftTree ref="leftTree" @select="onTreeSelect" @rootTreeData="onRootTreeData" />
|
||||
</a-col>
|
||||
<a-col :xl="12" :lg="24" :md="24" class="col-r">
|
||||
<div style="height: 100%" class="depart-manage">
|
||||
<a-tabs v-show="departData != null" v-model:activeKey="activeKey" defaultActiveKey="base-info">
|
||||
<a-tab-pane tab="基本信息" key="base-info" forceRender style="position: relative">
|
||||
<div style="padding: 20px; overflow: hidden; background-color: #ffffff">
|
||||
<DepartFormTab :data="departData" :rootTreeData="rootTreeData" @success="onSuccess" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<!-- <a-tab-pane tab="部门权限" key="role-info">-->
|
||||
<!-- <div style="padding: 0 20px 20px">-->
|
||||
<!-- <DepartRuleTab :data="departData" />-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-tab-pane>-->
|
||||
<a-tab-pane
|
||||
tab="部门管理员"
|
||||
key="depart-manage"
|
||||
v-if="(hasPermission(orgManageApi.departManageList) && manager) || hasPer"
|
||||
>
|
||||
<div style="padding: 0 20px 20px">
|
||||
<depart-manage-list :data="departData" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="部门用户" key="depart-user" v-if="hasPermission(orgManageApi.departUserList)">
|
||||
<div style="padding: 0 20px 20px">
|
||||
<depart-user-list :activeKey="activeKey" :data="departData" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-show="departData == null" style="padding-top: 40px">
|
||||
<a-empty description="尚未选择部门" />
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="system-depart">
|
||||
import { provide, ref, computed } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import DepartLeftTree from './components/DepartLeftTree.vue';
|
||||
import DepartFormTab from './components/DepartFormTab.vue';
|
||||
import DepartUserList from './components/departUser/departUserList.vue';
|
||||
import DepartManageList from './components/departManage/departManageList.vue';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { orgManageApi } from '/@/utils/auth/buttonAuth/system';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const { prefixCls } = useDesign('depart-manage');
|
||||
provide('prefixCls', prefixCls);
|
||||
|
||||
// 给子组件定义一个ref变量
|
||||
const leftTree = ref();
|
||||
const activeKey = ref('base-info');
|
||||
|
||||
// 当前选中的部门信息
|
||||
const departData = ref({});
|
||||
const rootTreeData = ref<any[]>([]);
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
// 左侧树选择后触发
|
||||
function onTreeSelect(data) {
|
||||
departData.value = data;
|
||||
manager.value = data?.manager;
|
||||
|
||||
if (data?.orgType == 1) {
|
||||
activeKey.value = 'base-info';
|
||||
}
|
||||
}
|
||||
|
||||
const manager = ref();
|
||||
// 左侧树rootTreeData触发
|
||||
function onRootTreeData(data) {
|
||||
rootTreeData.value = data;
|
||||
}
|
||||
|
||||
function onSuccess() {
|
||||
leftTree.value.loadRootTreeData();
|
||||
}
|
||||
const hasPer = computed(() => {
|
||||
// 修改此字段
|
||||
return !!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system'));
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import './index.less';
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
/*begin 兼容暗夜模式*/
|
||||
.depart-manage {
|
||||
background-color: @component-background;
|
||||
}
|
||||
/*end 兼容暗夜模式*/
|
||||
|
||||
.outer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.spin-tree .ant-tree .ant-tree-icon-hide) {
|
||||
height: 100px !important;
|
||||
}
|
||||
|
||||
.col-l,
|
||||
.col-r {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.col-l {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
:deep(.col-l, .col-r) {
|
||||
width: calc(50% - 20px);
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<Description @register="registerDesc" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject, onMounted, watch } from 'vue';
|
||||
import { queryIdTree } from '../depart.user.api';
|
||||
import { useBaseInfoForm } from '../depart.user.data';
|
||||
import { Description, useDescription } from '/@/components/Description/index';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const props = defineProps({
|
||||
data: { require: true, type: Object },
|
||||
});
|
||||
const treeData = ref([]);
|
||||
const { descItems } = useBaseInfoForm(treeData);
|
||||
|
||||
const [registerDesc, { setDescProps }] = useDescription({
|
||||
data: props.data,
|
||||
schema: descItems,
|
||||
column: 1,
|
||||
labelStyle: {
|
||||
width: '180px',
|
||||
},
|
||||
});
|
||||
|
||||
function setData(data) {
|
||||
setDescProps({ data });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => props.data,
|
||||
() => setData(props.data),
|
||||
{ immediate: true }
|
||||
);
|
||||
});
|
||||
// 动态查询 parentId 组件的 treeData
|
||||
queryIdTree().then((data) => (treeData.value = data));
|
||||
</script>
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<BasicDrawer
|
||||
title="部门角色权限配置"
|
||||
:width="650"
|
||||
:loading="loading"
|
||||
showFooter
|
||||
okText="保存并关闭"
|
||||
@ok="onSubmit(true)"
|
||||
@close="onClose"
|
||||
@register="registerDrawer"
|
||||
>
|
||||
<div>
|
||||
<a-spin :spinning="loading">
|
||||
<template v-if="treeData.length > 0">
|
||||
<BasicTree
|
||||
title="所拥有的部门权限"
|
||||
toolbar
|
||||
checkable
|
||||
:treeData="treeData"
|
||||
:checkedKeys="checkedKeys"
|
||||
:selectedKeys="selectedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:checkStrictly="checkStrictly"
|
||||
:clickRowToExpand="false"
|
||||
@check="onCheck"
|
||||
@expand="onExpand"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ slotTitle, ruleFlag }">
|
||||
<span>{{ slotTitle }}</span>
|
||||
<Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red" />
|
||||
</template>
|
||||
</BasicTree>
|
||||
</template>
|
||||
<a-empty v-else description="无可配置部门权限" />
|
||||
</a-spin>
|
||||
</div>
|
||||
|
||||
<template #centerFooter>
|
||||
<a-button :loading="loading" ghost type="primary" @click="onSubmit(false)">仅保存</a-button>
|
||||
</template>
|
||||
</BasicDrawer>
|
||||
<DepartRoleDataRuleDrawer @register="registerDataRuleDrawer" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { BasicTree } from '/@/components/Tree/index';
|
||||
import { BasicDrawer, useDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
import DepartRoleDataRuleDrawer from './DepartRoleDataRuleDrawer.vue';
|
||||
import { queryTreeListForDeptRole, queryDeptRolePermission, saveDeptRolePermission } from '../depart.user.api';
|
||||
|
||||
defineEmits(['register']);
|
||||
const { createMessage } = useMessage();
|
||||
const loading = ref(false);
|
||||
const departId = ref('');
|
||||
const roleId = ref('');
|
||||
const treeData = ref<Array<any>>([]);
|
||||
const checkedKeys = ref<Array<any>>([]);
|
||||
const lastCheckedKeys = ref<Array<any>>([]);
|
||||
const expandedKeys = ref<Array<any>>([]);
|
||||
const selectedKeys = ref<Array<any>>([]);
|
||||
const allTreeKeys = ref<Array<any>>([]);
|
||||
const checkStrictly = ref(true);
|
||||
|
||||
// 注册抽屉组件
|
||||
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
|
||||
roleId.value = data.record.id;
|
||||
departId.value = data.record.departId;
|
||||
loadData();
|
||||
});
|
||||
// 注册数据规则授权弹窗抽屉
|
||||
const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer();
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
// 用户角色授权功能,查询菜单权限树
|
||||
const { ids, treeList } = await queryTreeListForDeptRole({ departId: departId.value });
|
||||
if (ids.length > 0) {
|
||||
allTreeKeys.value = ids;
|
||||
expandedKeys.value = ids;
|
||||
treeData.value = treeList;
|
||||
// 查询角色授权
|
||||
checkedKeys.value = await queryDeptRolePermission({ roleId: roleId.value });
|
||||
lastCheckedKeys.value = [checkedKeys.value];
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 重置页面
|
||||
function reset() {
|
||||
treeData.value = [];
|
||||
expandedKeys.value = [];
|
||||
checkedKeys.value = [];
|
||||
lastCheckedKeys.value = [];
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// tree勾选复选框事件
|
||||
function onCheck(event) {
|
||||
if (checkStrictly.value) {
|
||||
checkedKeys.value = event.checked;
|
||||
} else {
|
||||
checkedKeys.value = event;
|
||||
}
|
||||
}
|
||||
|
||||
// tree展开事件
|
||||
function onExpand($expandedKeys) {
|
||||
expandedKeys.value = $expandedKeys;
|
||||
}
|
||||
|
||||
// tree选中事件
|
||||
function onSelect($selectedKeys, { selectedNodes }) {
|
||||
if (selectedNodes[0]?.ruleFlag) {
|
||||
let functionId = $selectedKeys[0];
|
||||
dataRuleDrawer.openDrawer(true, { roleId, departId, functionId });
|
||||
}
|
||||
selectedKeys.value = [];
|
||||
}
|
||||
|
||||
function doClose() {
|
||||
reset();
|
||||
closeDrawer();
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
reset();
|
||||
}
|
||||
|
||||
async function onSubmit(exit) {
|
||||
try {
|
||||
loading.value = true;
|
||||
let params = {
|
||||
roleId: roleId.value,
|
||||
permissionIds: checkedKeys.value.join(','),
|
||||
lastpermissionIds: lastCheckedKeys.value.join(','),
|
||||
};
|
||||
await saveDeptRolePermission(params);
|
||||
if (exit) {
|
||||
doClose();
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
if (!exit) {
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<BasicDrawer title="数据规则/按钮权限配置" :width="365" @close="onClose" @register="registerDrawer">
|
||||
<a-spin :spinning="loading">
|
||||
<a-tabs defaultActiveKey="1">
|
||||
<a-tab-pane tab="数据规则" key="1">
|
||||
<a-checkbox-group v-model:value="dataRuleChecked" v-if="dataRuleList.length > 0">
|
||||
<a-row>
|
||||
<a-col :span="24" v-for="(item, index) in dataRuleList" :key="'dr' + index">
|
||||
<a-checkbox :value="item.id">{{ item.ruleName }}</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<div style="width: 100%; margin-top: 15px">
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:size="'small'"
|
||||
preIcon="ant-design:save-filled"
|
||||
@click="saveDataRuleForRole"
|
||||
>
|
||||
<span>点击保存</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-checkbox-group>
|
||||
<a-empty v-else description="无配置信息" />
|
||||
</a-tab-pane>
|
||||
<!--<a-tab-pane tab="按钮权限" key="2">敬请期待!!!</a-tab-pane>-->
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
|
||||
import { queryDepartRoleDataRule, saveDepartRoleDataRule } from '../depart.user.api';
|
||||
|
||||
defineEmits(['register']);
|
||||
const loading = ref<boolean>(false);
|
||||
const departId = ref('');
|
||||
const functionId = ref('');
|
||||
const roleId = ref('');
|
||||
const dataRuleList = ref<Array<any>>([]);
|
||||
const dataRuleChecked = ref<Array<any>>([]);
|
||||
|
||||
// 注册抽屉组件
|
||||
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
|
||||
roleId.value = unref(data.roleId);
|
||||
departId.value = unref(data.departId);
|
||||
functionId.value = unref(data.functionId);
|
||||
loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { datarule, drChecked } = await queryDepartRoleDataRule(functionId, departId, roleId);
|
||||
dataRuleList.value = datarule;
|
||||
if (drChecked) {
|
||||
dataRuleChecked.value = drChecked.split(',');
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function saveDataRuleForRole() {
|
||||
let params = {
|
||||
permissionId: functionId.value,
|
||||
roleId: roleId.value,
|
||||
dataRuleIds: dataRuleChecked.value.join(','),
|
||||
};
|
||||
saveDepartRoleDataRule(params);
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
doReset();
|
||||
}
|
||||
|
||||
function doReset() {
|
||||
functionId.value = '';
|
||||
roleId.value = '';
|
||||
dataRuleList.value = [];
|
||||
dataRuleChecked.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="addDepartRole">添加部门角色</a-button>
|
||||
<template v-if="selectedRowKeys.length > 0">
|
||||
<a-divider type="vertical" />
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteDepartRoleBatch">
|
||||
<icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作 </span>
|
||||
<icon icon="akar-icons:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 插槽:行内操作按钮 -->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 添加部门弹窗 -->
|
||||
<DepartRoleModal :departId="departId" @register="registerFormModal" @success="onFormModalSuccess" />
|
||||
<DepartRoleAuthDrawer @register="registerAuthDrawer" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, ref, unref, watch, computed, onMounted } from 'vue';
|
||||
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
import DepartRoleModal from './DepartRoleModal.vue';
|
||||
import DepartRoleAuthDrawer from './DepartRoleAuthDrawer.vue';
|
||||
import { deleteBatchDepartRole, departRoleList } from '../depart.user.api';
|
||||
import { departRoleColumns, departRoleSearchFormSchema } from '../depart.user.data';
|
||||
import { ColEx } from '/@/components/Form/src/types';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const props = defineProps({
|
||||
data: { require: true, type: Object },
|
||||
});
|
||||
defineEmits(['register']);
|
||||
// 当前选中的部门ID,可能会为空,代表未选择部门
|
||||
const departId = computed(() => props.data?.id);
|
||||
|
||||
// 自适应列配置
|
||||
const adaptiveColProps: Partial<ColEx> = {
|
||||
xs: 24, // <576px
|
||||
sm: 24, // ≥576px
|
||||
md: 24, // ≥768px
|
||||
lg: 12, // ≥992px
|
||||
xl: 12, // ≥1200px
|
||||
xxl: 8, // ≥1600px
|
||||
};
|
||||
// 列表页面公共参数、方法
|
||||
const { tableContext, createMessage } = useListPage({
|
||||
tableProps: {
|
||||
api: departRoleList,
|
||||
columns: departRoleColumns,
|
||||
canResize: false,
|
||||
formConfig: {
|
||||
labelWidth: 100,
|
||||
schemas: departRoleSearchFormSchema,
|
||||
baseColProps: adaptiveColProps,
|
||||
labelAlign: 'left',
|
||||
labelCol: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 24,
|
||||
lg: 9,
|
||||
xl: 7,
|
||||
xxl: 6,
|
||||
},
|
||||
wrapperCol: {},
|
||||
// 操作按钮配置
|
||||
actionColOptions: {
|
||||
...adaptiveColProps,
|
||||
style: { textAlign: 'left' },
|
||||
},
|
||||
},
|
||||
// 请求之前对参数做处理
|
||||
beforeFetch(params) {
|
||||
params.deptId = departId.value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload, setProps, setLoading, updateTableDataRecord }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
// 注册Form弹窗
|
||||
const [registerFormModal, formModal] = useModal();
|
||||
// 注册授权弹窗抽屉
|
||||
const [registerAuthDrawer, authDrawer] = useDrawer();
|
||||
|
||||
// 监听 data 更改,重新加载数据
|
||||
watch(
|
||||
() => props.data,
|
||||
() => reload()
|
||||
);
|
||||
onMounted(() => {
|
||||
reload();
|
||||
});
|
||||
|
||||
// 清空选择的行
|
||||
function clearSelection() {
|
||||
selectedRowKeys.value = [];
|
||||
}
|
||||
|
||||
// 添加部门角色
|
||||
function addDepartRole() {
|
||||
formModal.openModal(true, {
|
||||
isUpdate: false,
|
||||
record: {},
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑部门角色
|
||||
function editDepartRole(record) {
|
||||
formModal.openModal(true, {
|
||||
isUpdate: true,
|
||||
record: record,
|
||||
});
|
||||
}
|
||||
|
||||
// 授权部门角色
|
||||
function permissionDepartRole(record) {
|
||||
authDrawer.openDrawer(true, { record });
|
||||
}
|
||||
|
||||
// 批量删除部门角色
|
||||
async function deleteDepartRole(idList, confirm) {
|
||||
if (!departId.value) {
|
||||
createMessage.warning('请先选择一个部门');
|
||||
} else {
|
||||
setLoading(true);
|
||||
let ids = unref(idList).join(',');
|
||||
try {
|
||||
await deleteBatchDepartRole({ ids }, confirm);
|
||||
return reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
// 批量删除部门角色事件
|
||||
async function onDeleteDepartRoleBatch() {
|
||||
try {
|
||||
await deleteDepartRole(selectedRowKeys, true);
|
||||
// 批量删除成功后清空选择
|
||||
clearSelection();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 表单弹窗成功后的回调
|
||||
function onFormModalSuccess({ isUpdate, values }) {
|
||||
isUpdate ? updateTableDataRecord(values.id, values) : reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [{ label: '编辑', onClick: editDepartRole.bind(null, record) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{ label: '授权', onClick: permissionDepartRole.bind(null, record) },
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: deleteDepartRole.bind(null, [record.id], false),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<BasicModal :title="title" :width="800" v-bind="$attrs" @ok="handleOk" @register="registerModal">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, ref, unref } from 'vue';
|
||||
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
// noinspection ES6UnusedImports
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
|
||||
import { saveOrUpdateDepartRole } from '../depart.user.api';
|
||||
import { departRoleModalFormSchema } from '../depart.user.data';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const props = defineProps({
|
||||
// 当前部门ID
|
||||
departId: { require: true, type: String },
|
||||
});
|
||||
const prefixCls = inject('prefixCls');
|
||||
// 当前是否是更新模式
|
||||
const isUpdate = ref<boolean>(true);
|
||||
// 当前的弹窗数据
|
||||
const model = ref<object>({});
|
||||
const title = computed(() => (isUpdate.value ? '编辑部门角色' : '新增部门角色'));
|
||||
|
||||
//注册表单
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: departRoleModalFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
// 注册弹窗
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
isUpdate.value = unref(data?.isUpdate);
|
||||
// 无论新增还是编辑,都可以设置表单值
|
||||
let record = unref(data?.record);
|
||||
if (typeof record === 'object') {
|
||||
model.value = record;
|
||||
await setFieldsValue({ ...record });
|
||||
}
|
||||
});
|
||||
|
||||
//提交事件
|
||||
async function handleOk() {
|
||||
try {
|
||||
setModalProps({ confirmLoading: true });
|
||||
let values = await validate();
|
||||
values.departId = unref(props.departId);
|
||||
//提交表单
|
||||
await saveOrUpdateDepartRole(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success', { isUpdate: unref(isUpdate), values });
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<BasicDrawer title="部门角色分配" :width="365" @close="onClose" @register="registerDrawer">
|
||||
<a-spin :spinning="loading">
|
||||
<template v-if="desformList.length > 0">
|
||||
<a-checkbox-group v-model:value="designNameValue">
|
||||
<a-row>
|
||||
<a-col :span="24" v-for="item of desformList">
|
||||
<a-checkbox :value="item.id">{{ item.roleName }}</a-checkbox>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-checkbox-group>
|
||||
<div style="width: 100%; margin-top: 15px">
|
||||
<a-button type="primary" :loading="loading" :size="'small'" preIcon="ant-design:save-filled" @click="onSubmit">
|
||||
<span>点击保存</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<a-empty v-else description="无配置信息" />
|
||||
</a-spin>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
|
||||
import { queryDepartRoleByUserId, queryDepartRoleUserList, saveDepartRoleUser } from '../depart.user.api';
|
||||
|
||||
defineEmits(['register']);
|
||||
const loading = ref<boolean>(false);
|
||||
const userId = ref('');
|
||||
const departId = ref('');
|
||||
const oldRoleId = ref('');
|
||||
const desformList = ref<Array<any>>([]);
|
||||
const designNameValue = ref<Array<any>>([]);
|
||||
|
||||
// 注册抽屉组件
|
||||
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
|
||||
userId.value = unref(data.userId);
|
||||
departId.value = unref(data.departId);
|
||||
loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const params = {
|
||||
departId: departId.value,
|
||||
userId: userId.value,
|
||||
};
|
||||
// 查询 DepartRole
|
||||
const [$desformList, $departRoleList] = await Promise.all([queryDepartRoleUserList(params), queryDepartRoleByUserId(params)]);
|
||||
desformList.value = $desformList;
|
||||
designNameValue.value = $departRoleList.map((item) => item.droleId);
|
||||
oldRoleId.value = designNameValue.value.join(',');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
loading.value = true;
|
||||
await saveDepartRoleUser({
|
||||
userId: userId.value,
|
||||
newRoleId: designNameValue.value.join(','),
|
||||
oldRoleId: oldRoleId.value,
|
||||
});
|
||||
doClose();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
doReset();
|
||||
}
|
||||
|
||||
function doClose() {
|
||||
doReset();
|
||||
closeDrawer();
|
||||
}
|
||||
|
||||
function doReset() {
|
||||
userId.value = '';
|
||||
departId.value = '';
|
||||
oldRoleId.value = '';
|
||||
desformList.value = [];
|
||||
designNameValue.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="bg-white m-4 mr-0 overflow-hidden">
|
||||
<a-spin :spinning="loading">
|
||||
<template v-if="userIdentity === '2'">
|
||||
<!--组织机构树-->
|
||||
<BasicTree
|
||||
v-if="!treeReloading"
|
||||
title="部门列表"
|
||||
toolbar
|
||||
search
|
||||
showLine
|
||||
:checkStrictly="true"
|
||||
:clickRowToExpand="false"
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:autoExpandParent="autoExpandParent"
|
||||
@select="onSelect"
|
||||
@expand="onExpand"
|
||||
@search="onSearch"
|
||||
/>
|
||||
</template>
|
||||
<a-empty v-else description="普通员工无此权限" />
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, nextTick, ref } from 'vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { BasicTree } from '/@/components/Tree';
|
||||
import { queryMyDepartTreeList, searchByKeywords } from '../depart.user.api';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const emit = defineEmits(['select']);
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
let loading = ref<boolean>(false);
|
||||
// 部门树列表数据
|
||||
let treeData = ref<any[]>([]);
|
||||
// 当前展开的项
|
||||
let expandedKeys = ref<any[]>([]);
|
||||
// 当前选中的项
|
||||
let selectedKeys = ref<any[]>([]);
|
||||
// 是否自动展开父级
|
||||
let autoExpandParent = ref<boolean>(true);
|
||||
// 用户身份
|
||||
let userIdentity = ref<string>('2');
|
||||
// 树组件重新加载
|
||||
let treeReloading = ref<boolean>(false);
|
||||
|
||||
// 加载部门信息
|
||||
function loadDepartTreeData() {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
queryMyDepartTreeList()
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
if (Array.isArray(res.result)) {
|
||||
treeData.value = res.result;
|
||||
userIdentity.value = res.message;
|
||||
autoExpandParentNode();
|
||||
}
|
||||
} else {
|
||||
createMessage.warning(res.message);
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
|
||||
loadDepartTreeData();
|
||||
|
||||
// 自动展开父节点,只展开一级
|
||||
function autoExpandParentNode() {
|
||||
let keys: Array<any> = [];
|
||||
treeData.value.forEach((item, index) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
keys.push(item.key);
|
||||
}
|
||||
if (index === 0) {
|
||||
// 默认选中第一个
|
||||
setSelectedKey(item.id, item);
|
||||
}
|
||||
});
|
||||
if (keys.length > 0) {
|
||||
reloadTree();
|
||||
expandedKeys.value = keys;
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载树组件,防止无法默认展开数据
|
||||
async function reloadTree() {
|
||||
await nextTick();
|
||||
treeReloading.value = true;
|
||||
await nextTick();
|
||||
treeReloading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中的行
|
||||
*/
|
||||
function setSelectedKey(key: string, data?: object) {
|
||||
selectedKeys.value = [key];
|
||||
if (data) {
|
||||
emit('select', data);
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
function onSearch(value: string) {
|
||||
if (value) {
|
||||
loading.value = true;
|
||||
searchByKeywords({ keyWord: value, myDeptSearch: '1' })
|
||||
.then((result) => {
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
} else {
|
||||
createMessage.warning('未查询到部门信息');
|
||||
treeData.value = [];
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
loadDepartTreeData();
|
||||
}
|
||||
}
|
||||
|
||||
// 树选择事件
|
||||
function onSelect(selKeys, event) {
|
||||
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
|
||||
setSelectedKey(selKeys[0], event.selectedNodes[0]);
|
||||
} else {
|
||||
// 这样可以防止用户取消选择
|
||||
setSelectedKey(selectedKeys.value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 树展开事件
|
||||
function onExpand(keys) {
|
||||
expandedKeys.value = keys;
|
||||
autoExpandParent.value = false;
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/*升级antd3后,查询框与树贴的太近,样式优化*/
|
||||
:deep(.jeecg-tree-header) {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="selectAddUser">添加已有用户</a-button>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="createUser">新建用户</a-button>
|
||||
<template v-if="selectedRowKeys.length > 0">
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onUnlinkDepartUserBatch">
|
||||
<icon icon="bx:bx-unlink" />
|
||||
<span>取消关联</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作 </span>
|
||||
<icon icon="akar-icons:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 插槽:行内操作按钮 -->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<UserDrawer @register="registerDrawer" @success="onUserDrawerSuccess" />
|
||||
<DepartRoleUserAuthDrawer @register="registerUserAuthDrawer" />
|
||||
<UserSelectModal rowKey="id" @register="registerSelUserModal" @getSelectResult="onSelectUserOk" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, ref, unref, watch } from 'vue';
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
import UserDrawer from '/@/views/system/user/UserDrawer.vue';
|
||||
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue';
|
||||
import DepartRoleUserAuthDrawer from './DepartRoleUserAuthDrawer.vue';
|
||||
import { departUserList, linkDepartUserBatch, unlinkDepartUserBatch } from '../depart.user.api';
|
||||
import { userInfoColumns, userInfoSearchFormSchema } from '../depart.user.data';
|
||||
import { ColEx } from '/@/components/Form/src/types';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const props = defineProps({
|
||||
data: { require: true, type: Object },
|
||||
});
|
||||
// 当前选中的部门ID,可能会为空,代表未选择部门
|
||||
const departId = computed(() => props.data?.id);
|
||||
|
||||
// 自适应列配置
|
||||
const adaptiveColProps: Partial<ColEx> = {
|
||||
xs: 24, // <576px
|
||||
sm: 24, // ≥576px
|
||||
md: 24, // ≥768px
|
||||
lg: 12, // ≥992px
|
||||
xl: 12, // ≥1200px
|
||||
xxl: 8, // ≥1600px
|
||||
};
|
||||
// 列表页面公共参数、方法
|
||||
const { tableContext, createMessage } = useListPage({
|
||||
tableProps: {
|
||||
api: departUserList,
|
||||
columns: userInfoColumns,
|
||||
canResize: false,
|
||||
formConfig: {
|
||||
schemas: userInfoSearchFormSchema,
|
||||
baseColProps: adaptiveColProps,
|
||||
labelAlign: 'left',
|
||||
labelCol: {
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 24,
|
||||
lg: 9,
|
||||
xl: 7,
|
||||
xxl: 5,
|
||||
},
|
||||
wrapperCol: {},
|
||||
// 操作按钮配置
|
||||
actionColOptions: {
|
||||
...adaptiveColProps,
|
||||
style: { textAlign: 'left' },
|
||||
},
|
||||
},
|
||||
// 请求之前对参数做处理
|
||||
beforeFetch(params) {
|
||||
params.depId = departId.value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload, setProps, setLoading, updateTableDataRecord }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
() => reload()
|
||||
);
|
||||
//注册drawer
|
||||
const [registerDrawer, { openDrawer, setDrawerProps }] = useDrawer();
|
||||
const [registerUserAuthDrawer, userAuthDrawer] = useDrawer();
|
||||
// 注册用户选择 modal
|
||||
const [registerSelUserModal, selUserModal] = useModal();
|
||||
|
||||
// 清空选择的行
|
||||
function clearSelection() {
|
||||
selectedRowKeys.value = [];
|
||||
}
|
||||
|
||||
// 查看部门角色
|
||||
function showDepartRole(record) {
|
||||
userAuthDrawer.openDrawer(true, { userId: record.id, departId });
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
function createUser() {
|
||||
if (!departId.value) {
|
||||
createMessage.warning('请先选择一个部门');
|
||||
} else {
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
departDisabled: true,
|
||||
// 初始化负责部门
|
||||
nextDepartOptions: { value: props.data?.key, label: props.data?.title },
|
||||
record: {
|
||||
activitiSync: 1,
|
||||
userIdentity: 1,
|
||||
selecteddeparts: departId.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 查看用户详情
|
||||
function showUserDetail(record) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
departDisabled: true,
|
||||
showFooter: false,
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑用户信息
|
||||
function editUserInfo(record) {
|
||||
openDrawer(true, { isUpdate: true, record, departDisabled: true });
|
||||
}
|
||||
|
||||
// 选择添加已有用户
|
||||
function selectAddUser() {
|
||||
selUserModal.openModal();
|
||||
}
|
||||
|
||||
// 批量取消关联部门和用户之间的关系
|
||||
async function unlinkDepartUser(idList, confirm) {
|
||||
if (!departId.value) {
|
||||
createMessage.warning('请先选择一个部门');
|
||||
} else {
|
||||
setLoading(true);
|
||||
let userIds = unref(idList).join(',');
|
||||
try {
|
||||
await unlinkDepartUserBatch({ depId: departId.value, userIds }, confirm);
|
||||
return reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
// 批量取消关联事件
|
||||
async function onUnlinkDepartUserBatch() {
|
||||
try {
|
||||
await unlinkDepartUser(selectedRowKeys, true);
|
||||
// 批量删除成功后清空选择
|
||||
clearSelection();
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 选择用户成功
|
||||
async function onSelectUserOk(options, userIdList) {
|
||||
if (userIdList.length > 0) {
|
||||
try {
|
||||
setLoading(true);
|
||||
await linkDepartUserBatch(departId.value, userIdList);
|
||||
reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户抽屉表单成功回调
|
||||
*/
|
||||
function onUserDrawerSuccess({ isUpdate, values }) {
|
||||
isUpdate ? updateTableDataRecord(values.id, values) : reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [{ label: '编辑', onClick: editUserInfo.bind(null, record) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{ label: '部门角色', onClick: showDepartRole.bind(null, record) },
|
||||
{ label: '用户详情', onClick: showUserDetail.bind(null, record) },
|
||||
{
|
||||
label: '取消关联',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认取消关联吗?',
|
||||
confirm: unlinkDepartUser.bind(null, [record.id], false),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,159 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
enum Api {
|
||||
treeList = '/sys/sysDepart/queryMyDeptTreeList',
|
||||
queryIdTree = '/sys/sysDepart/queryIdTree',
|
||||
searchBy = '/sys/sysDepart/searchBy',
|
||||
}
|
||||
|
||||
// 部门用户API
|
||||
enum DepartUserApi {
|
||||
list = '/sys/user/departUserList',
|
||||
link = '/sys/user/editSysDepartWithUser',
|
||||
unlink = '/sys/user/deleteUserInDepartBatch',
|
||||
}
|
||||
|
||||
// 部门角色API
|
||||
enum DepartRoleApi {
|
||||
list = '/sys/sysDepartRole/list',
|
||||
deleteBatch = '/sys/sysDepartRole/deleteBatch',
|
||||
save = '/sys/sysDepartRole/add',
|
||||
edit = '/sys/sysDepartRole/edit',
|
||||
queryTreeListForDeptRole = '/sys/sysDepartPermission/queryTreeListForDeptRole',
|
||||
queryDeptRolePermission = '/sys/sysDepartPermission/queryDeptRolePermission',
|
||||
saveDeptRolePermission = '/sys/sysDepartPermission/saveDeptRolePermission',
|
||||
dataRule = '/sys/sysDepartRole/datarule',
|
||||
getDeptRoleList = '/sys/sysDepartRole/getDeptRoleList',
|
||||
getDeptRoleByUserId = '/sys/sysDepartRole/getDeptRoleByUserId',
|
||||
saveDeptRoleUser = '/sys/sysDepartRole/deptRoleUserAdd',
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门树列表
|
||||
*/
|
||||
export const queryMyDepartTreeList = (params?) => defHttp.get({ url: Api.treeList, params }, { isTransformResponse: false });
|
||||
|
||||
/**
|
||||
* 查询数据,以树结构形式加载所有部门的名称
|
||||
*/
|
||||
export const queryIdTree = (params?) => defHttp.get({ url: Api.queryIdTree, params });
|
||||
|
||||
/**
|
||||
* 根据关键字搜索部门
|
||||
*/
|
||||
export const searchByKeywords = (params) => defHttp.get({ url: Api.searchBy, params });
|
||||
|
||||
/**
|
||||
* 查询部门下的用户信息
|
||||
*/
|
||||
export const departUserList = (params) => defHttp.get({ url: DepartUserApi.list, params });
|
||||
|
||||
/**
|
||||
* 批量添加部门和用户的关联关系
|
||||
*
|
||||
* @param departId 部门ID
|
||||
* @param userIdList 用户ID列表
|
||||
*/
|
||||
export const linkDepartUserBatch = (departId: string, userIdList: string[]) =>
|
||||
defHttp.post({ url: DepartUserApi.link, params: { depId: departId, userIdList } });
|
||||
|
||||
/**
|
||||
* 批量取消部门和用户的关联关系
|
||||
*/
|
||||
export const unlinkDepartUserBatch = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: DepartUserApi.unlink, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '取消关联',
|
||||
content: '确定要取消关联吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询部门角色信息
|
||||
*/
|
||||
export const departRoleList = (params) => defHttp.get({ url: DepartRoleApi.list, params });
|
||||
|
||||
/**
|
||||
* 保存或者更新部门角色
|
||||
*/
|
||||
export const saveOrUpdateDepartRole = (params, isUpdate) => {
|
||||
if (isUpdate) {
|
||||
return defHttp.put({ url: DepartRoleApi.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: DepartRoleApi.save, params });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除部门角色
|
||||
*/
|
||||
export const deleteBatchDepartRole = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: DepartRoleApi.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户角色授权功能,查询菜单权限树
|
||||
*/
|
||||
export const queryTreeListForDeptRole = (params) => defHttp.get({ url: DepartRoleApi.queryTreeListForDeptRole, params });
|
||||
/**
|
||||
* 查询角色授权
|
||||
*/
|
||||
export const queryDeptRolePermission = (params) => defHttp.get({ url: DepartRoleApi.queryDeptRolePermission, params });
|
||||
/**
|
||||
* 保存角色授权
|
||||
*/
|
||||
export const saveDeptRolePermission = (params) => defHttp.post({ url: DepartRoleApi.saveDeptRolePermission, params });
|
||||
|
||||
/**
|
||||
* 查询部门角色数据权限列表
|
||||
*/
|
||||
export const queryDepartRoleDataRule = (functionId, departId, roleId, params?) => {
|
||||
const url = `${DepartRoleApi.dataRule}/${unref(functionId)}/${unref(departId)}/${unref(roleId)}`;
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
/**
|
||||
* 保存部门角色数据权限
|
||||
*/
|
||||
export const saveDepartRoleDataRule = (params) => defHttp.post({ url: DepartRoleApi.dataRule, params });
|
||||
/**
|
||||
* 查询部门角色用户授权
|
||||
*/
|
||||
export const queryDepartRoleUserList = (params) => defHttp.get({ url: DepartRoleApi.getDeptRoleList, params });
|
||||
/**
|
||||
* 根据 userId 查询部门角色用户授权
|
||||
*/
|
||||
export const queryDepartRoleByUserId = (params) => defHttp.get({ url: DepartRoleApi.getDeptRoleByUserId, params });
|
||||
/**
|
||||
* 保存部门角色用户授权
|
||||
*/
|
||||
export const saveDepartRoleUser = (params) => defHttp.post({ url: DepartRoleApi.saveDeptRoleUser, params });
|
||||
@@ -0,0 +1,195 @@
|
||||
import { Ref } from 'vue';
|
||||
import { duplicateCheck } from '/@/views/system/user/user.api';
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { DescItem } from '/@/components/Description';
|
||||
import { findTree } from '/@/utils/common/compUtils';
|
||||
|
||||
// 用户信息 columns
|
||||
export const userInfoColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '用户名称',
|
||||
dataIndex: 'realname',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
dataIndex: 'orgCode',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex_dictText',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '电话',
|
||||
dataIndex: 'phone',
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
// 用户信息查询条件表单
|
||||
export const userInfoSearchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户账号',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
// 部门角色 columns
|
||||
export const departRoleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '部门角色名称',
|
||||
dataIndex: 'roleName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '部门角色编码',
|
||||
dataIndex: 'roleCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
dataIndex: 'departId_dictText',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'description',
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
|
||||
// 部门角色查询条件表单
|
||||
export const departRoleSearchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '部门角色名称',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
// 部门角色弹窗form表单
|
||||
export const departRoleModalFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: 'id',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名称',
|
||||
component: 'Input',
|
||||
rules: [
|
||||
{ required: true, message: '部门角色名称不能为空!' },
|
||||
{ min: 2, max: 30, message: '长度在 2 到 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'roleCode',
|
||||
label: '角色编码',
|
||||
component: 'Input',
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
dynamicRules: ({ model }) => {
|
||||
return [
|
||||
{ required: true, message: '部门角色编码不能为空!' },
|
||||
{ min: 0, max: 64, message: '长度不能超过 64 个字符', trigger: 'blur' },
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (/[\u4E00-\u9FA5]/g.test(value)) {
|
||||
return Promise.reject('部门角色编码不可输入汉字!');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const params = {
|
||||
tableName: 'sys_depart_role',
|
||||
fieldName: 'role_code',
|
||||
fieldVal: value,
|
||||
dataId: model.id,
|
||||
};
|
||||
duplicateCheck(params)
|
||||
.then((res) => {
|
||||
res.success ? resolve() : reject(res.message || '校验失败');
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '验证失败');
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '描述',
|
||||
component: 'Input',
|
||||
rules: [{ min: 0, max: 126, message: '长度不能超过 126 个字符', trigger: 'blur' }],
|
||||
},
|
||||
];
|
||||
|
||||
// 基本信息form
|
||||
export function useBaseInfoForm(treeData: Ref<any[]>) {
|
||||
const descItems: DescItem[] = [
|
||||
{
|
||||
field: 'departName',
|
||||
label: '机构名称',
|
||||
},
|
||||
{
|
||||
field: 'parentId',
|
||||
label: '上级部门',
|
||||
render(val) {
|
||||
if (val) {
|
||||
const data = findTree(treeData.value, (item) => item.key == val);
|
||||
return data?.title ?? val;
|
||||
}
|
||||
return val;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCode',
|
||||
label: '机构编码',
|
||||
},
|
||||
{
|
||||
field: 'orgCategory',
|
||||
label: '机构类型',
|
||||
render(val) {
|
||||
if (val === '1') {
|
||||
return '公司';
|
||||
} else if (val === '2') {
|
||||
return '部门';
|
||||
} else if (val === '3') {
|
||||
return '岗位';
|
||||
}
|
||||
return val;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'departOrder',
|
||||
label: '排序',
|
||||
},
|
||||
|
||||
{
|
||||
field: 'mobile',
|
||||
label: '手机号',
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '地址',
|
||||
},
|
||||
{
|
||||
field: 'memo',
|
||||
label: '备注',
|
||||
},
|
||||
];
|
||||
|
||||
return { descItems };
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
@prefix-cls: ~'@{namespace}-depart-user';
|
||||
|
||||
.@{prefix-cls} {
|
||||
&--tree-search {
|
||||
width: 100%;
|
||||
margin: 10px 0 20px;
|
||||
}
|
||||
|
||||
&--base-info-form {
|
||||
@media (min-width: 576px) {
|
||||
.no-border {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ant-select.ant-select-disabled {
|
||||
.ant-select-selector {
|
||||
border: 0;
|
||||
color: black;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.ant-select-selector,
|
||||
.ant-select-selection-item {
|
||||
cursor: text !important;
|
||||
user-select: initial !important;
|
||||
}
|
||||
|
||||
.ant-select-selection-search,
|
||||
.ant-select-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 夜间模式样式兼容
|
||||
[data-theme='dark'] .@{prefix-cls} {
|
||||
&--base-info-form {
|
||||
.ant-select.ant-select-disabled {
|
||||
.ant-select-selector {
|
||||
color: #c9d1d9;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<a-row :class="['p-4', `${prefixCls}--box`]" :gutter="10">
|
||||
<a-col :xl="6" :lg="8" :md="10" :sm="24" style="flex: 1">
|
||||
<a-card :bordered="false" style="height: 100%">
|
||||
<DepartTree @select="onTreeSelect" />
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xl="18" :lg="16" :md="14" :sm="24" style="flex: 1">
|
||||
<a-card :bordered="false" style="height: 100%">
|
||||
<a-tabs defaultActiveKey="user-info">
|
||||
<a-tab-pane tab="基本信息" key="base-info" forceRender>
|
||||
<DepartBaseInfoTab :data="departData" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="部门员工" key="user-info">
|
||||
<DepartUserInfoTab :data="departData" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="部门角色" key="role-info">
|
||||
<DepartRoleInfoTab :data="departData" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="system-depart-user">
|
||||
import { provide, ref } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import DepartTree from './components/DepartTree.vue';
|
||||
import DepartBaseInfoTab from './components/DepartBaseInfoTab.vue';
|
||||
import DepartUserInfoTab from './components/DepartUserInfoTab.vue';
|
||||
import DepartRoleInfoTab from './components/DepartRoleInfoTab.vue';
|
||||
|
||||
const { prefixCls } = useDesign('depart-user');
|
||||
provide('prefixCls', prefixCls);
|
||||
|
||||
// 当前选中的部门信息
|
||||
let departData = ref({});
|
||||
|
||||
// 左侧树选择后触发
|
||||
function onTreeSelect(data) {
|
||||
departData.value = data;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import './index.less';
|
||||
</style>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="字典列表" width="800px">
|
||||
<BasicTable @register="registerTable" :rowClassName="getRowClassName">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" @click="handleCreate"> 新增</a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicDrawer>
|
||||
<DictItemModal @register="registerModal" @success="reload" :dictId="dictId" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import DictItemModal from './DictItemModal.vue';
|
||||
import { dictItemColumns, dictItemSearchFormSchema } from '../dict.data';
|
||||
import { itemList, deleteItem } from '../dict.api';
|
||||
import { ColEx } from '/@/components/Form/src/types';
|
||||
|
||||
const { prefixCls } = useDesign('row-invalid');
|
||||
const dictId = ref('');
|
||||
//字典配置model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerDrawer] = useDrawerInner(async (data) => {
|
||||
dictId.value = data.id;
|
||||
setProps({ searchInfo: { dictId: unref(dictId) } });
|
||||
reload();
|
||||
});
|
||||
// 自适应列配置
|
||||
const adaptiveColProps: Partial<ColEx> = {
|
||||
xs: 24, // <576px
|
||||
sm: 24, // ≥576px
|
||||
md: 24, // ≥768px
|
||||
lg: 12, // ≥992px
|
||||
xl: 12, // ≥1200px
|
||||
xxl: 8, // ≥1600px
|
||||
};
|
||||
const [registerTable, { reload, setProps }] = useTable({
|
||||
api: itemList,
|
||||
columns: dictItemColumns,
|
||||
formConfig: {
|
||||
baseColProps: adaptiveColProps,
|
||||
labelAlign: 'right',
|
||||
labelCol: {
|
||||
offset: 1,
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 24,
|
||||
lg: 9,
|
||||
xl: 7,
|
||||
xxl: 4,
|
||||
},
|
||||
wrapperCol: {},
|
||||
schemas: dictItemSearchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
},
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
canResize: false,
|
||||
immediate: false,
|
||||
actionColumn: {
|
||||
width: 100,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteItem({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function getRowClassName(record) {
|
||||
return record.status == 0 ? prefixCls : '';
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
@prefix-cls: ~'@{namespace}-row-invalid';
|
||||
|
||||
:deep(.@{prefix-cls}) {
|
||||
background: #f4f4f4;
|
||||
color: #bababa;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" width="800px">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { defineProps, ref, computed, unref, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { itemFormSchema } from '../dict.data';
|
||||
import { saveOrUpdateDictItem } from '../dict.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const props = defineProps({ dictId: String });
|
||||
const isUpdate = ref(true);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: itemFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
mergeDynamicData: props,
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 4 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 18 },
|
||||
},
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//设置标题
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
|
||||
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
values.dictId = props.dictId;
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateDictItem(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" width="500px" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { formSchema } from '../dict.data';
|
||||
import { saveOrUpdateDict } from '../dict.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false, minHeight: 80 });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.record.id;
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
});
|
||||
//设置标题
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增字典' : '编辑字典'));
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateDict(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } });
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="字典回收站" :showOkBtn="false" width="1000px" destroyOnClose>
|
||||
<BasicTable @register="registerTable">
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRaw } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { recycleBincolumns } from '../dict.data';
|
||||
import { getRecycleBinList, putRecycleBin, deleteRecycleBin } from '../dict.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner();
|
||||
//注册table数据
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: getRecycleBinList,
|
||||
columns: recycleBincolumns,
|
||||
striped: true,
|
||||
useSearchForm: false,
|
||||
showTableSetting: false,
|
||||
clickToRowSelect: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
pagination: false,
|
||||
tableSetting: { fullScreen: true },
|
||||
canResize: false,
|
||||
actionColumn: {
|
||||
width: 100,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 还原事件
|
||||
*/
|
||||
async function handleRevert(record) {
|
||||
await putRecycleBin(record.id, reload);
|
||||
emit('success');
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteRecycleBin(record.id, reload);
|
||||
}
|
||||
/**
|
||||
* 批量还原事件
|
||||
*/
|
||||
function batchHandleRevert() {
|
||||
handleRevert({ id: toRaw(checkedKeys.value).join(',') });
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
function batchHandleDelete() {
|
||||
handleDelete({ id: toRaw(checkedKeys.value).join(',') });
|
||||
}
|
||||
//获取操作栏事件
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '取回',
|
||||
icon: 'ant-design:redo-outlined',
|
||||
popConfirm: {
|
||||
title: '是否确认取回',
|
||||
confirm: handleRevert.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '彻底删除',
|
||||
icon: 'ant-design:scissor-outlined',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,145 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/dict/list',
|
||||
save = '/sys/dict/add',
|
||||
edit = '/sys/dict/edit',
|
||||
duplicateCheck = '/sys/duplicate/check',
|
||||
deleteDict = '/sys/dict/delete',
|
||||
deleteBatch = '/sys/dict/deleteBatch',
|
||||
importExcel = '/sys/dict/importExcel',
|
||||
exportXls = '/sys/dict/exportXls',
|
||||
recycleBinList = '/sys/dict/deleteList',
|
||||
putRecycleBin = '/sys/dict/back',
|
||||
deleteRecycleBin = '/sys/dict/deletePhysic',
|
||||
itemList = '/sys/dictItem/list',
|
||||
deleteItem = '/sys/dictItem/delete',
|
||||
itemSave = '/sys/dictItem/add',
|
||||
itemEdit = '/sys/dictItem/edit',
|
||||
dictItemCheck = '/sys/dictItem/dictItemCheck',
|
||||
refreshCache = '/sys/dict/refleshCache',
|
||||
queryAllDictItems = '/sys/dict/queryAllDictItems',
|
||||
fakeSwitch = '/health-system/test/fake',
|
||||
fakeRestore = '/health-system/test/restore',
|
||||
fakeUser = '/health-system/test/fakeUser',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
* @param params
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 字典列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
/**
|
||||
* 删除字典
|
||||
*/
|
||||
export const deleteDict = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteDict, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除字典
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteDict = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新字典
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateDict = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 唯一校验
|
||||
* @param params
|
||||
*/
|
||||
export const duplicateCheck = (params) => defHttp.get({ url: Api.duplicateCheck, params }, { isTransformResponse: false });
|
||||
/**
|
||||
* 字典回收站列表
|
||||
* @param params
|
||||
*/
|
||||
export const getRecycleBinList = (params) => defHttp.get({ url: Api.recycleBinList, params });
|
||||
/**
|
||||
* 回收站还原
|
||||
* @param params
|
||||
*/
|
||||
export const putRecycleBin = (id, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.putRecycleBin + `/${id}` }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 回收站删除
|
||||
* @param params
|
||||
*/
|
||||
export const deleteRecycleBin = (id, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteRecycleBin + `/${id}` }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 字典配置列表
|
||||
* @param params
|
||||
*/
|
||||
export const itemList = (params) => defHttp.get({ url: Api.itemList, params });
|
||||
/**
|
||||
* 字典配置删除
|
||||
* @param params
|
||||
*/
|
||||
export const deleteItem = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteItem, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新字典配置
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateDictItem = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.itemEdit : Api.itemSave;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 校验字典数据值
|
||||
* @param params
|
||||
*/
|
||||
export const dictItemCheck = (params) => defHttp.get({ url: Api.dictItemCheck, params }, { isTransformResponse: false });
|
||||
/**
|
||||
* 刷新字典
|
||||
* @param params
|
||||
*/
|
||||
export const refreshCache = () => defHttp.get({ url: Api.refreshCache }, { isTransformResponse: false });
|
||||
/**
|
||||
* 获取所有字典项
|
||||
* @param params
|
||||
*/
|
||||
export const queryAllDictItems = () => defHttp.get({ url: Api.queryAllDictItems }, { isTransformResponse: false });
|
||||
|
||||
export const fakeSwitch = (userId) => defHttp.post({ url: Api.fakeSwitch + '?userId=' + userId }, { isTransformResponse: false });
|
||||
|
||||
export const fakeRestore = () => defHttp.post({ url: Api.fakeRestore }, { isTransformResponse: false });
|
||||
|
||||
export const fakeUser = () => defHttp.post({ url: Api.fakeUser });
|
||||
@@ -0,0 +1,184 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { dictItemCheck } from './dict.api';
|
||||
import { rules } from '/@/utils/helper/validator';
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '字典名称',
|
||||
dataIndex: 'dictName',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '字典编码',
|
||||
dataIndex: 'dictCode',
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
// width: 120
|
||||
},
|
||||
];
|
||||
|
||||
export const recycleBincolumns: BasicColumn[] = [
|
||||
{
|
||||
title: '字典名称',
|
||||
dataIndex: 'dictName',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '字典编码',
|
||||
dataIndex: 'dictCode',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '字典名称',
|
||||
field: 'dictName',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '字典编码',
|
||||
field: 'dictCode',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '字典名称',
|
||||
field: 'dictName',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '字典编码',
|
||||
field: 'dictCode',
|
||||
component: 'Input',
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_dict', 'dict_code', model, schema, true),
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
field: 'description',
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
export const dictItemColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'itemText',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '数据值',
|
||||
dataIndex: 'itemValue',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
|
||||
export const dictItemSearchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '名称',
|
||||
field: 'itemText',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
field: 'status',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'dict_item_status',
|
||||
stringToNumber: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const itemFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '名称',
|
||||
field: 'itemText',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '数据值',
|
||||
field: 'itemValue',
|
||||
component: 'Input',
|
||||
dynamicRules: ({ values, model }) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请输入数据值');
|
||||
}
|
||||
if (new RegExp("[`~!@#$^&*()=|{}'.<>《》/?!¥()—【】‘;:”“。,、?]").test(value)) {
|
||||
return Promise.reject('数据值不能包含特殊字符!');
|
||||
}
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const params = {
|
||||
dictId: values.dictId,
|
||||
id: model.id,
|
||||
itemValue: value,
|
||||
};
|
||||
dictItemCheck(params)
|
||||
.then((res) => {
|
||||
res.success ? resolve() : reject(res.message || '校验失败');
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '验证失败');
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '描述',
|
||||
field: 'description',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'sortOrder',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
defaultValue: 1,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '是否启用',
|
||||
defaultValue: 1,
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
type: 'radioButton',
|
||||
dictCode: 'dict_item_status',
|
||||
// stringToNumber: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleCreate"> 新增</a-button>
|
||||
<a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button preIcon="ant-design:import-outlined" type="primary" @click="onImportXls">导入</j-upload-button>
|
||||
<a-button preIcon="ant-design:sync-outlined" type="primary" @click="handlerRefreshCache"> 刷新缓存</a-button>
|
||||
<a-button preIcon="ant-design:hdd-outlined" type="primary" @click="openRecycleModal(true)"> 回收站</a-button>
|
||||
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--字典弹窗-->
|
||||
<DictModal @register="registerModal" @success="handleSuccess" />
|
||||
<!--字典配置抽屉-->
|
||||
<DictItemList @register="registerDrawer" />
|
||||
<!--回收站弹窗-->
|
||||
<DictRecycleBinModal @register="registerModal1" @success="reload" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-dict" setup>
|
||||
//ts语法
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import DictItemList from './components/DictItemList.vue';
|
||||
import DictModal from './components/DictModal.vue';
|
||||
import DictRecycleBinModal from './components/DictRecycleBinModal.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { columns, searchFormSchema } from './dict.data';
|
||||
import { batchDeleteDict, deleteDict, getExportUrl, getImportUrl, list, queryAllDictItems, refreshCache } from './dict.api';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { clearDictCache, setDictCache } from '/@/utils/dict';
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
//字典model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
//字典配置drawer
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
|
||||
//回收站model
|
||||
const [registerModal1, { openModal: openRecycleModal }] = useModal();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||
designScope: 'dict-template',
|
||||
tableProps: {
|
||||
title: '数据字典',
|
||||
api: list,
|
||||
columns: columns,
|
||||
beforeFetch: (params) => {
|
||||
params['dictName'] = params['dictName'] ? `*${params['dictName']}*` : '';
|
||||
params['dictCode'] = params['dictCode'] ? `*${params['dictCode']}*` : '';
|
||||
return params;
|
||||
},
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
//update-begin---author:wangshuai ---date:20220616 for:[issues/I5AMDD]导入/导出功能,操作后提示没有传递 export.url/import.url 参数------------
|
||||
exportConfig: {
|
||||
name: '数据字典列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
//update-end---author:wangshuai ---date:20220616 for:[issues/I5AMDD]导入/导出功能,操作后提示没有传递 export.url/import.url 参数--------------
|
||||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload, updateTableDataRecord }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
async function handleEdit(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
async function handleDetail(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteDict({ id: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteDict({ ids: selectedRowKeys.value }, reload);
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess({ isUpdate, values }) {
|
||||
if (isUpdate) {
|
||||
updateTableDataRecord(values.id, values);
|
||||
} else {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
async function handlerRefreshCache() {
|
||||
const result = await refreshCache();
|
||||
if (result.success) {
|
||||
const res = await queryAllDictItems();
|
||||
clearDictCache();
|
||||
const dictItems = res.result;
|
||||
setDictCache('', dictItems, true);
|
||||
createMessage.success('刷新缓存完成!');
|
||||
} else {
|
||||
createMessage.error('刷新缓存失败!');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 字典配置
|
||||
*/
|
||||
function handleItem(record) {
|
||||
openDrawer(true, {
|
||||
id: record.id,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '字典配置',
|
||||
onClick: handleItem.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="40%">
|
||||
<BasicForm @register="registerForm" :disabled="isDisabled" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref, defineProps } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './demo.data';
|
||||
import { saveOrUpdateDemo, getDemoById } from './demo.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
|
||||
//自定义接受参数
|
||||
const props = defineProps({
|
||||
//是否禁用页面
|
||||
isDisabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
//labelWidth: 150,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false, showOkBtn: !props.isDisabled });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (data.createBy) {
|
||||
await setFieldsValue({ createBy: data.createBy });
|
||||
}
|
||||
if (data.createTime) {
|
||||
await setFieldsValue({ createTime: data.createTime });
|
||||
}
|
||||
if (unref(isUpdate)) {
|
||||
//获取详情
|
||||
data.record = await getDemoById({ id: data.record.id });
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
});
|
||||
//设置标题
|
||||
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
|
||||
//表单提交事件
|
||||
async function handleSubmit(v) {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateDemo(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success', values);
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,73 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/test/jeecgDemo/list',
|
||||
save = '/test/jeecgDemo/add',
|
||||
edit = '/test/jeecgDemo/edit',
|
||||
get = '/test/jeecgDemo/queryById',
|
||||
delete = '/test/jeecgDemo/delete',
|
||||
deleteBatch = '/test/jeecgDemo/deleteBatch',
|
||||
exportXls = '/test/jeecgDemo/exportXls',
|
||||
importExcel = '/test/jeecgDemo/importExcel',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 查询示例列表
|
||||
* @param params
|
||||
*/
|
||||
export const getDemoList = (params) => {
|
||||
return defHttp.get({ url: Api.list, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更新示例
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateDemo = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询示例详情
|
||||
* @param params
|
||||
*/
|
||||
export const getDemoById = (params) => {
|
||||
return defHttp.get({ url: Api.get, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除示例
|
||||
* @param params
|
||||
*/
|
||||
export const deleteDemo = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.delete, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除示例
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteDemo = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,220 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'name',
|
||||
width: 170,
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
sorter: {
|
||||
multiple: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '关键词',
|
||||
dataIndex: 'keyWord',
|
||||
width: 130,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
title: '打卡时间',
|
||||
dataIndex: 'punchTime',
|
||||
width: 140,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
title: '工资',
|
||||
dataIndex: 'salaryMoney',
|
||||
width: 140,
|
||||
resizable: true,
|
||||
sorter: {
|
||||
multiple: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '奖金',
|
||||
dataIndex: 'bonusMoney',
|
||||
width: 140,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex',
|
||||
sorter: {
|
||||
multiple: 3,
|
||||
},
|
||||
customRender: ({ record }) => {
|
||||
return render.renderDict(record.sex, 'sex');
|
||||
// let v = record.sex ? (record.sex == '1' ? '男' : '女') : '';
|
||||
// return h('span', v);
|
||||
},
|
||||
width: 120,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
title: '生日',
|
||||
dataIndex: 'birthday',
|
||||
width: 120,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
dataIndex: 'email',
|
||||
width: 120,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
title: '个人简介',
|
||||
dataIndex: 'content',
|
||||
width: 120,
|
||||
resizable: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: '姓名',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'birthday',
|
||||
label: '生日',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
valueType: 'Date',
|
||||
},
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'age',
|
||||
label: '年龄',
|
||||
component: 'Input',
|
||||
slot: 'age',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'sex',
|
||||
label: '性别',
|
||||
colProps: { span: 8 },
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'sex',
|
||||
placeholder: '请选择性别',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'id',
|
||||
label: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'createBy',
|
||||
label: 'createBy',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
label: 'createTime',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '名字',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: '请输入名字',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'keyWord',
|
||||
label: '关键词',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入关键词',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'punchTime',
|
||||
label: '打卡时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择打卡时间',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'salaryMoney',
|
||||
label: '工资',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入工资',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sex',
|
||||
label: '性别',
|
||||
component: 'JDictSelectTag',
|
||||
defaultValue: '1',
|
||||
componentProps: {
|
||||
type: 'radio',
|
||||
dictCode: 'sex',
|
||||
placeholder: '请选择性别',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'age',
|
||||
label: '年龄',
|
||||
component: 'InputNumber',
|
||||
defaultValue: 1,
|
||||
componentProps: {
|
||||
placeholder: '请输入年龄',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'birthday',
|
||||
label: '生日',
|
||||
component: 'DatePicker',
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
placeholder: '请选择生日',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: '邮箱',
|
||||
component: 'Input',
|
||||
rules: [{ required: false, type: 'email', message: '邮箱格式不正确', trigger: 'blur' }],
|
||||
componentProps: {
|
||||
placeholder: '请输入邮箱',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
label: '个人简介 - To introduce myself',
|
||||
component: 'InputTextArea',
|
||||
labelLength: 4,
|
||||
componentProps: {
|
||||
placeholder: '请输入个人简介',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'updateCount',
|
||||
label: '乐观锁',
|
||||
show: false,
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--自定义查询区域-->
|
||||
<div class="jeecg-basic-table-form-container" @keyup.enter="searchQuery" v-if="customSearch">
|
||||
<a-form ref="formRef" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-row :gutter="24">
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="用户名">
|
||||
<a-input v-model:value="queryParam.name" placeholder="请输入名称模糊查询" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="年龄">
|
||||
<a-input v-model:value="queryParam.age_begin" placeholder="最小年龄" style="width: calc(50% - 15px)" type="ge" />
|
||||
<span>~</span>
|
||||
<a-input v-model:value="queryParam.age_end" placeholder="最大年龄" style="width: calc(50% - 15px)" type="le" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<template v-if="toggleSearchStatus">
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="性别">
|
||||
<JDictSelectTag v-model:value="queryParam.sex" placeholder="请选择性别" dictCode="sex" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="选择用户">
|
||||
<JDictSelectTag v-model:value="queryParam.id" placeholder="请选择用户" dictCode="demo,name,id" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</template>
|
||||
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
|
||||
<a-col :lg="6">
|
||||
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px"
|
||||
>重置</a-button
|
||||
>
|
||||
<a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
|
||||
{{ toggleSearchStatus ? '收起' : '展开' }}
|
||||
<Icon :icon="toggleSearchStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
|
||||
</a>
|
||||
</a-col>
|
||||
</span>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</div>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection" :class="{ 'p-4': customSearch }">
|
||||
<template #form-age="{ model, field }">
|
||||
<a-input v-model:value="min" placeholder="最小年龄" style="width: calc(50% - 15px)" type="ge" @change="ageChange(model, field)" />
|
||||
<span>~</span>
|
||||
<a-input v-model:value="max" placeholder="最大年龄" style="width: calc(50% - 15px)" type="le" @change="ageChange(model, field)" />
|
||||
</template>
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd">新增</a-button>
|
||||
<a-upload name="file" :showUploadList="false" :customRequest="(file) => handleImportXls(file, getImportUrl, reload)">
|
||||
<a-button preIcon="ant-design:import-outlined" type="primary">导入</a-button>
|
||||
</a-upload>
|
||||
<a-button preIcon="ant-design:export-outlined" type="primary" @click="handleExportXls('单表示例', getExportUrl, exportParams)"
|
||||
>导出</a-button
|
||||
>
|
||||
<a-button preIcon="ant-design:filter" type="primary" @click="">高级查询</a-button>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="openTab">打开Tab页</a-button>
|
||||
<a-button preIcon="ant-design:retweet-outlined" type="primary" @click="customSearch = !customSearch">{{
|
||||
customSearch ? '表单配置查询' : '自定义查询'
|
||||
}}</a-button>
|
||||
<a-button preIcon="ant-design:import-outlined" type="primary" @click="handleImport">弹窗导入</a-button>
|
||||
|
||||
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
||||
|
||||
<a-dropdown v-if="checkedKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined" style="fontsize: 12px" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<DemoModal @register="registerModal" @success="reload" :isDisabled="isDisabled" />
|
||||
<JImportModal @register="registerModalJimport" :url="getImportUrl" online />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, reactive, toRaw, watch, computed } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import DemoModal from './DemoModal.vue';
|
||||
import JImportModal from '/@/components/Form/src/jeecg/components/JImportModal.vue';
|
||||
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useMethods } from '/@/hooks/system/useMethods';
|
||||
import { getDemoList, deleteDemo, batchDeleteDemo, getExportUrl, getImportUrl } from './demo.api';
|
||||
import { columns, searchFormSchema } from './demo.data';
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
import { router } from '/@/router';
|
||||
import { filterObj } from '/@/utils/common/compUtils';
|
||||
|
||||
const go = useGo();
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerModalJimport, { openModal: openModalJimport }] = useModal();
|
||||
const { handleExportXls, handleImportXls } = useMethods();
|
||||
const min = ref();
|
||||
const max = ref();
|
||||
const isDisabled = ref(false);
|
||||
|
||||
const [registerTable, { reload, setProps }] = useTable({
|
||||
title: '单表示例',
|
||||
api: getDemoList,
|
||||
columns,
|
||||
formConfig: {
|
||||
//labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
fieldMapToTime: [['birthday', ['birthday_begin', 'birthday_end'], 'YYYY-MM-DD']],
|
||||
fieldMapToNumber: [['age', ['age_begin', 'age_end']]],
|
||||
autoAdvancedCol: 2,
|
||||
actionColOptions: {
|
||||
style: { textAlign: 'left' },
|
||||
},
|
||||
},
|
||||
//自定义默认排序
|
||||
defSort: {
|
||||
column: 'createTime,sex',
|
||||
order: 'desc',
|
||||
},
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
clickToRowSelect: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
tableSetting: { fullScreen: true },
|
||||
canResize: false,
|
||||
rowKey: 'id',
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
/**
|
||||
* 选择列配置
|
||||
*/
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
columnWidth: 40,
|
||||
selectedRowKeys: checkedKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
|
||||
function handleImport() {
|
||||
openModalJimport(true);
|
||||
}
|
||||
|
||||
const exportParams = computed(() => {
|
||||
let paramsForm = {};
|
||||
if (checkedKeys.value && checkedKeys.value.length > 0) {
|
||||
paramsForm['selections'] = checkedKeys.value.join(',');
|
||||
}
|
||||
return filterObj(paramsForm);
|
||||
});
|
||||
/**
|
||||
* 操作列定义
|
||||
* @param record
|
||||
*/
|
||||
function getActions(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择事件
|
||||
*/
|
||||
function onSelectChange(selectedRowKeys: (string | number)[]) {
|
||||
console.log('checkedKeys------>', checkedKeys);
|
||||
checkedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
isDisabled.value = false;
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
isDisabled.value = false;
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情页面
|
||||
*/
|
||||
function handleDetail(record) {
|
||||
isDisabled.value = true;
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteDemo({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteDemo({ ids: checkedKeys.value }, reload);
|
||||
}
|
||||
/**
|
||||
* 年龄修改事件
|
||||
*/
|
||||
function ageChange(model, field) {
|
||||
model[field] = [unref(min), unref(max)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开tab页面
|
||||
*/
|
||||
function openTab() {
|
||||
go(`/comp/jeecg/basic`);
|
||||
}
|
||||
//-----自定义查询----begin--------
|
||||
const formElRef = ref();
|
||||
const labelCol = reactive({
|
||||
xs: { span: 24 },
|
||||
sm: { span: 7 },
|
||||
});
|
||||
const wrapperCol = reactive({
|
||||
xs: { span: 24 },
|
||||
sm: { span: 16 },
|
||||
});
|
||||
const toggleSearchStatus = ref(false);
|
||||
const customSearch = ref(false);
|
||||
const queryParam = reactive({
|
||||
name: '',
|
||||
age_begin: '',
|
||||
age_end: '',
|
||||
sex: '',
|
||||
id: '',
|
||||
});
|
||||
watch(customSearch, () => {
|
||||
setProps({ useSearchForm: !unref(customSearch) });
|
||||
});
|
||||
function searchQuery() {
|
||||
setProps({ searchInfo: toRaw(queryParam) });
|
||||
reload();
|
||||
}
|
||||
function searchReset() {
|
||||
Object.assign(queryParam, { name: '', age_begin: '', age_end: '', sex: '', id: '' });
|
||||
reload();
|
||||
}
|
||||
//自定义查询----end---------
|
||||
|
||||
const superQueryConfig = reactive({
|
||||
name: { title: '名称', view: 'text', type: 'string', order: 1 },
|
||||
sex: { title: '性别', view: 'list', type: 'string', dictCode: 'sex', order: 2 },
|
||||
});
|
||||
|
||||
function handleSuperQuery(params) {
|
||||
Object.keys(params).map((k) => {
|
||||
queryParam[k] = params[k];
|
||||
});
|
||||
searchQuery();
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.jeecg-basic-table-form-container {
|
||||
.table-page-search-submitButtons {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" :width="800" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './fill.rule.data';
|
||||
import { saveFillRule, updateFillRule } from './fill.rule.api';
|
||||
|
||||
//设置标题
|
||||
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
|
||||
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, getFieldsValue }] = useForm({
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { span: 12 },
|
||||
});
|
||||
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let formValue = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
if (isUpdate.value) {
|
||||
let allFieldsValue = getFieldsValue();
|
||||
// 编辑页面 如果表单没有父级下拉框 则提交时候 validate方法不返该值 需要手动设置
|
||||
if (!formValue.parentId && allFieldsValue.parentId) {
|
||||
formValue.parentId = allFieldsValue.parentId;
|
||||
}
|
||||
await updateFillRule(formValue);
|
||||
} else {
|
||||
await saveFillRule(formValue);
|
||||
}
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,83 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/fillRule/list',
|
||||
test = '/sys/fillRule/testFillRule',
|
||||
save = '/sys/fillRule/add',
|
||||
edit = '/sys/fillRule/edit',
|
||||
delete = '/sys/fillRule/delete',
|
||||
deleteBatch = '/sys/fillRule/deleteBatch',
|
||||
exportXls = '/sys/fillRule/exportXls',
|
||||
importExcel = '/sys/fillRule/importExcel',
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出地址
|
||||
*/
|
||||
export const exportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入地址
|
||||
*/
|
||||
export const importUrl = Api.importExcel;
|
||||
|
||||
/**
|
||||
* 列表查询
|
||||
* @param params
|
||||
*/
|
||||
export const getFillRuleList = (params) => {
|
||||
return defHttp.get({ url: Api.list, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param params
|
||||
* @param handleSuccess
|
||||
*/
|
||||
export const deleteFillRule = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.delete, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteFillRule = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 规则功能测试
|
||||
* @param params
|
||||
*/
|
||||
export const handleTest = (params) => {
|
||||
return defHttp.get({ url: Api.test, params }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存
|
||||
* @param params
|
||||
*/
|
||||
export const saveFillRule = (params) => {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param params
|
||||
*/
|
||||
export const updateFillRule = (params) => {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { duplicateCheck } from '/@/views/system/user/user.api';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '规则名称',
|
||||
dataIndex: 'ruleName',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '规则编码',
|
||||
dataIndex: 'ruleCode',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '规则实现类',
|
||||
dataIndex: 'ruleClass',
|
||||
width: 300,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '规则参数',
|
||||
dataIndex: 'ruleParams',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'ruleName',
|
||||
label: '规则名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
field: 'ruleCode',
|
||||
label: '规则编码',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'ruleName',
|
||||
label: '规则名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
{
|
||||
field: 'ruleCode',
|
||||
label: '规则编码',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
dynamicRules: ({ model }) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!value) {
|
||||
return reject('请输入规则编码!');
|
||||
}
|
||||
const params = {
|
||||
tableName: 'sys_fill_rule',
|
||||
fieldName: 'rule_code',
|
||||
fieldVal: value,
|
||||
dataId: model.id,
|
||||
};
|
||||
duplicateCheck(params)
|
||||
.then((res) => {
|
||||
res.success ? resolve() : reject('规则编码已存在!');
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '校验失败');
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ruleClass',
|
||||
label: '规则实现类',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
{
|
||||
field: 'ruleParams',
|
||||
label: '规则参数',
|
||||
colProps: { span: 24 },
|
||||
component: 'JAddInput',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd">新增</a-button>
|
||||
<a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button preIcon="ant-design:import-outlined" type="primary" @click="onImportXls">导入</j-upload-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作</span>
|
||||
<Icon icon="mdi:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<FillRuleModal @register="registerModal" @success="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script name="system-fillrule" lang="ts" setup>
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { getFillRuleList, exportUrl, importUrl, deleteFillRule, batchDeleteFillRule, handleTest } from '/@/views/system/fillRule/fill.rule.api';
|
||||
import { columns, searchFormSchema } from '/@/views/system/fillRule/fill.rule.data';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { ActionItem } from '/@/components/Table';
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
import FillRuleModal from '/@/views/system/fillRule/FillRuleModal.vue';
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, createMessage, createSuccessModal, onExportXls, onImportXls } = useListPage({
|
||||
designScope: 'fill-rule',
|
||||
tableProps: {
|
||||
title: '填值规则管理页面',
|
||||
api: getFillRuleList,
|
||||
columns: columns,
|
||||
showIndexColumn: true,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
url: exportUrl,
|
||||
name: '填值规则列表',
|
||||
},
|
||||
importConfig: {
|
||||
url: importUrl,
|
||||
success: () => reload(),
|
||||
},
|
||||
});
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
console.log('record....', record);
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
console.log(12345, record);
|
||||
await deleteFillRule({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteFillRule({ ids: selectedRowKeys.value }, () => {
|
||||
selectedRowKeys.value = [];
|
||||
reload();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能测试
|
||||
*/
|
||||
function testRule(record) {
|
||||
let params = { ruleCode: record.ruleCode };
|
||||
handleTest(params).then((res) => {
|
||||
if (res.success) {
|
||||
createSuccessModal({
|
||||
title: '填值规则功能测试',
|
||||
content: '生成结果:' + res.result,
|
||||
});
|
||||
} else {
|
||||
createMessage.warn(res.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [{ label: '编辑', onClick: handleEdit.bind(null, record) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{ label: '功能测试', onClick: testRule.bind(null, record) },
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="adTextImg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox aui-formEwm">
|
||||
<div class="aui-formWell">
|
||||
<form>
|
||||
<div class="aui-flex aui-form-nav investment_title" style="padding-bottom: 19px">
|
||||
<div class="aui-flex-box activeNav">{{ t('sys.login.qrSignInFormTitle') }}</div>
|
||||
</div>
|
||||
<div class="aui-form-box">
|
||||
<div class="aui-account" style="padding: 30px 0">
|
||||
<div class="aui-ewm">
|
||||
<QrCode :value="qrCodeUrl" class="enter-x flex justify-center xl:justify-start" :width="280" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<a class="aui-linek-code aui-link-register" @click="goBackHandleClick">{{ t('sys.login.backSignIn') }}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="aui-flex aui-third-text">
|
||||
<div class="aui-flex-box aui-third-border">
|
||||
<span>{{ t('sys.login.otherSignIn') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex" :class="`${prefixCls}-sign-in-way`">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="github" @click="onThirdLogin('github')"><GithubFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="企业微信" @click="onThirdLogin('wechat_enterprise')"
|
||||
><icon-font class="item-icon" type="icon-qiyeweixin3"
|
||||
/></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="钉钉" @click="onThirdLogin('dingtalk')"><DingtalkCircleFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-third-login">
|
||||
<a href="" title="微信" @click="onThirdLogin('wechat_open')"><WechatFilled /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第三方登录相关弹框 -->
|
||||
<ThirdModal ref="thirdModalRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="mini-code-login">
|
||||
import { ref, onUnmounted } from 'vue';
|
||||
import { getLoginQrcode, getQrcodeToken } from '/@/api/sys/user';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { QrCode } from '/@/components/Qrcode/index';
|
||||
import ThirdModal from '/@/views/sys/login/ThirdModal.vue';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png';
|
||||
import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { GithubFilled, WechatFilled, DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
|
||||
});
|
||||
const { prefixCls } = useDesign('minilogin');
|
||||
const { t } = useI18n();
|
||||
const qrCodeUrl = ref<string>('');
|
||||
let timer: IntervalHandle;
|
||||
const state = ref('0');
|
||||
const thirdModalRef = ref();
|
||||
const userStore = useUserStore();
|
||||
const emit = defineEmits(['go-back', 'success', 'register']);
|
||||
|
||||
//加载二维码信息
|
||||
function loadQrCode() {
|
||||
state.value = '0';
|
||||
getLoginQrcode().then((res) => {
|
||||
qrCodeUrl.value = res.qrcodeId;
|
||||
if (res.qrcodeId) {
|
||||
openTimer(res.qrcodeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
//监控扫码状态
|
||||
function watchQrcodeToken(qrcodeId) {
|
||||
getQrcodeToken({ qrcodeId: qrcodeId }).then((res) => {
|
||||
let token = res.token;
|
||||
if (token == '-2') {
|
||||
//二维码过期重新获取
|
||||
loadQrCode();
|
||||
clearInterval(timer);
|
||||
}
|
||||
//扫码成功
|
||||
if (res.success) {
|
||||
state.value = '2';
|
||||
clearInterval(timer);
|
||||
setTimeout(() => {
|
||||
userStore.qrCodeLogin(token);
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 开启定时器 */
|
||||
function openTimer(qrcodeId) {
|
||||
watchQrcodeToken(qrcodeId);
|
||||
closeTimer();
|
||||
timer = setInterval(() => {
|
||||
watchQrcodeToken(qrcodeId);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
/** 关闭定时器 */
|
||||
function closeTimer() {
|
||||
if (timer) clearInterval(timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param type
|
||||
*/
|
||||
function onThirdLogin(type) {
|
||||
thirdModalRef.value.onThirdLogin(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单
|
||||
*/
|
||||
function initFrom() {
|
||||
loadQrCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
function goBackHandleClick() {
|
||||
emit('go-back');
|
||||
closeTimer();
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
closeTimer();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
initFrom,
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
</style>
|
||||
@@ -0,0 +1,292 @@
|
||||
<template>
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="adTextImg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<div class="aui-step-box">
|
||||
<div class="aui-step-item" :class="activeKey === 1 ? 'activeStep' : ''">
|
||||
<div class="aui-step-tags">
|
||||
<em>1</em>
|
||||
<p>{{ t('sys.login.authentication') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-step-item" :class="activeKey === 2 ? 'activeStep' : ''">
|
||||
<div class="aui-step-tags">
|
||||
<em>2</em>
|
||||
<p>{{ t('sys.login.resetLoginPassword') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-step-item" :class="activeKey === 3 ? 'activeStep' : ''">
|
||||
<div class="aui-step-tags">
|
||||
<em>3</em>
|
||||
<p>{{ t('sys.login.resetSuccess') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="" style="height: 230px; position: relative">
|
||||
<a-form ref="formRef" :model="formData" v-if="activeKey === 1">
|
||||
<!-- 身份验证 begin -->
|
||||
<div class="aui-account aui-account-line aui-forgot">
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-input type="text" :placeholder="t('sys.login.mobile')" v-model:value="formData.mobile" />
|
||||
</div>
|
||||
</a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-form-item>
|
||||
<a-input type="text" :placeholder="t('sys.login.smsCode')" v-model:value="formData.smscode" />
|
||||
</a-form-item>
|
||||
<div v-if="showInterval" class="aui-code-line" @click="getLoginCode">{{
|
||||
t('component.countdown.normalText')
|
||||
}}</div>
|
||||
<div v-else class="aui-code-line">{{ t('component.countdown.sendText', [unref(timeRuning)]) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 身份验证 end -->
|
||||
</a-form>
|
||||
<a-form ref="pwdFormRef" :model="pwdFormData" v-else-if="activeKey === 2">
|
||||
<!-- 重置密码 begin -->
|
||||
<div class="aui-account aui-account-line aui-forgot">
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-input
|
||||
type="password"
|
||||
:placeholder="t('sys.login.passwordPlaceholder')"
|
||||
v-model:value="pwdFormData.password"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<a-input
|
||||
type="password"
|
||||
:placeholder="t('sys.login.confirmPassword')"
|
||||
v-model:value="pwdFormData.confirmPassword"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<!-- 重置密码 end -->
|
||||
</a-form>
|
||||
<!-- 重置成功 begin -->
|
||||
<div class="aui-success" v-else>
|
||||
<div class="aui-success-icon">
|
||||
<img :src="successImg" />
|
||||
</div>
|
||||
<h3>恭喜您,重置密码成功!</h3>
|
||||
</div>
|
||||
<!-- 重置成功 end -->
|
||||
</div>
|
||||
<div class="aui-formButton" style="padding-bottom: 40px">
|
||||
<div class="aui-flex" v-if="activeKey === 1 || activeKey === 2">
|
||||
<a class="aui-link-login aui-flex-box" @click="nextStepClick">{{ t('sys.login.nextStep') }}</a>
|
||||
</div>
|
||||
<div class="aui-flex" v-else>
|
||||
<a class="aui-linek-code aui-flex-box" @click="toLogin">{{ t('sys.login.goToLogin') }}</a>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<a class="aui-linek-code aui-flex-box" @click="goBack"> {{ t('sys.login.backSignIn') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" name="mini-forgotpad" setup>
|
||||
import { reactive, ref, toRaw, unref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { SmsEnum, useFormRules, useFormValid, useLoginState } from '/@/views/sys/login/useLogin';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getCaptcha, passwordChange, phoneVerify } from '/@/api/sys/user';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png';
|
||||
import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
|
||||
import successImg from '/@/assets/loginmini/icon/icon-success.png';
|
||||
|
||||
//下一步控制
|
||||
const activeKey = ref<number>(1);
|
||||
const { t } = useI18n();
|
||||
const { handleBackLogin } = useLoginState();
|
||||
const { notification, createMessage, createErrorModal } = useMessage();
|
||||
//是否显示获取验证码
|
||||
const showInterval = ref<boolean>(true);
|
||||
//60s
|
||||
const timeRuning = ref<number>(60);
|
||||
//定时器
|
||||
const timer = ref<any>(null);
|
||||
const formRef = ref();
|
||||
const pwdFormRef = ref();
|
||||
//账号数据
|
||||
const accountInfo = reactive<any>({});
|
||||
//手机号表单
|
||||
const formData = reactive({
|
||||
mobile: '',
|
||||
smscode: '',
|
||||
});
|
||||
//密码表单
|
||||
const pwdFormData = reactive<any>({
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
});
|
||||
const emit = defineEmits(['go-back', 'success', 'register']);
|
||||
|
||||
/**
|
||||
* 下一步
|
||||
*/
|
||||
async function handleNext() {
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.smscode) {
|
||||
createMessage.warn(t('sys.login.smsPlaceholder'));
|
||||
return;
|
||||
}
|
||||
const resultInfo = await phoneVerify(
|
||||
toRaw({
|
||||
phone: formData.mobile,
|
||||
smscode: formData.smscode,
|
||||
})
|
||||
);
|
||||
if (resultInfo.success) {
|
||||
Object.assign(accountInfo, {
|
||||
username: resultInfo.result.username,
|
||||
phone: formData.mobile,
|
||||
smscode: formData.smscode,
|
||||
});
|
||||
activeKey.value = 2;
|
||||
setTimeout(() => {
|
||||
pwdFormRef.value.resetFields();
|
||||
}, 300);
|
||||
} else {
|
||||
notification.error({
|
||||
message: '错误提示',
|
||||
description: resultInfo.message || t('sys.api.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成修改密码
|
||||
*/
|
||||
async function finishedPwd() {
|
||||
if (!pwdFormData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!pwdFormData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.confirmPassword'));
|
||||
return;
|
||||
}
|
||||
if (pwdFormData.password !== pwdFormData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.diffPwd'));
|
||||
return;
|
||||
}
|
||||
const resultInfo = await passwordChange(
|
||||
toRaw({
|
||||
username: accountInfo.username,
|
||||
password: pwdFormData.password,
|
||||
smscode: accountInfo.smscode,
|
||||
phone: accountInfo.phone,
|
||||
})
|
||||
);
|
||||
if (resultInfo.success) {
|
||||
accountInfo.password = pwdFormData.password;
|
||||
//修改密码
|
||||
activeKey.value = 3;
|
||||
} else {
|
||||
//错误提示
|
||||
createErrorModal({
|
||||
title: t('sys.api.errorTip'),
|
||||
content: resultInfo.message || t('sys.api.networkExceptionMsg'),
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 下一步
|
||||
*/
|
||||
function nextStepClick() {
|
||||
if (unref(activeKey) == 1) {
|
||||
handleNext();
|
||||
} else if (unref(activeKey) == 2) {
|
||||
finishedPwd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去登录
|
||||
*/
|
||||
function toLogin() {
|
||||
emit('success', { username: accountInfo.username, password: accountInfo.password });
|
||||
initForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
function goBack() {
|
||||
emit('go-back');
|
||||
initForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机验证码
|
||||
*/
|
||||
async function getLoginCode() {
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
const result = await getCaptcha({ mobile: formData.mobile, smsmode: SmsEnum.FORGET_PASSWORD });
|
||||
if (result) {
|
||||
const TIME_COUNT = 60;
|
||||
if (!unref(timer)) {
|
||||
timeRuning.value = TIME_COUNT;
|
||||
showInterval.value = false;
|
||||
timer.value = setInterval(() => {
|
||||
if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
|
||||
timeRuning.value = timeRuning.value - 1;
|
||||
} else {
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单
|
||||
*/
|
||||
function initForm() {
|
||||
activeKey.value = 1;
|
||||
Object.assign(formData, { phone: '', smscode: '' });
|
||||
Object.assign(pwdFormData, { password: '', confirmPassword: '' });
|
||||
Object.assign(accountInfo, {});
|
||||
if (unref(timer)) {
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
showInterval.value = true;
|
||||
}
|
||||
setTimeout(() => {
|
||||
formRef.value.resetFields();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initForm,
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
</style>
|
||||
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<div :class="prefixCls" class="login-background-img">
|
||||
<!-- <AppLocalePicker :showText="false" class="absolute top-4 right-4 enter-x xl:text-gray-600" />-->
|
||||
<!-- <AppDarkModeToggle class="absolute top-3 right-7 enter-x" />-->
|
||||
<div v-show="type === 'login'">
|
||||
<div class="aui-content">
|
||||
<div class="aui-top">
|
||||
<div class="aui-title">{{ t('sys.login.loginTitle') }}</div>
|
||||
<div class="aui-title-image"></div>
|
||||
</div>
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<div class="form-title">{{ t('sys.login.formTitle') }}</div>
|
||||
<div class="aui-form-box" style="height: 180px">
|
||||
<a-form ref="loginRef" :model="formData" @keyup.enter.native="loginHandleClick">
|
||||
<div class="aui-account">
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-code"></i>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formData.username"
|
||||
:placeholder="t('sys.login.userName')"
|
||||
class="fix-auto-fill"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-password"></i>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formData.password"
|
||||
:placeholder="t('sys.login.password')"
|
||||
class="fix-auto-fill"
|
||||
type="password"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-code"></i>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formData.inputCode"
|
||||
:placeholder="t('sys.login.inputCode')"
|
||||
class="fix-auto-fill"
|
||||
type="text"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="aui-code">
|
||||
<img
|
||||
v-if="randCodeData.requestCodeSuccess"
|
||||
:src="randCodeData.randCodeImage"
|
||||
@click="handleChangeCheckCode"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
:src="codeImg"
|
||||
style="margin-top: 2px; max-width: initial"
|
||||
@click="handleChangeCheckCode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex remember">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-choice">
|
||||
<a-input v-model:value="rememberMe" class="fix-auto-fill" type="checkbox" />
|
||||
<span class="aui-check">{{ t('sys.login.rememberMe') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<div class="aui-flex">
|
||||
<a-button
|
||||
:loading="loginLoading"
|
||||
class="aui-link-login aui-flex-box"
|
||||
type="primary"
|
||||
@click="loginHandleClick"
|
||||
>
|
||||
{{ t('sys.login.loginButton') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" name="login-mini" setup>
|
||||
import { getCodeInfo } from '/@/api/sys/user';
|
||||
import { onMounted, reactive, ref, toRaw, watch } from 'vue';
|
||||
import codeImg from '/@/assets/images/checkcode.png';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { encipher } from '/@/utils/jsencrypt';
|
||||
|
||||
const { prefixCls } = useDesign('mini-login');
|
||||
const { notification, createMessage } = useMessage();
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
const randCodeData = reactive<any>({
|
||||
randCodeImage: '',
|
||||
requestCodeSuccess: false,
|
||||
checkKey: null,
|
||||
});
|
||||
const rememberMe = ref<string>('0');
|
||||
const type = ref<string>('login');
|
||||
//账号登录表单字段
|
||||
const formData = reactive<any>({
|
||||
inputCode: '',
|
||||
username: '',
|
||||
password: '',
|
||||
});
|
||||
const loginRef = ref();
|
||||
const loginLoading = ref<boolean>(false);
|
||||
defineProps({
|
||||
sessionTimeout: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
function handleChangeCheckCode() {
|
||||
formData.inputCode = '';
|
||||
|
||||
randCodeData.checkKey = 1629428467008;
|
||||
getCodeInfo(randCodeData.checkKey).then((res) => {
|
||||
randCodeData.randCodeImage = res;
|
||||
randCodeData.requestCodeSuccess = true;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 账号或者手机登录
|
||||
*/
|
||||
async function loginHandleClick() {
|
||||
accountLogin();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formData.password,
|
||||
() => {
|
||||
formData.password = formData.password.replace(/[\u4e00-\u9fa5]/g, '');
|
||||
}
|
||||
);
|
||||
async function accountLogin() {
|
||||
if (!formData.username) {
|
||||
createMessage.warn(t('sys.login.accountPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.inputCode) {
|
||||
createMessage.warn(t('sys.login.inputCodePlaceholder'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loginLoading.value = true;
|
||||
const { userInfo } = await userStore.login(
|
||||
toRaw({
|
||||
password: encipher(formData.password) as string,
|
||||
// password: formData.password,
|
||||
username: formData.username,
|
||||
captcha: formData.inputCode,
|
||||
checkKey: randCodeData.checkKey,
|
||||
mode: 'none', //不要默认的错误提示
|
||||
})
|
||||
);
|
||||
if (userInfo) {
|
||||
notification.success({
|
||||
message: t('sys.login.loginSuccessTitle'),
|
||||
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: error.message || t('sys.login.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
handleChangeCheckCode();
|
||||
} finally {
|
||||
loginLoading.value = false;
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
//加载验证码
|
||||
handleChangeCheckCode();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-mini-login';
|
||||
@dark-bg: #293146;
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
background-color: @dark-bg !important;
|
||||
background-image: none;
|
||||
|
||||
&::before {
|
||||
background-image: url(/@/assets/svg/login-bg-dark.svg);
|
||||
}
|
||||
|
||||
.aui-inputClear {
|
||||
background-color: #232a3b !important;
|
||||
}
|
||||
|
||||
.ant-input,
|
||||
.ant-input-password {
|
||||
background-color: #232a3b !important;
|
||||
}
|
||||
|
||||
.ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
|
||||
border: 1px solid #4a5569 !important;
|
||||
}
|
||||
|
||||
&-form {
|
||||
background: @dark-bg !important;
|
||||
}
|
||||
|
||||
.app-iconify {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.aui-inputClear input,
|
||||
.aui-input-line input,
|
||||
.aui-choice {
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
.aui-formBox {
|
||||
background-color: @dark-bg !important;
|
||||
}
|
||||
|
||||
.aui-third-text span {
|
||||
background-color: @dark-bg !important;
|
||||
}
|
||||
|
||||
.aui-form-nav .aui-flex-box {
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
.aui-formButton .aui-linek-code {
|
||||
background: @dark-bg !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.aui-code-line {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
.ant-checkbox-inner,
|
||||
.aui-success h3 {
|
||||
border-color: #c9d1d9;
|
||||
}
|
||||
}
|
||||
|
||||
input.fix-auto-fill,
|
||||
.fix-auto-fill input {
|
||||
-webkit-text-fill-color: #c9d1d9 !important;
|
||||
box-shadow: inherit !important;
|
||||
}
|
||||
|
||||
&-sign-in-way {
|
||||
.anticon {
|
||||
font-size: 22px !important;
|
||||
color: #888 !important;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-divider-inner-text {
|
||||
font-size: 12px !important;
|
||||
color: @text-color-secondary !important;
|
||||
}
|
||||
|
||||
.aui-third-login a {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
<div :class="prefixCls" class="login-background-img">
|
||||
<!-- <AppLocalePicker :showText="false" class="absolute top-4 right-4 enter-x xl:text-gray-600" />-->
|
||||
<!-- <AppDarkModeToggle class="absolute top-3 right-7 enter-x" />-->
|
||||
<div v-show="type === 'login'">
|
||||
<div class="aui-content">
|
||||
<div class="aui-top">
|
||||
<div class="aui-title">{{ t('sys.login.loginTitle') }}</div>
|
||||
<div class="aui-title-image"></div>
|
||||
</div>
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<div class="form-title">{{ t('sys.login.formTitle') }}</div>
|
||||
<div class="aui-form-box" style="height: 180px">
|
||||
<a-form ref="loginRef" :model="formData" @keyup.enter.native="loginHandleClick">
|
||||
<div class="aui-account">
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-code"></i>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formData.username"
|
||||
:placeholder="t('sys.login.userName')"
|
||||
class="fix-auto-fill"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-password"></i>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formData.password"
|
||||
:placeholder="t('sys.login.password')"
|
||||
class="fix-auto-fill"
|
||||
type="password"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="aui-inputClear">
|
||||
<i class="icon icon-code"></i>
|
||||
<a-form-item>
|
||||
<a-input
|
||||
v-model:value="formData.inputCode"
|
||||
:placeholder="t('sys.login.inputCode')"
|
||||
class="fix-auto-fill"
|
||||
type="text"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="aui-code">
|
||||
<img
|
||||
v-if="randCodeData.requestCodeSuccess"
|
||||
:src="randCodeData.randCodeImage"
|
||||
@click="handleChangeCheckCode"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
:src="codeImg"
|
||||
style="margin-top: 2px; max-width: initial"
|
||||
@click="handleChangeCheckCode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-flex remember">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-choice">
|
||||
<a-input v-model:value="rememberMe" class="fix-auto-fill" type="checkbox" />
|
||||
<span class="aui-check">{{ t('sys.login.rememberMe') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<div class="aui-flex">
|
||||
<a-button
|
||||
:loading="loginLoading"
|
||||
class="aui-link-login aui-flex-box"
|
||||
style="background: #53b173; color: #ffffff"
|
||||
@click="loginHandleClick"
|
||||
>
|
||||
{{ t('sys.login.loginButton') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" name="login-mini" setup>
|
||||
import { getCodeInfo } from '/@/api/sys/user';
|
||||
import { onMounted, reactive, ref, toRaw, watch } from 'vue';
|
||||
import codeImg from '/@/assets/images/checkcode.png';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { encipher } from '/@/utils/jsencrypt';
|
||||
|
||||
const { prefixCls } = useDesign('mini-login');
|
||||
const { notification, createMessage } = useMessage();
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
const randCodeData = reactive<any>({
|
||||
randCodeImage: '',
|
||||
requestCodeSuccess: false,
|
||||
checkKey: null,
|
||||
});
|
||||
const rememberMe = ref<string>('0');
|
||||
const type = ref<string>('login');
|
||||
//账号登录表单字段
|
||||
const formData = reactive<any>({
|
||||
inputCode: '',
|
||||
username: '',
|
||||
password: '',
|
||||
});
|
||||
const loginRef = ref();
|
||||
const loginLoading = ref<boolean>(false);
|
||||
defineProps({
|
||||
sessionTimeout: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
function handleChangeCheckCode() {
|
||||
formData.inputCode = '';
|
||||
|
||||
randCodeData.checkKey = 1629428467008;
|
||||
getCodeInfo(randCodeData.checkKey).then((res) => {
|
||||
randCodeData.randCodeImage = res;
|
||||
randCodeData.requestCodeSuccess = true;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 账号或者手机登录
|
||||
*/
|
||||
async function loginHandleClick() {
|
||||
accountLogin();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => formData.password,
|
||||
() => {
|
||||
formData.password = formData.password.replace(/[\u4e00-\u9fa5]/g, '');
|
||||
}
|
||||
);
|
||||
async function accountLogin() {
|
||||
if (!formData.username) {
|
||||
createMessage.warn(t('sys.login.accountPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.inputCode) {
|
||||
createMessage.warn(t('sys.login.inputCodePlaceholder'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
loginLoading.value = true;
|
||||
const { userInfo } = await userStore.login(
|
||||
toRaw({
|
||||
password: encipher(formData.password) as string,
|
||||
// password: formData.password,
|
||||
username: formData.username,
|
||||
captcha: formData.inputCode,
|
||||
checkKey: randCodeData.checkKey,
|
||||
mode: 'none', //不要默认的错误提示
|
||||
})
|
||||
);
|
||||
if (userInfo) {
|
||||
notification.success({
|
||||
message: t('sys.login.loginSuccessTitle'),
|
||||
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: error.message || t('sys.login.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
handleChangeCheckCode();
|
||||
} finally {
|
||||
loginLoading.value = false;
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
//加载验证码
|
||||
handleChangeCheckCode();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home1.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-mini-login';
|
||||
@dark-bg: #293146;
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
background-color: @dark-bg !important;
|
||||
background-image: none;
|
||||
|
||||
&::before {
|
||||
background-image: url(/@/assets/svg/login-bg-dark.svg);
|
||||
}
|
||||
|
||||
.aui-inputClear {
|
||||
background-color: #232a3b !important;
|
||||
}
|
||||
|
||||
.ant-input,
|
||||
.ant-input-password {
|
||||
background-color: #232a3b !important;
|
||||
}
|
||||
|
||||
.ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
|
||||
border: 1px solid #4a5569 !important;
|
||||
}
|
||||
|
||||
&-form {
|
||||
background: @dark-bg !important;
|
||||
}
|
||||
|
||||
.app-iconify {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.aui-inputClear input,
|
||||
.aui-input-line input,
|
||||
.aui-choice {
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
.aui-formBox {
|
||||
background-color: @dark-bg !important;
|
||||
}
|
||||
|
||||
.aui-third-text span {
|
||||
background-color: @dark-bg !important;
|
||||
}
|
||||
|
||||
.aui-form-nav .aui-flex-box {
|
||||
color: #c9d1d9 !important;
|
||||
}
|
||||
|
||||
.aui-formButton .aui-linek-code {
|
||||
background: @dark-bg !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.aui-code-line {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
.ant-checkbox-inner,
|
||||
.aui-success h3 {
|
||||
border-color: #c9d1d9;
|
||||
}
|
||||
}
|
||||
|
||||
input.fix-auto-fill,
|
||||
.fix-auto-fill input {
|
||||
-webkit-text-fill-color: #c9d1d9 !important;
|
||||
box-shadow: inherit !important;
|
||||
}
|
||||
|
||||
&-sign-in-way {
|
||||
.anticon {
|
||||
font-size: 22px !important;
|
||||
color: #888 !important;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-divider-inner-text {
|
||||
font-size: 12px !important;
|
||||
color: @text-color-secondary !important;
|
||||
}
|
||||
|
||||
.aui-third-login a {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div class="aui-content">
|
||||
<div class="aui-container">
|
||||
<div class="aui-form">
|
||||
<div class="aui-image">
|
||||
<div class="aui-image-text">
|
||||
<img :src="jeecgAdTextImg" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formBox">
|
||||
<div class="aui-formWell">
|
||||
<a-form ref="formRef" :model="formData">
|
||||
<div class="aui-flex aui-form-nav aui-clear-left" style="padding-bottom: 21px">
|
||||
<div class="aui-flex-box activeNav on">{{ t('sys.login.signUpFormTitle') }}</div>
|
||||
</div>
|
||||
<div class="aui-form-box">
|
||||
<div class="aui-account aui-account-line">
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:user-outlined" />
|
||||
<a-input
|
||||
class="fix-auto-fill"
|
||||
type="text"
|
||||
:placeholder="t('sys.login.userName')"
|
||||
v-model:value="formData.username"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:mobile-outlined" />
|
||||
<a-input
|
||||
class="fix-auto-fill"
|
||||
type="text"
|
||||
:placeholder="t('sys.login.mobile')"
|
||||
v-model:value="formData.mobile"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:mail-outlined" />
|
||||
<a-input
|
||||
class="fix-auto-fill"
|
||||
type="text"
|
||||
:placeholder="t('sys.login.smsCode')"
|
||||
v-model:value="formData.smscode"
|
||||
/>
|
||||
<div v-if="showInterval" class="aui-code-line" @click="getLoginCode">{{
|
||||
t('component.countdown.normalText')
|
||||
}}</div>
|
||||
<div v-else class="aui-code-line">{{ t('component.countdown.sendText', [unref(timeRuning)]) }}</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:lock-outlined" />
|
||||
<a-input
|
||||
class="fix-auto-fill"
|
||||
:type="pwdIndex === 'close' ? 'password' : 'text'"
|
||||
:placeholder="t('sys.login.password')"
|
||||
v-model:value="formData.password"
|
||||
/>
|
||||
<div class="aui-eye">
|
||||
<img :src="eyeKImg" alt="开启" v-if="pwdIndex === 'open'" @click="pwdClick('close')" />
|
||||
<img :src="eyeGImg" alt="关闭" v-else-if="pwdIndex === 'close'" @click="pwdClick('open')" />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<div class="aui-input-line">
|
||||
<Icon class="aui-icon" icon="ant-design:lock-outlined" />
|
||||
<a-input
|
||||
class="fix-auto-fill"
|
||||
:type="confirmPwdIndex === 'close' ? 'password' : 'text'"
|
||||
:placeholder="t('sys.login.confirmPassword')"
|
||||
v-model:value="formData.confirmPassword"
|
||||
/>
|
||||
<div class="aui-eye">
|
||||
<img :src="eyeKImg" alt="开启" v-if="confirmPwdIndex === 'open'" @click="confirmPwdClick('close')" />
|
||||
<img
|
||||
:src="eyeGImg"
|
||||
alt="关闭"
|
||||
v-else-if="confirmPwdIndex === 'close'"
|
||||
@click="confirmPwdClick('open')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item name="policy">
|
||||
<div class="aui-flex">
|
||||
<div class="aui-flex-box">
|
||||
<div class="aui-choice">
|
||||
<a-checkbox v-model:checked="formData.policy" />
|
||||
<span style="color: #1b90ff; margin-left: 4px">{{ t('sys.login.policy') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aui-formButton">
|
||||
<div class="aui-flex">
|
||||
<a class="aui-link-login aui-flex-box" @click="registerHandleClick"> {{ t('sys.login.registerButton') }}</a>
|
||||
</div>
|
||||
<div class="aui-flex">
|
||||
<a class="aui-linek-code aui-flex-box" @click="goBackHandleClick">{{ t('sys.login.backSignIn') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="mini-register">
|
||||
import { ref, reactive, unref, toRaw } from 'vue';
|
||||
import { getCaptcha, register } from '/@/api/sys/user';
|
||||
import { SmsEnum } from '/@/views/sys/login/useLogin';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import logoImg from '/@/assets/loginmini/icon/jeecg_logo.png';
|
||||
import jeecgAdTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
|
||||
import eyeKImg from '/@/assets/loginmini/icon/icon-eye-k.png';
|
||||
import eyeGImg from '/@/assets/loginmini/icon/icon-eye-g.png';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { notification, createErrorModal, createMessage } = useMessage();
|
||||
const emit = defineEmits(['go-back', 'success', 'register']);
|
||||
const formRef = ref();
|
||||
const formData = reactive<any>({
|
||||
username: '',
|
||||
mobile: '',
|
||||
smscode: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
policy: false,
|
||||
});
|
||||
//是否显示获取验证码
|
||||
const showInterval = ref<boolean>(true);
|
||||
//60s
|
||||
const timeRuning = ref<number>(60);
|
||||
//定时器
|
||||
const timer = ref<any>(null);
|
||||
//密码眼睛打开关闭
|
||||
const pwdIndex = ref<string>('close');
|
||||
//确认密码眼睛打开关闭
|
||||
const confirmPwdIndex = ref<string>('close');
|
||||
|
||||
/**
|
||||
* 返回
|
||||
*/
|
||||
function goBackHandleClick() {
|
||||
emit('go-back');
|
||||
initForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手机验证码
|
||||
*/
|
||||
async function getLoginCode() {
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
const result = await getCaptcha({ mobile: formData.mobile, smsmode: SmsEnum.REGISTER });
|
||||
if (result) {
|
||||
const TIME_COUNT = 60;
|
||||
if (!unref(timer)) {
|
||||
timeRuning.value = TIME_COUNT;
|
||||
showInterval.value = false;
|
||||
timer.value = setInterval(() => {
|
||||
if (unref(timeRuning) > 0 && unref(timeRuning) <= TIME_COUNT) {
|
||||
timeRuning.value = timeRuning.value - 1;
|
||||
} else {
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerHandleClick() {
|
||||
if (!formData.username) {
|
||||
createMessage.warn(t('sys.login.accountPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.mobile) {
|
||||
createMessage.warn(t('sys.login.mobilePlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.smscode) {
|
||||
createMessage.warn(t('sys.login.smsPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.password) {
|
||||
createMessage.warn(t('sys.login.passwordPlaceholder'));
|
||||
return;
|
||||
}
|
||||
if (!formData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.confirmPassword'));
|
||||
return;
|
||||
}
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
createMessage.warn(t('sys.login.diffPwd'));
|
||||
return;
|
||||
}
|
||||
if (!formData.policy) {
|
||||
createMessage.warn(t('sys.login.policyPlaceholder'));
|
||||
return;
|
||||
}
|
||||
registerAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册账号
|
||||
*/
|
||||
async function registerAccount() {
|
||||
try {
|
||||
const resultInfo = await register(
|
||||
toRaw({
|
||||
username: formData.username,
|
||||
password: formData.password,
|
||||
phone: formData.mobile,
|
||||
smscode: formData.smscode,
|
||||
})
|
||||
);
|
||||
if (resultInfo && resultInfo.data.success) {
|
||||
notification.success({
|
||||
description: resultInfo.data.message || t('sys.api.registerMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
emit('success', { username: formData.username, password: formData.password });
|
||||
initForm();
|
||||
} else {
|
||||
notification.warning({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: resultInfo.data.message || t('sys.api.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: t('sys.api.errorTip'),
|
||||
description: error.message || t('sys.api.networkExceptionMsg'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单
|
||||
*/
|
||||
function initForm() {
|
||||
Object.assign(formData, { username: '', mobile: '', smscode: '', password: '', confirmPassword: '', policy: false });
|
||||
if (!unref(timer)) {
|
||||
showInterval.value = true;
|
||||
clearInterval(unref(timer));
|
||||
timer.value = null;
|
||||
}
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码打开或关闭
|
||||
* @param value
|
||||
*/
|
||||
function pwdClick(value) {
|
||||
pwdIndex.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认密码打开或关闭
|
||||
* @param value
|
||||
*/
|
||||
function confirmPwdClick(value) {
|
||||
confirmPwdIndex.value = value;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initForm,
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '/@/assets/loginmini/style/home.less';
|
||||
@import '/@/assets/loginmini/style/base.less';
|
||||
.aui-input-line .aui-icon {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div> </div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { isOAuth2AppEnv, sysOAuth2Login } from '/@/views/sys/login/useLogin';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { router } from '/@/router';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { getTenantId } from '/@/utils/auth';
|
||||
|
||||
const isOAuth = ref<boolean>(isOAuth2AppEnv());
|
||||
const env = ref<any>({ thirdApp: false, wxWork: false, dingtalk: false });
|
||||
const { currentRoute } = useRouter();
|
||||
const route = currentRoute.value;
|
||||
if (!isOAuth2AppEnv()) {
|
||||
router.replace({ path: PageEnum.BASE_LOGIN, query: route.query });
|
||||
}
|
||||
|
||||
if (isOAuth.value) {
|
||||
checkEnv();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测当前的环境
|
||||
*/
|
||||
function checkEnv() {
|
||||
// 判断当时是否是企业微信环境
|
||||
if (/wxwork/i.test(navigator.userAgent)) {
|
||||
env.value.thirdApp = true;
|
||||
env.value.wxWork = true;
|
||||
}
|
||||
// 判断当时是否是钉钉环境
|
||||
if (/dingtalk/i.test(navigator.userAgent)) {
|
||||
env.value.thirdApp = true;
|
||||
env.value.dingtalk = true;
|
||||
}
|
||||
doOAuth2Login();
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行OAuth2登录操作
|
||||
*/
|
||||
function doOAuth2Login() {
|
||||
if (env.value.thirdApp) {
|
||||
// 判断是否携带了Token,是就说明登录成功
|
||||
if (route.query.oauth2LoginToken) {
|
||||
let token = route.query.oauth2LoginToken;
|
||||
//执行登录操作
|
||||
thirdLogin({ token, thirdType: route.query.thirdType, tenantId: getTenantId });
|
||||
} else if (env.value.wxWork) {
|
||||
sysOAuth2Login('wechat_enterprise');
|
||||
} else if (env.value.dingtalk) {
|
||||
sysOAuth2Login('dingtalk');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方登录
|
||||
* @param params
|
||||
*/
|
||||
function thirdLogin(params) {
|
||||
const userStore = useUserStore();
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
userStore.ThirdLogin(params).then((res) => {
|
||||
if (res && res.userInfo) {
|
||||
notification.success({
|
||||
message: t('sys.login.loginSuccessTitle'),
|
||||
description: `${t('sys.login.loginSuccessDesc')}: ${res.userInfo.realname}`,
|
||||
duration: 3,
|
||||
});
|
||||
} else {
|
||||
notification.error({
|
||||
message: t('sys.login.errorTip'),
|
||||
description: ((res.response || {}).data || {}).message || res.message || t('sys.login.networkExceptionMsg'),
|
||||
duration: 4,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="数据权限规则" :width="adaptiveWidth">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" @click="handleCreate"> 新增</a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicDrawer>
|
||||
<DataRuleModal @register="registerModal" @success="reload" :permissionId="permissionId" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import DataRuleModal from './DataRuleModal.vue';
|
||||
import { dataRuleColumns, dataRuleSearchFormSchema } from './menu.data';
|
||||
import { dataRuleList, deleteRule } from './menu.api';
|
||||
import { ColEx } from '/@/components/Form/src/types';
|
||||
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
|
||||
const permissionId = ref('');
|
||||
const { adaptiveWidth } = useDrawerAdaptiveWidth();
|
||||
//权限规则model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerDrawer] = useDrawerInner(async (data) => {
|
||||
permissionId.value = data.id;
|
||||
setProps({ searchInfo: { permissionId: unref(permissionId) } });
|
||||
reload();
|
||||
});
|
||||
// 自适应列配置
|
||||
const adaptiveColProps: Partial<ColEx> = {
|
||||
xs: 24, // <576px
|
||||
sm: 24, // ≥576px
|
||||
md: 24, // ≥768px
|
||||
lg: 12, // ≥992px
|
||||
xl: 8, // ≥1200px
|
||||
xxl: 8, // ≥1600px
|
||||
};
|
||||
const [registerTable, { reload, setProps }] = useTable({
|
||||
api: dataRuleList,
|
||||
columns: dataRuleColumns,
|
||||
size: 'small',
|
||||
formConfig: {
|
||||
baseColProps: adaptiveColProps,
|
||||
labelAlign: 'right',
|
||||
labelCol: {
|
||||
offset: 1,
|
||||
xs: 24,
|
||||
sm: 24,
|
||||
md: 24,
|
||||
lg: 8,
|
||||
xl: 8,
|
||||
xxl: 8,
|
||||
},
|
||||
wrapperCol: {},
|
||||
schemas: dataRuleSearchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
},
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
showTableSetting: false,
|
||||
canResize: false,
|
||||
immediate: false,
|
||||
actionColumn: {
|
||||
width: 100,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteRule({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" width="700px">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { defineProps, ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { dataRuleFormSchema } from './menu.data';
|
||||
import { saveOrUpdateRule } from './menu.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const props = defineProps({ permissionId: String });
|
||||
const isUpdate = ref(true);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: dataRuleFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//设置标题
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增规则' : '编辑规则'));
|
||||
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
values.permissionId = props.permissionId;
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateRule(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" showFooter :width="adaptiveWidth" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" class="menuForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref, useAttrs } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema, ComponentTypes } from './menu.data';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { list, saveOrUpdateMenu } from './menu.api';
|
||||
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const { adaptiveWidth } = useDrawerAdaptiveWidth();
|
||||
const attrs = useAttrs();
|
||||
const isUpdate = ref(true);
|
||||
const menuType = ref(0);
|
||||
const isButton = (type) => type === 2;
|
||||
const [registerForm, { setProps, resetFields, setFieldsValue, updateSchema, validate, clearValidate }] = useForm({
|
||||
labelCol: {
|
||||
md: { span: 4 },
|
||||
sm: { span: 6 },
|
||||
},
|
||||
wrapperCol: {
|
||||
md: { span: 20 },
|
||||
sm: { span: 18 },
|
||||
},
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await resetFields();
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
menuType.value = data?.record?.menuType;
|
||||
let treeProps = data.record?.parentId ? { treeDefaultExpandedKeys: [data.record.parentId] } : {};
|
||||
//获取下拉树信息
|
||||
const treeData = await list();
|
||||
updateSchema([
|
||||
{
|
||||
field: 'parentId',
|
||||
componentProps: { treeData, ...treeProps },
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: isButton(unref(menuType)) ? '按钮/权限' : '菜单名称',
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
required: !isButton(unref(menuType)),
|
||||
componentProps: {
|
||||
onChange: (e) => onUrlChange(e.target.value),
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// 无论新增还是编辑,都可以设置表单值
|
||||
if (typeof data.record === 'object') {
|
||||
let values = { ...data.record };
|
||||
setFieldsValue(values);
|
||||
onUrlChange(values.url);
|
||||
}
|
||||
await clearValidate();
|
||||
//禁用表单
|
||||
setProps({ disabled: !attrs.showFooter });
|
||||
});
|
||||
//获取弹窗标题
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增菜单' : '编辑菜单'));
|
||||
//提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
// iframe兼容
|
||||
if (ComponentTypes.IFrame === values.component) {
|
||||
values.component = values.frameSrc;
|
||||
}
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateMenu(values, unref(isUpdate));
|
||||
closeDrawer();
|
||||
emit('success');
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
/** url 变化时,动态设置组件名称placeholder */
|
||||
function onUrlChange(url) {
|
||||
let placeholder = '';
|
||||
let httpUrl = url;
|
||||
if (url != null && url != '') {
|
||||
if (url.startsWith('/')) {
|
||||
url = url.substring(1);
|
||||
}
|
||||
url = url.replaceAll('/', '-');
|
||||
// 特殊标记
|
||||
url = url.replaceAll(':', '@');
|
||||
placeholder = `${url}`;
|
||||
} else {
|
||||
placeholder = '请输入组件名称';
|
||||
}
|
||||
updateSchema([{ field: 'componentName', componentProps: { placeholder } }]);
|
||||
//update-begin---author:wangshuai ---date:20230204 for:[QQYUN-4058]菜单添加智能化处理------------
|
||||
if (httpUrl != null && httpUrl != '') {
|
||||
if (httpUrl.startsWith('http://') || httpUrl.startsWith('https://')) {
|
||||
setFieldsValue({ component: httpUrl });
|
||||
}
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230204 for:[QQYUN-4058]菜单添加智能化处理------------
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleCreate"> 新增菜单</a-button>
|
||||
<a-button preIcon="ic:round-expand" type="primary" @click="expandAll">展开全部</a-button>
|
||||
<a-button preIcon="ic:round-compress" type="primary" @click="collapseAll">折叠全部</a-button>
|
||||
|
||||
<a-dropdown v-if="checkedKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<MenuDrawer @register="registerDrawer" @success="handleSuccess" :showFooter="showFooter" />
|
||||
<DataRuleList @register="registerDrawer1" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" name="system-menu" setup>
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import MenuDrawer from './MenuDrawer.vue';
|
||||
import DataRuleList from './DataRuleList.vue';
|
||||
import { columns, searchFormSchema } from './menu.data';
|
||||
import { list, deleteMenu, batchDeleteMenu } from './menu.api';
|
||||
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const showFooter = ref(true);
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [registerDrawer1, { openDrawer: openDataRule }] = useDrawer();
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
tableProps: {
|
||||
title: '菜单列表',
|
||||
api: list,
|
||||
columns: columns,
|
||||
size: 'small',
|
||||
pagination: false,
|
||||
isTreeTable: true,
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
tableSetting: { fullScreen: true },
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
autoAdvancedCol: 4,
|
||||
baseColProps: { span: 12 },
|
||||
actionColOptions: { xs: 24, sm: 12, md: 12, lg: 12, xl: 12, xxl: 12 },
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
});
|
||||
//注册table数据
|
||||
const [registerTable, { reload, expandAll, collapseAll }] = tableContext;
|
||||
|
||||
/**
|
||||
* 选择列配置
|
||||
*/
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
columnWidth: 30,
|
||||
selectedRowKeys: checkedKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择事件
|
||||
*/
|
||||
function onSelectChange(selectedRowKeys: (string | number)[]) {
|
||||
checkedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function handleCreate() {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
function handleDetail(record) {
|
||||
showFooter.value = false;
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 添加下级
|
||||
*/
|
||||
function handleAddSub(record) {
|
||||
openDrawer(true, {
|
||||
record: { parentId: record.id, menuType: 1 },
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 数据权限弹窗
|
||||
*/
|
||||
function handleDataRule(record) {
|
||||
openDataRule(true, { id: record.id });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteMenu({ id: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteMenu({ ids: checkedKeys.value }, reload);
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
function onFetchSuccess() {
|
||||
// 演示默认展开所有表项
|
||||
nextTick(expandAll);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
// {
|
||||
// label: '详情',
|
||||
// onClick: handleDetail.bind(null, record),
|
||||
// },
|
||||
{
|
||||
label: '添加下级',
|
||||
onClick: handleAddSub.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '数据规则',
|
||||
onClick: handleDataRule.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,121 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/permission/list',
|
||||
save = '/sys/permission/add',
|
||||
edit = '/sys/permission/edit',
|
||||
delete = '/sys/permission/delete',
|
||||
deleteBatch = '/sys/permission/deleteBatch',
|
||||
ruleList = '/sys/permission/queryPermissionRule',
|
||||
ruleSave = '/sys/permission/addPermissionRule',
|
||||
ruleEdit = '/sys/permission/editPermissionRule',
|
||||
ruleDelete = '/sys/permission/deletePermissionRule',
|
||||
checkPermDuplication = '/sys/permission/checkPermDuplication',
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params = {}) => {
|
||||
return defHttp.get({ url: Api.list, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*/
|
||||
export const deleteMenu = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.delete, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除菜单
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteMenu = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新菜单
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateMenu = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 菜单数据权限列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const dataRuleList = (params) => defHttp.get({ url: Api.ruleList, params });
|
||||
/**
|
||||
* 保存或者更新数据规则
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateRule = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.ruleEdit : Api.ruleSave;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除数据权限
|
||||
*/
|
||||
export const deleteRule = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.ruleDelete, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 根据code获取字典数值
|
||||
* @param params
|
||||
*/
|
||||
export const ajaxGetDictItems = (params) => defHttp.get({ url: `/sys/dict/getDictItems/${params.code}` });
|
||||
|
||||
/**
|
||||
* 唯一校验
|
||||
* @param params
|
||||
*/
|
||||
export const getCheckPermDuplication = (params) => defHttp.get({ url: Api.checkPermDuplication, params }, { isTransformResponse: false });
|
||||
|
||||
/**
|
||||
* 校验菜单是否存在
|
||||
* @param model
|
||||
* @param schema
|
||||
* @param required
|
||||
*/
|
||||
export const checkPermDuplication = (model, schema, required?) => {
|
||||
return [
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (!value && required) {
|
||||
return Promise.reject(`请输入${schema.label}`);
|
||||
}
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
getCheckPermDuplication({
|
||||
id: model.id,
|
||||
url: model.url,
|
||||
alwaysShow: model.alwaysShow,
|
||||
})
|
||||
.then((res) => {
|
||||
res.success ? resolve() : reject(res.message || '校验失败');
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '验证失败');
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,415 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { h } from 'vue';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { duplicateCheck } from '../user/user.api';
|
||||
import { ajaxGetDictItems, checkPermDuplication } from './menu.api';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
|
||||
const isDir = (type) => type === 0;
|
||||
const isMenu = (type) => type === 1;
|
||||
const isButton = (type) => type === 2;
|
||||
|
||||
// 定义可选择的组件类型
|
||||
export enum ComponentTypes {
|
||||
Default = 'layouts/default/index',
|
||||
IFrame = 'sys/iframe/FrameBlank',
|
||||
}
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '菜单类型',
|
||||
dataIndex: 'menuType',
|
||||
width: 150,
|
||||
customRender: ({ text }) => {
|
||||
return render.renderDict(text, 'menu_type');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
dataIndex: 'icon',
|
||||
width: 50,
|
||||
customRender: ({ record }) => {
|
||||
return h(Icon, { icon: record.icon });
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '组件',
|
||||
dataIndex: 'component',
|
||||
align: 'left',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '路径',
|
||||
dataIndex: 'url',
|
||||
align: 'left',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNo',
|
||||
width: 50,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: '菜单名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: 'id',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'menuType',
|
||||
label: '菜单类型',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: 0,
|
||||
componentProps: ({ formActionType, formModel }) => {
|
||||
return {
|
||||
options: [
|
||||
{ label: '一级菜单', value: 0 },
|
||||
{ label: '子菜单', value: 1 },
|
||||
{ label: '按钮/权限', value: 2 },
|
||||
],
|
||||
onChange: (e) => {
|
||||
const { updateSchema, clearValidate } = formActionType;
|
||||
const label = isButton(e) ? '按钮/权限' : '菜单名称';
|
||||
//清除校验
|
||||
clearValidate();
|
||||
updateSchema([
|
||||
{
|
||||
field: 'name',
|
||||
label: label,
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
required: !isButton(e),
|
||||
},
|
||||
]);
|
||||
//update-begin---author:wangshuai ---date:20220729 for:[VUEN-1834]只有一级菜单,才默认值,子菜单的时候,清空------------
|
||||
if (isMenu(e) && !formModel.id && formModel.component == 'layouts/RouteView') {
|
||||
formModel.component = '';
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20220729 for:[VUEN-1834]只有一级菜单,才默认值,子菜单的时候,清空------------
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '菜单名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'parentId',
|
||||
label: '上级菜单',
|
||||
component: 'TreeSelect',
|
||||
required: true,
|
||||
componentProps: {
|
||||
replaceFields: {
|
||||
title: 'name',
|
||||
key: 'id',
|
||||
value: 'id',
|
||||
},
|
||||
dropdownStyle: {
|
||||
maxHeight: '50vh',
|
||||
},
|
||||
getPopupContainer: (node) => node.parentNode,
|
||||
},
|
||||
ifShow: ({ values }) => !isDir(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'url',
|
||||
label: '访问路径',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
ifShow: ({ values }) => !(values.component === ComponentTypes.IFrame && values.internalOrExternal) && values.menuType !== 2,
|
||||
//update-begin-author:zyf date:2022-11-02 for: 聚合路由允许路径重复
|
||||
dynamicRules: ({ model, schema }) => {
|
||||
return checkPermDuplication(model, schema, true);
|
||||
},
|
||||
//update-end-author:zyf date:2022-11-02 for: 聚合路由允许路径重复
|
||||
},
|
||||
{
|
||||
field: 'component',
|
||||
label: '前端组件',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入前端组件',
|
||||
},
|
||||
defaultValue: 'layouts/RouteView',
|
||||
required: true,
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'componentName',
|
||||
label: '组件名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入组件名称',
|
||||
},
|
||||
helpMessage: [
|
||||
'此处名称应和vue组件的name属性保持一致。',
|
||||
'组件名称不能重复,主要用于路由缓存功能。',
|
||||
'如果组件名称和vue组件的name属性不一致,则会导致路由缓存失效。',
|
||||
'非必填,留空则会根据访问路径自动生成。',
|
||||
],
|
||||
defaultValue: '',
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'frameSrc',
|
||||
label: 'Iframe地址',
|
||||
component: 'Input',
|
||||
rules: [
|
||||
{ required: true, message: '请输入Iframe地址' },
|
||||
{ type: 'url', message: '请输入正确的url地址' },
|
||||
],
|
||||
ifShow: ({ values }) => !isButton(values.menuType) && values.component === ComponentTypes.IFrame,
|
||||
},
|
||||
{
|
||||
field: 'redirect',
|
||||
label: '默认跳转地址',
|
||||
component: 'Input',
|
||||
ifShow: ({ values }) => isDir(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'perms',
|
||||
label: '授权标识',
|
||||
component: 'Input',
|
||||
ifShow: ({ values }) => isButton(values.menuType),
|
||||
// dynamicRules: ({ model }) => {
|
||||
// return [
|
||||
// {
|
||||
// required: false,
|
||||
// validator: (_, value) => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// let params = {
|
||||
// tableName: 'sys_permission',
|
||||
// fieldName: 'perms',
|
||||
// fieldVal: value,
|
||||
// dataId: model.id,
|
||||
// };
|
||||
// duplicateCheck(params)
|
||||
// .then((res) => {
|
||||
// res.success ? resolve() : reject(res.message || '校验失败');
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// reject(err.message || '校验失败');
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
// ];
|
||||
// },
|
||||
},
|
||||
{
|
||||
field: 'permsType',
|
||||
label: '授权策略',
|
||||
component: 'RadioGroup',
|
||||
defaultValue: '1',
|
||||
helpMessage: ['可见/可访问(授权后可见/可访问)', '可编辑(未授权时禁用)'],
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '可见/可访问', value: '1' },
|
||||
{ label: '可编辑', value: '2' },
|
||||
],
|
||||
},
|
||||
ifShow: ({ values }) => isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
defaultValue: '1',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '有效', value: '1' },
|
||||
{ label: '无效', value: '0' },
|
||||
],
|
||||
},
|
||||
ifShow: ({ values }) => isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
label: '菜单图标',
|
||||
component: 'IconPicker',
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'sortNo',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
defaultValue: 1,
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'route',
|
||||
label: '是否路由菜单',
|
||||
component: 'Switch',
|
||||
defaultValue: true,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'hidden',
|
||||
label: '隐藏路由',
|
||||
component: 'Switch',
|
||||
defaultValue: 0,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'hideTab',
|
||||
label: '隐藏Tab',
|
||||
component: 'Switch',
|
||||
defaultValue: 0,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'keepAlive',
|
||||
label: '是否缓存路由',
|
||||
component: 'Switch',
|
||||
defaultValue: false,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'alwaysShow',
|
||||
label: '聚合路由',
|
||||
component: 'Switch',
|
||||
defaultValue: false,
|
||||
componentProps: {
|
||||
checkedChildren: '是',
|
||||
unCheckedChildren: '否',
|
||||
},
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
{
|
||||
field: 'internalOrExternal',
|
||||
label: '打开方式',
|
||||
component: 'Switch',
|
||||
defaultValue: false,
|
||||
componentProps: {
|
||||
checkedChildren: '外部',
|
||||
unCheckedChildren: '内部',
|
||||
},
|
||||
ifShow: ({ values }) => !isButton(values.menuType),
|
||||
},
|
||||
];
|
||||
|
||||
export const dataRuleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '规则名称',
|
||||
dataIndex: 'ruleName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '规则字段',
|
||||
dataIndex: 'ruleColumn',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '规则值',
|
||||
dataIndex: 'ruleValue',
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
|
||||
export const dataRuleSearchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'ruleName',
|
||||
label: '规则名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
field: 'ruleValue',
|
||||
label: '规则值',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const dataRuleFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: 'id',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'ruleName',
|
||||
label: '规则名称',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'ruleColumn',
|
||||
label: '规则字段',
|
||||
component: 'Input',
|
||||
ifShow: ({ values }) => {
|
||||
return values.ruleConditions !== 'USE_SQL_RULES';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ruleConditions',
|
||||
label: '条件规则',
|
||||
required: true,
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: ajaxGetDictItems,
|
||||
params: { code: 'rule_conditions' },
|
||||
labelField: 'text',
|
||||
valueField: 'value',
|
||||
getPopupContainer: (node) => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ruleValue',
|
||||
label: '规则值',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioButtonGroup',
|
||||
defaultValue: '1',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '无效', value: '0' },
|
||||
{ label: '有效', value: '1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<a-list item-layout="horizontal" :data-source="messageList">
|
||||
<template #loadMore>
|
||||
<div
|
||||
v-if="messageList && messageList.length > 0 && !loadEndStatus && !loadingMoreStatus"
|
||||
:style="{ textAlign: 'center', marginTop: '12px', height: '32px', lineHeight: '32px' }"
|
||||
>
|
||||
<a-button @click="onLoadMore">加载更多</a-button>
|
||||
</div>
|
||||
<div
|
||||
v-if="messageList && messageList.length > 0 && loadEndStatus"
|
||||
:style="{ textAlign: 'center', marginTop: '12px', height: '32px', lineHeight: '32px' }"
|
||||
>
|
||||
没有更多了
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<template #actions>
|
||||
<a-rate :value="item.starFlag == '1' ? 1 : 0" :count="1" @click="clickStar(item)" style="cursor: pointer" disabled />
|
||||
</template>
|
||||
|
||||
<a-list-item-meta :description="item.createTime">
|
||||
<template #title>
|
||||
<div style="position: relative">
|
||||
<!-- <span style="display: inline-block; position: absolute; left: -16px">
|
||||
<exclamation-outlined v-if="noRead(item)" title="未读消息" style="color: red" />
|
||||
</span>-->
|
||||
|
||||
<span>{{ getMsgCategory(item) }}</span>
|
||||
<span v-if="item.busType == 'bpm'" class="bpm-cuiban-content" v-html="item.msgContent"> </span>
|
||||
<a-tooltip v-else>
|
||||
<template #title>
|
||||
<div v-html="item.msgContent"></div>
|
||||
</template>
|
||||
{{ item.titile }}
|
||||
</a-tooltip>
|
||||
|
||||
<a @click="showMessageDetail(item)" style="margin-left: 16px">查看详情</a>
|
||||
</div>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<template v-if="item.busType == 'email'">
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><mail-outlined style="font-size: 16px" title="未读消息" /></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><mail-outlined style="font-size: 16px" /></a-avatar>
|
||||
</template>
|
||||
|
||||
<template v-else-if="item.busType == 'bpm_task'">
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><interaction-outlined style="font-size: 16px" title="未读消息" /></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><interaction-outlined style="font-size: 16px" /></a-avatar>
|
||||
</template>
|
||||
|
||||
<template v-else-if="item.busType == 'bpm'">
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><alert-outlined style="font-size: 16px" title="未读消息" /></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><alert-outlined style="font-size: 16px" /></a-avatar>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<a-badge dot v-if="noRead(item)" class="msg-no-read">
|
||||
<a-avatar style="background: #79919d"><bell-filled style="font-size: 16px" title="未读消息" /></a-avatar>
|
||||
</a-badge>
|
||||
<a-avatar v-else style="background: #79919d"><bell-filled style="font-size: 16px" /></a-avatar>
|
||||
</template>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
FilterOutlined,
|
||||
CloseOutlined,
|
||||
BellFilled,
|
||||
ExclamationOutlined,
|
||||
MailOutlined,
|
||||
InteractionOutlined,
|
||||
AlertOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useSysMessage, useMessageHref } from './useSysMessage';
|
||||
|
||||
export default {
|
||||
name: 'SysMessageList',
|
||||
components: {
|
||||
FilterOutlined,
|
||||
CloseOutlined,
|
||||
BellFilled,
|
||||
ExclamationOutlined,
|
||||
MailOutlined,
|
||||
InteractionOutlined,
|
||||
AlertOutlined,
|
||||
},
|
||||
props: {
|
||||
star: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['close', 'detail'],
|
||||
setup(props, { emit }) {
|
||||
const {
|
||||
messageList,
|
||||
loadEndStatus,
|
||||
loadingMoreStatus,
|
||||
onLoadMore,
|
||||
noRead,
|
||||
getMsgCategory,
|
||||
searchParams,
|
||||
reset,
|
||||
loadData,
|
||||
updateStarMessage,
|
||||
} = useSysMessage();
|
||||
|
||||
function reload(params) {
|
||||
let { fromUser, rangeDateKey, rangeDate } = params;
|
||||
searchParams.fromUser = fromUser || '';
|
||||
searchParams.rangeDateKey = rangeDateKey || '';
|
||||
searchParams.rangeDate = rangeDate || [];
|
||||
if (props.star === true) {
|
||||
searchParams.starFlag = '1';
|
||||
} else {
|
||||
searchParams.starFlag = '';
|
||||
}
|
||||
reset();
|
||||
loadData();
|
||||
}
|
||||
|
||||
function clickStar(item) {
|
||||
console.log(item);
|
||||
updateStarMessage(item);
|
||||
if (item.starFlag == '1') {
|
||||
item.starFlag = '0';
|
||||
} else {
|
||||
item.starFlag = '1';
|
||||
}
|
||||
}
|
||||
|
||||
const { goPage } = useMessageHref(emit);
|
||||
|
||||
function showMessageDetail(record) {
|
||||
record.readFlag = '1';
|
||||
goPage(record);
|
||||
emit('close', record.id);
|
||||
}
|
||||
|
||||
return {
|
||||
messageList,
|
||||
loadEndStatus,
|
||||
loadingMoreStatus,
|
||||
onLoadMore,
|
||||
noRead,
|
||||
getMsgCategory,
|
||||
reload,
|
||||
clickStar,
|
||||
showMessageDetail,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.msg-no-read {
|
||||
:deep(.ant-badge-dot) {
|
||||
top: 5px;
|
||||
right: 3px;
|
||||
}
|
||||
}
|
||||
:deep(.bpm-cuiban-content) p {
|
||||
display: inherit;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,497 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
:canFullscreen="false"
|
||||
:draggable="false"
|
||||
:closable="false"
|
||||
@register="registerModal"
|
||||
wrapClassName="sys-msg-modal"
|
||||
:width="800"
|
||||
:footer="null"
|
||||
destroyOnClose
|
||||
>
|
||||
<template #title>
|
||||
<div class="sys-msg-modal-title">
|
||||
<div class="title"></div>
|
||||
<div class="ant-tabs-nav-wrap">
|
||||
<div class="ant-tabs-nav-scroll">
|
||||
<div class="ant-tabs-nav ant-tabs-nav-animated">
|
||||
<div>
|
||||
<div
|
||||
@click="(e) => handleChangeTab(e, 'all')"
|
||||
role="tab"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab"
|
||||
:class="{ 'ant-tabs-tab-active': activeKey == 'all' }"
|
||||
>
|
||||
全部消息
|
||||
</div>
|
||||
<div
|
||||
@click="(e) => handleChangeTab(e, 'star')"
|
||||
role="tab"
|
||||
aria-disabled="false"
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab"
|
||||
:class="{ 'ant-tabs-tab-active': activeKey == 'star' }"
|
||||
>
|
||||
标星消息
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-tabs-ink-bar ant-tabs-ink-bar-animated"
|
||||
:style="{
|
||||
transform: activeKey == 'all' ? 'translate3d(0px, 0px, 0px)' : 'translate3d(120px, 0px, 0px)',
|
||||
display: 'block',
|
||||
width: '88px',
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头部图标 -->
|
||||
<div class="icon-right">
|
||||
<div class="icons">
|
||||
<a-popover placement="bottomRight" :overlayStyle="{ width: '400px' }" trigger="click" v-model:visible="showSearch">
|
||||
<template #content>
|
||||
<div>
|
||||
<span class="search-label">回复、提到我的人?:</span>
|
||||
<span style="display: inline-block">
|
||||
<div v-if="searchParams.fromUser" class="selected-user">
|
||||
<span>{{ searchParams.realname }}</span>
|
||||
<span class="clear-user-icon"
|
||||
><close-outlined style="font-size: 12px" @click="clearSearchParamsUser"
|
||||
/></span>
|
||||
</div>
|
||||
<a-button v-else type="dashed" shape="circle" @click="openSelectPerson">
|
||||
<plus-outlined />
|
||||
</a-button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="search-date">
|
||||
<div class="date-label">时间:</div>
|
||||
<div class="date-tags">
|
||||
<div class="tags-container">
|
||||
<div
|
||||
v-for="item in dateTags"
|
||||
:class="item.active == true ? 'tag active' : 'tag'"
|
||||
@click="handleClickDateTag(item)"
|
||||
>{{ item.text }}</div
|
||||
>
|
||||
</div>
|
||||
<div class="cust-range-date" v-if="showRangeDate">
|
||||
<a-range-picker v-model:value="searchRangeDate" @change="handleChangeSearchDate" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<span v-if="conditionStr" class="anticon filtera">
|
||||
<filter-outlined />
|
||||
<span style="font-size: 12px; margin-left: 3px">{{ conditionStr }}</span>
|
||||
<span style="display: flex; margin: 0 5px"><close-outlined style="font-size: 12px" @click="clearAll" /></span>
|
||||
</span>
|
||||
<filter-outlined v-else />
|
||||
</a-popover>
|
||||
<close-outlined @click="closeModal" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="sys-message-card">
|
||||
<a-tabs :activeKey="activeKey" center @tabClick="handleChangePanel">
|
||||
<template #renderTabBar>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<a-tab-pane tab="全部消息" key="all" forceRender>
|
||||
<sys-message-list ref="allMessageRef" @close="hrefThenClose" @detail="showDetailModal" />
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- 标星 -->
|
||||
<a-tab-pane tab="标星消息" key="star" forceRender>
|
||||
<sys-message-list ref="starMessageRef" star @close="hrefThenClose" @detail="showDetailModal" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</BasicModal>
|
||||
|
||||
<user-select-modal
|
||||
isRadioSelection
|
||||
:showButton="false"
|
||||
labelKey="realname"
|
||||
rowKey="username"
|
||||
@register="regModal"
|
||||
@getSelectResult="getSelectedUser"
|
||||
/>
|
||||
|
||||
<DetailModal @register="registerDetail" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
|
||||
import { FilterOutlined, CloseOutlined, BellFilled, ExclamationOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { JSelectUser } from '/@/components/Form';
|
||||
import { ref, unref, reactive, computed } from 'vue';
|
||||
import SysMessageList from './SysMessageList.vue';
|
||||
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue';
|
||||
import DetailModal from '/@/views/monitor/mynews/DetailModal.vue';
|
||||
|
||||
export default {
|
||||
name: 'SysMessageModal',
|
||||
components: {
|
||||
BasicModal,
|
||||
FilterOutlined,
|
||||
CloseOutlined,
|
||||
BellFilled,
|
||||
ExclamationOutlined,
|
||||
JSelectUser,
|
||||
SysMessageList,
|
||||
UserSelectModal,
|
||||
PlusOutlined,
|
||||
DetailModal,
|
||||
},
|
||||
emits: ['register', 'refresh'],
|
||||
setup(_p, { emit }) {
|
||||
const allMessageRef = ref();
|
||||
const starMessageRef = ref();
|
||||
const activeKey = ref('all');
|
||||
function handleChangeTab(e, key) {
|
||||
activeKey.value = key;
|
||||
loadData();
|
||||
}
|
||||
function handleChangePanel(key) {
|
||||
activeKey.value = key;
|
||||
}
|
||||
|
||||
// 查询区域存储值
|
||||
const searchParams = reactive({
|
||||
fromUser: '',
|
||||
realname: '',
|
||||
rangeDateKey: '',
|
||||
rangeDate: [],
|
||||
});
|
||||
|
||||
function loadData() {
|
||||
let params = {
|
||||
fromUser: searchParams.fromUser,
|
||||
rangeDateKey: searchParams.rangeDateKey,
|
||||
rangeDate: searchParams.rangeDate,
|
||||
};
|
||||
if (activeKey.value == 'all') {
|
||||
allMessageRef.value.reload(params);
|
||||
} else {
|
||||
starMessageRef.value.reload(params);
|
||||
}
|
||||
}
|
||||
|
||||
//useModalInner
|
||||
const [registerModal, { closeModal }] = useModalInner(async () => {
|
||||
//每次弹窗打开 加载最新的数据
|
||||
loadData();
|
||||
});
|
||||
|
||||
const showSearch = ref(false);
|
||||
|
||||
function handleChangeSearchPerson(value, a) {
|
||||
console.log('选择改变', value, a);
|
||||
showSearch.value = true;
|
||||
}
|
||||
|
||||
const dateTags = reactive([
|
||||
{ key: 'jt', text: '今天', active: false },
|
||||
{ key: 'zt', text: '昨天', active: false },
|
||||
{ key: 'qt', text: '前天', active: false },
|
||||
{ key: 'bz', text: '本周', active: false },
|
||||
{ key: 'sz', text: '上周', active: false },
|
||||
{ key: 'by', text: '本月', active: false },
|
||||
{ key: 'sy', text: '上月', active: false },
|
||||
{ key: 'zdy', text: '自定义', active: false },
|
||||
]);
|
||||
function handleClickDateTag(item) {
|
||||
for (let a of dateTags) {
|
||||
if (a.key != item.key) {
|
||||
a.active = false;
|
||||
}
|
||||
}
|
||||
item.active = !item.active;
|
||||
if (item.active == false) {
|
||||
searchParams.rangeDateKey = '';
|
||||
} else {
|
||||
searchParams.rangeDateKey = item.key;
|
||||
}
|
||||
if (item.key == 'zdy') {
|
||||
// 自定义日期查询走的是 handleChangeSearchDate
|
||||
if (item.active == false) {
|
||||
searchParams.rangeDate = [];
|
||||
loadData();
|
||||
}
|
||||
} else {
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
const showRangeDate = computed(() => {
|
||||
let temp = dateTags.filter((i) => i.active == true);
|
||||
if (temp && temp.length > 0) {
|
||||
if (temp[0].text == '自定义') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const searchRangeDate = ref([]);
|
||||
function handleChangeSearchDate(_value, dateStringArray) {
|
||||
searchParams.rangeDate = [...dateStringArray];
|
||||
loadData();
|
||||
}
|
||||
|
||||
function hrefThenClose(id) {
|
||||
emit('refresh', id);
|
||||
// closeModal();
|
||||
}
|
||||
|
||||
// 有查询条件值的时候显示该字符串
|
||||
const conditionStr = computed(() => {
|
||||
const { fromUser, rangeDateKey, realname } = searchParams;
|
||||
if (!fromUser && !rangeDateKey) {
|
||||
return '';
|
||||
}
|
||||
let arr = [];
|
||||
if (fromUser) {
|
||||
arr.push(realname);
|
||||
}
|
||||
if (rangeDateKey) {
|
||||
let rangDates = dateTags.filter((item) => item.key == rangeDateKey);
|
||||
if (rangDates && rangDates.length > 0) {
|
||||
arr.push(rangDates[0].text);
|
||||
}
|
||||
}
|
||||
return arr.join('、');
|
||||
});
|
||||
|
||||
//注册model
|
||||
const [regModal, { openModal }] = useModal();
|
||||
|
||||
function getSelectedUser(options, value) {
|
||||
if (options && options.length > 0) {
|
||||
searchParams.fromUser = value;
|
||||
searchParams.realname = options[0].label;
|
||||
}
|
||||
}
|
||||
|
||||
function openSelectPerson() {
|
||||
openModal(true, {});
|
||||
}
|
||||
|
||||
function clearSearchParamsUser() {
|
||||
searchParams.fromUser = '';
|
||||
searchParams.realname = '';
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
searchParams.fromUser = '';
|
||||
searchParams.realname = '';
|
||||
searchParams.rangeDateKey = '';
|
||||
searchParams.rangeDate = [];
|
||||
for (let a of dateTags) {
|
||||
a.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
const [registerDetail, { openModal: openDetailModal }] = useModal();
|
||||
function showDetailModal(record) {
|
||||
console.error(123, record);
|
||||
openDetailModal(true, { record: unref(record), isUpdate: true });
|
||||
}
|
||||
return {
|
||||
conditionStr,
|
||||
regModal,
|
||||
getSelectedUser,
|
||||
openSelectPerson,
|
||||
clearSearchParamsUser,
|
||||
clearAll,
|
||||
|
||||
registerModal,
|
||||
activeKey,
|
||||
handleChangePanel,
|
||||
handleChangeTab,
|
||||
|
||||
showSearch,
|
||||
searchParams,
|
||||
handleChangeSearchPerson,
|
||||
dateTags,
|
||||
handleClickDateTag,
|
||||
showRangeDate,
|
||||
searchRangeDate,
|
||||
handleChangeSearchDate,
|
||||
closeModal,
|
||||
hrefThenClose,
|
||||
|
||||
allMessageRef,
|
||||
starMessageRef,
|
||||
registerDetail,
|
||||
showDetailModal,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@keyframes move22 {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(600px);
|
||||
}
|
||||
}
|
||||
|
||||
.sys-msg-modal {
|
||||
.ant-modal-header {
|
||||
padding-bottom: 0;
|
||||
padding-top: 6px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.ant-modal-body {
|
||||
height: 550px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ant-modal {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: calc(100% - 600px);
|
||||
/* animation-name: move22;
|
||||
animation-duration:0.8s;
|
||||
animation-timing-function:ease;*/
|
||||
}
|
||||
}
|
||||
.sys-msg-modal-title {
|
||||
.title {
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
}
|
||||
.icon-right {
|
||||
display: inline-block;
|
||||
width: 220px;
|
||||
vertical-align: top;
|
||||
|
||||
.icons {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
> span.anticon {
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
&:active {
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
> span.filtera {
|
||||
background-color: #d3eafd;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
height: 27px;
|
||||
padding-top: 7px;
|
||||
margin-top: 3px;
|
||||
line-height: 25px;
|
||||
color: #2196f3;
|
||||
display: flex;
|
||||
|
||||
> span.anticon {
|
||||
height: 30px;
|
||||
line-height: 9px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-tabs-nav-wrap {
|
||||
display: inline-block;
|
||||
width: calc(100% - 340px);
|
||||
}
|
||||
.ant-tabs-nav-scroll {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.sys-message-card {
|
||||
}
|
||||
|
||||
.search-label {
|
||||
font-weight: 500;
|
||||
font-size: 14px !important;
|
||||
color: #757575 !important;
|
||||
display: inline-block;
|
||||
margin-right: 15px !important;
|
||||
}
|
||||
.search-date {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
margin-top: 15px;
|
||||
.date-label {
|
||||
margin-top: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 14px !important;
|
||||
color: #757575 !important;
|
||||
margin-right: 15px !important;
|
||||
}
|
||||
|
||||
.date-tags {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
-ms-flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
-ms-flex: 1;
|
||||
.tags-container {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex-wrap: wrap;
|
||||
.tag {
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 17px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
background-color: #d3eafd !important;
|
||||
color: #2196f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-user {
|
||||
background: #f5f5f5;
|
||||
padding: 2px 14px;
|
||||
border-radius: 30px;
|
||||
.clear-user-icon {
|
||||
margin-left: 5px;
|
||||
|
||||
.anticon {
|
||||
&:hover {
|
||||
color: #2196f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,226 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { getDictItemsByCode } from '/@/utils/dict/index';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
import { useTabs } from '/@/hooks/web/useTabs';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
const queryMessageList = (params) => {
|
||||
const url = '/sys/annountCement/vue3List';
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取消息列表数据
|
||||
*/
|
||||
export function useSysMessage() {
|
||||
const rangeDateArray = getDictItemsByCode('rangeDate');
|
||||
console.log('+++++++++++++++++++++');
|
||||
console.log('rangeDateArray', rangeDateArray);
|
||||
console.log('+++++++++++++++++++++');
|
||||
|
||||
const messageList = ref<any[]>([]);
|
||||
const pageNo = ref(1);
|
||||
const pageSize = 10;
|
||||
|
||||
const searchParams = reactive({
|
||||
fromUser: '',
|
||||
rangeDateKey: '',
|
||||
rangeDate: [],
|
||||
starFlag: '',
|
||||
});
|
||||
|
||||
function getQueryParams() {
|
||||
const { fromUser, rangeDateKey, rangeDate, starFlag } = searchParams;
|
||||
const params = {
|
||||
fromUser,
|
||||
starFlag,
|
||||
rangeDateKey,
|
||||
beginDate: '',
|
||||
endDate: '',
|
||||
pageNo: pageNo.value,
|
||||
pageSize,
|
||||
};
|
||||
if (rangeDateKey == 'zdy') {
|
||||
params.beginDate = rangeDate[0] + ' 00:00:00';
|
||||
params.endDate = rangeDate[1] + ' 23:59:59';
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
// 数据是否加载完了
|
||||
const loadEndStatus = ref(false);
|
||||
|
||||
//请求数据
|
||||
async function loadData() {
|
||||
if (loadEndStatus.value === true) {
|
||||
return;
|
||||
}
|
||||
const params = getQueryParams();
|
||||
const data = await queryMessageList(params);
|
||||
console.log('获取结果', data);
|
||||
if (!data || data.length <= 0) {
|
||||
loadEndStatus.value = true;
|
||||
return;
|
||||
}
|
||||
if (data.length < pageSize) {
|
||||
loadEndStatus.value = true;
|
||||
}
|
||||
pageNo.value = pageNo.value + 1;
|
||||
const temp: any[] = messageList.value;
|
||||
temp.push(...data);
|
||||
messageList.value = temp;
|
||||
}
|
||||
|
||||
//重置
|
||||
function reset() {
|
||||
messageList.value = [];
|
||||
pageNo.value = 1;
|
||||
loadEndStatus.value = false;
|
||||
}
|
||||
|
||||
//标星
|
||||
function updateStarMessage(item) {
|
||||
const url = '/sys/sysAnnouncementSend/edit';
|
||||
let starFlag = '1';
|
||||
if (item.starFlag == starFlag) {
|
||||
starFlag = '0';
|
||||
}
|
||||
const params = {
|
||||
starFlag,
|
||||
id: item.sendId,
|
||||
};
|
||||
defHttp.put({ url, params });
|
||||
}
|
||||
|
||||
const loadingMoreStatus = ref(false);
|
||||
async function onLoadMore() {
|
||||
loadingMoreStatus.value = true;
|
||||
await loadData();
|
||||
loadingMoreStatus.value = false;
|
||||
}
|
||||
|
||||
function noRead(item) {
|
||||
if (item.readFlag === '1') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 消息类型
|
||||
function getMsgCategory(item) {
|
||||
if (item.busType == 'email') {
|
||||
return '邮件提醒:';
|
||||
} else if (item.busType == 'bpm') {
|
||||
return '流程催办:';
|
||||
} else if (item.busType == 'bpm_task') {
|
||||
return '流程任务:';
|
||||
} else if (item.msgCategory == '2') {
|
||||
return '系统消息:';
|
||||
} else if (item.msgCategory == '1') {
|
||||
return '通知公告:';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
return {
|
||||
messageList,
|
||||
reset,
|
||||
loadData,
|
||||
loadEndStatus,
|
||||
searchParams,
|
||||
updateStarMessage,
|
||||
|
||||
onLoadMore,
|
||||
noRead,
|
||||
getMsgCategory,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于消息跳转
|
||||
*/
|
||||
export function useMessageHref(emit) {
|
||||
const messageHrefArray: any[] = getDictItemsByCode('messageHref');
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const rt = useRoute();
|
||||
const { close: closeTab, closeSameRoute } = useTabs();
|
||||
// const defaultPath = '/monitor/mynews';
|
||||
//const bpmPath = '/task/handle/'
|
||||
|
||||
async function goPage(record, openModalFun?) {
|
||||
if (!record.busType) {
|
||||
if (!openModalFun) {
|
||||
// 从首页的消息通知跳转
|
||||
await goPageFromOuter(record);
|
||||
} else {
|
||||
// 从消息页面列表点击详情查看 直接打开modal
|
||||
openModalFun();
|
||||
}
|
||||
} else {
|
||||
await goPageWithBusType(record);
|
||||
}
|
||||
/* busId: "1562035005173587970"
|
||||
busType: "email"
|
||||
openPage: "modules/eoa/email/modals/EoaEmailInForm"
|
||||
openType: "component"*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据busType不同跳转不同页面
|
||||
* @param record
|
||||
*/
|
||||
async function goPageWithBusType(record) {
|
||||
const { busType, busId, msgAbstract } = record;
|
||||
const temp = messageHrefArray.filter((item) => item.value === busType);
|
||||
if (!temp || temp.length == 0) {
|
||||
console.error('当前业务类型不识别', busType);
|
||||
return;
|
||||
}
|
||||
let path = temp[0].text;
|
||||
path = path.replace('{DETAIL_ID}', busId);
|
||||
//固定参数 detailId 用于查询表单数据
|
||||
const query: any = {
|
||||
detailId: busId,
|
||||
};
|
||||
// 额外参数处理
|
||||
if (msgAbstract) {
|
||||
try {
|
||||
const json = JSON.parse(msgAbstract);
|
||||
Object.keys(json).map((k) => {
|
||||
query[k] = json[k];
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('msgAbstract参数不是JSON格式', msgAbstract);
|
||||
}
|
||||
}
|
||||
// 跳转路由
|
||||
appStore.setMessageHrefParams(query);
|
||||
if (rt.path.indexOf(path) >= 0) {
|
||||
await closeTab();
|
||||
await router.replace({ path: path, query: { time: new Date().getTime() } });
|
||||
} else {
|
||||
closeSameRoute(path);
|
||||
await router.push({ path: path });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从首页的消息通知跳转消息列表打开modal
|
||||
* @param record
|
||||
*/
|
||||
async function goPageFromOuter(record) {
|
||||
//没有定义业务类型 直接跳转我的消息页面
|
||||
emit('detail', record);
|
||||
}
|
||||
|
||||
return {
|
||||
goPage,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<BasicDrawer @register="registerModal" title="详情" :width="600" v-bind="$attrs" @ok="closeDrawer">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { formSchemas } from './manage.data';
|
||||
|
||||
// 声明 emits
|
||||
const emit = defineEmits(['register']);
|
||||
// 注册 form
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// 注册 modal
|
||||
const [registerModal, { closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...data.record });
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-message-manage';
|
||||
|
||||
.@{prefix-cls} {
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-dropdown v-if="showBatchBtn">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteBatch">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作</span>
|
||||
<Icon icon="mdi:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<ManageDrawer @register="registerDrawer" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="message-manage">
|
||||
import { unref, computed } from 'vue';
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import ManageDrawer from './ManageDrawer.vue';
|
||||
import { Api, list, deleteBatch } from './manage.api';
|
||||
import { columns, searchFormSchema } from './manage.data';
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
designScope: 'message-manage',
|
||||
tableProps: {
|
||||
title: '消息中心模板列表数据',
|
||||
api: list,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
url: Api.exportXls,
|
||||
name: '消息中心模板列表',
|
||||
},
|
||||
importConfig: {
|
||||
url: Api.importXls,
|
||||
success: () => reload(),
|
||||
},
|
||||
});
|
||||
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload, setLoading }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const showBatchBtn = computed(() => selectedRowKeys.value.length > 0);
|
||||
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
|
||||
function onDetail(record) {
|
||||
openDrawer(true, { record: record });
|
||||
}
|
||||
|
||||
function onDelete(record) {
|
||||
if (record) {
|
||||
doDeleteDepart([record.id], false);
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteBatch() {
|
||||
try {
|
||||
await doDeleteDepart(selectedRowKeys);
|
||||
selectedRowKeys.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ids 批量删除
|
||||
* @param idListRef array
|
||||
* @param confirm 是否显示确认提示框
|
||||
*/
|
||||
async function doDeleteDepart(idListRef, confirm = true) {
|
||||
const idList = unref(idListRef);
|
||||
if (idList.length > 0) {
|
||||
try {
|
||||
setLoading(true);
|
||||
await deleteBatch({ ids: idList.join(',') }, confirm);
|
||||
await reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [{ label: '详情', onClick: onDetail.bind(null, record) }];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: onDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import 'index';
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
export enum Api {
|
||||
list = '/sys/message/sysMessage/list',
|
||||
delete = '/sys/message/sysMessage/delete',
|
||||
deleteBatch = '/sys/message/sysMessage/deleteBatch',
|
||||
exportXls = 'sys/message/sysMessage/exportXls',
|
||||
importXls = 'sys/message/sysMessage/importExcel',
|
||||
save = '/sys/message/sysMessage/add',
|
||||
edit = '/sys/message/sysMessage/edit',
|
||||
}
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
* @param confirm
|
||||
*/
|
||||
export const deleteBatch = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更改消息模板
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
if (unref(isUpdate)) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '消息标题',
|
||||
dataIndex: 'esTitle',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: '发送内容',
|
||||
dataIndex: 'esContent',
|
||||
width: 200,
|
||||
// slots: { customRender: 'esContent' },
|
||||
},
|
||||
{
|
||||
title: '接收人',
|
||||
dataIndex: 'esReceiver',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: '发送次数',
|
||||
dataIndex: 'esSendNum',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '发送状态',
|
||||
dataIndex: 'esSendStatus_dictText',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '发送时间',
|
||||
dataIndex: 'esSendTime',
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
title: '发送方式',
|
||||
dataIndex: 'esType_dictText',
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '消息标题',
|
||||
field: 'esTitle',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '接收人',
|
||||
field: 'esReceiver',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '发送状态',
|
||||
field: 'esSendStatus',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'msgSendStatus',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '发送方式',
|
||||
field: 'esType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'es_type',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchemas: FormSchema[] = [
|
||||
{
|
||||
label: 'ID',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '消息标题',
|
||||
field: 'esTitle',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送内容',
|
||||
field: 'esContent',
|
||||
component: 'InputTextArea',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送参数',
|
||||
field: 'esParam',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
|
||||
{
|
||||
label: '接收人',
|
||||
field: 'esReceiver',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送方式',
|
||||
field: 'esType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { disabled: true, dictCode: 'es_type' },
|
||||
},
|
||||
{
|
||||
label: '发送时间',
|
||||
field: 'esSendTime',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送状态',
|
||||
field: 'esSendStatus',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { disabled: true, dictCode: 'msgSendStatus' },
|
||||
},
|
||||
{
|
||||
label: '发送次数',
|
||||
field: 'esSendNum',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '发送失败原因',
|
||||
field: 'esResult',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'remark',
|
||||
component: 'InputTextArea',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" :title="title" :width="800" v-bind="$attrs" @ok="onSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { formSchemas } from './template.data';
|
||||
import { saveOrUpdate } from './template.api';
|
||||
|
||||
// 声明 emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const title = ref<string>('');
|
||||
const isUpdate = ref<boolean>(false);
|
||||
// 注册 form
|
||||
//update-begin---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能------------
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema, setProps }] = useForm({
|
||||
//update-end---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能--------------z
|
||||
schemas: formSchemas,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// 注册 modal
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
|
||||
isUpdate.value = unref(data.isUpdate);
|
||||
title.value = unref(data.title);
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...data.record });
|
||||
// 隐藏底部时禁用整个表单
|
||||
setProps({ disabled: !data?.showFooter });
|
||||
});
|
||||
|
||||
//表单提交事件
|
||||
async function onSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
// 提交表单
|
||||
await saveOrUpdate(values, isUpdate);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" title="发送测试" :width="800" v-bind="$attrs" @ok="onSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { sendTestFormSchemas } from './template.data';
|
||||
import { sendMessageTest } from './template.api';
|
||||
|
||||
// 声明 emits
|
||||
const emit = defineEmits(['register']);
|
||||
// 注册 form
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: sendTestFormSchemas,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// 注册 modal
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...unref(data.record) });
|
||||
});
|
||||
|
||||
//表单提交事件
|
||||
async function onSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
// 提交表单
|
||||
await sendMessageTest(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-message-template';
|
||||
|
||||
.@{prefix-cls} {
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<div :class="prefixCls">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="onAdd">新增</a-button>
|
||||
<!-- <a-button preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button preIcon="ant-design:import-outlined" type="primary" @click="onImportXls">导入</j-upload-button>-->
|
||||
<a-dropdown v-if="showBatchBtn">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteBatch">
|
||||
<Icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作</span>
|
||||
<Icon icon="mdi:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<TemplateModal @register="registerModal" @success="reload" />
|
||||
<TemplateTestModal @register="registerTestModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="message-template">
|
||||
import { unref, computed, toRaw } from 'vue';
|
||||
import { ActionItem, BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import TemplateModal from './TemplateModal.vue';
|
||||
import TemplateTestModal from './TemplateTestModal.vue';
|
||||
import { Api, saveOrUpdate, list, deleteBatch } from './template.api';
|
||||
import { columns, searchFormSchema } from './template.data';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, onExportXls, onImportXls, tableContext } = useListPage({
|
||||
designScope: 'message-template',
|
||||
tableProps: {
|
||||
title: '消息中心模板列表数据',
|
||||
api: list,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
url: Api.exportXls,
|
||||
name: '消息中心模板列表',
|
||||
},
|
||||
importConfig: {
|
||||
url: Api.importXls,
|
||||
success: () => reload(),
|
||||
},
|
||||
});
|
||||
|
||||
// 注册 ListTable
|
||||
const [registerTable, { reload, setLoading }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerTestModal, testModal] = useModal();
|
||||
const showBatchBtn = computed(() => selectedRowKeys.value.length > 0);
|
||||
|
||||
function onAdd() {
|
||||
openModal(true, {
|
||||
title: '新增消息模板',
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
record: {},
|
||||
});
|
||||
}
|
||||
|
||||
function onEdit(record) {
|
||||
if (record.useStatus === '1') {
|
||||
createMessage.warning('此模板已被应用,禁止编辑!');
|
||||
return;
|
||||
}
|
||||
openModal(true, {
|
||||
title: '修改消息模板',
|
||||
isUpdate: true,
|
||||
record: record,
|
||||
showFooter: true,
|
||||
});
|
||||
}
|
||||
|
||||
function onDelete(record) {
|
||||
if (record) {
|
||||
//update-begin-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
if (record.useStatus == '1') {
|
||||
createMessage.warning('该模板已被应用禁止删除!');
|
||||
return;
|
||||
}
|
||||
//update-end-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
doDeleteDepart([record.id], false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ids 批量删除
|
||||
* @param idListRef array
|
||||
* @param confirm 是否显示确认提示框
|
||||
*/
|
||||
async function doDeleteDepart(idListRef, confirm = true) {
|
||||
const idList = unref(idListRef);
|
||||
if (idList.length > 0) {
|
||||
try {
|
||||
setLoading(true);
|
||||
await deleteBatch({ ids: idList.join(',') }, confirm);
|
||||
await reload();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteBatch() {
|
||||
try {
|
||||
//update-begin-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
let arr = toRaw(selectedRows.value);
|
||||
let temp = arr.filter((item) => item.useStatus == '1');
|
||||
if (temp.length > 0) {
|
||||
createMessage.warning('选中的模板已被应用禁止删除!');
|
||||
return;
|
||||
}
|
||||
//update-end-author:taoyan date:2022-7-14 for: VUEN-1652【bug】应用状态下不允许删除
|
||||
await doDeleteDepart(selectedRowKeys);
|
||||
selectedRowKeys.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息测试
|
||||
function onSendTest(record) {
|
||||
testModal.openModal(true, { record });
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
//update-begin---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能------------
|
||||
return [
|
||||
{ label: '查看', onClick: handleDetail.bind(null, record) },
|
||||
{ label: '编辑', onClick: onEdit.bind(null, record) },
|
||||
];
|
||||
//update-end---author:wangshuai ---date:20221123 for:[VUEN-2807]消息模板加一个查看功能------------
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{ label: '应用', onClick: handleUse.bind(null, record) },
|
||||
{ label: '停用', onClick: handleNotUse.bind(null, record) },
|
||||
{ label: '发送测试', onClick: onSendTest.bind(null, record) },
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
popConfirm: {
|
||||
title: '确认要删除吗?',
|
||||
confirm: onDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用
|
||||
*/
|
||||
async function handleUse(record) {
|
||||
let param = { id: record.id, useStatus: '1' };
|
||||
await saveOrUpdate(param, true);
|
||||
await reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用
|
||||
*/
|
||||
async function handleNotUse(record) {
|
||||
let param = { id: record.id, useStatus: '0' };
|
||||
await saveOrUpdate(param, true);
|
||||
await reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看
|
||||
* @param record
|
||||
*/
|
||||
function handleDetail(record) {
|
||||
openModal(true, {
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
record: record,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import 'index';
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
export enum Api {
|
||||
list = '/sys/message/sysMessageTemplate/list',
|
||||
delete = '/sys/message/sysMessageTemplate/delete',
|
||||
deleteBatch = '/sys/message/sysMessageTemplate/deleteBatch',
|
||||
exportXls = 'sys/message/sysMessageTemplate/exportXls',
|
||||
importXls = 'sys/message/sysMessageTemplate/importExcel',
|
||||
save = '/sys/message/sysMessageTemplate/add',
|
||||
edit = '/sys/message/sysMessageTemplate/edit',
|
||||
// 发送测试
|
||||
send = '/sys/message/sysMessageTemplate/sendTestSms',
|
||||
}
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
* @param confirm
|
||||
*/
|
||||
export const deleteBatch = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更改消息模板
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
if (unref(isUpdate)) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送消息测试
|
||||
* @param params
|
||||
*/
|
||||
export const sendMessageTest = (params) => defHttp.post({ url: Api.send, params });
|
||||
@@ -0,0 +1,184 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { rules } from '/@/utils/helper/validator';
|
||||
import { filterDictTextByCache } from '/@/utils/dict/JDictSelectUtil';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '模板标题',
|
||||
dataIndex: 'templateName',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '模板编码',
|
||||
dataIndex: 'templateCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '通知模板',
|
||||
dataIndex: 'templateContent',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '模板类型',
|
||||
dataIndex: 'templateType',
|
||||
width: 100,
|
||||
customRender: ({ text }) => filterDictTextByCache('msgType', text),
|
||||
},
|
||||
{
|
||||
title: '是否应用',
|
||||
dataIndex: 'useStatus',
|
||||
width: 90,
|
||||
customRender: function ({ text }) {
|
||||
if (text == '1') {
|
||||
return '是';
|
||||
} else {
|
||||
return '否';
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '模板标题',
|
||||
field: 'templateName',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'templateCode',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '模板类型',
|
||||
field: 'templateType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'msgType',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchemas: FormSchema[] = [
|
||||
{
|
||||
label: 'ID',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '模板标题',
|
||||
field: 'templateName',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'templateCode',
|
||||
component: 'Input',
|
||||
dynamicRules: ({ model, schema }) => {
|
||||
return [...rules.duplicateCheckRule('sys_sms_template', 'template_code', model, schema, true)];
|
||||
},
|
||||
// 编辑模式下不可修改编码
|
||||
dynamicDisabled: (params) => !!params.values.id,
|
||||
},
|
||||
{
|
||||
label: '模板类型',
|
||||
field: 'templateType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'msgType',
|
||||
placeholder: '请选择模板类型',
|
||||
disabled: true,
|
||||
},
|
||||
required: true,
|
||||
defaultValue: '1',
|
||||
},
|
||||
{
|
||||
label: '是否应用',
|
||||
field: 'useStatus',
|
||||
component: 'JSwitch',
|
||||
componentProps: {
|
||||
options: ['1', '0'],
|
||||
},
|
||||
defaultValue: '0',
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'InputTextArea',
|
||||
componentProps: {
|
||||
autoSize: {
|
||||
minRows: 8,
|
||||
maxRows: 8,
|
||||
},
|
||||
},
|
||||
ifShow: ({ values }) => {
|
||||
return !['2', '4', '5'].includes(values.templateType);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'JEditor',
|
||||
ifShow: ({ values }) => {
|
||||
return ['2', '4'].includes(values.templateType);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'JMarkdownEditor',
|
||||
ifShow: ({ values }) => {
|
||||
return ['5'].includes(values.templateType);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const sendTestFormSchemas: FormSchema[] = [
|
||||
{
|
||||
label: '模板编码',
|
||||
field: 'templateCode',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '模板标题',
|
||||
field: 'templateName',
|
||||
component: 'Input',
|
||||
componentProps: { disabled: true },
|
||||
},
|
||||
{
|
||||
label: '模板内容',
|
||||
field: 'templateContent',
|
||||
component: 'InputTextArea',
|
||||
componentProps: { disabled: true, rows: 5 },
|
||||
},
|
||||
{
|
||||
label: '测试数据',
|
||||
field: 'testData',
|
||||
component: 'InputTextArea',
|
||||
required: true,
|
||||
helpMessage: 'JSON数据',
|
||||
defaultValue: '{}',
|
||||
componentProps: {
|
||||
placeholder: '请输入JSON格式测试数据',
|
||||
rows: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '消息类型',
|
||||
field: 'msgType',
|
||||
component: 'JDictSelectTag',
|
||||
required: true,
|
||||
defaultValue: 'system',
|
||||
componentProps: { dictCode: 'messageType', type: 'radio' },
|
||||
},
|
||||
{
|
||||
label: '消息接收方',
|
||||
field: 'receiver',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,89 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
enum Api {
|
||||
// list = '/sys/api/messageRemind/selectUserMessageList',
|
||||
list = '/sys/sysMessageRemind/list',
|
||||
save = '/sys/sysMessageRemind/add',
|
||||
edit = '/sys/sysMessageRemind/edit',
|
||||
deleteOne = '/sys/sysMessageRemind/delete',
|
||||
deleteBatch = '/sys/sysMessageRemind/deleteBatch',
|
||||
importExcel = '/sys/sysMessageRemind/importExcel',
|
||||
exportXls = '/sys/sysMessageRemind/exportXls',
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 删除单个
|
||||
*/
|
||||
export const deleteOne = (params, handleSuccess) => {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp
|
||||
.delete(
|
||||
{
|
||||
url: Api.deleteOne,
|
||||
data: params,
|
||||
},
|
||||
{ joinParamsToUrl: true }
|
||||
)
|
||||
.then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
*/
|
||||
export const batchDelete = (params, handleSuccess) => {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp
|
||||
.delete(
|
||||
{
|
||||
url: Api.deleteBatch,
|
||||
data: params,
|
||||
},
|
||||
{ joinParamsToUrl: true }
|
||||
)
|
||||
.then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
const url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
//列表数据
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '标题',
|
||||
align: 'center',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
// {
|
||||
// title: '内容',
|
||||
// align: 'center',
|
||||
// dataIndex: 'content',
|
||||
// slots: 'htmlSlot',
|
||||
// },
|
||||
{
|
||||
title: '类型',
|
||||
align: 'center',
|
||||
dataIndex: 'type_dictText',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
align: 'center',
|
||||
dataIndex: 'createTime',
|
||||
},
|
||||
];
|
||||
//查询数据
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '标题',
|
||||
field: 'title',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
field: 'type',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'message_remind',
|
||||
},
|
||||
},
|
||||
];
|
||||
//表单数据
|
||||
export const formSchema: FormSchema[] = [
|
||||
// {
|
||||
// label: '用户id',
|
||||
// field: 'userId',
|
||||
// component: 'Input',
|
||||
// },
|
||||
{
|
||||
label: '类型',
|
||||
field: 'type',
|
||||
required: true,
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'message_remind',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '用户姓名',
|
||||
field: 'userName',
|
||||
component: 'Input',
|
||||
required: ({ values }): boolean => values.type != '1',
|
||||
// componentProps: ({ formModel, schema }) => {
|
||||
// schema.required = formModel.type == '1';
|
||||
// },
|
||||
componentProps: {
|
||||
placeholder: '请选择用户',
|
||||
},
|
||||
// ifShow: ({ values }): boolean => values.type == '1',
|
||||
},
|
||||
{
|
||||
label: '标题',
|
||||
field: 'title',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
field: 'userId',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '内容',
|
||||
field: 'content',
|
||||
component: 'JEditor',
|
||||
componentProps: {
|
||||
showImageUpload: true,
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 流程表单调用这个方法获取formSchema
|
||||
* @param _formData
|
||||
*/
|
||||
export function getBpmFormSchema(_formData): FormSchema[] {
|
||||
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
|
||||
return formSchema;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" @click="onAdd" preIcon="ant-design:plus-outlined"> 新增 </a-button>
|
||||
<a-button type="primary" preIcon="ant-design:delete-outlined" @click="batchHandleDelete">批量删除 </a-button>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
<!--字段回显插槽-->
|
||||
<template #htmlSlot="{ text }">
|
||||
<div v-html="text"></div>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 表单区域 -->
|
||||
<SysMessageRemindModal @register="registerModal" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-sysMessageRemind" setup>
|
||||
import { ref } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import SysMessageRemindModal from './components/SysMessageRemindModal.vue';
|
||||
import { columns, searchFormSchema } from './SysMessageRemind.data';
|
||||
import { list, deleteOne, batchDelete } from './SysMessageRemind.api';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
//注册model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
//注册table数据
|
||||
const { tableContext } = useListPage({
|
||||
tableProps: {
|
||||
title: '消息公告',
|
||||
api: list,
|
||||
columns,
|
||||
canResize: false,
|
||||
formConfig: {
|
||||
//labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
showAdvancedButton: false,
|
||||
fieldMapToNumber: [],
|
||||
fieldMapToTime: [],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 155,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function onAdd() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
title: '新增',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function onEdit(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
showFooter: true,
|
||||
title: '编辑',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
function onDetail(record: Recordable) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
title: '详情',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function onDeleteOne(record) {
|
||||
await deleteOne({ id: record.id }, handleSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
message.warning('未选中任何数据');
|
||||
return;
|
||||
}
|
||||
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
(selectedRowKeys.value = []) && reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: onEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '详情',
|
||||
onClick: onDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
onClick: onDeleteOne.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div style="min-height: 400px">
|
||||
<BasicForm @register="registerForm"></BasicForm>
|
||||
<div style="width: 100%;text-align: center" v-if="!formDisabled">
|
||||
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {BasicForm, useForm} from '/@/components/Form/index';
|
||||
import {computed, defineComponent} from 'vue';
|
||||
import {defHttp} from '/@/utils/http/axios';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import {getBpmFormSchema} from '../SysMessageRemind.data';
|
||||
import {saveOrUpdate} from '../SysMessageRemind.api';
|
||||
|
||||
export default defineComponent({
|
||||
name: "SysMessageRemindForm",
|
||||
components:{
|
||||
BasicForm
|
||||
},
|
||||
props:{
|
||||
formData: propTypes.object.def({}),
|
||||
formBpm: propTypes.bool.def(true),
|
||||
},
|
||||
setup(props){
|
||||
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
|
||||
labelWidth: 150,
|
||||
schemas: getBpmFormSchema(props.formData),
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: {span: 24}
|
||||
});
|
||||
|
||||
const formDisabled = computed(()=>{
|
||||
if(props.formData.disabled === false){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
let formData = {};
|
||||
const queryByIdUrl = '/system/sysMessageRemind/queryById';
|
||||
async function initFormData(){
|
||||
let params = {id: props.formData.dataId};
|
||||
const data = await defHttp.get({url: queryByIdUrl, params});
|
||||
formData = {...data}
|
||||
//设置表单的值
|
||||
await setFieldsValue(formData);
|
||||
//默认是禁用
|
||||
await setProps({disabled: formDisabled.value})
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
let data = getFieldsValue();
|
||||
let params = Object.assign({}, formData, data);
|
||||
console.log('表单数据', params)
|
||||
await saveOrUpdate(params, true)
|
||||
}
|
||||
|
||||
initFormData();
|
||||
|
||||
return {
|
||||
registerForm,
|
||||
formDisabled,
|
||||
submitForm,
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
style="z-index: 999"
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
destroyOnClose
|
||||
:title="title"
|
||||
:width="700"
|
||||
@ok="handleSubmit"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<replace-other title="选择用户" ref="replaceOtherRef" @choose-employ="onSelectUserOk" />
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
<!-- <UserSelectModal style="z-index: 1001" rowKey="id" @register="registerSelUserModal" @get-select-result="onSelectUserOk" />-->
|
||||
<!-- <replace-other ref="replaceOtherRef" @choose-employ="onSelectUserOk" />-->
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from '../SysMessageRemind.data';
|
||||
import { saveOrUpdate } from '../SysMessageRemind.api';
|
||||
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModalIllness.vue';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import ReplaceOther from '/@/views/emergency/communication/components/replaceOther.vue';
|
||||
const [registerSelUserModal, { openDrawer }] = useDrawer();
|
||||
|
||||
// Emits声明
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
const replaceOtherRef = ref();
|
||||
// 设置标题
|
||||
let title = ref('');
|
||||
//表单配置
|
||||
const [registerForm, { setProps, resetFields, setFieldsValue, validate, clearValidate }] = useForm({
|
||||
//labelWidth: 150,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { span: 24 },
|
||||
});
|
||||
// let findIndex: number = formSchema.findIndex((item) => item.field === 'userName');
|
||||
// if (formSchema[findIndex].componentProps) {
|
||||
formSchema[1]!.componentProps.onClick = () => {
|
||||
replaceOtherRef.value.showModal();
|
||||
};
|
||||
// }
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
title.value = data.title;
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
await clearValidate();
|
||||
// 隐藏底部时禁用整个表单
|
||||
setProps({ disabled: !data?.showFooter });
|
||||
});
|
||||
// 选择用户成功
|
||||
async function onSelectUserOk(d) {
|
||||
const json: object = JSON.parse(d);
|
||||
await setFieldsValue({
|
||||
userName: json.realname,
|
||||
userId: json.id,
|
||||
});
|
||||
}
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdate(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/** 时间和数字输入框样式 */
|
||||
:deep(.ant-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-calendar-picker) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="查看详情" :showCancelBtn="false" :showOkBtn="false" :maxHeight="500">
|
||||
<iframe :src="frameSrc" class="detail-iframe"></iframe>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
// 获取props
|
||||
defineProps({
|
||||
frameSrc: propTypes.string.def(''),
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal] = useModalInner();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.detail-iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="900px" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './notice.data';
|
||||
import { saveOrUpdate } from './notice.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
//表单配置
|
||||
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({
|
||||
confirmLoading: false,
|
||||
showCancelBtn: !!data?.showFooter,
|
||||
showOkBtn: !!data?.showFooter,
|
||||
});
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
// if (data.record.userIds) {
|
||||
// data.record.userIds = data.record.userIds.substring(0, data.record.userIds.length - 1);
|
||||
// }
|
||||
//表单赋值
|
||||
if (data.record.msgType === 'DOC') {
|
||||
//如果是指定专家,这里处理一下初始值
|
||||
data.record.docIds = data.record.userIds;
|
||||
data.record.userIds = '';
|
||||
}
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
// 隐藏底部时禁用整个表单
|
||||
setProps({ disabled: !data?.showFooter });
|
||||
});
|
||||
//设置标题
|
||||
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
|
||||
//表单提交事件
|
||||
async function handleSubmit(v) {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
//update-begin-author:liusq---date:20230404--for: [issue#429]新增通知公告提交指定用户参数有undefined ---
|
||||
if (values.msgType === 'ALL' || values.msgType === 'ALL_DOC') {//全选置空
|
||||
values.userIds = '';
|
||||
} else {
|
||||
if (values.msgType === 'DOC'){
|
||||
values.userIds = values.docIds;
|
||||
}
|
||||
// values.userIds += ',';
|
||||
}
|
||||
//update-end-author:liusq---date:20230404--for: [issue#429]新增通知公告提交指定用户参数有undefined ---
|
||||
await saveOrUpdate(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTitle>
|
||||
<a-button preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<NoticeModal @register="registerModal" @success="reload" />
|
||||
<DetailModal @register="register" :frameSrc="iframeUrl" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" name="system-notice" setup>
|
||||
import {ref} from 'vue';
|
||||
import {BasicTable, TableAction} from '/@/components/Table';
|
||||
import {useModal} from '/@/components/Modal';
|
||||
import NoticeModal from './NoticeModal.vue';
|
||||
import DetailModal from './DetailModal.vue';
|
||||
import {useGlobSetting} from '/@/hooks/setting';
|
||||
import {columns, searchFormSchema} from './notice.data';
|
||||
import {deleteNotice, getExportUrl, getImportUrl, getList, publishData, resEditData} from './notice.api';
|
||||
import {useListPage} from '/@/hooks/system/useListPage';
|
||||
|
||||
const glob = useGlobSetting();
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [register, { openModal: openDetail }] = useModal();
|
||||
const iframeUrl = ref('');
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, doRequest } = useListPage({
|
||||
designScope: 'notice-template',
|
||||
tableProps: {
|
||||
title: '消息通知',
|
||||
api: getList,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '消息通知列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function handleEdit(record) {
|
||||
resEditData({ id: record.id }).then((res) => {
|
||||
openModal(true, {
|
||||
record: res,
|
||||
isUpdate: true,
|
||||
showFooter: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleDetail(record) {
|
||||
resEditData({ id: record.id }).then((res) => {
|
||||
openModal(true, {
|
||||
record: res,
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteNotice({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
// async function batchHandleDelete() {
|
||||
// doRequest(() => batchDeleteNotice({ ids: selectedRowKeys.value }));
|
||||
// }
|
||||
/**
|
||||
* 发布
|
||||
*/
|
||||
async function handleRelease(id) {
|
||||
await publishData({ id: id }, reload);
|
||||
}
|
||||
/**
|
||||
* 操作列定义
|
||||
* @param record
|
||||
*/
|
||||
function getActions(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
ifShow: record.sendStatus == '0',
|
||||
},
|
||||
|
||||
{
|
||||
label: '发布',
|
||||
disabled: record.sendStatus !== '0',
|
||||
onClick: handleRelease.bind(null, record.id),
|
||||
},
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '查看',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
disabled: record.sendStatus === '3',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,89 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
const { createConfirm } = useMessage();
|
||||
enum Api {
|
||||
list = '/sys/annountCement/list',
|
||||
save = '/sys/annountCement/add',
|
||||
edit = '/sys/annountCement/edit',
|
||||
delete = '/sys/annountCement/delete',
|
||||
deleteBatch = '/sys/annountCement/deleteBatch',
|
||||
exportXls = '/sys/annountCement/exportXls',
|
||||
importExcel = '/sys/annountCement/importExcel',
|
||||
releaseData = '/sys/annountCement/doReleaseData',
|
||||
reovkeData = '/sys/annountCement/doReovkeData',
|
||||
publishData = '/sys/annountCement/publishData',
|
||||
queryById = '/sys/annountCement/queryById',
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出url
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入url
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 查询租户列表
|
||||
* @param params
|
||||
*/
|
||||
export const getList = (params) => {
|
||||
return defHttp.get({ url: Api.list, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求编辑数据
|
||||
*/
|
||||
export const resEditData = async (params, handleSuccess) => {
|
||||
return await defHttp.get({ url: Api.queryById, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存或者更新通告
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除通告
|
||||
* @param params
|
||||
*/
|
||||
export const deleteNotice = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.delete, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
|
||||
// /**
|
||||
// * 批量消息公告
|
||||
// * @param params
|
||||
// */
|
||||
// export const batchDeleteNotice = (params) => defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true });
|
||||
//
|
||||
// /**
|
||||
// * 发布
|
||||
// * @param id
|
||||
// */
|
||||
// export const doReleaseData = (params) => defHttp.get({ url: Api.releaseData, params });
|
||||
// /**
|
||||
// * 撤销
|
||||
// * @param id
|
||||
// */
|
||||
// export const doReovkeData = (params) => defHttp.get({ url: Api.reovkeData, params });
|
||||
export const publishData = (params, handleSuccess) => {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认发布',
|
||||
content: '是否发布该消息?发布之后会逐步发短信给用户',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.post({ url: Api.publishData, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
import { rules } from '/@/utils/helper/validator';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '标题',
|
||||
width: 150,
|
||||
dataIndex: 'titile',
|
||||
},
|
||||
{
|
||||
title: '消息类型',
|
||||
dataIndex: 'msgCategory_dictText',
|
||||
width: 100,
|
||||
},
|
||||
|
||||
/*{
|
||||
title: '优先级',
|
||||
dataIndex: 'priority',
|
||||
width: 70,
|
||||
customRender: ({ text }) => {
|
||||
const color = text == 'L' ? 'blue' : text == 'M' ? 'yellow' : 'red';
|
||||
return render.renderTag(render.renderDict(text, 'priority'), color);
|
||||
},
|
||||
},*/
|
||||
{
|
||||
title: '通知对象',
|
||||
dataIndex: 'msgType_dictText',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
width: 100,
|
||||
dataIndex: 'createBy',
|
||||
},
|
||||
{
|
||||
title: '发布状态',
|
||||
dataIndex: 'sendStatus',
|
||||
width: 70,
|
||||
customRender: ({ text }) => {
|
||||
const color = text == '0' ? 'red' : text == '1' ? 'green' : 'orange';
|
||||
return render.renderTag(render.renderDict(text, 'send_status'), color);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '发布人',
|
||||
width: 100,
|
||||
dataIndex: 'sender',
|
||||
},
|
||||
{
|
||||
title: '发布时间',
|
||||
width: 100,
|
||||
dataIndex: 'sendTime',
|
||||
},
|
||||
{
|
||||
title: '发布进度',
|
||||
width: 100,
|
||||
dataIndex: 'msgAbstract',
|
||||
},
|
||||
/*{
|
||||
title: '撤销时间',
|
||||
width: 100,
|
||||
dataIndex: 'cancelTime',
|
||||
},*/
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'titile',
|
||||
label: '标题',
|
||||
component: 'JInput',
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
{
|
||||
field: 'sendStatus',
|
||||
label: '发布状态',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'send_status',
|
||||
},
|
||||
colProps: { span: 8 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'id',
|
||||
label: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'msgCategory',
|
||||
label: '消息类型',
|
||||
required: true,
|
||||
component: 'JDictSelectTag',
|
||||
defaultValue: '3',
|
||||
componentProps: {
|
||||
type: 'radio',
|
||||
dictCode: 'msg_category',
|
||||
placeholder: '请选择类型',
|
||||
},
|
||||
},
|
||||
|
||||
/*{
|
||||
field: 'msgAbstract',
|
||||
label: '摘要',
|
||||
component: 'InputTextArea',
|
||||
required: true,
|
||||
},*/
|
||||
// {
|
||||
// field: 'endTime',
|
||||
// label: '截至日期',
|
||||
// component: 'DatePicker',
|
||||
// componentProps: {
|
||||
// showTime: true,
|
||||
// valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
// placeholder: '请选择截至日期',
|
||||
// },
|
||||
// dynamicRules: ({ model }) => rules.endTime(model.startTime, true),
|
||||
// },
|
||||
{
|
||||
field: 'msgType',
|
||||
label: '通知对象',
|
||||
defaultValue: 'ALL',
|
||||
component: 'JDictSelectTag',
|
||||
required: true,
|
||||
componentProps: {
|
||||
type: 'radioButton',
|
||||
dictCode: 'msg_type',
|
||||
placeholder: '请选择发布范围',
|
||||
onChange: function (arg1) {
|
||||
console.log(arg1);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'userIds',
|
||||
label: '指定用户',
|
||||
component: 'JSelectUserByDept',
|
||||
required: true,
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
rowKey: 'id',
|
||||
labelKey: 'realname',
|
||||
// onChange: function (arg1) {
|
||||
// formModel.userIds = arg1.join(',');
|
||||
// }
|
||||
};
|
||||
},
|
||||
ifShow: ({ values }) => values.msgType == 'USER',
|
||||
},
|
||||
{
|
||||
field: 'docIds',
|
||||
label: '指定专家',
|
||||
component: 'JSelectDocByType',
|
||||
required: true,
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
rowKey: 'id',
|
||||
labelKey: 'doctorName',
|
||||
// onChange: function (arg1) {
|
||||
// formModel.userIds = arg1.join(',');
|
||||
// }
|
||||
};
|
||||
},
|
||||
ifShow: ({ values }) => values.msgType == 'DOC',
|
||||
},
|
||||
|
||||
/*{
|
||||
field: 'priority',
|
||||
label: '优先级',
|
||||
defaultValue: 'H',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'priority',
|
||||
type: 'radio',
|
||||
placeholder: '请选择优先级',
|
||||
},
|
||||
},*/
|
||||
{
|
||||
field: 'titile',
|
||||
label: '标题',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: '请输入标题',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'msgContent',
|
||||
label: '短信内容',
|
||||
component: 'InputTextArea',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: '请输入短信内容',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/online/list',
|
||||
forceLogout = '/sys/online/forceLogout',
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 批量删除角色
|
||||
* @param params
|
||||
*/
|
||||
export const forceLogout = (params) => {
|
||||
return defHttp.post({ url: Api.forceLogout, params }, { isTransformResponse: false });
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
|
||||
//列表
|
||||
export const columns = [
|
||||
{
|
||||
title: '用户账号',
|
||||
align: 'center',
|
||||
dataIndex: 'username',
|
||||
customRender: ({ text, record }) => {
|
||||
const token = getToken();
|
||||
if (record.token === token) {
|
||||
return text + '(我)';
|
||||
}
|
||||
return text;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
align: 'center',
|
||||
dataIndex: 'realname',
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
align: 'center',
|
||||
width: 120,
|
||||
dataIndex: 'avatar',
|
||||
customRender: render.renderAvatar,
|
||||
},
|
||||
{
|
||||
title: '生日',
|
||||
align: 'center',
|
||||
dataIndex: 'birthday',
|
||||
},
|
||||
{
|
||||
title: '性别',
|
||||
align: 'center',
|
||||
dataIndex: 'sex',
|
||||
customRender: ({ text }) => {
|
||||
return render.renderDict(text, 'sex');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
align: 'center',
|
||||
dataIndex: 'phone',
|
||||
},
|
||||
];
|
||||
|
||||
//查询区域
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户账号',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="online-user" setup>
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { columns, searchFormSchema } from './OnlineUser.data';
|
||||
import { list, forceLogout } from './OnlineUser.api';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, onImportXls, onExportXls } = useListPage({
|
||||
designScope: 'online-user',
|
||||
tableProps: {
|
||||
title: '在线用户',
|
||||
api: list,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
rowSelection: null,
|
||||
},
|
||||
});
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const $message = useMessage();
|
||||
|
||||
//操作栏
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '强退',
|
||||
popConfirm: {
|
||||
title: '强制退出用户?',
|
||||
confirm: handleForce.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 强退
|
||||
* @param record
|
||||
*/
|
||||
function handleForce(record) {
|
||||
forceLogout({ token: record.token }).then((res) => {
|
||||
if (res.success) {
|
||||
reload();
|
||||
$message.createMessage.success('强制退出用户”' + record.realname + '“成功!');
|
||||
} else {
|
||||
$message.createMessage.warn(res.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-upload
|
||||
name="file"
|
||||
:showUploadList="false"
|
||||
:action="ossAction"
|
||||
:headers="tokenHeader"
|
||||
:beforeUpload="beforeUpload"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-button preIcon="ant-design:upload-outlined" type="primary">OSS文件上传</a-button>
|
||||
</a-upload>
|
||||
<a-upload
|
||||
name="file"
|
||||
:showUploadList="false"
|
||||
:action="minioAction"
|
||||
:headers="tokenHeader"
|
||||
:beforeUpload="beforeUpload"
|
||||
@change="handleChange"
|
||||
>
|
||||
<a-button preIcon="ant-design:upload-outlined" type="primary">MINIO文件上传</a-button>
|
||||
</a-upload>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-ossfile" setup>
|
||||
//ts语法
|
||||
import { computed } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { columns, searchFormSchema } from './ossfile.data';
|
||||
import { list, deleteFile, getOssUrl, getMinioUrl } from './ossfile.api';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { getToken } from '/@/utils/auth';
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const glob = useGlobSetting();
|
||||
const tokenHeader = { 'X-Access-Token': getToken() };
|
||||
//注册table数据
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: list,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
formConfig: {
|
||||
labelWidth: 120,
|
||||
schemas: searchFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
},
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
clickToRowSelect: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
tableSetting: { fullScreen: true },
|
||||
beforeFetch: (params) => {
|
||||
return Object.assign({ column: 'createTime', order: 'desc' }, params);
|
||||
},
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
/**
|
||||
* 上传url
|
||||
*/
|
||||
const ossAction = computed(() => `${glob.uploadUrl}${getOssUrl}`);
|
||||
const minioAction = computed(() => `${glob.uploadUrl}${getMinioUrl}`);
|
||||
|
||||
/**
|
||||
* 预览
|
||||
*/
|
||||
function handleView(record) {
|
||||
if (record && record.url) {
|
||||
console.log('glob.onlineUrl', glob.viewUrl);
|
||||
let filePath = encodeURIComponent(record.url);
|
||||
//文档采用pdf预览高级模式
|
||||
if (filePath.endsWith('.pdf') || filePath.endsWith('.doc') || filePath.endsWith('.docx')) {
|
||||
filePath = filePath + '&officePreviewType=pdf';
|
||||
}
|
||||
let url = `${glob.viewUrl}?url=` + filePath;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteFile({ id: record.id }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传前事件
|
||||
*/
|
||||
function beforeUpload(file) {
|
||||
var fileType = file.type;
|
||||
if (fileType === 'image') {
|
||||
if (fileType.indexOf('image') < 0) {
|
||||
createMessage.warning('请上传图片');
|
||||
return false;
|
||||
}
|
||||
} else if (fileType === 'file') {
|
||||
if (fileType.indexOf('image') >= 0) {
|
||||
createMessage.warning('请上传文件');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传事件
|
||||
*/
|
||||
function handleChange(info) {
|
||||
if (info.file.status === 'done') {
|
||||
if (info.file.response.success) {
|
||||
reload();
|
||||
createMessage.success(`${info.file.name} 上传成功!`);
|
||||
} else {
|
||||
createMessage.error(`${info.file.response.message}`);
|
||||
}
|
||||
} else if (info.file.status === 'error') {
|
||||
createMessage.error(`${info.file.response.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '预览',
|
||||
onClick: handleView.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,33 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/oss/file/list',
|
||||
deleteFile = '/sys/oss/file/delete',
|
||||
ossUpload = '/sys/oss/file/upload',
|
||||
minioUpload = '/sys/upload/uploadMinio',
|
||||
}
|
||||
|
||||
/**
|
||||
* oss上传
|
||||
* @param params
|
||||
*/
|
||||
export const getOssUrl = Api.ossUpload;
|
||||
/**
|
||||
* minio上传
|
||||
* @param params
|
||||
*/
|
||||
export const getMinioUrl = Api.minioUpload;
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
export const deleteFile = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteFile, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user