Merge remote-tracking branch 'yudao/dev' into dev-new

# Conflicts:
#	apps/web-antd/src/api/infra/codegen/index.ts
#	apps/web-antd/src/views/infra/codegen/data.ts
This commit is contained in:
puhui999
2025-05-20 14:13:14 +08:00
96 changed files with 2168 additions and 1856 deletions

View File

@@ -300,4 +300,5 @@ export type OnActionClickParams<T = Recordable<any>> = {
export type OnActionClickFn<T = Recordable<any>> = (
params: OnActionClickParams<T>,
) => void;
export * from '#/components/table-action';
export type * from '@vben/plugins/vxe-table';

View File

@@ -47,6 +47,7 @@ export function deleteDemo01Contact(id: number) {
}
// 批量删除示例联系人
// TODO @puhui999注释风格哈。
export function deleteDemo01ContactByIds(ids: number[]) {
return requestClient.delete(
`/infra/demo01-contact/delete-batch?ids=${ids.join(',')}`,

View File

@@ -0,0 +1,161 @@
// TODO @芋艿:是否有更好的组织形式?!
<script lang="ts" setup>
import type { DataNode } from 'ant-design-vue/es/tree';
import type { SystemDeptApi } from '#/api/system/dept';
import { defineProps, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { handleTree } from '@vben/utils';
import { Button, Card, Col, Row, Tree } from 'ant-design-vue';
import { getSimpleDeptList } from '#/api/system/dept';
defineOptions({ name: 'DeptSelectModal' });
const props = withDefaults(
defineProps<{
// 取消按钮文本
cancelText?: string;
// checkable 状态下节点选择完全受控
checkStrictly?: boolean;
// 确认按钮文本
confirmText?: string;
// 是否支持多选
multiple?: boolean;
// 标题
title?: string;
}>(),
{
cancelText: '取消',
checkStrictly: false,
confirmText: '确认',
multiple: true,
title: '部门选择',
},
);
const emit = defineEmits<{
confirm: [deptList: SystemDeptApi.Dept[]];
}>();
// 对话框配置
const [Modal, modalApi] = useVbenModal({
title: props.title,
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
resetData();
return;
}
modalApi.setState({ loading: true });
try {
deptData.value = await getSimpleDeptList();
deptTree.value = handleTree(deptData.value) as DataNode[];
} finally {
modalApi.setState({ loading: false });
}
},
destroyOnClose: true,
});
type checkedKeys = number[] | { checked: number[]; halfChecked: number[] };
// 部门树形结构
const deptTree = ref<DataNode[]>([]);
// 选中的部门 ID 列表
const selectedDeptIds = ref<checkedKeys>([]);
// 部门数据
const deptData = ref<SystemDeptApi.Dept[]>([]);
/** 打开对话框 */
const open = async (selectedList?: SystemDeptApi.Dept[]) => {
modalApi.open();
// // 设置已选择的部门
if (selectedList?.length) {
const selectedIds = selectedList
.map((dept) => dept.id)
.filter((id): id is number => id !== undefined);
selectedDeptIds.value = props.checkStrictly
? {
checked: selectedIds,
halfChecked: [],
}
: selectedIds;
}
};
/** 处理选中状态变化 */
const handleCheck = () => {
if (!props.multiple) {
// 单选模式下,只保留最后选择的节点
if (Array.isArray(selectedDeptIds.value)) {
const lastSelectedId =
selectedDeptIds.value[selectedDeptIds.value.length - 1];
if (lastSelectedId) {
selectedDeptIds.value = [lastSelectedId];
}
} else {
// checkStrictly 为 true 时selectedDeptIds 是一个对象
const checked = selectedDeptIds.value.checked || [];
if (checked.length > 0) {
const lastSelectedId = checked[checked.length - 1];
selectedDeptIds.value = {
checked: [lastSelectedId!],
halfChecked: [],
};
}
}
}
};
/** 提交选择 */
const handleConfirm = async () => {
// 获取选中的部门ID
const selectedIds: number[] = Array.isArray(selectedDeptIds.value)
? selectedDeptIds.value
: selectedDeptIds.value.checked || [];
const deptArray = deptData.value.filter((dept) =>
selectedIds.includes(dept.id!),
);
// 关闭并提示
await modalApi.close();
emit('confirm', deptArray);
};
const handleCancel = () => {
modalApi.close();
};
/** 重置数据 */
const resetData = () => {
deptTree.value = [];
selectedDeptIds.value = [];
};
/** 提供 open 方法,用于打开对话框 */
defineExpose({ open });
</script>
<template>
<Modal>
<Row class="h-full">
<Col :span="24">
<Card class="h-full">
<Tree
:tree-data="deptTree"
v-if="deptTree.length > 0"
v-model:checked-keys="selectedDeptIds"
:checkable="true"
:check-strictly="checkStrictly"
:field-names="{ title: 'name', key: 'id' }"
:default-expand-all="true"
@check="handleCheck"
/>
</Card>
</Col>
</Row>
<template #footer>
<Button @click="handleCancel">{{ cancelText }}</Button>
<Button type="primary" @click="handleConfirm">{{ confirmText }}</Button>
</template>
</Modal>
</template>

View File

@@ -0,0 +1 @@
export { default as DeptSelectModal } from './dept-select-modal.vue';

View File

@@ -0,0 +1,12 @@
export const ACTION_ICON = {
DOWNLOAD: 'lucide:download',
UPLOAD: 'lucide:upload',
ADD: 'lucide:plus',
EDIT: 'lucide:edit',
DELETE: 'lucide:trash',
REFRESH: 'lucide:refresh-cw',
SEARCH: 'lucide:search',
FILTER: 'lucide:filter',
MORE: 'lucide:ellipsis-vertical',
VIEW: 'lucide:eye',
};

View File

@@ -1,2 +1,4 @@
export * from './icons';
export { default as TableAction } from './table-action.vue';
export * from './typing';

View File

@@ -1,7 +1,5 @@
<!-- add by 星语参考 vben2 的方式增加 TableAction 组件 -->
<script setup lang="ts">
import type { ButtonType } from 'ant-design-vue/es/button';
import type { PropType } from 'vue';
import type { ActionItem, PopConfirm } from './typing';
@@ -69,8 +67,7 @@ const getActions = computed(() => {
.map((action) => {
const { popConfirm } = action;
return {
// getPopupContainer: document.body,
type: 'link' as ButtonType,
type: action.type || 'link',
...action,
...popConfirm,
onConfirm: popConfirm?.confirm,
@@ -135,7 +132,7 @@ function handleMenuClick(e: any) {
</script>
<template>
<div class="m-table-action">
<div class="table-actions">
<Space
:size="
getActions?.some((item: ActionItem) => item.type === 'link') ? 0 : 8
@@ -184,10 +181,10 @@ function handleMenuClick(e: any) {
<Dropdown v-if="getDropdownList.length > 0" :trigger="['hover']">
<slot name="more">
<Button size="small" type="link">
<Button :type="getDropdownList[0].type">
<template #icon>
{{ $t('page.action.more') }}
<IconifyIcon class="icon-more" icon="ant-design:more-outlined" />
<IconifyIcon icon="lucide:ellipsis-vertical" />
</template>
</Button>
</slot>
@@ -230,8 +227,8 @@ function handleMenuClick(e: any) {
</div>
</template>
<style lang="scss">
.m-table-action {
.ant-btn {
.table-actions {
.ant-btn-link {
padding: 4px;
margin-left: 0;
}

View File

@@ -1,4 +1,7 @@
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
import type {
ButtonProps,
ButtonType,
} from 'ant-design-vue/es/button/buttonTypes';
import type { TooltipProps } from 'ant-design-vue/es/tooltip/Tooltip';
export interface PopConfirm {
@@ -13,6 +16,7 @@ export interface PopConfirm {
export interface ActionItem extends ButtonProps {
onClick?: () => void;
type?: ButtonType;
label?: string;
color?: 'error' | 'success' | 'warning';
icon?: string;

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import type { BpmCategoryApi } from '#/api/bpm/category';
import type { BpmProcessDefinitionApi } from '#/api/bpm/definition';
import type { BpmFormApi } from '#/api/bpm/form';
import type { SystemDeptApi } from '#/api/system/dept';
import type { SystemUserApi } from '#/api/system/user';
@@ -12,10 +13,11 @@ import { useTabs } from '@vben/hooks';
import { ArrowLeft } from '@vben/icons';
import { useUserStore } from '@vben/stores';
import { Button, message } from 'ant-design-vue';
import { Button, Card, message } from 'ant-design-vue';
import { getCategorySimpleList } from '#/api/bpm/category';
import { getProcessDefinition } from '#/api/bpm/definition';
import { getFormSimpleList } from '#/api/bpm/form';
import {
createModel,
deployModel,
@@ -26,10 +28,12 @@ import { getSimpleDeptList } from '#/api/system/dept';
import { getSimpleUserList } from '#/api/system/user';
import BasicInfo from './modules/basic-info.vue';
import FormDesign from './modules/form-design.vue';
defineOptions({ name: 'BpmModelCreate' });
// TODO 这个常量是不是所有 apps 都可以使用, 放 @utils/constant.ts 不能共享, @芋艿 这些常量放哪里合适!
// TODO @jason/Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/utils/constants.ts先不多个 apps 共享哈;
const BpmModelType = {
BPMN: 10, // BPMN 设计器
SIMPLE: 20, // 简易设计器
@@ -62,7 +66,9 @@ const route = useRoute();
const userStore = useUserStore();
// 基础信息组件引用
const basicInfoRef = ref();
const basicInfoRef = ref<InstanceType<typeof BasicInfo>>();
// 表单设计组件引用
const formDesignRef = ref<InstanceType<typeof FormDesign>>();
/** 步骤校验函数 */
const validateBasic = async () => {
@@ -71,7 +77,7 @@ const validateBasic = async () => {
/** 表单设计校验 */
const validateForm = async () => {
// TODO
await formDesignRef.value?.validate();
};
/** 流程设计校验 */
@@ -132,7 +138,7 @@ provide('processData', processData);
provide('modelData', formData);
// 数据列表
// const formList = ref([])
const formList = ref<BpmFormApi.FormVO[]>([]);
const categoryList = ref<BpmCategoryApi.CategoryVO[]>([]);
const userList = ref<SystemUserApi.User[]>([]);
const deptList = ref<SystemDeptApi.Dept[]>([]);
@@ -187,8 +193,8 @@ const initData = async () => {
formData.value.managerUserIds.push(userStore.userInfo?.userId);
}
// TODO 获取表单列表
// formList.value = await getFormSimpleList()
// 获取表单列表
formList.value = await getFormSimpleList();
categoryList.value = await getCategorySimpleList();
// 获取用户列表
userList.value = await getSimpleUserList();
@@ -393,9 +399,8 @@ onMounted(async () => {
/** 添加组件卸载前的清理 */
onBeforeUnmount(() => {
// 清理所有的引用
basicInfoRef.value = null;
// TODO 后续加
// formDesignRef.value = null;
basicInfoRef.value = undefined;
formDesignRef.value = undefined;
// processDesignRef.value = null;
});
</script>
@@ -467,27 +472,34 @@ onBeforeUnmount(() => {
</Button>
</div>
</div>
<!-- 主体内容 -->
<div class="mt-[50px]">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0" class="mx-auto w-[560px]">
<BasicInfo
v-model="formData"
:category-list="categoryList"
:user-list="userList"
:dept-list="deptList"
ref="basicInfoRef"
/>
<Card :body-style="{ padding: '10px' }" class="mb-4">
<div class="mt-[50px]">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0" class="mx-auto w-4/6">
<BasicInfo
v-model="formData"
:category-list="categoryList"
:user-list="userList"
:dept-list="deptList"
ref="basicInfoRef"
/>
</div>
<!-- 第二步表单设计 -->
<div v-show="currentStep === 1" class="mx-auto w-4/6">
<FormDesign
v-model="formData"
:form-list="formList"
ref="formDesignRef"
/>
</div>
<!-- 第三步流程设计 TODO -->
<!-- 第四步更多设置 TODO -->
<div v-show="currentStep === 3" class="mx-auto w-4/6"></div>
</div>
<!-- 第二步表单设计 TODO -->
<!-- 第三步流程设计 TODO -->
<!-- 第四步更多设置 TODO -->
<div v-show="currentStep === 3" class="mx-auto w-[700px]"></div>
</div>
</Card>
</div>
</Page>
</template>

View File

@@ -2,13 +2,15 @@
import type { Rule } from 'ant-design-vue/es/form';
import type { SelectValue } from 'ant-design-vue/es/select';
import type { PropType } from 'vue';
import type { BpmCategoryApi } from '#/api/bpm/category';
import type { SystemDeptApi } from '#/api/system/dept';
import type { SystemUserApi } from '#/api/system/user';
import { ref, watch } from 'vue';
import { IconifyIcon, Plus, ShieldQuestion, X } from '@vben/icons';
import { CircleHelp, IconifyIcon, Plus, X } from '@vben/icons';
import {
Avatar,
@@ -20,18 +22,25 @@ import {
Tooltip,
} from 'ant-design-vue';
import { DeptSelectModal } from '#/components/dept-select-modal';
import { ImageUpload } from '#/components/upload';
import { UserSelectModal } from '#/components/user-select-modal';
import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '#/utils';
const props = withDefaults(
defineProps<{
categoryList: BpmCategoryApi.CategoryVO[];
deptList: SystemDeptApi.Dept[];
userList: SystemUserApi.User[];
}>(),
{},
);
const props = defineProps({
categoryList: {
type: Array as PropType<BpmCategoryApi.CategoryVO[]>,
required: true,
},
userList: {
type: Array as PropType<SystemUserApi.User[]>,
required: true,
},
deptList: {
type: Array as PropType<SystemDeptApi.Dept[]>,
required: true,
},
});
// 表单引用
const formRef = ref();
@@ -45,6 +54,7 @@ const selectedStartDepts = ref<SystemDeptApi.Dept[]>([]);
// 选中的流程管理员
const selectedManagerUsers = ref<SystemUserApi.User[]>([]);
const userSelectFormRef = ref();
const deptSelectFormRef = ref();
const currentSelectType = ref<'manager' | 'start'>('start');
// 选中的用户
const selectedUsers = ref<number[]>();
@@ -55,6 +65,7 @@ const rules: Record<string, Rule[]> = {
category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }],
type: [{ required: true, message: '流程类型不能为空', trigger: 'blur' }],
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
// TODO 这个的校验好像没有起作用
managerUserIds: [
{ required: true, message: '流程管理员不能为空', trigger: 'blur' },
],
@@ -99,8 +110,14 @@ const openStartUserSelect = () => {
/** 打开部门选择 */
const openStartDeptSelect = () => {
// TODO 部门选择组件暂时还没有
console.warn('部门选择功能暂未实现');
deptSelectFormRef.value.open(selectedStartDepts.value);
};
/** 处理部门选择确认 */
const handleDeptSelectConfirm = (depts: SystemDeptApi.Dept[]) => {
modelData.value = {
...modelData.value,
startDeptIds: depts.map((d) => d.id),
};
};
/** 打开管理员选择 */
@@ -203,9 +220,7 @@ const validate = async () => {
await formRef.value?.validate();
};
defineExpose({
validate,
});
defineExpose({ validate });
</script>
<template>
@@ -213,8 +228,8 @@ defineExpose({
ref="formRef"
:model="modelData"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
class="mt-5"
>
<Form.Item label="流程标识" name="key" class="mb-5">
@@ -231,7 +246,7 @@ defineExpose({
"
placement="top"
>
<ShieldQuestion class="ml-1 text-gray-500" />
<CircleHelp class="ml-1 size-5 text-gray-900" />
</Tooltip>
</div>
</Form.Item>
@@ -343,7 +358,7 @@ defineExpose({
:key="dept.id"
class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2"
>
<IconifyIcon icon="mdi:building-outline" class="size-5" />
<IconifyIcon icon="ep:office-building" class="size-6 px-1" />
{{ dept.name }}
<X
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500"
@@ -408,6 +423,13 @@ defineExpose({
@closed="handleUserSelectClosed"
@cancel="handleUserSelectCancel"
/>
<!-- 部门选择对话框 -->
<DeptSelectModal
ref="deptSelectFormRef"
title="发起人部门选择"
:check-strictly="true"
@confirm="handleDeptSelectConfirm"
/>
</template>
<style lang="scss" scoped>

View File

@@ -0,0 +1,187 @@
<script lang="ts" setup>
import type { Rule } from 'ant-design-vue/es/form';
import type { BpmFormApi } from '#/api/bpm/form';
import { ref, watch } from 'vue';
import { CircleHelp } from '@vben/icons';
import FormCreate from '@form-create/ant-design-vue';
import {
Form,
FormItem,
Input,
Radio,
RadioGroup,
Select,
SelectOption,
Tooltip,
} from 'ant-design-vue';
import { getFormDetail } from '#/api/bpm/form';
import {
BpmModelFormType,
DICT_TYPE,
getDictOptions,
setConfAndFields2,
} from '#/utils';
const props = defineProps({
formList: {
type: Array<BpmFormApi.FormVO>,
required: true,
},
});
const formRef = ref();
// 创建本地数据副本
const modelData = defineModel<any>();
// 表单预览数据
const formPreview = ref({
formData: {} as any,
rule: [],
option: {
submitBtn: false,
resetBtn: false,
formData: {},
},
});
/** 监听表单ID变化加载表单数据 */
watch(
() => modelData.value.formId,
async (newFormId) => {
if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) {
const data = await getFormDetail(newFormId);
setConfAndFields2(formPreview.value, data.conf, data.fields);
// 设置只读
formPreview.value.rule.forEach((item: any) => {
item.props = { ...item.props, disabled: true };
});
} else {
formPreview.value.rule = [];
}
},
{ immediate: true },
);
const rules: Record<string, Rule[]> = {
formType: [{ required: true, message: '表单类型不能为空', trigger: 'blur' }],
formId: [{ required: true, message: '流程表单不能为空', trigger: 'blur' }],
formCustomCreatePath: [
{ required: true, message: '表单提交路由不能为空', trigger: 'blur' },
],
formCustomViewPath: [
{ required: true, message: '表单查看地址不能为空', trigger: 'blur' },
],
};
/** 表单校验 */
const validate = async () => {
await formRef.value?.validate();
};
defineExpose({ validate });
</script>
<template>
<Form
ref="formRef"
:model="modelData"
:rules="rules"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
class="mt-5"
>
<FormItem label="表单类型" name="formType" class="mb-5">
<RadioGroup v-model:value="modelData.formType">
<Radio
v-for="dict in getDictOptions(
DICT_TYPE.BPM_MODEL_FORM_TYPE,
'number',
)"
:key="dict.value as string"
:value="dict.value"
>
{{ dict.label }}
</Radio>
</RadioGroup>
</FormItem>
<FormItem
v-if="modelData.formType === BpmModelFormType.NORMAL"
label="流程表单"
name="formId"
class="mb-5"
>
<Select v-model:value="modelData.formId" clearable>
<SelectOption
v-for="form in props.formList"
:key="form.id"
:value="form.id"
>
{{ form.name }}
</SelectOption>
>
</Select>
</FormItem>
<FormItem
v-if="modelData.formType === BpmModelFormType.CUSTOM"
label="表单提交路由"
name="formCustomCreatePath"
class="mb-5"
>
<div class="flex items-center">
<Input
v-model:value="modelData.formCustomCreatePath"
placeholder="请输入表单提交路由"
/>
<Tooltip
title="自定义表单的提交路径,使用 Vue 的路由地址, 例如说: bpm/oa/leave/create.vue"
placement="top"
>
<CircleHelp class="ml-1 size-5 text-gray-900" />
</Tooltip>
</div>
</FormItem>
<FormItem
v-if="modelData.formType === BpmModelFormType.CUSTOM"
label="表单查看地址"
name="formCustomViewPath"
class="mb-5"
>
<div class="flex items-center">
<Input
v-model:value="modelData.formCustomViewPath"
placeholder="请输入表单查看的组件地址"
/>
<Tooltip
title="自定义表单的查看组件地址,使用 Vue 的组件地址例如说bpm/oa/leave/detail.vue"
placement="top"
>
<CircleHelp class="ml-1 size-5 text-gray-900" />
</Tooltip>
</div>
</FormItem>
<!-- 表单预览 -->
<div
v-if="
modelData.formType === BpmModelFormType.NORMAL &&
modelData.formId &&
formPreview.rule.length > 0
"
class="mb-5 mt-7 rounded-sm border border-solid border-gray-200 p-5"
>
<div class="mb-[15px] flex items-center">
<div class="mr-[10px] h-[15px] w-[4px] bg-[#1890ff]"></div>
<span class="text-[15px] font-bold">表单预览</span>
</div>
<FormCreate
v-model:api="formPreview.formData"
:rule="formPreview.rule"
:option="formPreview.option"
/>
</div>
</Form>
</template>

View File

@@ -162,6 +162,7 @@ const handleCategorySortSubmit = async () => {
<template>
<Page auto-content-height>
<!-- TODO @jaosn没头像的图标展示文字头像哈 -->
<!-- 流程分类表单弹窗 -->
<CategoryFormModal @success="getList" />
<Card

View File

@@ -151,6 +151,7 @@ export function useGridColumns(
minWidth: 200,
align: 'center',
fixed: 'right',
// TODO @puhui999headerAlign 要使用 headerAlign: 'center' 么?看着现在分成了 align 和 headerAlign 两种
headerAlign: 'center',
showOverflow: false,
cellRender: {

View File

@@ -61,6 +61,7 @@ async function onDelete(row: Demo01ContactApi.Demo01Contact) {
}
}
// TODO @puhui999:1/** 批量删除示例联系人 */ 是不是放在 deleteIds 上面2showDeleteBatchBtn 是不是直接 disabled 哪里判断哈;
const deleteIds = ref<number[]>([]); // 待删除示例联系人 ID
const showDeleteBatchBtn = computed(() => isEmpty(deleteIds.value));
function setDeleteIds({

View File

@@ -24,7 +24,7 @@ function onRefresh() {
}
/** 查询 IP */
function onQueryIp() {
function handleQueryIp() {
formModalApi.setData(null).open();
}
@@ -66,7 +66,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="地区列表">
<template #toolbar-tools>
<Button type="primary" @click="onQueryIp">
<Button type="primary" @click="handleQueryIp">
<Search class="size-5" />
IP 查询
</Button>

View File

@@ -1,8 +1,7 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemDeptApi } from '#/api/system/dept';
import { useAccess } from '@vben/access';
import { handleTree } from '@vben/utils';
import { z } from '#/adapter/form';
@@ -10,8 +9,6 @@ import { getDeptList } from '#/api/system/dept';
import { getSimpleUserList } from '#/api/system/user';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -113,7 +110,6 @@ export function useFormSchema(): VbenFormSchema[] {
/** 列表的字段 */
export function useGridColumns(
onActionClick?: OnActionClickFn<SystemDeptApi.Dept>,
getLeaderName?: (userId: number) => string | undefined,
): VxeTableGridOptions<SystemDeptApi.Dept>['columns'] {
return [
@@ -154,39 +150,10 @@ export function useGridColumns(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 200,
align: 'right',
width: 220,
fixed: 'right',
headerAlign: 'center',
showOverflow: false,
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '部门',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'append',
text: '新增下级',
show: hasAccessByCodes(['system:dept:create']),
},
{
code: 'edit',
show: hasAccessByCodes(['system:dept:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:dept:delete']),
disabled: (row: SystemDeptApi.Dept) => {
return !!(row.children && row.children.length > 0);
},
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,19 +1,15 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemDeptApi } from '#/api/system/dept';
import type { SystemUserApi } from '#/api/system/user';
import { onMounted, ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteDept, getDeptList } from '#/api/system/dept';
import { getSimpleUserList } from '#/api/system/user';
import { $t } from '#/locales';
@@ -29,9 +25,9 @@ const [FormModal, formModalApi] = useVbenModal({
const userList = ref<SystemUserApi.User[]>([]);
/** 获取负责人名称 */
const getLeaderName = (userId: number) => {
function getLeaderName(userId: number) {
return userList.value.find((user) => user.id === userId)?.nickname;
};
}
/** 刷新表格 */
function onRefresh() {
@@ -46,57 +42,41 @@ function toggleExpand() {
}
/** 创建部门 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 添加下级部门 */
function onAppend(row: SystemDeptApi.Dept) {
function handleAppend(row: SystemDeptApi.Dept) {
formModalApi.setData({ parentId: row.id }).open();
}
/** 编辑部门 */
function onEdit(row: SystemDeptApi.Dept) {
function handleEdit(row: SystemDeptApi.Dept) {
formModalApi.setData(row).open();
}
/** 删除部门 */
async function onDelete(row: SystemDeptApi.Dept) {
async function handleDelete(row: SystemDeptApi.Dept) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteDept(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({ code, row }: OnActionClickParams<SystemDeptApi.Dept>) {
switch (code) {
case 'append': {
onAppend(row);
break;
}
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useGridColumns(onActionClick, getLeaderName),
columns: useGridColumns(getLeaderName),
height: 'auto',
keepSource: true,
pagerConfig: {
@@ -136,17 +116,54 @@ onMounted(async () => {
<FormModal @success="onRefresh" />
<Grid table-title="部门列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:dept:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['部门']) }}
</Button>
<Button class="ml-2" @click="toggleExpand">
{{ isExpanded ? '收缩' : '展开' }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['菜单']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:dept:create'],
onClick: handleCreate,
},
{
label: isExpanded ? '收缩' : '展开',
type: 'primary',
onClick: toggleExpand,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '新增下级',
type: 'link',
icon: ACTION_ICON.ADD,
auth: ['system:menu:create'],
onClick: handleAppend.bind(null, row),
},
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:menu:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:menu:delete'],
disabled: !!(row.children && row.children.length > 0),
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,16 +1,10 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemDictDataApi } from '#/api/system/dict/data';
import type { SystemDictTypeApi } from '#/api/system/dict/type';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { getSimpleDictTypeList } from '#/api/system/dict/type';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
// ============================== 字典类型 ==============================
/** 类型新增/修改的表单 */
@@ -96,9 +90,7 @@ export function useTypeGridFormSchema(): VbenFormSchema[] {
}
/** 类型列表的字段 */
export function useTypeGridColumns<T = SystemDictTypeApi.DictType>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useTypeGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -136,29 +128,10 @@ export function useTypeGridColumns<T = SystemDictTypeApi.DictType>(
formatter: 'formatDateTime',
},
{
minWidth: 120,
title: '操作',
field: 'operation',
width: 160,
fixed: 'right',
align: 'center',
cellRender: {
attrs: {
nameField: 'type',
nameTitle: '字典类型',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:dict:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:dict:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}
@@ -310,9 +283,7 @@ export function useDataGridFormSchema(): VbenFormSchema[] {
/**
* 字典数据表格列
*/
export function useDataGridColumns<T = SystemDictDataApi.DictData>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useDataGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -360,29 +331,10 @@ export function useDataGridColumns<T = SystemDictDataApi.DictData>(
formatter: 'formatDateTime',
},
{
minWidth: 120,
title: '操作',
field: 'operation',
width: 160,
fixed: 'right',
align: 'center',
cellRender: {
attrs: {
nameField: 'label',
nameTitle: '字典数据',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:dict:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:dict:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -10,7 +10,7 @@ import TypeGrid from './modules/type-grid.vue';
const searchDictType = ref<string>(); // 搜索的字典类型
function onDictTypeSelect(dictType: string) {
function handleDictTypeSelect(dictType: string) {
searchDictType.value = dictType;
}
</script>
@@ -24,7 +24,7 @@ function onDictTypeSelect(dictType: string) {
<div class="flex h-full">
<!-- 左侧字典类型列表 -->
<div class="w-1/2 pr-3">
<TypeGrid @select="onDictTypeSelect" />
<TypeGrid @select="handleDictTypeSelect" />
</div>
<!-- 右侧字典数据列表 -->
<div class="w-1/2">

View File

@@ -1,19 +1,15 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemDictDataApi } from '#/api/system/dict/data';
import { watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteDictData,
exportDictData,
@@ -42,33 +38,32 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportDictData(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '字典数据.xls', source: data });
}
/** 创建字典数据 */
function onCreate() {
function handleCreate() {
dataFormModalApi.setData({ dictType: props.dictType }).open();
}
/** 编辑字典数据 */
function onEdit(row: any) {
function handleEdit(row: SystemDictDataApi.DictData) {
dataFormModalApi.setData(row).open();
}
/** 删除字典数据 */
async function onDelete(row: any) {
async function handleDelete(row: SystemDictDataApi.DictData) {
const hideLoading = message.loading({
content: $t('common.processing'),
duration: 0,
key: 'process_message',
content: $t('ui.actionMessage.deleting', [row.label]),
key: 'action_key_msg',
});
try {
await deleteDictData(row.id);
await deleteDictData(row.id as number);
message.success({
content: $t('common.operationSuccess'),
key: 'process_message',
content: $t('ui.actionMessage.deleteSuccess', [row.label]),
key: 'action_key_msg',
});
onRefresh();
} finally {
@@ -76,26 +71,12 @@ async function onDelete(row: any) {
}
}
/** 表格操作按钮回调 */
function onActionClick({ code, row }: OnActionClickParams) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useDataGridFormSchema(),
},
gridOptions: {
columns: useDataGridColumns(onActionClick),
columns: useDataGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -137,23 +118,48 @@ watch(
<Grid table-title="字典数据列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:dict:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['字典数据']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:dict:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['字典数据']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:dict:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:dict:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:dict:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:dict:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.label]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>

View File

@@ -1,18 +1,16 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeGridListeners,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { SystemDictTypeApi } from '#/api/system/dict/type';
import { useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteDictType,
exportDictType,
@@ -36,33 +34,32 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportDictType(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '字典类型.xls', source: data });
}
/** 创建字典类型 */
function onCreate() {
function handleCreate() {
typeFormModalApi.setData(null).open();
}
/** 编辑字典类型 */
function onEdit(row: any) {
function handleEdit(row: any) {
typeFormModalApi.setData(row).open();
}
/** 删除字典类型 */
async function onDelete(row: SystemDictTypeApi.DictType) {
async function handleDelete(row: SystemDictTypeApi.DictType) {
const hideLoading = message.loading({
content: $t('common.processing'),
duration: 0,
key: 'process_message',
content: $t('ui.actionMessage.deleting', [row.name]),
key: 'action_key_msg',
});
try {
await deleteDictType(row.id as number);
message.success({
content: $t('common.operationSuccess'),
key: 'process_message',
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} finally {
@@ -70,23 +67,6 @@ async function onDelete(row: SystemDictTypeApi.DictType) {
}
}
/** 表格操作按钮回调 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemDictTypeApi.DictType>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
/** 表格事件 */
const gridEvents: VxeGridListeners<SystemDictTypeApi.DictType> = {
cellClick: ({ row }) => {
@@ -99,7 +79,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: useTypeGridFormSchema(),
},
gridOptions: {
columns: useTypeGridColumns(onActionClick),
columns: useTypeGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -132,23 +112,48 @@ const [Grid, gridApi] = useVbenVxeGrid({
<Grid table-title="字典类型列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:dict:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['字典类型']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:dict:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['字典类型']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:dict:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:dict:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:dict:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:dict:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</div>

View File

@@ -1,13 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemLoginLogApi } from '#/api/system/login-log';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE, getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -42,9 +37,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemLoginLogApi.LoginLog>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -91,26 +84,10 @@ export function useGridColumns<T = SystemLoginLogApi.LoginLog>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 120,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'username',
nameTitle: '登录日志',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '详情',
show: hasAccessByCodes(['system:login-log:query']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,11 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemLoginLogApi } from '#/api/system/login-log';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { exportLoginLog, getLoginLogPage } from '#/api/system/login-log';
import { DocAlert } from '#/components/doc-alert';
import { $t } from '#/locales';
@@ -30,35 +24,22 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportLoginLog(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '登录日志.xls', source: data });
}
/** 查看登录日志详情 */
function onDetail(row: SystemLoginLogApi.LoginLog) {
function handleDetail(row: SystemLoginLogApi.LoginLog) {
detailModalApi.setData(row).open();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemLoginLogApi.LoginLog>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -92,15 +73,30 @@ const [Grid, gridApi] = useVbenVxeGrid({
<DetailModal @success="onRefresh" />
<Grid table-title="登录日志列表">
<template #toolbar-tools>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:login-log:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:login-log:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['system:login-log:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMailAccountApi } from '#/api/system/mail/account';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -125,9 +120,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemMailAccountApi.MailAccount>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -179,29 +172,10 @@ export function useGridColumns<T = SystemMailAccountApi.MailAccount>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 130,
align: 'center',
width: 130,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'mail',
nameTitle: '邮箱账号',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:mail-account:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:mail-account:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMailAccountApi } from '#/api/system/mail/account';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteMailAccount,
getMailAccountPage,
@@ -32,54 +28,39 @@ function onRefresh() {
}
/** 创建邮箱账号 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑邮箱账号 */
function onEdit(row: SystemMailAccountApi.MailAccount) {
function handleEdit(row: SystemMailAccountApi.MailAccount) {
formModalApi.setData(row).open();
}
/** 删除邮箱账号 */
async function onDelete(row: SystemMailAccountApi.MailAccount) {
async function handleDelete(row: SystemMailAccountApi.MailAccount) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.mail]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteMailAccount(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.mail]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.mail]),
key: 'action_key_msg',
});
onRefresh();
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemMailAccountApi.MailAccount>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -112,14 +93,41 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="邮箱账号列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:mail-account:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['邮箱账号']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['邮箱账号']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:mail-account:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:mail-account:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:mail-account:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMailLogApi } from '#/api/system/mail/log';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleMailAccountList } from '#/api/system/mail/account';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -75,9 +70,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemMailLogApi.MailLog>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -125,26 +118,10 @@ export function useGridColumns<T = SystemMailLogApi.MailLog>(
minWidth: 120,
},
{
field: 'operation',
title: '操作',
minWidth: 80,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'toMail',
nameTitle: '邮件日志',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '查看',
show: hasAccessByCodes(['system:mail-log:query']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,13 +1,10 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMailLogApi } from '#/api/system/mail/log';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getMailLogPage } from '#/api/system/mail/log';
import { DocAlert } from '#/components/doc-alert';
@@ -25,29 +22,16 @@ function onRefresh() {
}
/** 查看邮件日志 */
function onDetail(row: SystemMailLogApi.MailLog) {
function handleDetail(row: SystemMailLogApi.MailLog) {
detailModalApi.setData(row).open();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemMailLogApi.MailLog>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -79,7 +63,19 @@ const [Grid, gridApi] = useVbenVxeGrid({
<DetailModal @success="onRefresh" />
<Grid table-title="邮件日志列表">
<template #toolbar-tools> </template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['system:mail-log:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -1,8 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMailTemplateApi } from '#/api/system/mail/template';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { getSimpleMailAccountList } from '#/api/system/mail/account';
@@ -13,8 +10,6 @@ import {
getRangePickerDefaultProps,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -193,8 +188,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemMailTemplateApi.MailTemplate>(
onActionClick: OnActionClickFn<T>,
export function useGridColumns(
getAccountMail?: (accountId: number) => string | undefined,
): VxeTableGridOptions['columns'] {
return [
@@ -245,34 +239,10 @@ export function useGridColumns<T = SystemMailTemplateApi.MailTemplate>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 150,
align: 'center',
width: 220,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '邮件模板',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:mail-template:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:mail-template:delete']),
},
{
code: 'send',
text: '测试',
show: hasAccessByCodes(['system:mail-template:send-mail']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,19 +1,15 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMailAccountApi } from '#/api/system/mail/account';
import type { SystemMailTemplateApi } from '#/api/system/mail/template';
import { onMounted, ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getSimpleMailAccountList } from '#/api/system/mail/account';
import {
deleteMailTemplate,
@@ -28,11 +24,6 @@ import SendForm from './modules/send-form.vue';
const accountList = ref<SystemMailAccountApi.MailAccount[]>([]);
/** 获取邮箱账号 */
const getAccountMail = (accountId: number) => {
return accountList.value.find((account) => account.id === accountId)?.mail;
};
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
destroyOnClose: true,
@@ -49,55 +40,38 @@ function onRefresh() {
}
/** 创建邮件模板 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑邮件模板 */
function onEdit(row: SystemMailTemplateApi.MailTemplate) {
function handleEdit(row: SystemMailTemplateApi.MailTemplate) {
formModalApi.setData(row).open();
}
/** 发送测试邮件 */
function onSend(row: SystemMailTemplateApi.MailTemplate) {
function handleSend(row: SystemMailTemplateApi.MailTemplate) {
sendModalApi.setData(row).open();
}
/** 删除邮件模板 */
async function onDelete(row: SystemMailTemplateApi.MailTemplate) {
const hideLoading = message.loading({
async function handleDelete(row: SystemMailTemplateApi.MailTemplate) {
message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
});
try {
await deleteMailTemplate(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
onRefresh();
} finally {
hideLoading();
}
await deleteMailTemplate(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemMailTemplateApi.MailTemplate>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
case 'send': {
onSend(row);
break;
}
}
/** 获取邮箱账号 */
function getAccountMail(accountId: number) {
return accountList.value.find((account) => account.id === accountId)?.mail;
}
const [Grid, gridApi] = useVbenVxeGrid({
@@ -105,7 +79,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick, getAccountMail),
columns: useGridColumns(getAccountMail),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -144,14 +118,48 @@ onMounted(async () => {
<SendModal />
<Grid table-title="邮件模板列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:mail-template:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['邮件模板']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['邮件模板']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:mail-template:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:mail-template:update'],
onClick: handleEdit.bind(null, row),
},
{
label: '测试',
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:mail-template:send-mail'],
onClick: handleSend.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:mail-template:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
});
/** 动态构建表单 schema */
const buildFormSchema = () => {
function buildFormSchema() {
const schema = useSendMailFormSchema();
if (formData.value?.params) {
formData.value.params?.forEach((param: string) => {
@@ -99,7 +99,7 @@ const buildFormSchema = () => {
});
}
return schema;
};
}
</script>
<template>

View File

@@ -1,12 +1,11 @@
import type { Recordable } from '@vben/types';
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMenuApi } from '#/api/system/menu';
import { h } from 'vue';
import { useAccess } from '@vben/access';
import { IconifyIcon } from '@vben/icons';
import { handleTree, isHttpUrl } from '@vben/utils';
@@ -21,8 +20,6 @@ import {
SystemMenuTypeEnum,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -269,9 +266,7 @@ export function useFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns(
onActionClick: OnActionClickFn<SystemMenuApi.Menu>,
): VxeTableGridOptions<SystemMenuApi.Menu>['columns'] {
export function useGridColumns(): VxeTableGridOptions<SystemMenuApi.Menu>['columns'] {
return [
{
field: 'name',
@@ -321,34 +316,10 @@ export function useGridColumns(
},
},
{
field: 'operation',
title: '操作',
minWidth: 200,
width: 220,
fixed: 'right',
align: 'center',
showOverflow: false,
cellRender: {
attrs: {
nameField: 'name',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'append',
text: '新增下级',
show: hasAccessByCodes(['system:menu:create']),
},
{
code: 'edit',
show: hasAccessByCodes(['system:menu:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:menu:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,18 +1,15 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemMenuApi } from '#/api/system/menu';
import { ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { IconifyIcon, Plus } from '@vben/icons';
import { IconifyIcon } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteMenu, getMenuList } from '#/api/system/menu';
import { DocAlert } from '#/components/doc-alert';
import { $t } from '#/locales';
@@ -32,54 +29,38 @@ function onRefresh() {
}
/** 创建菜单 */
function onCreate() {
function handleCreate() {
formModalApi.setData({}).open();
}
/** 添加下级菜单 */
function onAppend(row: SystemMenuApi.Menu) {
function handleAppend(row: SystemMenuApi.Menu) {
formModalApi.setData({ pid: row.id }).open();
}
/** 编辑菜单 */
function onEdit(row: SystemMenuApi.Menu) {
function handleEdit(row: SystemMenuApi.Menu) {
formModalApi.setData(row).open();
}
/** 删除菜单 */
async function onDelete(row: SystemMenuApi.Menu) {
async function handleDelete(row: SystemMenuApi.Menu) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteMenu(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({ code, row }: OnActionClickParams<SystemMenuApi.Menu>) {
switch (code) {
case 'append': {
onAppend(row);
break;
}
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
/** 切换树形展开/收缩状态 */
const isExpanded = ref(false);
function toggleExpand() {
@@ -89,7 +70,7 @@ function toggleExpand() {
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
pagerConfig: {
@@ -131,17 +112,22 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid>
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:menu:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['菜单']) }}
</Button>
<Button class="ml-2" @click="toggleExpand">
{{ isExpanded ? '收缩' : '展开' }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['菜单']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:menu:create'],
onClick: handleCreate,
},
{
label: isExpanded ? '收缩' : '展开',
type: 'primary',
onClick: toggleExpand,
},
]"
/>
</template>
<template #name="{ row }">
<div class="flex w-full items-center gap-1">
@@ -161,6 +147,37 @@ const [Grid, gridApi] = useVbenVxeGrid({
<div class="items-center justify-end"></div>
</div>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '新增下级',
type: 'link',
icon: ACTION_ICON.ADD,
auth: ['system:menu:create'],
onClick: handleAppend.bind(null, row),
},
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:menu:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:menu:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNoticeApi } from '#/api/system/notice';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -91,9 +86,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemNoticeApi.Notice>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -130,34 +123,10 @@ export function useGridColumns<T = SystemNoticeApi.Notice>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 180,
align: 'center',
width: 220,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'title',
nameTitle: '公告',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:notice:update']),
},
{
code: 'push',
text: '推送',
show: hasAccessByCodes(['system:notice:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:notice:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNoticeApi } from '#/api/system/notice';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteNotice, getNoticePage, pushNotice } from '#/api/system/notice';
import { $t } from '#/locales';
@@ -28,73 +24,56 @@ function onRefresh() {
}
/** 创建公告 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑公告 */
function onEdit(row: SystemNoticeApi.Notice) {
function handleEdit(row: SystemNoticeApi.Notice) {
formModalApi.setData(row).open();
}
/** 删除公告 */
async function onDelete(row: SystemNoticeApi.Notice) {
async function handleDelete(row: SystemNoticeApi.Notice) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.title]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteNotice(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.title]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.title]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 推送公告 */
async function onPush(row: SystemNoticeApi.Notice) {
async function handlePush(row: SystemNoticeApi.Notice) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.processing', ['推送']),
duration: 0,
key: 'action_process_msg',
});
try {
await pushNotice(row.id as number);
message.success($t('ui.actionMessage.operationSuccess'));
} catch {
message.success({
content: $t('ui.actionMessage.operationSuccess'),
key: 'action_key_msg',
});
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemNoticeApi.Notice>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
case 'push': {
onPush(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -124,14 +103,48 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="公告列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:notice:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['公告']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['公告']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:notice:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:notice:update'],
onClick: handleEdit.bind(null, row),
},
{
label: '推送',
type: 'link',
icon: ACTION_ICON.ADD,
auth: ['system:notice:update'],
onClick: handlePush.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:notice:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,13 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNotifyMessageApi } from '#/api/system/notify/message';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -65,9 +60,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemNotifyMessageApi.NotifyMessage>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -146,26 +139,10 @@ export function useGridColumns<T = SystemNotifyMessageApi.NotifyMessage>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 180,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'id',
nameTitle: '站内信',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '详情',
show: hasAccessByCodes(['system:notify-message:query']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,13 +1,10 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNotifyMessageApi } from '#/api/system/notify/message';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getNotifyMessagePage } from '#/api/system/notify/message';
import { DocAlert } from '#/components/doc-alert';
@@ -25,29 +22,16 @@ function onRefresh() {
}
/** 查看站内信详情 */
function onDetail(row: SystemNotifyMessageApi.NotifyMessage) {
function handleDetail(row: SystemNotifyMessageApi.NotifyMessage) {
detailModalApi.setData(row).open();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemNotifyMessageApi.NotifyMessage>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -79,6 +63,20 @@ const [Grid, gridApi] = useVbenVxeGrid({
</template>
<DetailModal @success="onRefresh" />
<Grid table-title="站内信列表" />
<Grid table-title="站内信列表">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['system:notify-message:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -1,6 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNotifyMessageApi } from '#/api/system/notify/message';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue';
@@ -36,9 +35,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemNotifyMessageApi.NotifyMessage>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
title: '',
@@ -86,31 +83,10 @@ export function useGridColumns<T = SystemNotifyMessageApi.NotifyMessage>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 180,
align: 'center',
width: 130,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'id',
nameTitle: '站内信',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '查看',
show: (row: any) => row.readStatus,
},
{
code: 'read',
text: '已读',
show: (row: any) => !row.readStatus,
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNotifyMessageApi } from '#/api/system/notify/message';
import { Page, useVbenModal } from '@vben/common-ui';
import { MdiCheckboxMarkedCircleOutline } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
getMyNotifyMessagePage,
updateAllNotifyMessageRead,
@@ -32,12 +28,12 @@ function onRefresh() {
}
/** 查看站内信详情 */
function onDetail(row: SystemNotifyMessageApi.NotifyMessage) {
function handleDetail(row: SystemNotifyMessageApi.NotifyMessage) {
detailModalApi.setData(row).open();
}
/** 标记一条站内信已读 */
async function onRead(row: SystemNotifyMessageApi.NotifyMessage) {
async function handleRead(row: SystemNotifyMessageApi.NotifyMessage) {
message.loading({
content: '正在标记已读...',
duration: 0,
@@ -46,15 +42,18 @@ async function onRead(row: SystemNotifyMessageApi.NotifyMessage) {
// 执行标记已读操作
await updateNotifyMessageRead([row.id]);
// 提示成功
message.success('标记已读成功');
message.success({
content: '标记已读成功',
key: 'action_process_msg',
});
onRefresh();
// 打开详情
onDetail(row);
handleDetail(row);
}
/** 标记选中的站内信为已读 */
async function onMarkRead() {
async function handleMarkRead() {
const rows = gridApi.grid.getCheckboxRecords();
if (!rows || rows.length === 0) {
message.warning('请选择需要标记的站内信');
@@ -62,48 +61,43 @@ async function onMarkRead() {
}
const ids = rows.map((row: SystemNotifyMessageApi.NotifyMessage) => row.id);
message.loading({
const hideLoading = message.loading({
content: '正在标记已读...',
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
// 执行标记已读操作
await updateNotifyMessageRead(ids);
// 提示成功
message.success('标记已读成功');
await gridApi.grid.setAllCheckboxRow(false);
onRefresh();
try {
// 执行标记已读操作
await updateNotifyMessageRead(ids);
// 提示成功
message.success({
content: '标记已读成功',
key: 'action_key_msg',
});
await gridApi.grid.setAllCheckboxRow(false);
onRefresh();
} finally {
hideLoading();
}
}
/** 标记所有站内信为已读 */
async function onMarkAllRead() {
message.loading({
async function handleMarkAllRead() {
const hideLoading = message.loading({
content: '正在标记全部已读...',
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
// 执行标记已读操作
await updateAllNotifyMessageRead();
// 提示成功
message.success('全部标记已读成功');
await gridApi.grid.setAllCheckboxRow(false);
onRefresh();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemNotifyMessageApi.NotifyMessage>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
case 'read': {
onRead(row);
break;
}
try {
// 执行标记已读操作
await updateAllNotifyMessageRead();
// 提示成功
message.success({
content: '全部标记已读成功',
key: 'action_key_msg',
});
await gridApi.grid.setAllCheckboxRow(false);
onRefresh();
} finally {
hideLoading();
}
}
@@ -112,7 +106,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -150,14 +144,42 @@ const [Grid, gridApi] = useVbenVxeGrid({
<DetailModal @success="onRefresh" />
<Grid table-title="我的站内信">
<template #toolbar-tools>
<Button type="primary" @click="onMarkRead">
<MdiCheckboxMarkedCircleOutline />
标记已读
</Button>
<Button type="primary" class="ml-2" @click="onMarkAllRead">
<MdiCheckboxMarkedCircleOutline />
全部已读
</Button>
<TableAction
:actions="[
{
label: '标记已读',
type: 'primary',
icon: 'mdi:checkbox-marked-circle-outline',
onClick: handleMarkRead,
},
{
label: '全部已读',
type: 'primary',
icon: 'mdi:checkbox-marked-circle-outline',
onClick: handleMarkAllRead,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '查看',
type: 'link',
ifShow: row.readStatus,
icon: ACTION_ICON.VIEW,
onClick: handleDetail.bind(null, row),
},
{
label: '已读',
type: 'link',
ifShow: !row.readStatus,
icon: ACTION_ICON.DELETE,
onClick: handleRead.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,8 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNotifyTemplateApi } from '#/api/system/notify/template';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { getSimpleUserList } from '#/api/system/user';
@@ -14,8 +11,6 @@ import {
UserTypeEnum,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -229,9 +224,7 @@ export function useSendNotifyFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemNotifyTemplateApi.NotifyTemplate>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -288,34 +281,10 @@ export function useGridColumns<T = SystemNotifyTemplateApi.NotifyTemplate>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 180,
align: 'center',
width: 220,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '站内信模板',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:notify-template:update']),
},
{
code: 'send',
text: '测试',
show: hasAccessByCodes(['system:notify-template:send-notify']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:notify-template:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,13 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemNotifyTemplateApi } from '#/api/system/notify/template';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteNotifyTemplate,
exportNotifyTemplate,
@@ -40,69 +36,50 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportNotifyTemplate(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '站内信模板.xls', source: data });
}
/** 创建站内信模板 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑站内信模板 */
function onEdit(row: SystemNotifyTemplateApi.NotifyTemplate) {
function handleEdit(row: SystemNotifyTemplateApi.NotifyTemplate) {
formModalApi.setData(row).open();
}
/** 发送测试站内信 */
function onSend(row: SystemNotifyTemplateApi.NotifyTemplate) {
function handleSend(row: SystemNotifyTemplateApi.NotifyTemplate) {
sendModalApi.setData(row).open();
}
/** 删除站内信模板 */
async function onDelete(row: SystemNotifyTemplateApi.NotifyTemplate) {
async function handleDelete(row: SystemNotifyTemplateApi.NotifyTemplate) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteNotifyTemplate(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemNotifyTemplateApi.NotifyTemplate>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
case 'send': {
onSend(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -137,23 +114,55 @@ const [Grid, gridApi] = useVbenVxeGrid({
<SendModal />
<Grid table-title="站内信模板列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:notify-template:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['站内信模板']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:notify-template:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['短信渠道']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:notify-template:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:notify-template:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:notify-template:update'],
onClick: handleEdit.bind(null, row),
},
{
label: '测试',
type: 'link',
icon: ACTION_ICON.ADD,
auth: ['system:notify-template:send-notify'],
onClick: handleSend.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:notify-template:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -86,7 +86,7 @@ const [Modal, modalApi] = useVbenModal({
});
/** 动态构建表单 schema */
const buildFormSchema = () => {
function buildFormSchema() {
const schema = useSendNotifyFormSchema();
if (formData.value?.params) {
formData.value.params.forEach((param: string) => {
@@ -102,7 +102,7 @@ const buildFormSchema = () => {
});
}
return schema;
};
}
</script>
<template>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemOAuth2ClientApi } from '#/api/system/oauth2/client';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -191,9 +186,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemOAuth2ClientApi.OAuth2Client>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'clientId',
@@ -251,29 +244,10 @@ export function useGridColumns<T = SystemOAuth2ClientApi.OAuth2Client>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 130,
align: 'center',
width: 130,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: 'OAuth2 客户端',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:oauth2-client:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:oauth2-client:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemOAuth2ClientApi } from '#/api/system/oauth2/client';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteOAuth2Client,
getOAuth2ClientPage,
@@ -32,54 +28,39 @@ function onRefresh() {
}
/** 创建 OAuth2 客户端 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑 OAuth2 客户端 */
function onEdit(row: SystemOAuth2ClientApi.OAuth2Client) {
function handleEdit(row: SystemOAuth2ClientApi.OAuth2Client) {
formModalApi.setData(row).open();
}
/** 删除 OAuth2 客户端 */
async function onDelete(row: SystemOAuth2ClientApi.OAuth2Client) {
async function handleDelete(row: SystemOAuth2ClientApi.OAuth2Client) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteOAuth2Client(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemOAuth2ClientApi.OAuth2Client>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -116,14 +97,41 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="OAuth2 客户端列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:oauth2-client:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', [' OAuth2.0 客户端']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', [' OAuth2.0 客户端']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:oauth2-client:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:oauth2-client:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:oauth2-client:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,13 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemOAuth2TokenApi } from '#/api/system/oauth2/token';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -40,9 +35,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemOAuth2TokenApi.OAuth2Token>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'accessToken',
@@ -86,26 +79,10 @@ export function useGridColumns<T = SystemOAuth2TokenApi.OAuth2Token>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 100,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'accessToken',
nameTitle: 'OAuth2 令牌',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'delete',
text: '强退',
show: hasAccessByCodes(['system:oauth2-token:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,15 +1,12 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemOAuth2TokenApi } from '#/api/system/oauth2/token';
import { Page } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteOAuth2Token,
getOAuth2TokenPage,
@@ -25,40 +22,29 @@ function onRefresh() {
}
/** 删除 OAuth2 令牌 */
async function onDelete(row: SystemOAuth2TokenApi.OAuth2Token) {
async function handleDelete(row: SystemOAuth2TokenApi.OAuth2Token) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', ['令牌']),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteOAuth2Token(row.accessToken);
message.success($t('ui.actionMessage.operationSuccess'));
message.success({
content: $t('ui.actionMessage.deleteSuccess', ['令牌']),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemOAuth2TokenApi.OAuth2Token>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -92,6 +78,24 @@ const [Grid, gridApi] = useVbenVxeGrid({
/>
</template>
<Grid table-title="令牌列表" />
<Grid table-title="令牌列表">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '强退',
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:oauth2-token:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemOperateLogApi } from '#/api/system/operate-log';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -75,9 +70,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemOperateLogApi.OperateLog>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -121,26 +114,10 @@ export function useGridColumns<T = SystemOperateLogApi.OperateLog>(
minWidth: 120,
},
{
field: 'operation',
title: '操作',
minWidth: 120,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'action',
nameTitle: '操作日志',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '详情',
show: hasAccessByCodes(['system:operate-log:query']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,11 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemOperateLogApi } from '#/api/system/operate-log';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { exportOperateLog, getOperateLogPage } from '#/api/system/operate-log';
import { DocAlert } from '#/components/doc-alert';
import { $t } from '#/locales';
@@ -30,35 +24,22 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportOperateLog(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '操作日志.xls', source: data });
}
/** 查看操作日志详情 */
function onDetail(row: SystemOperateLogApi.OperateLog) {
function handleDetail(row: SystemOperateLogApi.OperateLog) {
detailModalApi.setData(row).open();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemOperateLogApi.OperateLog>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -92,15 +73,30 @@ const [Grid, gridApi] = useVbenVxeGrid({
<DetailModal @success="onRefresh" />
<Grid table-title="操作日志列表">
<template #toolbar-tools>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:operate-log:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:operate-log:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['system:operate-log:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemPostApi } from '#/api/system/post';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -86,9 +81,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemPostApi.Post>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -131,29 +124,10 @@ export function useGridColumns<T = SystemPostApi.Post>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 130,
align: 'center',
width: 130,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '岗位',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:post:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:post:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,13 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemPostApi } from '#/api/system/post';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deletePost, exportPost, getPostPage } from '#/api/system/post';
import { $t } from '#/locales';
@@ -29,57 +25,45 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportPost(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '岗位.xls', source: data });
}
/** 创建岗位 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑岗位 */
function onEdit(row: SystemPostApi.Post) {
function handleEdit(row: SystemPostApi.Post) {
formModalApi.setData(row).open();
}
/** 删除岗位 */
async function onDelete(row: SystemPostApi.Post) {
async function handleDelete(row: SystemPostApi.Post) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deletePost(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({ code, row }: OnActionClickParams<SystemPostApi.Post>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -109,23 +93,48 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="岗位列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:post:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['岗位']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:post:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['短信渠道']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:post:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:post:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:post:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:post:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -3,15 +3,13 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemRoleApi } from '#/api/system/role';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteRole, exportRole, getRolePage } from '#/api/system/role';
import { DocAlert } from '#/components/doc-alert';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
@@ -41,45 +39,46 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportRole(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '角色.xls', source: data });
}
/** 编辑角色 */
function onEdit(row: SystemRoleApi.Role) {
function handleEdit(row: SystemRoleApi.Role) {
formModalApi.setData(row).open();
}
/** 创建角色 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 删除角色 */
async function onDelete(row: SystemRoleApi.Role) {
async function handleDelete(row: SystemRoleApi.Role) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteRole(row.id as number);
hideLoading();
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 分配角色的数据权限 */
function onAssignDataPermission(row: SystemRoleApi.Role) {
function handleAssignDataPermission(row: SystemRoleApi.Role) {
assignDataPermissionFormApi.setData(row).open();
}
/** 分配角色的菜单权限 */
function onAssignMenu(row: SystemRoleApi.Role) {
function handleAssignMenu(row: SystemRoleApi.Role) {
assignMenuFormApi.setData(row).open();
}
@@ -128,23 +127,24 @@ const [Grid, gridApi] = useVbenVxeGrid({
<AssignMenuFormModel @success="onRefresh" />
<Grid table-title="角色列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:role:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['角色']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:role:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['角色']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:role:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:role:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
@@ -152,19 +152,19 @@ const [Grid, gridApi] = useVbenVxeGrid({
{
label: $t('common.edit'),
type: 'link',
icon: 'ant-design:edit-outlined',
icon: ACTION_ICON.EDIT,
auth: ['system:role:update'],
onClick: onEdit.bind(null, row),
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: 'ant-design:delete-outlined',
icon: ACTION_ICON.DELETE,
auth: ['system:role:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: onDelete.bind(null, row),
confirm: handleDelete.bind(null, row),
},
},
]"
@@ -173,13 +173,13 @@ const [Grid, gridApi] = useVbenVxeGrid({
label: '数据权限',
type: 'link',
auth: ['system:permission:assign-role-data-scope'],
onClick: onAssignDataPermission.bind(null, row),
onClick: handleAssignDataPermission.bind(null, row),
},
{
label: '菜单权限',
type: 'link',
auth: ['system:permission:assign-role-menu'],
onClick: onAssignMenu.bind(null, row),
onClick: handleAssignMenu.bind(null, row),
},
]"
/>

View File

@@ -1,8 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSmsChannelApi } from '#/api/system/sms/channel';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import {
@@ -12,8 +9,6 @@ import {
getRangePickerDefaultProps,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -135,9 +130,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemSmsChannelApi.SmsChannel>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -194,29 +187,10 @@ export function useGridColumns<T = SystemSmsChannelApi.SmsChannel>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 180,
align: 'center',
width: 130,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'signature',
nameTitle: '短信渠道',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:sms-channel:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:sms-channel:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,13 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSmsChannelApi } from '#/api/system/sms/channel';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteSmsChannel,
exportSmsChannel,
@@ -34,60 +30,45 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportSmsChannel(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '短信渠道.xls', source: data });
}
/** 创建短信渠道 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑短信渠道 */
function onEdit(row: SystemSmsChannelApi.SmsChannel) {
function handleEdit(row: SystemSmsChannelApi.SmsChannel) {
formModalApi.setData(row).open();
}
/** 删除短信渠道 */
async function onDelete(row: SystemSmsChannelApi.SmsChannel) {
async function handleDelete(row: SystemSmsChannelApi.SmsChannel) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.signature]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteSmsChannel(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.signature]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.signature]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemSmsChannelApi.SmsChannel>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -121,23 +102,48 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="短信渠道列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:sms-channel:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['短信渠道']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:sms-channel:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['短信渠道']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:sms-channel:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:sms-channel:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:sms-channel:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:sms-channel:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,14 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSmsLogApi } from '#/api/system/sms/log';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getSimpleSmsChannelList } from '#/api/system/sms/channel';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -84,9 +79,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemSmsLogApi.SmsLog>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -163,26 +156,10 @@ export function useGridColumns<T = SystemSmsLogApi.SmsLog>(
},
},
{
field: 'operation',
title: '操作',
minWidth: 120,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'mobile',
nameTitle: '短信日志',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '详情',
show: hasAccessByCodes(['system:sms-log:query']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,11 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSmsLogApi } from '#/api/system/sms/log';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { exportSmsLog, getSmsLogPage } from '#/api/system/sms/log';
import { DocAlert } from '#/components/doc-alert';
import { $t } from '#/locales';
@@ -30,35 +24,22 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportSmsLog(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '短信日志.xls', source: data });
}
/** 查看短信日志详情 */
function onDetail(row: SystemSmsLogApi.SmsLog) {
function handleDetail(row: SystemSmsLogApi.SmsLog) {
detailModalApi.setData(row).open();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemSmsLogApi.SmsLog>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -92,15 +73,30 @@ const [Grid, gridApi] = useVbenVxeGrid({
<DetailModal @success="onRefresh" />
<Grid table-title="短信日志列表">
<template #toolbar-tools>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:sms-log:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:sms-log:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['system:sms-log:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,8 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSmsTemplateApi } from '#/api/system/sms/template';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import { getSimpleSmsChannelList } from '#/api/system/sms/channel';
@@ -13,8 +10,6 @@ import {
getRangePickerDefaultProps,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -204,9 +199,7 @@ export function useSendSmsFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemSmsTemplateApi.SmsTemplate>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -272,34 +265,10 @@ export function useGridColumns<T = SystemSmsTemplateApi.SmsTemplate>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 180,
align: 'center',
width: 220,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '短信模板',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:sms-template:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:sms-template:delete']),
},
{
code: 'sms-send',
text: '发送短信',
show: hasAccessByCodes(['system:sms-template:send-sms']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,17 +1,13 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSmsTemplateApi } from '#/api/system/sms/template';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteSmsTemplate,
exportSmsTemplate,
@@ -40,69 +36,50 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportSmsTemplate(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '短信模板.xls', source: data });
}
/** 创建短信模板 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑短信模板 */
function onEdit(row: SystemSmsTemplateApi.SmsTemplate) {
function handleEdit(row: SystemSmsTemplateApi.SmsTemplate) {
formModalApi.setData(row).open();
}
/** 发送测试短信 */
function onSend(row: SystemSmsTemplateApi.SmsTemplate) {
function handleSend(row: SystemSmsTemplateApi.SmsTemplate) {
sendModalApi.setData(row).open();
}
/** 删除短信模板 */
async function onDelete(row: SystemSmsTemplateApi.SmsTemplate) {
async function handleDelete(row: SystemSmsTemplateApi.SmsTemplate) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteSmsTemplate(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemSmsTemplateApi.SmsTemplate>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
case 'sms-send': {
onSend(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -137,23 +114,55 @@ const [Grid, gridApi] = useVbenVxeGrid({
<SendModal />
<Grid table-title="短信模板列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:sms-template:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['短信模板']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:sms-template:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['短信模板']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:sms-template:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:sms-template:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:sms-template:update'],
onClick: handleEdit.bind(null, row),
},
{
label: '发送短信',
type: 'link',
icon: ACTION_ICON.ADD,
auth: ['system:sms-template:send-sms'],
onClick: handleSend.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:sms-template:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,8 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSocialClientApi } from '#/api/system/social/client';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { z } from '#/adapter/form';
import {
@@ -12,8 +9,6 @@ import {
SystemUserSocialTypeEnum,
} from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] {
return [
@@ -152,9 +147,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemSocialClientApi.SocialClient>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
@@ -205,29 +198,10 @@ export function useGridColumns<T = SystemSocialClientApi.SocialClient>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 130,
align: 'center',
width: 130,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'name',
nameTitle: '社交客户端',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'edit',
show: hasAccessByCodes(['system:social-client:update']),
},
{
code: 'delete',
show: hasAccessByCodes(['system:social-client:delete']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,16 +1,12 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSocialClientApi } from '#/api/system/social/client';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteSocialClient,
getSocialClientPage,
@@ -32,54 +28,39 @@ function onRefresh() {
}
/** 创建社交客户端 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑社交客户端 */
function onEdit(row: SystemSocialClientApi.SocialClient) {
function handleEdit(row: SystemSocialClientApi.SocialClient) {
formModalApi.setData(row).open();
}
/** 删除社交客户端 */
async function onDelete(row: SystemSocialClientApi.SocialClient) {
async function handleDelete(row: SystemSocialClientApi.SocialClient) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteSocialClient(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemSocialClientApi.SocialClient>) {
switch (code) {
case 'delete': {
onDelete(row);
break;
}
case 'edit': {
onEdit(row);
break;
}
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -113,14 +94,41 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="社交客户端列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:social-client:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['社交客户端']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['社交客户端']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:social-client:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
icon: ACTION_ICON.EDIT,
auth: ['system:social-client:update'],
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: ACTION_ICON.DELETE,
auth: ['system:social-client:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},
},
]"
/>
</template>
</Grid>
</Page>

View File

@@ -1,13 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSocialUserApi } from '#/api/system/social/user';
import { useAccess } from '@vben/access';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
const { hasAccessByCodes } = useAccess();
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -52,9 +47,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
}
/** 列表的字段 */
export function useGridColumns<T = SystemSocialUserApi.SocialUser>(
onActionClick: OnActionClickFn<T>,
): VxeTableGridOptions['columns'] {
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'type',
@@ -96,26 +89,10 @@ export function useGridColumns<T = SystemSocialUserApi.SocialUser>(
formatter: 'formatDateTime',
},
{
field: 'operation',
title: '操作',
minWidth: 100,
align: 'center',
width: 80,
fixed: 'right',
cellRender: {
attrs: {
nameField: 'nickname',
nameTitle: '社交用户',
onClick: onActionClick,
},
name: 'CellOperation',
options: [
{
code: 'detail',
text: '详情',
show: hasAccessByCodes(['system:social-user:query']),
},
],
},
slots: { default: 'actions' },
},
];
}

View File

@@ -1,13 +1,10 @@
<script lang="ts" setup>
import type {
OnActionClickParams,
VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemSocialUserApi } from '#/api/system/social/user';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getSocialUserPage } from '#/api/system/social/user';
import { DocAlert } from '#/components/doc-alert';
@@ -19,35 +16,17 @@ const [DetailModal, detailModalApi] = useVbenModal({
destroyOnClose: true,
});
/** 刷新表格 */
// function onRefresh() {
// gridApi.query();
// }
/** 查看详情 */
function onDetail(row: SystemSocialUserApi.SocialUser) {
function handleDetail(row: SystemSocialUserApi.SocialUser) {
detailModalApi.setData(row).open();
}
/** 表格操作按钮的回调函数 */
function onActionClick({
code,
row,
}: OnActionClickParams<SystemSocialUserApi.SocialUser>) {
switch (code) {
case 'detail': {
onDetail(row);
break;
}
}
}
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onActionClick),
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -79,6 +58,20 @@ const [Grid] = useVbenVxeGrid({
</template>
<DetailModal />
<Grid table-title="社交用户列表" />
<Grid table-title="社交用户列表">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'link',
icon: ACTION_ICON.VIEW,
auth: ['system:social-user:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -6,12 +6,11 @@ import type { SystemTenantPackageApi } from '#/api/system/tenant-package';
import { onMounted, ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteTenant, exportTenant, getTenantPage } from '#/api/system/tenant';
import { getTenantPackageList } from '#/api/system/tenant-package';
import { DocAlert } from '#/components/doc-alert';
@@ -41,33 +40,35 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportTenant(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '租户.xls', source: data });
}
/** 创建租户 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑租户 */
function onEdit(row: SystemTenantApi.Tenant) {
function handleEdit(row: SystemTenantApi.Tenant) {
formModalApi.setData(row).open();
}
/** 删除租户 */
async function onDelete(row: SystemTenantApi.Tenant) {
async function handleDelete(row: SystemTenantApi.Tenant) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteTenant(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
@@ -78,8 +79,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
gridOptions: {
columns: useGridColumns(getPackageName),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
@@ -115,23 +114,24 @@ onMounted(async () => {
<FormModal @success="onRefresh" />
<Grid table-title="租户列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:tenant:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['租户']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:tenant:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['租户']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:tenant:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:tenant:export'],
onClick: handleExport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
@@ -139,19 +139,19 @@ onMounted(async () => {
{
label: $t('common.edit'),
type: 'link',
icon: 'ant-design:edit-outlined',
icon: ACTION_ICON.EDIT,
auth: ['system:role:update'],
onClick: onEdit.bind(null, row),
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: 'ant-design:delete-outlined',
icon: ACTION_ICON.DELETE,
auth: ['system:role:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: onDelete.bind(null, row),
confirm: handleDelete.bind(null, row),
},
},
]"

View File

@@ -3,11 +3,10 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { SystemTenantPackageApi } from '#/api/system/tenant-package';
import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteTenantPackage,
getTenantPackagePage,
@@ -29,27 +28,29 @@ function onRefresh() {
}
/** 创建租户套餐 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑租户套餐 */
function onEdit(row: SystemTenantPackageApi.TenantPackage) {
function handleEdit(row: SystemTenantPackageApi.TenantPackage) {
formModalApi.setData(row).open();
}
/** 删除租户套餐 */
async function onDelete(row: SystemTenantPackageApi.TenantPackage) {
async function handleDelete(row: SystemTenantPackageApi.TenantPackage) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteTenantPackage(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
@@ -94,14 +95,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
<FormModal @success="onRefresh" />
<Grid table-title="租户套餐列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:tenant-package:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['套餐']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['套餐']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:tenant-package:create'],
onClick: handleCreate,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
@@ -109,19 +113,19 @@ const [Grid, gridApi] = useVbenVxeGrid({
{
label: $t('common.edit'),
type: 'link',
icon: 'ant-design:edit-outlined',
icon: ACTION_ICON.EDIT,
auth: ['system:role:update'],
onClick: onEdit.bind(null, row),
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: 'ant-design:delete-outlined',
icon: ACTION_ICON.DELETE,
auth: ['system:role:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: onDelete.bind(null, row),
confirm: handleDelete.bind(null, row),
},
},
]"

View File

@@ -312,7 +312,7 @@ export function useGridColumns<T = SystemUserApi.User>(
},
{
title: '操作',
width: 160,
width: 180,
fixed: 'right',
slots: { default: 'actions' },
},

View File

@@ -6,12 +6,11 @@ import type { SystemUserApi } from '#/api/system/user';
import { ref } from 'vue';
import { confirm, Page, useVbenModal } from '@vben/common-ui';
import { Download, Plus, Upload } from '@vben/icons';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button, message } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
deleteUser,
exportUser,
@@ -19,7 +18,6 @@ import {
updateUserStatus,
} from '#/api/system/user';
import { DocAlert } from '#/components/doc-alert';
import { TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import { DICT_TYPE, getDictLabel } from '#/utils';
@@ -56,61 +54,64 @@ function onRefresh() {
}
/** 导出表格 */
async function onExport() {
async function handleExport() {
const data = await exportUser(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '用户.xls', source: data });
}
/** 选择部门 */
const searchDeptId = ref<number | undefined>(undefined);
async function onDeptSelect(dept: SystemDeptApi.Dept) {
async function handleDeptSelect(dept: SystemDeptApi.Dept) {
searchDeptId.value = dept.id;
onRefresh();
}
/** 创建用户 */
function onCreate() {
function handleCreate() {
formModalApi.setData(null).open();
}
/** 导入用户 */
function onImport() {
function handleImport() {
importModalApi.open();
}
/** 编辑用户 */
function onEdit(row: SystemUserApi.User) {
function handleEdit(row: SystemUserApi.User) {
formModalApi.setData(row).open();
}
/** 删除用户 */
async function onDelete(row: SystemUserApi.User) {
async function handleDelete(row: SystemUserApi.User) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.username]),
duration: 0,
key: 'action_process_msg',
key: 'action_key_msg',
});
try {
await deleteUser(row.id as number);
message.success($t('ui.actionMessage.deleteSuccess', [row.username]));
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.username]),
key: 'action_key_msg',
});
onRefresh();
} catch {
} finally {
hideLoading();
}
}
/** 重置密码 */
function onResetPassword(row: SystemUserApi.User) {
function handleResetPassword(row: SystemUserApi.User) {
resetPasswordModalApi.setData(row).open();
}
/** 分配角色 */
function onAssignRole(row: SystemUserApi.User) {
function handleAssignRole(row: SystemUserApi.User) {
assignRoleModalApi.setData(row).open();
}
/** 更新用户状态 */
async function onStatusChange(
async function handleStatusChange(
newStatus: number,
row: SystemUserApi.User,
): Promise<boolean | undefined> {
@@ -140,7 +141,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(onStatusChange),
columns: useGridColumns(handleStatusChange),
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -185,38 +186,37 @@ const [Grid, gridApi] = useVbenVxeGrid({
<div class="flex h-full w-full">
<!-- 左侧部门树 -->
<div class="h-full w-1/6 pr-4">
<DeptTree @select="onDeptSelect" />
<DeptTree @select="handleDeptSelect" />
</div>
<!-- 右侧用户列表 -->
<div class="w-5/6">
<Grid table-title="用户列表">
<template #toolbar-tools>
<Button
type="primary"
@click="onCreate"
v-access:code="['system:user:create']"
>
<Plus class="size-5" />
{{ $t('ui.actionTitle.create', ['用户']) }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onExport"
v-access:code="['system:user:export']"
>
<Download class="size-5" />
{{ $t('ui.actionTitle.export') }}
</Button>
<Button
type="primary"
class="ml-2"
@click="onImport"
v-access:code="['system:user:import']"
>
<Upload class="size-5" />
{{ $t('ui.actionTitle.import', ['用户']) }}
</Button>
<TableAction
:actions="[
{
label: $t('ui.actionTitle.create', ['用户']),
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['system:user:create'],
onClick: handleCreate,
},
{
label: $t('ui.actionTitle.export'),
type: 'primary',
icon: ACTION_ICON.DOWNLOAD,
auth: ['system:user:export'],
onClick: handleExport,
},
{
label: $t('ui.actionTitle.import', ['用户']),
type: 'primary',
icon: ACTION_ICON.UPLOAD,
auth: ['system:user:import'],
onClick: handleImport,
},
]"
/>
</template>
<template #actions="{ row }">
<TableAction
@@ -224,34 +224,34 @@ const [Grid, gridApi] = useVbenVxeGrid({
{
label: $t('common.edit'),
type: 'link',
icon: 'ant-design:edit-outlined',
icon: ACTION_ICON.EDIT,
auth: ['system:user:update'],
onClick: onEdit.bind(null, row),
onClick: handleEdit.bind(null, row),
},
{
label: $t('common.delete'),
type: 'link',
danger: true,
icon: 'ant-design:delete-outlined',
icon: ACTION_ICON.DELETE,
auth: ['system:user:delete'],
popConfirm: {
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: onDelete.bind(null, row),
confirm: handleDelete.bind(null, row),
},
},
]"
:drop-down-actions="[
{
label: '数据权限',
label: '分配角色',
type: 'link',
auth: ['system:permission:assign-user-role'],
onClick: onAssignRole.bind(null, row),
onClick: handleAssignRole.bind(null, row),
},
{
label: '菜单权限',
label: '重置密码',
type: 'link',
auth: ['system:user:update-password'],
onClick: onResetPassword.bind(null, row),
onClick: handleResetPassword.bind(null, row),
},
]"
/>

View File

@@ -55,7 +55,7 @@ function beforeUpload(file: FileType) {
}
/** 下载模版 */
async function onDownload() {
async function handleDownload() {
const data = await importUserTemplate();
downloadFileFromBlobPart({ fileName: '用户导入模板.xls', source: data });
}
@@ -78,7 +78,7 @@ async function onDownload() {
</Form>
<template #prepend-footer>
<div class="flex flex-auto items-center">
<Button @click="onDownload"> 下载导入模板 </Button>
<Button @click="handleDownload"> 下载导入模板 </Button>
</div>
</template>
</Modal>