refactor: 升级框架
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@vben/web-antd",
|
"name": "@vben/web-antd",
|
||||||
"version": "5.5.6",
|
"version": "5.5.7",
|
||||||
"homepage": "https://vben.pro",
|
"homepage": "https://vben.pro",
|
||||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -53,7 +53,8 @@
|
|||||||
"pinia": "catalog:",
|
"pinia": "catalog:",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
"vue-dompurify-html": "catalog:",
|
"vue-dompurify-html": "catalog:",
|
||||||
"vue-router": "catalog:"
|
"vue-router": "catalog:",
|
||||||
|
"vue3-signature": "catalog:"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/crypto-js": "catalog:"
|
"@types/crypto-js": "catalog:"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { $t } from '@vben/locales';
|
|||||||
/** 手机号正则表达式(中国) */
|
/** 手机号正则表达式(中国) */
|
||||||
const MOBILE_REGEX = /(?:0|86|\+86)?1[3-9]\d{9}/;
|
const MOBILE_REGEX = /(?:0|86|\+86)?1[3-9]\d{9}/;
|
||||||
|
|
||||||
|
async function initSetupVbenForm() {
|
||||||
setupVbenForm<ComponentType>({
|
setupVbenForm<ComponentType>({
|
||||||
config: {
|
config: {
|
||||||
// ant design vue组件库默认都是 v-model:value
|
// ant design vue组件库默认都是 v-model:value
|
||||||
@@ -20,7 +21,6 @@ setupVbenForm<ComponentType>({
|
|||||||
modelPropNameMap: {
|
modelPropNameMap: {
|
||||||
Checkbox: 'checked',
|
Checkbox: 'checked',
|
||||||
Radio: 'checked',
|
Radio: 'checked',
|
||||||
RichTextarea: 'modelValue',
|
|
||||||
Switch: 'checked',
|
Switch: 'checked',
|
||||||
Upload: 'fileList',
|
Upload: 'fileList',
|
||||||
},
|
},
|
||||||
@@ -61,10 +61,11 @@ setupVbenForm<ComponentType>({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const useVbenForm = useForm<ComponentType>;
|
const useVbenForm = useForm<ComponentType>;
|
||||||
|
|
||||||
export { useVbenForm, z };
|
export { initSetupVbenForm, useVbenForm, z };
|
||||||
|
|
||||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||||
export type { VbenFormProps };
|
export type { VbenFormProps };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* 来自 @vben/plugins/vxe-table style.css TODO @puhui999:可以写下目的哈; */
|
/* 来自 @vben/plugins/vxe-table style.css,覆盖 vxe-table 原有的样式定义,使用 vben 的样式主题 */
|
||||||
:root {
|
:root {
|
||||||
--vxe-ui-font-color: hsl(var(--foreground));
|
--vxe-ui-font-color: hsl(var(--foreground));
|
||||||
--vxe-ui-font-primary-color: hsl(var(--primary));
|
--vxe-ui-font-primary-color: hsl(var(--primary));
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
@@ -9,9 +10,22 @@ import {
|
|||||||
setupVbenVxeTable,
|
setupVbenVxeTable,
|
||||||
useVbenVxeGrid,
|
useVbenVxeGrid,
|
||||||
} from '@vben/plugins/vxe-table';
|
} from '@vben/plugins/vxe-table';
|
||||||
import { isFunction, isString } from '@vben/utils';
|
import {
|
||||||
|
erpCountInputFormatter,
|
||||||
|
erpNumberFormatter,
|
||||||
|
formatPast2,
|
||||||
|
isFunction,
|
||||||
|
isString,
|
||||||
|
} from '@vben/utils';
|
||||||
|
|
||||||
import { Button, Image, Popconfirm, Switch } from 'ant-design-vue';
|
import {
|
||||||
|
Button,
|
||||||
|
Image,
|
||||||
|
ImagePreviewGroup,
|
||||||
|
Popconfirm,
|
||||||
|
Switch,
|
||||||
|
Tag,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
import { DictTag, DictTagGroup } from '#/components/dict-tag';
|
import { DictTag, DictTagGroup } from '#/components/dict-tag';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -63,7 +77,7 @@ setupVbenVxeTable({
|
|||||||
round: true,
|
round: true,
|
||||||
showOverflow: true,
|
showOverflow: true,
|
||||||
size: 'small',
|
size: 'small',
|
||||||
},
|
} as VxeTableGridOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
||||||
@@ -74,6 +88,20 @@ setupVbenVxeTable({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vxeUI.renderer.add('CellImages', {
|
||||||
|
renderTableDefault(_renderOpts, params) {
|
||||||
|
const { column, row } = params;
|
||||||
|
if (column && column.field && row[column.field]) {
|
||||||
|
return h(ImagePreviewGroup, {}, () => {
|
||||||
|
return row[column.field].map((item: any) =>
|
||||||
|
h(Image, { src: item }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
||||||
vxeUI.renderer.add('CellLink', {
|
vxeUI.renderer.add('CellLink', {
|
||||||
renderTableDefault(renderOpts) {
|
renderTableDefault(renderOpts) {
|
||||||
@@ -86,6 +114,35 @@ setupVbenVxeTable({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 表格配置项可以用 cellRender: { name: 'CellTag' },
|
||||||
|
vxeUI.renderer.add('CellTag', {
|
||||||
|
renderTableDefault(renderOpts, params) {
|
||||||
|
const { props } = renderOpts;
|
||||||
|
const { column, row } = params;
|
||||||
|
return h(Tag, { color: props?.color }, () => row[column.field]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
vxeUI.renderer.add('CellTags', {
|
||||||
|
renderTableDefault(renderOpts, params) {
|
||||||
|
const { props } = renderOpts;
|
||||||
|
const { column, row } = params;
|
||||||
|
if (!row[column.field] || row[column.field].length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{ class: 'flex items-center justify-center' },
|
||||||
|
{
|
||||||
|
default: () =>
|
||||||
|
row[column.field].map((item: any) =>
|
||||||
|
h(Tag, { color: props?.color }, { default: () => item }),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 表格配置项可以用 cellRender: { name: 'CellDict', props:{dictType: ''} },
|
// 表格配置项可以用 cellRender: { name: 'CellDict', props:{dictType: ''} },
|
||||||
vxeUI.renderer.add('CellDict', {
|
vxeUI.renderer.add('CellDict', {
|
||||||
renderTableDefault(renderOpts, params) {
|
renderTableDefault(renderOpts, params) {
|
||||||
@@ -283,20 +340,23 @@ setupVbenVxeTable({
|
|||||||
|
|
||||||
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
||||||
// vxeUI.formats.add
|
// vxeUI.formats.add
|
||||||
|
|
||||||
|
vxeUI.formats.add('formatPast2', {
|
||||||
|
tableCellFormatMethod({ cellValue }) {
|
||||||
|
return formatPast2(cellValue);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// add by 星语:数量格式化,例如说:金额
|
// add by 星语:数量格式化,例如说:金额
|
||||||
vxeUI.formats.add('formatAmount', {
|
vxeUI.formats.add('formatNumber', {
|
||||||
cellFormatMethod({ cellValue }, digits = 2) {
|
tableCellFormatMethod({ cellValue }) {
|
||||||
if (cellValue === null || cellValue === undefined) {
|
return erpCountInputFormatter(cellValue);
|
||||||
return '';
|
},
|
||||||
}
|
});
|
||||||
if (isString(cellValue)) {
|
|
||||||
cellValue = Number.parseFloat(cellValue);
|
vxeUI.formats.add('formatAmount2', {
|
||||||
}
|
tableCellFormatMethod({ cellValue }, digits = 2) {
|
||||||
// 如果非 number,则直接返回空串
|
return `${erpNumberFormatter(cellValue, digits)}元`;
|
||||||
if (Number.isNaN(cellValue)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return cellValue.toFixed(digits);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -316,4 +376,5 @@ export type OnActionClickParams<T = Recordable<any>> = {
|
|||||||
export type OnActionClickFn<T = Recordable<any>> = (
|
export type OnActionClickFn<T = Recordable<any>> = (
|
||||||
params: OnActionClickParams<T>,
|
params: OnActionClickParams<T>,
|
||||||
) => void;
|
) => void;
|
||||||
|
export * from '#/components/table-action';
|
||||||
export type * from '@vben/plugins/vxe-table';
|
export type * from '@vben/plugins/vxe-table';
|
||||||
|
|||||||
@@ -65,13 +65,13 @@ export namespace InfraCodegenApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 更新代码生成请求 */
|
/** 更新代码生成请求 */
|
||||||
export interface CodegenUpdateReqVO {
|
export interface CodegenUpdateReq {
|
||||||
table: any | CodegenTable;
|
table: any | CodegenTable;
|
||||||
columns: CodegenColumn[];
|
columns: CodegenColumn[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 创建代码生成请求 */
|
/** 创建代码生成请求 */
|
||||||
export interface CodegenCreateListReqVO {
|
export interface CodegenCreateListReq {
|
||||||
dataSourceConfigId?: number;
|
dataSourceConfigId?: number;
|
||||||
tableNames: string[];
|
tableNames: string[];
|
||||||
}
|
}
|
||||||
@@ -106,32 +106,25 @@ export function getCodegenTable(tableId: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 修改代码生成表定义 */
|
/** 修改代码生成表定义 */
|
||||||
export function updateCodegenTable(data: InfraCodegenApi.CodegenUpdateReqVO) {
|
export function updateCodegenTable(data: InfraCodegenApi.CodegenUpdateReq) {
|
||||||
return requestClient.put('/infra/codegen/update', data);
|
return requestClient.put('/infra/codegen/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 基于数据库的表结构,同步数据库的表和字段定义 */
|
/** 基于数据库的表结构,同步数据库的表和字段定义 */
|
||||||
export function syncCodegenFromDB(tableId: number) {
|
export function syncCodegenFromDB(tableId: number) {
|
||||||
return requestClient.put('/infra/codegen/sync-from-db', {
|
return requestClient.put(`/infra/codegen/sync-from-db?tableId=${tableId}`);
|
||||||
params: { tableId },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 预览生成代码 */
|
/** 预览生成代码 */
|
||||||
export function previewCodegen(tableId: number) {
|
export function previewCodegen(tableId: number) {
|
||||||
return requestClient.get<InfraCodegenApi.CodegenPreview[]>(
|
return requestClient.get<InfraCodegenApi.CodegenPreview[]>(
|
||||||
'/infra/codegen/preview',
|
`/infra/codegen/preview?tableId=${tableId}`,
|
||||||
{
|
|
||||||
params: { tableId },
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 下载生成代码 */
|
/** 下载生成代码 */
|
||||||
export function downloadCodegen(tableId: number) {
|
export function downloadCodegen(tableId: number) {
|
||||||
return requestClient.download('/infra/codegen/download', {
|
return requestClient.download(`/infra/codegen/download?tableId=${tableId}`);
|
||||||
params: { tableId },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获得表定义 */
|
/** 获得表定义 */
|
||||||
@@ -143,9 +136,7 @@ export function getSchemaTableList(params: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 基于数据库的表结构,创建代码生成器的表定义 */
|
/** 基于数据库的表结构,创建代码生成器的表定义 */
|
||||||
export function createCodegenList(
|
export function createCodegenList(data: InfraCodegenApi.CodegenCreateListReq) {
|
||||||
data: InfraCodegenApi.CodegenCreateListReqVO,
|
|
||||||
) {
|
|
||||||
return requestClient.post('/infra/codegen/create-list', data);
|
return requestClient.post('/infra/codegen/create-list', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,3 +146,10 @@ export function deleteCodegenTable(tableId: number) {
|
|||||||
params: { tableId },
|
params: { tableId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除代码生成表定义 */
|
||||||
|
export function deleteCodegenTableList(tableIds: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/codegen/delete-list?tableIds=${tableIds.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,9 +54,14 @@ export function deleteConfig(id: number) {
|
|||||||
return requestClient.delete(`/infra/config/delete?id=${id}`);
|
return requestClient.delete(`/infra/config/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除参数 */
|
||||||
|
export function deleteConfigList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/infra/config/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出参数 */
|
/** 导出参数 */
|
||||||
export function exportConfig(params: any) {
|
export function exportConfig(params: any) {
|
||||||
return requestClient.download('/infra/config/export', {
|
return requestClient.download('/infra/config/export-excel', {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export namespace Demo01ContactApi {
|
|||||||
export interface Demo01Contact {
|
export interface Demo01Contact {
|
||||||
id: number; // 编号
|
id: number; // 编号
|
||||||
name?: string; // 名字
|
name?: string; // 名字
|
||||||
sex?: boolean; // 性别
|
sex?: number; // 性别
|
||||||
birthday?: Dayjs | string; // 出生年
|
birthday?: Dayjs | string; // 出生年
|
||||||
description?: string; // 简介
|
description?: string; // 简介
|
||||||
avatar: string; // 头像
|
avatar: string; // 头像
|
||||||
@@ -46,6 +46,13 @@ export function deleteDemo01Contact(id: number) {
|
|||||||
return requestClient.delete(`/infra/demo01-contact/delete?id=${id}`);
|
return requestClient.delete(`/infra/demo01-contact/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除示例联系人 */
|
||||||
|
export function deleteDemo01ContactList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/demo01-contact/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出示例联系人 */
|
/** 导出示例联系人 */
|
||||||
export function exportDemo01Contact(params: any) {
|
export function exportDemo01Contact(params: any) {
|
||||||
return requestClient.download('/infra/demo01-contact/export-excel', params);
|
return requestClient.download('/infra/demo01-contact/export-excel', params);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export namespace Demo03StudentApi {
|
|||||||
/** 查询学生分页 */
|
/** 查询学生分页 */
|
||||||
export function getDemo03StudentPage(params: PageParam) {
|
export function getDemo03StudentPage(params: PageParam) {
|
||||||
return requestClient.get<PageResult<Demo03StudentApi.Demo03Student>>(
|
return requestClient.get<PageResult<Demo03StudentApi.Demo03Student>>(
|
||||||
'/infra/demo03-student/page',
|
'/infra/demo03-student-erp/page',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -42,28 +42,38 @@ export function getDemo03StudentPage(params: PageParam) {
|
|||||||
/** 查询学生详情 */
|
/** 查询学生详情 */
|
||||||
export function getDemo03Student(id: number) {
|
export function getDemo03Student(id: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Student>(
|
return requestClient.get<Demo03StudentApi.Demo03Student>(
|
||||||
`/infra/demo03-student/get?id=${id}`,
|
`/infra/demo03-student-erp/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增学生 */
|
/** 新增学生 */
|
||||||
export function createDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
export function createDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
||||||
return requestClient.post('/infra/demo03-student/create', data);
|
return requestClient.post('/infra/demo03-student-erp/create', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改学生 */
|
/** 修改学生 */
|
||||||
export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
||||||
return requestClient.put('/infra/demo03-student/update', data);
|
return requestClient.put('/infra/demo03-student-erp/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除学生 */
|
/** 删除学生 */
|
||||||
export function deleteDemo03Student(id: number) {
|
export function deleteDemo03Student(id: number) {
|
||||||
return requestClient.delete(`/infra/demo03-student/delete?id=${id}`);
|
return requestClient.delete(`/infra/demo03-student-erp/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生 */
|
||||||
|
export function deleteDemo03StudentList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出学生 */
|
/** 导出学生 */
|
||||||
export function exportDemo03Student(params: any) {
|
export function exportDemo03Student(params: any) {
|
||||||
return requestClient.download('/infra/demo03-student/export-excel', params);
|
return requestClient.download(
|
||||||
|
'/infra/demo03-student-erp/export-excel',
|
||||||
|
params,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生课程) ====================
|
// ==================== 子表(学生课程) ====================
|
||||||
@@ -71,33 +81,44 @@ export function exportDemo03Student(params: any) {
|
|||||||
/** 获得学生课程分页 */
|
/** 获得学生课程分页 */
|
||||||
export function getDemo03CoursePage(params: PageParam) {
|
export function getDemo03CoursePage(params: PageParam) {
|
||||||
return requestClient.get<PageResult<Demo03StudentApi.Demo03Course>>(
|
return requestClient.get<PageResult<Demo03StudentApi.Demo03Course>>(
|
||||||
`/infra/demo03-student/demo03-course/page`,
|
`/infra/demo03-student-erp/demo03-course/page`,
|
||||||
{
|
{ params },
|
||||||
params,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/** 新增学生课程 */
|
/** 新增学生课程 */
|
||||||
export function createDemo03Course(data: Demo03StudentApi.Demo03Course) {
|
export function createDemo03Course(data: Demo03StudentApi.Demo03Course) {
|
||||||
return requestClient.post(`/infra/demo03-student/demo03-course/create`, data);
|
return requestClient.post(
|
||||||
|
`/infra/demo03-student-erp/demo03-course/create`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改学生课程 */
|
/** 修改学生课程 */
|
||||||
export function updateDemo03Course(data: Demo03StudentApi.Demo03Course) {
|
export function updateDemo03Course(data: Demo03StudentApi.Demo03Course) {
|
||||||
return requestClient.put(`/infra/demo03-student/demo03-course/update`, data);
|
return requestClient.put(
|
||||||
|
`/infra/demo03-student-erp/demo03-course/update`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除学生课程 */
|
/** 删除学生课程 */
|
||||||
export function deleteDemo03Course(id: number) {
|
export function deleteDemo03Course(id: number) {
|
||||||
return requestClient.delete(
|
return requestClient.delete(
|
||||||
`/infra/demo03-student/demo03-course/delete?id=${id}`,
|
`/infra/demo03-student-erp/demo03-course/delete?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生课程 */
|
||||||
|
export function deleteDemo03CourseList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获得学生课程 */
|
/** 获得学生课程 */
|
||||||
export function getDemo03Course(id: number) {
|
export function getDemo03Course(id: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Course>(
|
return requestClient.get<Demo03StudentApi.Demo03Course>(
|
||||||
`/infra/demo03-student/demo03-course/get?id=${id}`,
|
`/infra/demo03-student-erp/demo03-course/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,32 +127,43 @@ export function getDemo03Course(id: number) {
|
|||||||
/** 获得学生班级分页 */
|
/** 获得学生班级分页 */
|
||||||
export function getDemo03GradePage(params: PageParam) {
|
export function getDemo03GradePage(params: PageParam) {
|
||||||
return requestClient.get<PageResult<Demo03StudentApi.Demo03Grade>>(
|
return requestClient.get<PageResult<Demo03StudentApi.Demo03Grade>>(
|
||||||
`/infra/demo03-student/demo03-grade/page`,
|
`/infra/demo03-student-erp/demo03-grade/page`,
|
||||||
{
|
{ params },
|
||||||
params,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/** 新增学生班级 */
|
/** 新增学生班级 */
|
||||||
export function createDemo03Grade(data: Demo03StudentApi.Demo03Grade) {
|
export function createDemo03Grade(data: Demo03StudentApi.Demo03Grade) {
|
||||||
return requestClient.post(`/infra/demo03-student/demo03-grade/create`, data);
|
return requestClient.post(
|
||||||
|
`/infra/demo03-student-erp/demo03-grade/create`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改学生班级 */
|
/** 修改学生班级 */
|
||||||
export function updateDemo03Grade(data: Demo03StudentApi.Demo03Grade) {
|
export function updateDemo03Grade(data: Demo03StudentApi.Demo03Grade) {
|
||||||
return requestClient.put(`/infra/demo03-student/demo03-grade/update`, data);
|
return requestClient.put(
|
||||||
|
`/infra/demo03-student-erp/demo03-grade/update`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除学生班级 */
|
/** 删除学生班级 */
|
||||||
export function deleteDemo03Grade(id: number) {
|
export function deleteDemo03Grade(id: number) {
|
||||||
return requestClient.delete(
|
return requestClient.delete(
|
||||||
`/infra/demo03-student/demo03-grade/delete?id=${id}`,
|
`/infra/demo03-student-erp/demo03-grade/delete?id=${id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生班级 */
|
||||||
|
export function deleteDemo03GradeList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获得学生班级 */
|
/** 获得学生班级 */
|
||||||
export function getDemo03Grade(id: number) {
|
export function getDemo03Grade(id: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Grade>(
|
return requestClient.get<Demo03StudentApi.Demo03Grade>(
|
||||||
`/infra/demo03-student/demo03-grade/get?id=${id}`,
|
`/infra/demo03-student-erp/demo03-grade/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
import type { PageParam, PageResult } from '@vben/request';
|
import type { PageParam, PageResult } from '@vben/request';
|
||||||
|
|
||||||
import { requestClient } from '#/api/request';
|
import { requestClient } from '#/api/request';
|
||||||
@@ -24,7 +26,7 @@ export namespace Demo03StudentApi {
|
|||||||
id: number; // 编号
|
id: number; // 编号
|
||||||
name?: string; // 名字
|
name?: string; // 名字
|
||||||
sex?: number; // 性别
|
sex?: number; // 性别
|
||||||
birthday?: Date; // 出生日期
|
birthday?: Dayjs | string; // 出生日期
|
||||||
description?: string; // 简介
|
description?: string; // 简介
|
||||||
demo03courses?: Demo03Course[];
|
demo03courses?: Demo03Course[];
|
||||||
demo03grade?: Demo03Grade;
|
demo03grade?: Demo03Grade;
|
||||||
@@ -34,7 +36,7 @@ export namespace Demo03StudentApi {
|
|||||||
/** 查询学生分页 */
|
/** 查询学生分页 */
|
||||||
export function getDemo03StudentPage(params: PageParam) {
|
export function getDemo03StudentPage(params: PageParam) {
|
||||||
return requestClient.get<PageResult<Demo03StudentApi.Demo03Student>>(
|
return requestClient.get<PageResult<Demo03StudentApi.Demo03Student>>(
|
||||||
'/infra/demo03-student/page',
|
'/infra/demo03-student-inner/page',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -42,28 +44,38 @@ export function getDemo03StudentPage(params: PageParam) {
|
|||||||
/** 查询学生详情 */
|
/** 查询学生详情 */
|
||||||
export function getDemo03Student(id: number) {
|
export function getDemo03Student(id: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Student>(
|
return requestClient.get<Demo03StudentApi.Demo03Student>(
|
||||||
`/infra/demo03-student/get?id=${id}`,
|
`/infra/demo03-student-inner/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增学生 */
|
/** 新增学生 */
|
||||||
export function createDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
export function createDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
||||||
return requestClient.post('/infra/demo03-student/create', data);
|
return requestClient.post('/infra/demo03-student-inner/create', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改学生 */
|
/** 修改学生 */
|
||||||
export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
||||||
return requestClient.put('/infra/demo03-student/update', data);
|
return requestClient.put('/infra/demo03-student-inner/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除学生 */
|
/** 删除学生 */
|
||||||
export function deleteDemo03Student(id: number) {
|
export function deleteDemo03Student(id: number) {
|
||||||
return requestClient.delete(`/infra/demo03-student/delete?id=${id}`);
|
return requestClient.delete(`/infra/demo03-student-inner/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生 */
|
||||||
|
export function deleteDemo03StudentList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出学生 */
|
/** 导出学生 */
|
||||||
export function exportDemo03Student(params: any) {
|
export function exportDemo03Student(params: any) {
|
||||||
return requestClient.download('/infra/demo03-student/export-excel', params);
|
return requestClient.download(
|
||||||
|
'/infra/demo03-student-inner/export-excel',
|
||||||
|
params,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生课程) ====================
|
// ==================== 子表(学生课程) ====================
|
||||||
@@ -71,7 +83,7 @@ export function exportDemo03Student(params: any) {
|
|||||||
/** 获得学生课程列表 */
|
/** 获得学生课程列表 */
|
||||||
export function getDemo03CourseListByStudentId(studentId: number) {
|
export function getDemo03CourseListByStudentId(studentId: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Course[]>(
|
return requestClient.get<Demo03StudentApi.Demo03Course[]>(
|
||||||
`/infra/demo03-student/demo03-course/list-by-student-id?studentId=${studentId}`,
|
`/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=${studentId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +92,6 @@ export function getDemo03CourseListByStudentId(studentId: number) {
|
|||||||
/** 获得学生班级 */
|
/** 获得学生班级 */
|
||||||
export function getDemo03GradeByStudentId(studentId: number) {
|
export function getDemo03GradeByStudentId(studentId: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Grade>(
|
return requestClient.get<Demo03StudentApi.Demo03Grade>(
|
||||||
`/infra/demo03-student/demo03-grade/get-by-student-id?studentId=${studentId}`,
|
`/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=${studentId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export namespace Demo03StudentApi {
|
|||||||
/** 查询学生分页 */
|
/** 查询学生分页 */
|
||||||
export function getDemo03StudentPage(params: PageParam) {
|
export function getDemo03StudentPage(params: PageParam) {
|
||||||
return requestClient.get<PageResult<Demo03StudentApi.Demo03Student>>(
|
return requestClient.get<PageResult<Demo03StudentApi.Demo03Student>>(
|
||||||
'/infra/demo03-student/page',
|
'/infra/demo03-student-normal/page',
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -44,28 +44,38 @@ export function getDemo03StudentPage(params: PageParam) {
|
|||||||
/** 查询学生详情 */
|
/** 查询学生详情 */
|
||||||
export function getDemo03Student(id: number) {
|
export function getDemo03Student(id: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Student>(
|
return requestClient.get<Demo03StudentApi.Demo03Student>(
|
||||||
`/infra/demo03-student/get?id=${id}`,
|
`/infra/demo03-student-normal/get?id=${id}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增学生 */
|
/** 新增学生 */
|
||||||
export function createDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
export function createDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
||||||
return requestClient.post('/infra/demo03-student/create', data);
|
return requestClient.post('/infra/demo03-student-normal/create', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改学生 */
|
/** 修改学生 */
|
||||||
export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) {
|
||||||
return requestClient.put('/infra/demo03-student/update', data);
|
return requestClient.put('/infra/demo03-student-normal/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除学生 */
|
/** 删除学生 */
|
||||||
export function deleteDemo03Student(id: number) {
|
export function deleteDemo03Student(id: number) {
|
||||||
return requestClient.delete(`/infra/demo03-student/delete?id=${id}`);
|
return requestClient.delete(`/infra/demo03-student-normal/delete?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生 */
|
||||||
|
export function deleteDemo03StudentList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出学生 */
|
/** 导出学生 */
|
||||||
export function exportDemo03Student(params: any) {
|
export function exportDemo03Student(params: any) {
|
||||||
return requestClient.download('/infra/demo03-student/export-excel', params);
|
return requestClient.download(
|
||||||
|
'/infra/demo03-student-normal/export-excel',
|
||||||
|
params,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生课程) ====================
|
// ==================== 子表(学生课程) ====================
|
||||||
@@ -73,7 +83,7 @@ export function exportDemo03Student(params: any) {
|
|||||||
/** 获得学生课程列表 */
|
/** 获得学生课程列表 */
|
||||||
export function getDemo03CourseListByStudentId(studentId: number) {
|
export function getDemo03CourseListByStudentId(studentId: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Course[]>(
|
return requestClient.get<Demo03StudentApi.Demo03Course[]>(
|
||||||
`/infra/demo03-student/demo03-course/list-by-student-id?studentId=${studentId}`,
|
`/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=${studentId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +92,6 @@ export function getDemo03CourseListByStudentId(studentId: number) {
|
|||||||
/** 获得学生班级 */
|
/** 获得学生班级 */
|
||||||
export function getDemo03GradeByStudentId(studentId: number) {
|
export function getDemo03GradeByStudentId(studentId: number) {
|
||||||
return requestClient.get<Demo03StudentApi.Demo03Grade>(
|
return requestClient.get<Demo03StudentApi.Demo03Grade>(
|
||||||
`/infra/demo03-student/demo03-grade/get-by-student-id?studentId=${studentId}`,
|
`/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=${studentId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ export function deleteFileConfig(id: number) {
|
|||||||
return requestClient.delete(`/infra/file-config/delete?id=${id}`);
|
return requestClient.delete(`/infra/file-config/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除文件配置 */
|
||||||
|
export function deleteFileConfigList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/infra/file-config/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 测试文件配置 */
|
/** 测试文件配置 */
|
||||||
export function testFileConfig(id: number) {
|
export function testFileConfig(id: number) {
|
||||||
return requestClient.get(`/infra/file-config/test?id=${id}`);
|
return requestClient.get(`/infra/file-config/test?id=${id}`);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export namespace InfraFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 文件预签名地址 */
|
/** 文件预签名地址 */
|
||||||
export interface FilePresignedUrlRespVO {
|
export interface FilePresignedUrlResp {
|
||||||
configId: number; // 文件配置编号
|
configId: number; // 文件配置编号
|
||||||
uploadUrl: string; // 文件上传 URL
|
uploadUrl: string; // 文件上传 URL
|
||||||
url: string; // 文件 URL
|
url: string; // 文件 URL
|
||||||
@@ -27,7 +27,7 @@ export namespace InfraFileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 上传文件 */
|
/** 上传文件 */
|
||||||
export interface FileUploadReqVO {
|
export interface FileUploadReq {
|
||||||
file: globalThis.File;
|
file: globalThis.File;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
}
|
}
|
||||||
@@ -45,9 +45,14 @@ export function deleteFile(id: number) {
|
|||||||
return requestClient.delete(`/infra/file/delete?id=${id}`);
|
return requestClient.delete(`/infra/file/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除文件 */
|
||||||
|
export function deleteFileList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/infra/file/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取文件预签名地址 */
|
/** 获取文件预签名地址 */
|
||||||
export function getFilePresignedUrl(name: string, directory?: string) {
|
export function getFilePresignedUrl(name: string, directory?: string) {
|
||||||
return requestClient.get<InfraFileApi.FilePresignedUrlRespVO>(
|
return requestClient.get<InfraFileApi.FilePresignedUrlResp>(
|
||||||
'/infra/file/presigned-url',
|
'/infra/file/presigned-url',
|
||||||
{
|
{
|
||||||
params: { name, directory },
|
params: { name, directory },
|
||||||
@@ -62,7 +67,7 @@ export function createFile(data: InfraFileApi.File) {
|
|||||||
|
|
||||||
/** 上传文件 */
|
/** 上传文件 */
|
||||||
export function uploadFile(
|
export function uploadFile(
|
||||||
data: InfraFileApi.FileUploadReqVO,
|
data: InfraFileApi.FileUploadReq,
|
||||||
onUploadProgress?: AxiosProgressEvent,
|
onUploadProgress?: AxiosProgressEvent,
|
||||||
) {
|
) {
|
||||||
// 特殊:由于 upload 内部封装,即使 directory 为 undefined,也会传递给后端
|
// 特殊:由于 upload 内部封装,即使 directory 为 undefined,也会传递给后端
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export namespace InfraJobApi {
|
|||||||
retryInterval: number;
|
retryInterval: number;
|
||||||
monitorTimeout: number;
|
monitorTimeout: number;
|
||||||
createTime?: Date;
|
createTime?: Date;
|
||||||
|
nextTimes?: Date[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +46,11 @@ export function deleteJob(id: number) {
|
|||||||
return requestClient.delete(`/infra/job/delete?id=${id}`);
|
return requestClient.delete(`/infra/job/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除定时任务调度 */
|
||||||
|
export function deleteJobList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/infra/job/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出定时任务调度 */
|
/** 导出定时任务调度 */
|
||||||
export function exportJob(params: any) {
|
export function exportJob(params: any) {
|
||||||
return requestClient.download('/infra/job/export-excel', { params });
|
return requestClient.download('/infra/job/export-excel', { params });
|
||||||
@@ -56,7 +62,7 @@ export function updateJobStatus(id: number, status: number) {
|
|||||||
id,
|
id,
|
||||||
status,
|
status,
|
||||||
};
|
};
|
||||||
return requestClient.put('/infra/job/update-status', { params });
|
return requestClient.put('/infra/job/update-status', {}, { params });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 定时任务立即执行一次 */
|
/** 定时任务立即执行一次 */
|
||||||
|
|||||||
@@ -45,3 +45,8 @@ export async function updateDept(data: SystemDeptApi.Dept) {
|
|||||||
export async function deleteDept(id: number) {
|
export async function deleteDept(id: number) {
|
||||||
return requestClient.delete(`/system/dept/delete?id=${id}`);
|
return requestClient.delete(`/system/dept/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除部门 */
|
||||||
|
export async function deleteDeptList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/system/dept/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,7 +48,14 @@ export function deleteDictData(id: number) {
|
|||||||
return requestClient.delete(`/system/dict-data/delete?id=${id}`);
|
return requestClient.delete(`/system/dict-data/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量删除字典数据
|
||||||
|
export function deleteDictDataList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/dict-data/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 导出字典类型数据
|
// 导出字典类型数据
|
||||||
export function exportDictData(params: any) {
|
export function exportDictData(params: any) {
|
||||||
return requestClient.download('/system/dict-data/export', { params });
|
return requestClient.download('/system/dict-data/export-excel', { params });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,14 @@ export function deleteDictType(id: number) {
|
|||||||
return requestClient.delete(`/system/dict-type/delete?id=${id}`);
|
return requestClient.delete(`/system/dict-type/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量删除字典
|
||||||
|
export function deleteDictTypeList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/dict-type/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 导出字典类型
|
// 导出字典类型
|
||||||
export function exportDictType(params: any) {
|
export function exportDictType(params: any) {
|
||||||
return requestClient.download('/system/dict-type/export', { params });
|
return requestClient.download('/system/dict-type/export-excel', { params });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,13 @@ export function deleteMailAccount(id: number) {
|
|||||||
return requestClient.delete(`/system/mail-account/delete?id=${id}`);
|
return requestClient.delete(`/system/mail-account/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除邮箱账号 */
|
||||||
|
export function deleteMailAccountList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/mail-account/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 获得邮箱账号精简列表 */
|
/** 获得邮箱账号精简列表 */
|
||||||
export function getSimpleMailAccountList() {
|
export function getSimpleMailAccountList() {
|
||||||
return requestClient.get<SystemMailAccountApi.MailAccount[]>(
|
return requestClient.get<SystemMailAccountApi.MailAccount[]>(
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export namespace SystemMailTemplateApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 邮件发送信息 */
|
/** 邮件发送信息 */
|
||||||
export interface MailSendReqVO {
|
export interface MailSendReq {
|
||||||
mail: string;
|
mail: string;
|
||||||
templateCode: string;
|
templateCode: string;
|
||||||
templateParams: Record<string, any>;
|
templateParams: Record<string, any>;
|
||||||
@@ -56,7 +56,14 @@ export function deleteMailTemplate(id: number) {
|
|||||||
return requestClient.delete(`/system/mail-template/delete?id=${id}`);
|
return requestClient.delete(`/system/mail-template/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除邮件模板 */
|
||||||
|
export function deleteMailTemplateList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/mail-template/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 发送邮件 */
|
/** 发送邮件 */
|
||||||
export function sendMail(data: SystemMailTemplateApi.MailSendReqVO) {
|
export function sendMail(data: SystemMailTemplateApi.MailSendReq) {
|
||||||
return requestClient.post('/system/mail-template/send-mail', data);
|
return requestClient.post('/system/mail-template/send-mail', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,3 +53,8 @@ export async function updateMenu(data: SystemMenuApi.Menu) {
|
|||||||
export async function deleteMenu(id: number) {
|
export async function deleteMenu(id: number) {
|
||||||
return requestClient.delete(`/system/menu/delete?id=${id}`);
|
return requestClient.delete(`/system/menu/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除菜单 */
|
||||||
|
export async function deleteMenuList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/system/menu/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ export function deleteNotice(id: number) {
|
|||||||
return requestClient.delete(`/system/notice/delete?id=${id}`);
|
return requestClient.delete(`/system/notice/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除公告 */
|
||||||
|
export function deleteNoticeList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/notice/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 推送公告 */
|
/** 推送公告 */
|
||||||
export function pushNotice(id: number) {
|
export function pushNotice(id: number) {
|
||||||
return requestClient.post(`/system/notice/push?id=${id}`);
|
return requestClient.post(`/system/notice/push?id=${id}`);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export namespace SystemNotifyTemplateApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 发送站内信请求 */
|
/** 发送站内信请求 */
|
||||||
export interface NotifySendReqVO {
|
export interface NotifySendReq {
|
||||||
userId: number;
|
userId: number;
|
||||||
userType: number;
|
userType: number;
|
||||||
templateCode: string;
|
templateCode: string;
|
||||||
@@ -59,6 +59,13 @@ export function deleteNotifyTemplate(id: number) {
|
|||||||
return requestClient.delete(`/system/notify-template/delete?id=${id}`);
|
return requestClient.delete(`/system/notify-template/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除站内信模板 */
|
||||||
|
export function deleteNotifyTemplateList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/notify-template/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出站内信模板 */
|
/** 导出站内信模板 */
|
||||||
export function exportNotifyTemplate(params: any) {
|
export function exportNotifyTemplate(params: any) {
|
||||||
return requestClient.download('/system/notify-template/export-excel', {
|
return requestClient.download('/system/notify-template/export-excel', {
|
||||||
@@ -67,6 +74,6 @@ export function exportNotifyTemplate(params: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 发送站内信 */
|
/** 发送站内信 */
|
||||||
export function sendNotify(data: SystemNotifyTemplateApi.NotifySendReqVO) {
|
export function sendNotify(data: SystemNotifyTemplateApi.NotifySendReq) {
|
||||||
return requestClient.post('/system/notify-template/send-notify', data);
|
return requestClient.post('/system/notify-template/send-notify', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { requestClient } from '#/api/request';
|
|||||||
/** OAuth2.0 授权信息响应 */
|
/** OAuth2.0 授权信息响应 */
|
||||||
export namespace SystemOAuth2ClientApi {
|
export namespace SystemOAuth2ClientApi {
|
||||||
/** 授权信息 */
|
/** 授权信息 */
|
||||||
export interface AuthorizeInfoRespVO {
|
export interface AuthorizeInfoResp {
|
||||||
client: {
|
client: {
|
||||||
logo: string;
|
logo: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -17,7 +17,7 @@ export namespace SystemOAuth2ClientApi {
|
|||||||
|
|
||||||
/** 获得授权信息 */
|
/** 获得授权信息 */
|
||||||
export function getAuthorize(clientId: string) {
|
export function getAuthorize(clientId: string) {
|
||||||
return requestClient.get<SystemOAuth2ClientApi.AuthorizeInfoRespVO>(
|
return requestClient.get<SystemOAuth2ClientApi.AuthorizeInfoResp>(
|
||||||
`/system/oauth2/authorize?clientId=${clientId}`,
|
`/system/oauth2/authorize?clientId=${clientId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,3 +32,10 @@ export function deleteOAuth2Token(accessToken: string) {
|
|||||||
`/system/oauth2-token/delete?accessToken=${accessToken}`,
|
`/system/oauth2-token/delete?accessToken=${accessToken}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除 OAuth2.0 令牌 */
|
||||||
|
export function deleteOAuth2TokenList(accessTokens: string[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/oauth2-token/delete-list?accessTokens=${accessTokens.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import { requestClient } from '#/api/request';
|
|||||||
|
|
||||||
export namespace SystemPermissionApi {
|
export namespace SystemPermissionApi {
|
||||||
/** 分配用户角色请求 */
|
/** 分配用户角色请求 */
|
||||||
export interface AssignUserRoleReqVO {
|
export interface AssignUserRoleReq {
|
||||||
userId: number;
|
userId: number;
|
||||||
roleIds: number[];
|
roleIds: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分配角色菜单请求 */
|
/** 分配角色菜单请求 */
|
||||||
export interface AssignRoleMenuReqVO {
|
export interface AssignRoleMenuReq {
|
||||||
roleId: number;
|
roleId: number;
|
||||||
menuIds: number[];
|
menuIds: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分配角色数据权限请求 */
|
/** 分配角色数据权限请求 */
|
||||||
export interface AssignRoleDataScopeReqVO {
|
export interface AssignRoleDataScopeReq {
|
||||||
roleId: number;
|
roleId: number;
|
||||||
dataScope: number;
|
dataScope: number;
|
||||||
dataScopeDeptIds: number[];
|
dataScopeDeptIds: number[];
|
||||||
@@ -30,14 +30,14 @@ export async function getRoleMenuList(roleId: number) {
|
|||||||
|
|
||||||
/** 赋予角色菜单权限 */
|
/** 赋予角色菜单权限 */
|
||||||
export async function assignRoleMenu(
|
export async function assignRoleMenu(
|
||||||
data: SystemPermissionApi.AssignRoleMenuReqVO,
|
data: SystemPermissionApi.AssignRoleMenuReq,
|
||||||
) {
|
) {
|
||||||
return requestClient.post('/system/permission/assign-role-menu', data);
|
return requestClient.post('/system/permission/assign-role-menu', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 赋予角色数据权限 */
|
/** 赋予角色数据权限 */
|
||||||
export async function assignRoleDataScope(
|
export async function assignRoleDataScope(
|
||||||
data: SystemPermissionApi.AssignRoleDataScopeReqVO,
|
data: SystemPermissionApi.AssignRoleDataScopeReq,
|
||||||
) {
|
) {
|
||||||
return requestClient.post('/system/permission/assign-role-data-scope', data);
|
return requestClient.post('/system/permission/assign-role-data-scope', data);
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ export async function getUserRoleList(userId: number) {
|
|||||||
|
|
||||||
/** 赋予用户角色 */
|
/** 赋予用户角色 */
|
||||||
export async function assignUserRole(
|
export async function assignUserRole(
|
||||||
data: SystemPermissionApi.AssignUserRoleReqVO,
|
data: SystemPermissionApi.AssignUserRoleReq,
|
||||||
) {
|
) {
|
||||||
return requestClient.post('/system/permission/assign-user-role', data);
|
return requestClient.post('/system/permission/assign-user-role', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,14 @@ export function deletePost(id: number) {
|
|||||||
return requestClient.delete(`/system/post/delete?id=${id}`);
|
return requestClient.delete(`/system/post/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除岗位 */
|
||||||
|
export function deletePostList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/system/post/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出岗位 */
|
/** 导出岗位 */
|
||||||
export function exportPost(params: any) {
|
export function exportPost(params: any) {
|
||||||
return requestClient.download('/system/post/export', {
|
return requestClient.download('/system/post/export-excel', {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ export function deleteRole(id: number) {
|
|||||||
return requestClient.delete(`/system/role/delete?id=${id}`);
|
return requestClient.delete(`/system/role/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除角色 */
|
||||||
|
export function deleteRoleList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/system/role/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出角色 */
|
/** 导出角色 */
|
||||||
export function exportRole(params: any) {
|
export function exportRole(params: any) {
|
||||||
return requestClient.download('/system/role/export-excel', {
|
return requestClient.download('/system/role/export-excel', {
|
||||||
|
|||||||
@@ -54,7 +54,14 @@ export function deleteSmsChannel(id: number) {
|
|||||||
return requestClient.delete(`/system/sms-channel/delete?id=${id}`);
|
return requestClient.delete(`/system/sms-channel/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除短信渠道 */
|
||||||
|
export function deleteSmsChannelList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/sms-channel/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出短信渠道 */
|
/** 导出短信渠道 */
|
||||||
export function exportSmsChannel(params: any) {
|
export function exportSmsChannel(params: any) {
|
||||||
return requestClient.download('/system/sms-channel/export', { params });
|
return requestClient.download('/system/sms-channel/export-excel', { params });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export namespace SystemSmsTemplateApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 发送短信请求 */
|
/** 发送短信请求 */
|
||||||
export interface SmsSendReqVO {
|
export interface SmsSendReq {
|
||||||
mobile: string;
|
mobile: string;
|
||||||
templateCode: string;
|
templateCode: string;
|
||||||
templateParams: Record<string, any>;
|
templateParams: Record<string, any>;
|
||||||
@@ -57,6 +57,13 @@ export function deleteSmsTemplate(id: number) {
|
|||||||
return requestClient.delete(`/system/sms-template/delete?id=${id}`);
|
return requestClient.delete(`/system/sms-template/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除短信模板 */
|
||||||
|
export function deleteSmsTemplateList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/sms-template/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出短信模板 */
|
/** 导出短信模板 */
|
||||||
export function exportSmsTemplate(params: any) {
|
export function exportSmsTemplate(params: any) {
|
||||||
return requestClient.download('/system/sms-template/export-excel', {
|
return requestClient.download('/system/sms-template/export-excel', {
|
||||||
@@ -65,6 +72,6 @@ export function exportSmsTemplate(params: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 发送短信 */
|
/** 发送短信 */
|
||||||
export function sendSms(data: SystemSmsTemplateApi.SmsSendReqVO) {
|
export function sendSms(data: SystemSmsTemplateApi.SmsSendReq) {
|
||||||
return requestClient.post('/system/sms-template/send-sms', data);
|
return requestClient.post('/system/sms-template/send-sms', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ export namespace SystemSocialUserApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 社交绑定请求 */
|
/** 社交绑定请求 */
|
||||||
export interface SocialUserBindReqVO {
|
export interface SocialUserBindReq {
|
||||||
type: number;
|
type: number;
|
||||||
code: string;
|
code: string;
|
||||||
state: string;
|
state: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 取消社交绑定请求 */
|
/** 取消社交绑定请求 */
|
||||||
export interface SocialUserUnbindReqVO {
|
export interface SocialUserUnbindReq {
|
||||||
type: number;
|
type: number;
|
||||||
openid: string;
|
openid: string;
|
||||||
}
|
}
|
||||||
@@ -49,12 +49,12 @@ export function getSocialUser(id: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 社交绑定,使用 code 授权码 */
|
/** 社交绑定,使用 code 授权码 */
|
||||||
export function socialBind(data: SystemSocialUserApi.SocialUserBindReqVO) {
|
export function socialBind(data: SystemSocialUserApi.SocialUserBindReq) {
|
||||||
return requestClient.post<boolean>('/system/social-user/bind', data);
|
return requestClient.post<boolean>('/system/social-user/bind', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 取消社交绑定 */
|
/** 取消社交绑定 */
|
||||||
export function socialUnbind(data: SystemSocialUserApi.SocialUserUnbindReqVO) {
|
export function socialUnbind(data: SystemSocialUserApi.SocialUserUnbindReq) {
|
||||||
return requestClient.delete<boolean>('/system/social-user/unbind', { data });
|
return requestClient.delete<boolean>('/system/social-user/unbind', { data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,13 @@ export function deleteTenantPackage(id: number) {
|
|||||||
return requestClient.delete(`/system/tenant-package/delete?id=${id}`);
|
return requestClient.delete(`/system/tenant-package/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除租户套餐 */
|
||||||
|
export function deleteTenantPackageList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/tenant-package/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取租户套餐精简信息列表 */
|
/** 获取租户套餐精简信息列表 */
|
||||||
export function getTenantPackageList() {
|
export function getTenantPackageList() {
|
||||||
return requestClient.get<SystemTenantPackageApi.TenantPackage[]>(
|
return requestClient.get<SystemTenantPackageApi.TenantPackage[]>(
|
||||||
|
|||||||
@@ -61,6 +61,13 @@ export function deleteTenant(id: number) {
|
|||||||
return requestClient.delete(`/system/tenant/delete?id=${id}`);
|
return requestClient.delete(`/system/tenant/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除租户 */
|
||||||
|
export function deleteTenantList(ids: number[]) {
|
||||||
|
return requestClient.delete(
|
||||||
|
`/system/tenant/delete-list?ids=${ids.join(',')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出租户 */
|
/** 导出租户 */
|
||||||
export function exportTenant(params: any) {
|
export function exportTenant(params: any) {
|
||||||
return requestClient.download('/system/tenant/export-excel', {
|
return requestClient.download('/system/tenant/export-excel', {
|
||||||
|
|||||||
@@ -49,9 +49,14 @@ export function deleteUser(id: number) {
|
|||||||
return requestClient.delete(`/system/user/delete?id=${id}`);
|
return requestClient.delete(`/system/user/delete?id=${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除用户 */
|
||||||
|
export function deleteUserList(ids: number[]) {
|
||||||
|
return requestClient.delete(`/system/user/delete-list?ids=${ids.join(',')}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出用户 */
|
/** 导出用户 */
|
||||||
export function exportUser(params: any) {
|
export function exportUser(params: any) {
|
||||||
return requestClient.download('/system/user/export', params);
|
return requestClient.download('/system/user/export-excel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 下载用户导入模板 */
|
/** 下载用户导入模板 */
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { requestClient } from '#/api/request';
|
|||||||
|
|
||||||
export namespace SystemUserProfileApi {
|
export namespace SystemUserProfileApi {
|
||||||
/** 用户个人中心信息 */
|
/** 用户个人中心信息 */
|
||||||
export interface UserProfileRespVO {
|
export interface UserProfileResp {
|
||||||
id: number;
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
nickname: string;
|
nickname: string;
|
||||||
@@ -19,13 +19,13 @@ export namespace SystemUserProfileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 更新密码请求 */
|
/** 更新密码请求 */
|
||||||
export interface UpdatePasswordReqVO {
|
export interface UpdatePasswordReq {
|
||||||
oldPassword: string;
|
oldPassword: string;
|
||||||
newPassword: string;
|
newPassword: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新个人信息请求 */
|
/** 更新个人信息请求 */
|
||||||
export interface UpdateProfileReqVO {
|
export interface UpdateProfileReq {
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
@@ -36,21 +36,19 @@ export namespace SystemUserProfileApi {
|
|||||||
|
|
||||||
/** 获取登录用户信息 */
|
/** 获取登录用户信息 */
|
||||||
export function getUserProfile() {
|
export function getUserProfile() {
|
||||||
return requestClient.get<SystemUserProfileApi.UserProfileRespVO>(
|
return requestClient.get<SystemUserProfileApi.UserProfileResp>(
|
||||||
'/system/user/profile/get',
|
'/system/user/profile/get',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改用户个人信息 */
|
/** 修改用户个人信息 */
|
||||||
export function updateUserProfile(
|
export function updateUserProfile(data: SystemUserProfileApi.UpdateProfileReq) {
|
||||||
data: SystemUserProfileApi.UpdateProfileReqVO,
|
|
||||||
) {
|
|
||||||
return requestClient.put('/system/user/profile/update', data);
|
return requestClient.put('/system/user/profile/update', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 修改用户个人密码 */
|
/** 修改用户个人密码 */
|
||||||
export function updateUserPassword(
|
export function updateUserPassword(
|
||||||
data: SystemUserProfileApi.UpdatePasswordReqVO,
|
data: SystemUserProfileApi.UpdatePasswordReq,
|
||||||
) {
|
) {
|
||||||
return requestClient.put('/system/user/profile/update-password', data);
|
return requestClient.put('/system/user/profile/update-password', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { $t, setupI18n } from '#/locales';
|
|||||||
import { setupFormCreate } from '#/plugins/form-create';
|
import { setupFormCreate } from '#/plugins/form-create';
|
||||||
|
|
||||||
import { initComponentAdapter } from './adapter/component';
|
import { initComponentAdapter } from './adapter/component';
|
||||||
|
import { initSetupVbenForm } from './adapter/form';
|
||||||
import App from './app.vue';
|
import App from './app.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
|
||||||
@@ -21,6 +22,9 @@ async function bootstrap(namespace: string) {
|
|||||||
// 初始化组件适配器
|
// 初始化组件适配器
|
||||||
await initComponentAdapter();
|
await initComponentAdapter();
|
||||||
|
|
||||||
|
// 初始化表单组件
|
||||||
|
await initSetupVbenForm();
|
||||||
|
|
||||||
// // 设置弹窗的默认配置
|
// // 设置弹窗的默认配置
|
||||||
// setDefaultModalProps({
|
// setDefaultModalProps({
|
||||||
// fullscreenButton: false,
|
// fullscreenButton: false,
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ withDefaults(
|
|||||||
<Card :body-style="bodyStyle" :title="title" class="mb-4">
|
<Card :body-style="bodyStyle" :title="title" class="mb-4">
|
||||||
<template v-if="title" #title>
|
<template v-if="title" #title>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-4 font-[700]">{{ title }}</span>
|
<span class="text-base font-bold">{{ title }}</span>
|
||||||
<Tooltip placement="right">
|
<Tooltip placement="right">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="max-w-[200px]">{{ message }}</div>
|
<div class="max-w-[200px]">{{ message }}</div>
|
||||||
</template>
|
</template>
|
||||||
<ShieldQuestion :size="14" class="ml-5px" />
|
<ShieldQuestion :size="14" class="ml-1" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div class="pl-20px flex flex-grow">
|
<div class="flex flex-grow pl-5">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { CropperAvatarProps } from './typing';
|
|||||||
import { computed, ref, unref, watch, watchEffect } from 'vue';
|
import { computed, ref, unref, watch, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
@@ -27,13 +28,10 @@ const props = withDefaults(defineProps<CropperAvatarProps>(), {
|
|||||||
const emit = defineEmits(['update:value', 'change']);
|
const emit = defineEmits(['update:value', 'change']);
|
||||||
|
|
||||||
const sourceValue = ref(props.value || '');
|
const sourceValue = ref(props.value || '');
|
||||||
const prefixCls = 'cropper-avatar';
|
|
||||||
const [CropperModal, modalApi] = useVbenModal({
|
const [CropperModal, modalApi] = useVbenModal({
|
||||||
connectedComponent: cropperModal,
|
connectedComponent: cropperModal,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getClass = computed(() => [prefixCls]);
|
|
||||||
|
|
||||||
const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`);
|
const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`);
|
||||||
|
|
||||||
const getIconWidth = computed(
|
const getIconWidth = computed(
|
||||||
@@ -73,28 +71,42 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="getClass" :style="getStyle">
|
<!-- 头像容器 -->
|
||||||
|
<div class="inline-block text-center" :style="getStyle">
|
||||||
|
<!-- 图片包装器 -->
|
||||||
<div
|
<div
|
||||||
:class="`${prefixCls}-image-wrapper`"
|
class="bg-card group relative cursor-pointer overflow-hidden rounded-full border border-gray-200"
|
||||||
:style="getImageWrapperStyle"
|
:style="getImageWrapperStyle"
|
||||||
@click="openModal"
|
@click="openModal"
|
||||||
>
|
>
|
||||||
<div :class="`${prefixCls}-image-mask`" :style="getImageWrapperStyle">
|
<!-- 遮罩层 -->
|
||||||
<span
|
<div
|
||||||
|
class="duration-400 absolute inset-0 flex cursor-pointer items-center justify-center rounded-full bg-black bg-opacity-40 opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
|
:style="getImageWrapperStyle"
|
||||||
|
>
|
||||||
|
<IconifyIcon
|
||||||
|
icon="lucide:cloud-upload"
|
||||||
|
class="m-auto text-gray-400"
|
||||||
:style="{
|
:style="{
|
||||||
...getImageWrapperStyle,
|
...getImageWrapperStyle,
|
||||||
width: `${getIconWidth}`,
|
width: getIconWidth,
|
||||||
height: `${getIconWidth}`,
|
height: getIconWidth,
|
||||||
lineHeight: `${getIconWidth}`,
|
lineHeight: getIconWidth,
|
||||||
}"
|
}"
|
||||||
class="icon-[ant-design--cloud-upload-outlined] text-[#d6d6d6]"
|
/>
|
||||||
></span>
|
|
||||||
</div>
|
</div>
|
||||||
<img v-if="sourceValue" :src="sourceValue" alt="avatar" />
|
<!-- 头像图片 -->
|
||||||
|
<img
|
||||||
|
v-if="sourceValue"
|
||||||
|
:src="sourceValue"
|
||||||
|
alt="avatar"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 上传按钮 -->
|
||||||
<Button
|
<Button
|
||||||
v-if="showBtn"
|
v-if="showBtn"
|
||||||
:class="`${prefixCls}-upload-btn`"
|
class="mx-auto mt-2"
|
||||||
@click="openModal"
|
@click="openModal"
|
||||||
v-bind="btnProps"
|
v-bind="btnProps"
|
||||||
>
|
>
|
||||||
@@ -109,49 +121,3 @@ defineExpose({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.cropper-avatar {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&-image-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-image-mask {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: inherit;
|
|
||||||
height: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
background: rgb(0 0 0 / 40%);
|
|
||||||
border: inherit;
|
|
||||||
border-radius: inherit;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.4s;
|
|
||||||
|
|
||||||
::v-deep(svg) {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-image-mask:hover {
|
|
||||||
opacity: 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-upload-btn {
|
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { CropendResult, CropperModalProps, CropperType } from './typing';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
import { dataURLtoBlob, isFunction } from '@vben/utils';
|
import { dataURLtoBlob, isFunction } from '@vben/utils';
|
||||||
|
|
||||||
@@ -36,13 +37,20 @@ const cropper = ref<CropperType>();
|
|||||||
let scaleX = 1;
|
let scaleX = 1;
|
||||||
let scaleY = 1;
|
let scaleY = 1;
|
||||||
|
|
||||||
const prefixCls = 'cropper-am';
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
onConfirm: handleOk,
|
onConfirm: handleOk,
|
||||||
onOpenChange(isOpen) {
|
onOpenChange(isOpen) {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
// 打开时,进行 loading 加载。后续 CropperImage 组件加载完毕,会自动关闭 loading(通过 handleReady)
|
// 打开时,进行 loading 加载。后续 CropperImage 组件加载完毕,会自动关闭 loading(通过 handleReady)
|
||||||
modalLoading(true);
|
modalLoading(true);
|
||||||
|
const img = new Image();
|
||||||
|
img.src = src.value;
|
||||||
|
img.addEventListener('load', () => {
|
||||||
|
modalLoading(false);
|
||||||
|
});
|
||||||
|
img.addEventListener('error', () => {
|
||||||
|
modalLoading(false);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 关闭时,清空右侧预览
|
// 关闭时,清空右侧预览
|
||||||
previewSource.value = '';
|
previewSource.value = '';
|
||||||
@@ -118,11 +126,15 @@ async function handleOk() {
|
|||||||
:confirm-text="$t('ui.cropper.okText')"
|
:confirm-text="$t('ui.cropper.okText')"
|
||||||
:fullscreen-button="false"
|
:fullscreen-button="false"
|
||||||
:title="$t('ui.cropper.modalTitle')"
|
:title="$t('ui.cropper.modalTitle')"
|
||||||
class="w-[800px]"
|
class="w-2/3"
|
||||||
|
>
|
||||||
|
<div class="flex h-96">
|
||||||
|
<!-- 左侧区域 -->
|
||||||
|
<div class="h-full w-3/5">
|
||||||
|
<!-- 裁剪器容器 -->
|
||||||
|
<div
|
||||||
|
class="relative h-[300px] bg-gradient-to-b from-neutral-50 to-neutral-200"
|
||||||
>
|
>
|
||||||
<div :class="prefixCls">
|
|
||||||
<div :class="`${prefixCls}-left`" class="w-full">
|
|
||||||
<div :class="`${prefixCls}-cropper`">
|
|
||||||
<CropperImage
|
<CropperImage
|
||||||
v-if="src"
|
v-if="src"
|
||||||
:circled="circled"
|
:circled="circled"
|
||||||
@@ -133,7 +145,8 @@ async function handleOk() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="`${prefixCls}-toolbar`">
|
<!-- 工具栏 -->
|
||||||
|
<div class="mt-4 flex items-center justify-between">
|
||||||
<Upload
|
<Upload
|
||||||
:before-upload="handleBeforeUpload"
|
:before-upload="handleBeforeUpload"
|
||||||
:file-list="[]"
|
:file-list="[]"
|
||||||
@@ -143,7 +156,7 @@ async function handleOk() {
|
|||||||
<Button size="small" type="primary">
|
<Button size="small" type="primary">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span class="icon-[ant-design--upload-outlined]"></span>
|
<IconifyIcon icon="lucide:upload" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -159,7 +172,7 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span class="icon-[ant-design--reload-outlined]"></span>
|
<IconifyIcon icon="lucide:rotate-ccw" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -176,9 +189,7 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span
|
<IconifyIcon icon="ant-design:rotate-left-outlined" />
|
||||||
class="icon-[ant-design--rotate-left-outlined]"
|
|
||||||
></span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -189,16 +200,13 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
:disabled="!src"
|
:disabled="!src"
|
||||||
pre-icon="ant-design:rotate-right-outlined"
|
|
||||||
size="small"
|
size="small"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handlerToolbar('rotate', 45)"
|
@click="handlerToolbar('rotate', 45)"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span
|
<IconifyIcon icon="ant-design:rotate-right-outlined" />
|
||||||
class="icon-[ant-design--rotate-right-outlined]"
|
|
||||||
></span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -212,7 +220,7 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span class="icon-[vaadin--arrows-long-h]"></span>
|
<IconifyIcon icon="vaadin:arrows-long-h" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -226,7 +234,7 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span class="icon-[vaadin--arrows-long-v]"></span>
|
<IconifyIcon icon="vaadin:arrows-long-v" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -240,7 +248,7 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span class="icon-[ant-design--zoom-in-outlined]"></span>
|
<IconifyIcon icon="lucide:zoom-in" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -254,7 +262,7 @@ async function handleOk() {
|
|||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<span class="icon-[ant-design--zoom-out-outlined]"></span>
|
<IconifyIcon icon="lucide:zoom-out" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -262,16 +270,26 @@ async function handleOk() {
|
|||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="`${prefixCls}-right`">
|
|
||||||
<div :class="`${prefixCls}-preview`">
|
<!-- 右侧区域 -->
|
||||||
|
<div class="h-full w-2/5">
|
||||||
|
<!-- 预览区域 -->
|
||||||
|
<div
|
||||||
|
class="mx-auto h-56 w-56 overflow-hidden rounded-full border border-gray-200"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
v-if="previewSource"
|
v-if="previewSource"
|
||||||
:alt="$t('ui.cropper.preview')"
|
:alt="$t('ui.cropper.preview')"
|
||||||
:src="previewSource"
|
:src="previewSource"
|
||||||
|
class="h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 头像组合预览 -->
|
||||||
<template v-if="previewSource">
|
<template v-if="previewSource">
|
||||||
<div :class="`${prefixCls}-group`">
|
<div
|
||||||
|
class="mt-2 flex items-center justify-around border-t border-gray-200 pt-2"
|
||||||
|
>
|
||||||
<Avatar :src="previewSource" size="large" />
|
<Avatar :src="previewSource" size="large" />
|
||||||
<Avatar :size="48" :src="previewSource" />
|
<Avatar :size="48" :src="previewSource" />
|
||||||
<Avatar :size="64" :src="previewSource" />
|
<Avatar :size="64" :src="previewSource" />
|
||||||
@@ -282,76 +300,3 @@ async function handleOk() {
|
|||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.cropper-am {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&-left,
|
|
||||||
&-right {
|
|
||||||
height: 340px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-left {
|
|
||||||
width: 55%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-right {
|
|
||||||
width: 45%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-cropper {
|
|
||||||
height: 300px;
|
|
||||||
background: #eee;
|
|
||||||
background-image:
|
|
||||||
linear-gradient(
|
|
||||||
45deg,
|
|
||||||
rgb(0 0 0 / 25%) 25%,
|
|
||||||
transparent 0,
|
|
||||||
transparent 75%,
|
|
||||||
rgb(0 0 0 / 25%) 0
|
|
||||||
),
|
|
||||||
linear-gradient(
|
|
||||||
45deg,
|
|
||||||
rgb(0 0 0 / 25%) 25%,
|
|
||||||
transparent 0,
|
|
||||||
transparent 75%,
|
|
||||||
rgb(0 0 0 / 25%) 0
|
|
||||||
);
|
|
||||||
background-position:
|
|
||||||
0 0,
|
|
||||||
12px 12px;
|
|
||||||
background-size: 24px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-preview {
|
|
||||||
width: 220px;
|
|
||||||
height: 220px;
|
|
||||||
margin: 0 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
padding-top: 8px;
|
|
||||||
margin-top: 8px;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ const imgElRef = ref<ElRef<HTMLImageElement>>();
|
|||||||
const cropper = ref<Cropper | null>();
|
const cropper = ref<Cropper | null>();
|
||||||
const isReady = ref(false);
|
const isReady = ref(false);
|
||||||
|
|
||||||
const prefixCls = 'cropper-image';
|
|
||||||
const debounceRealTimeCropped = useDebounceFn(realTimeCropped, 80);
|
const debounceRealTimeCropped = useDebounceFn(realTimeCropped, 80);
|
||||||
|
|
||||||
const getImageStyle = computed((): CSSProperties => {
|
const getImageStyle = computed((): CSSProperties => {
|
||||||
@@ -46,10 +45,9 @@ const getImageStyle = computed((): CSSProperties => {
|
|||||||
|
|
||||||
const getClass = computed(() => {
|
const getClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
prefixCls,
|
|
||||||
attrs.class,
|
attrs.class,
|
||||||
{
|
{
|
||||||
[`${prefixCls}--circled`]: props.circled,
|
'cropper-image--circled': props.circled,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
@@ -115,10 +113,9 @@ function cropped() {
|
|||||||
imgInfo,
|
imgInfo,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
fileReader.addEventListener('error', () => {
|
||||||
fileReader.onerror = () => {
|
|
||||||
emit('cropendError');
|
emit('cropendError');
|
||||||
};
|
});
|
||||||
}, 'image/png');
|
}, 'image/png');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +154,7 @@ function getRoundedCanvas() {
|
|||||||
:crossorigin="crossorigin"
|
:crossorigin="crossorigin"
|
||||||
:src="src"
|
:src="src"
|
||||||
:style="getImageStyle"
|
:style="getImageStyle"
|
||||||
|
class="h-auto max-w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { isValidColor, TinyColor } from '@vben/utils';
|
||||||
|
|
||||||
import { Tag } from 'ant-design-vue';
|
import { Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
// import { isHexColor } from '@/utils/color' // TODO @芋艿:【可优化】增加 cssClass 的处理 https://gitee.com/yudaocode/yudao-ui-admin-vben/blob/v2.4.1/src/components/DictTag/src/DictTag.vue#L60
|
|
||||||
import { getDictObj } from '#/utils';
|
import { getDictObj } from '#/utils';
|
||||||
|
|
||||||
interface DictTagProps {
|
interface DictTagProps {
|
||||||
@@ -58,15 +59,23 @@ const dictTag = computed(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isValidColor(dict.cssClass)) {
|
||||||
|
colorType = new TinyColor(dict.cssClass).toHexString();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: dict.label || '',
|
label: dict.label || '',
|
||||||
colorType,
|
colorType,
|
||||||
|
cssClass: dict.cssClass,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Tag v-if="dictTag" :color="dictTag.colorType">
|
<Tag
|
||||||
|
v-if="dictTag"
|
||||||
|
:color="dictTag.colorType ? dictTag.colorType : dictTag.cssClass"
|
||||||
|
>
|
||||||
{{ dictTag.label }}
|
{{ dictTag.label }}
|
||||||
</Tag>
|
</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
SelectOption,
|
SelectOption,
|
||||||
} from 'ant-design-vue';
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
import { getDictObj, getIntDictOptions, getStrDictOptions } from '#/utils';
|
import { getDictOptions } from '#/utils';
|
||||||
|
|
||||||
defineOptions({ name: 'DictSelect' });
|
defineOptions({ name: 'DictSelect' });
|
||||||
|
|
||||||
@@ -25,17 +25,16 @@ const props = withDefaults(defineProps<DictSelectProps>(), {
|
|||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
|
|
||||||
// 获得字典配置
|
// 获得字典配置
|
||||||
// TODO @dhb:可以使用 getDictOptions 替代么?
|
const getDictOption = computed(() => {
|
||||||
const getDictOptions = computed(() => {
|
|
||||||
switch (props.valueType) {
|
switch (props.valueType) {
|
||||||
case 'bool': {
|
case 'bool': {
|
||||||
return getDictObj(props.dictType, 'bool');
|
return getDictOptions(props.dictType, 'boolean');
|
||||||
}
|
}
|
||||||
case 'int': {
|
case 'int': {
|
||||||
return getIntDictOptions(props.dictType);
|
return getDictOptions(props.dictType, 'number');
|
||||||
}
|
}
|
||||||
case 'str': {
|
case 'str': {
|
||||||
return getStrDictOptions(props.dictType);
|
return getDictOptions(props.dictType, 'string');
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return [];
|
return [];
|
||||||
@@ -45,27 +44,27 @@ const getDictOptions = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Select v-if="selectType === 'select'" class="w-1/1" v-bind="attrs">
|
<Select v-if="selectType === 'select'" class="w-full" v-bind="attrs">
|
||||||
<SelectOption
|
<SelectOption
|
||||||
v-for="(dict, index) in getDictOptions"
|
v-for="(dict, index) in getDictOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
>
|
>
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
</SelectOption>
|
</SelectOption>
|
||||||
</Select>
|
</Select>
|
||||||
<RadioGroup v-if="selectType === 'radio'" class="w-1/1" v-bind="attrs">
|
<RadioGroup v-if="selectType === 'radio'" class="w-full" v-bind="attrs">
|
||||||
<Radio
|
<Radio
|
||||||
v-for="(dict, index) in getDictOptions"
|
v-for="(dict, index) in getDictOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
>
|
>
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
</Radio>
|
</Radio>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<CheckboxGroup v-if="selectType === 'checkbox'" class="w-1/1" v-bind="attrs">
|
<CheckboxGroup v-if="selectType === 'checkbox'" class="w-full" v-bind="attrs">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-for="(dict, index) in getDictOptions"
|
v-for="(dict, index) in getDictOption"
|
||||||
:key="index"
|
:key="index"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|||||||
// fix:多写此步是为了解决 multiple 属性问题
|
// fix:多写此步是为了解决 multiple 属性问题
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
class="w-1/1"
|
class="w-full"
|
||||||
loading={loading.value}
|
loading={loading.value}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
{...attrs}
|
{...attrs}
|
||||||
@@ -210,7 +210,7 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
class="w-1/1"
|
class="w-full"
|
||||||
loading={loading.value}
|
loading={loading.value}
|
||||||
{...attrs}
|
{...attrs}
|
||||||
// TODO: @dhb52 remote 对等实现, 还是说没作用
|
// TODO: @dhb52 remote 对等实现, 还是说没作用
|
||||||
@@ -235,7 +235,7 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<CheckboxGroup class="w-1/1" {...attrs}>
|
<CheckboxGroup class="w-full" {...attrs}>
|
||||||
{options.value.map(
|
{options.value.map(
|
||||||
(item: { label: any; value: any }, index: any) => (
|
(item: { label: any; value: any }, index: any) => (
|
||||||
<Checkbox key={index} value={item.value}>
|
<Checkbox key={index} value={item.value}>
|
||||||
@@ -254,7 +254,7 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<RadioGroup class="w-1/1" {...attrs}>
|
<RadioGroup class="w-full" {...attrs}>
|
||||||
{options.value.map(
|
{options.value.map(
|
||||||
(item: { label: any; value: any }, index: any) => (
|
(item: { label: any; value: any }, index: any) => (
|
||||||
<Radio key={index} value={item.value}>
|
<Radio key={index} value={item.value}>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ const apiSelectRule = [
|
|||||||
field: 'data',
|
field: 'data',
|
||||||
title: '请求参数 JSON 格式',
|
title: '请求参数 JSON 格式',
|
||||||
props: {
|
props: {
|
||||||
autosize: true,
|
autoSize: true,
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
placeholder: '{"type": 1}',
|
placeholder: '{"type": 1}',
|
||||||
},
|
},
|
||||||
@@ -155,7 +155,7 @@ const apiSelectRule = [
|
|||||||
info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表
|
info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表
|
||||||
(data: any)=>{ label: string; value: any }[]`,
|
(data: any)=>{ label: string; value: any }[]`,
|
||||||
props: {
|
props: {
|
||||||
autosize: true,
|
autoSize: true,
|
||||||
rows: { minRows: 2, maxRows: 6 },
|
rows: { minRows: 2, maxRows: 6 },
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
placeholder: `
|
placeholder: `
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
|
export * from './icons';
|
||||||
|
|
||||||
export { default as TableAction } from './table-action.vue';
|
export { default as TableAction } from './table-action.vue';
|
||||||
export * from './typing';
|
export * from './typing';
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<!-- add by 星语:参考 vben2 的方式,增加 TableAction 组件 -->
|
<!-- add by 星语:参考 vben2 的方式,增加 TableAction 组件 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ButtonType } from 'ant-design-vue/es/button';
|
|
||||||
|
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
import type { ActionItem, PopConfirm } from './typing';
|
import type { ActionItem, PopConfirm } from './typing';
|
||||||
|
|
||||||
import { computed, toRaw } from 'vue';
|
import { computed, unref } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
import { useAccess } from '@vben/access';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
@@ -43,34 +41,31 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
const { hasAccessByCodes } = useAccess();
|
||||||
|
|
||||||
|
/** 检查是否显示 */
|
||||||
function isIfShow(action: ActionItem): boolean {
|
function isIfShow(action: ActionItem): boolean {
|
||||||
const ifShow = action.ifShow;
|
const ifShow = action.ifShow;
|
||||||
|
|
||||||
let isIfShow = true;
|
let isIfShow = true;
|
||||||
|
|
||||||
if (isBoolean(ifShow)) {
|
if (isBoolean(ifShow)) {
|
||||||
isIfShow = ifShow;
|
isIfShow = ifShow;
|
||||||
}
|
}
|
||||||
if (isFunction(ifShow)) {
|
if (isFunction(ifShow)) {
|
||||||
isIfShow = ifShow(action);
|
isIfShow = ifShow(action);
|
||||||
}
|
}
|
||||||
|
if (isIfShow) {
|
||||||
|
isIfShow =
|
||||||
|
hasAccessByCodes(action.auth || []) || (action.auth || []).length === 0;
|
||||||
|
}
|
||||||
return isIfShow;
|
return isIfShow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 处理按钮 actions */
|
||||||
const getActions = computed(() => {
|
const getActions = computed(() => {
|
||||||
return (toRaw(props.actions) || [])
|
return (props.actions || [])
|
||||||
.filter((action) => {
|
.filter((action: ActionItem) => isIfShow(action))
|
||||||
return (
|
.map((action: ActionItem) => {
|
||||||
(hasAccessByCodes(action.auth || []) ||
|
|
||||||
(action.auth || []).length === 0) &&
|
|
||||||
isIfShow(action)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map((action) => {
|
|
||||||
const { popConfirm } = action;
|
const { popConfirm } = action;
|
||||||
return {
|
return {
|
||||||
// getPopupContainer: document.body,
|
type: action.type || 'link',
|
||||||
type: 'link' as ButtonType,
|
|
||||||
...action,
|
...action,
|
||||||
...popConfirm,
|
...popConfirm,
|
||||||
onConfirm: popConfirm?.confirm,
|
onConfirm: popConfirm?.confirm,
|
||||||
@@ -80,19 +75,16 @@ const getActions = computed(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const getDropdownList = computed((): any[] => {
|
/** 处理下拉菜单 actions */
|
||||||
return (toRaw(props.dropDownActions) || [])
|
const getDropdownList = computed(() => {
|
||||||
.filter((action) => {
|
return (props.dropDownActions || [])
|
||||||
return (
|
.filter((action: ActionItem) => isIfShow(action))
|
||||||
(hasAccessByCodes(action.auth || []) ||
|
.map((action: ActionItem, index: number) => {
|
||||||
(action.auth || []).length === 0) &&
|
|
||||||
isIfShow(action)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map((action, index) => {
|
|
||||||
const { label, popConfirm } = action;
|
const { label, popConfirm } = action;
|
||||||
|
const processedAction = { ...action };
|
||||||
|
delete processedAction.icon;
|
||||||
return {
|
return {
|
||||||
...action,
|
...processedAction,
|
||||||
...popConfirm,
|
...popConfirm,
|
||||||
onConfirm: popConfirm?.confirm,
|
onConfirm: popConfirm?.confirm,
|
||||||
onCancel: popConfirm?.cancel,
|
onCancel: popConfirm?.cancel,
|
||||||
@@ -103,8 +95,16 @@ const getDropdownList = computed((): any[] => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Space 组件的 size */
|
||||||
|
const spaceSize = computed(() => {
|
||||||
|
return unref(getActions)?.some((item: ActionItem) => item.type === 'link')
|
||||||
|
? 0
|
||||||
|
: 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 获取 PopConfirm 属性 */
|
||||||
function getPopConfirmProps(attrs: PopConfirm) {
|
function getPopConfirmProps(attrs: PopConfirm) {
|
||||||
const originAttrs: any = attrs;
|
const originAttrs: any = { ...attrs };
|
||||||
delete originAttrs.icon;
|
delete originAttrs.icon;
|
||||||
if (attrs.confirm && isFunction(attrs.confirm)) {
|
if (attrs.confirm && isFunction(attrs.confirm)) {
|
||||||
originAttrs.onConfirm = attrs.confirm;
|
originAttrs.onConfirm = attrs.confirm;
|
||||||
@@ -117,31 +117,44 @@ function getPopConfirmProps(attrs: PopConfirm) {
|
|||||||
return originAttrs;
|
return originAttrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取 Button 属性 */
|
||||||
function getButtonProps(action: ActionItem) {
|
function getButtonProps(action: ActionItem) {
|
||||||
const res = {
|
return {
|
||||||
type: action.type || 'primary',
|
type: action.type || 'link',
|
||||||
...action,
|
danger: action.danger || false,
|
||||||
|
disabled: action.disabled,
|
||||||
|
loading: action.loading,
|
||||||
|
size: action.size,
|
||||||
};
|
};
|
||||||
delete res.icon;
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取 Tooltip 属性 */
|
||||||
|
function getTooltipProps(tooltip: any | string) {
|
||||||
|
if (!tooltip) return {};
|
||||||
|
return typeof tooltip === 'string' ? { title: tooltip } : { ...tooltip };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理菜单点击 */
|
||||||
function handleMenuClick(e: any) {
|
function handleMenuClick(e: any) {
|
||||||
const action = getDropdownList.value[e.key];
|
const action = getDropdownList.value[e.key];
|
||||||
if (action.onClick && isFunction(action.onClick)) {
|
if (action && action.onClick && isFunction(action.onClick)) {
|
||||||
action.onClick();
|
action.onClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 生成稳定的 key */
|
||||||
|
function getActionKey(action: ActionItem, index: number) {
|
||||||
|
return `${action.label || ''}-${action.type || ''}-${index}`;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="m-table-action">
|
<div class="table-actions">
|
||||||
<Space
|
<Space :size="spaceSize">
|
||||||
:size="
|
<template
|
||||||
getActions?.some((item: ActionItem) => item.type === 'link') ? 0 : 8
|
v-for="(action, index) in getActions"
|
||||||
"
|
:key="getActionKey(action, index)"
|
||||||
>
|
>
|
||||||
<template v-for="(action, index) in getActions" :key="index">
|
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
v-if="action.popConfirm"
|
v-if="action.popConfirm"
|
||||||
v-bind="getPopConfirmProps(action.popConfirm)"
|
v-bind="getPopConfirmProps(action.popConfirm)"
|
||||||
@@ -149,13 +162,7 @@ function handleMenuClick(e: any) {
|
|||||||
<template v-if="action.popConfirm.icon" #icon>
|
<template v-if="action.popConfirm.icon" #icon>
|
||||||
<IconifyIcon :icon="action.popConfirm.icon" />
|
<IconifyIcon :icon="action.popConfirm.icon" />
|
||||||
</template>
|
</template>
|
||||||
<Tooltip
|
<Tooltip v-bind="getTooltipProps(action.tooltip)">
|
||||||
v-bind="
|
|
||||||
typeof action.tooltip === 'string'
|
|
||||||
? { title: action.tooltip }
|
|
||||||
: { ...action.tooltip }
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<Button v-bind="getButtonProps(action)">
|
<Button v-bind="getButtonProps(action)">
|
||||||
<template v-if="action.icon" #icon>
|
<template v-if="action.icon" #icon>
|
||||||
<IconifyIcon :icon="action.icon" />
|
<IconifyIcon :icon="action.icon" />
|
||||||
@@ -164,14 +171,7 @@ function handleMenuClick(e: any) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<Tooltip
|
<Tooltip v-else v-bind="getTooltipProps(action.tooltip)">
|
||||||
v-else
|
|
||||||
v-bind="
|
|
||||||
typeof action.tooltip === 'string'
|
|
||||||
? { title: action.tooltip }
|
|
||||||
: { ...action.tooltip }
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<Button v-bind="getButtonProps(action)" @click="action.onClick">
|
<Button v-bind="getButtonProps(action)" @click="action.onClick">
|
||||||
<template v-if="action.icon" #icon>
|
<template v-if="action.icon" #icon>
|
||||||
<IconifyIcon :icon="action.icon" />
|
<IconifyIcon :icon="action.icon" />
|
||||||
@@ -184,16 +184,21 @@ function handleMenuClick(e: any) {
|
|||||||
|
|
||||||
<Dropdown v-if="getDropdownList.length > 0" :trigger="['hover']">
|
<Dropdown v-if="getDropdownList.length > 0" :trigger="['hover']">
|
||||||
<slot name="more">
|
<slot name="more">
|
||||||
<Button size="small" type="link">
|
<Button :type="getDropdownList[0]?.type">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
{{ $t('page.action.more') }}
|
{{ $t('page.action.more') }}
|
||||||
<IconifyIcon class="icon-more" icon="ant-design:more-outlined" />
|
<IconifyIcon icon="lucide:ellipsis-vertical" />
|
||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</slot>
|
</slot>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<Menu @click="handleMenuClick">
|
<Menu>
|
||||||
<Menu.Item v-for="(action, index) in getDropdownList" :key="index">
|
<Menu.Item
|
||||||
|
v-for="(action, index) in getDropdownList"
|
||||||
|
:key="index"
|
||||||
|
:disabled="action.disabled"
|
||||||
|
@click="!action.popConfirm && handleMenuClick({ key: index })"
|
||||||
|
>
|
||||||
<template v-if="action.popConfirm">
|
<template v-if="action.popConfirm">
|
||||||
<Popconfirm v-bind="getPopConfirmProps(action.popConfirm)">
|
<Popconfirm v-bind="getPopConfirmProps(action.popConfirm)">
|
||||||
<template v-if="action.popConfirm.icon" #icon>
|
<template v-if="action.popConfirm.icon" #icon>
|
||||||
@@ -207,7 +212,9 @@ function handleMenuClick(e: any) {
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<IconifyIcon v-if="action.icon" :icon="action.icon" />
|
<IconifyIcon v-if="action.icon" :icon="action.icon" />
|
||||||
<span class="ml-1">{{ action.text }}</span>
|
<span :class="action.icon ? 'ml-1' : ''">
|
||||||
|
{{ action.text }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</template>
|
</template>
|
||||||
@@ -229,9 +236,10 @@ function handleMenuClick(e: any) {
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.m-table-action {
|
.table-actions {
|
||||||
.ant-btn {
|
.ant-btn-link {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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';
|
import type { TooltipProps } from 'ant-design-vue/es/tooltip/Tooltip';
|
||||||
|
|
||||||
export interface PopConfirm {
|
export interface PopConfirm {
|
||||||
@@ -13,6 +16,7 @@ export interface PopConfirm {
|
|||||||
|
|
||||||
export interface ActionItem extends ButtonProps {
|
export interface ActionItem extends ButtonProps {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
type?: ButtonType;
|
||||||
label?: string;
|
label?: string;
|
||||||
color?: 'error' | 'success' | 'warning';
|
color?: 'error' | 'success' | 'warning';
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { VxeToolbarInstance } from '#/adapter/vxe-table';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useContentMaximize, useRefresh } from '@vben/hooks';
|
import { useContentMaximize, useRefresh } from '@vben/hooks';
|
||||||
import { Expand, MsRefresh, Search, TMinimize } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { Button, Tooltip } from 'ant-design-vue';
|
import { Button, Tooltip } from 'ant-design-vue';
|
||||||
|
|
||||||
@@ -41,37 +41,39 @@ defineExpose({
|
|||||||
<slot></slot>
|
<slot></slot>
|
||||||
<Tooltip placement="bottom">
|
<Tooltip placement="bottom">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="max-w-[200px]">搜索</div>
|
<div class="max-w-52">搜索</div>
|
||||||
</template>
|
</template>
|
||||||
<Button
|
<Button
|
||||||
class="ml-2 font-[8px]"
|
class="ml-2 font-normal"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@click="onHiddenSearchBar"
|
@click="onHiddenSearchBar"
|
||||||
>
|
>
|
||||||
<Search :size="15" />
|
<IconifyIcon icon="lucide:search" :size="15" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip placement="bottom">
|
<Tooltip placement="bottom">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="max-w-[200px]">刷新</div>
|
<div class="max-w-52">刷新</div>
|
||||||
</template>
|
</template>
|
||||||
<Button class="ml-2 font-[8px]" shape="circle" @click="refresh">
|
<Button class="ml-2 font-medium" shape="circle" @click="refresh">
|
||||||
<MsRefresh :size="15" />
|
<IconifyIcon icon="lucide:refresh-cw" :size="15" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip placement="bottom">
|
<Tooltip placement="bottom">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="max-w-[200px]">
|
<div class="max-w-52">
|
||||||
{{ contentIsMaximize ? '还原' : '全屏' }}
|
{{ contentIsMaximize ? '还原' : '全屏' }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<Button
|
<Button
|
||||||
class="ml-2 font-[8px]"
|
class="ml-2 font-medium"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@click="toggleMaximizeAndTabbarHidden"
|
@click="toggleMaximizeAndTabbarHidden"
|
||||||
>
|
>
|
||||||
<Expand v-if="!contentIsMaximize" :size="15" />
|
<IconifyIcon
|
||||||
<TMinimize v-else :size="15" />
|
:icon="contentIsMaximize ? 'lucide:minimize' : 'lucide:maximize'"
|
||||||
|
:size="15"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||||
|
|
||||||
import type { AxiosResponse } from '@vben/request';
|
import type { FileUploadProps } from './typing';
|
||||||
|
|
||||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||||
|
|
||||||
@@ -20,31 +20,7 @@ import { useUpload, useUploadType } from './use-upload';
|
|||||||
|
|
||||||
defineOptions({ name: 'FileUpload', inheritAttrs: false });
|
defineOptions({ name: 'FileUpload', inheritAttrs: false });
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||||
defineProps<{
|
|
||||||
// 根据后缀,或者其他
|
|
||||||
accept?: string[];
|
|
||||||
api?: (
|
|
||||||
file: File,
|
|
||||||
onUploadProgress?: AxiosProgressEvent,
|
|
||||||
) => Promise<AxiosResponse<any>>;
|
|
||||||
// 上传的目录
|
|
||||||
directory?: string;
|
|
||||||
disabled?: boolean;
|
|
||||||
helpText?: string;
|
|
||||||
// 最大数量的文件,Infinity不限制
|
|
||||||
maxNumber?: number;
|
|
||||||
// 文件最大多少MB
|
|
||||||
maxSize?: number;
|
|
||||||
// 是否支持多选
|
|
||||||
multiple?: boolean;
|
|
||||||
// support xxx.xxx.xx
|
|
||||||
resultField?: string;
|
|
||||||
// 是否显示下面的描述
|
|
||||||
showDescription?: boolean;
|
|
||||||
value?: string | string[];
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
value: () => [],
|
value: () => [],
|
||||||
directory: undefined,
|
directory: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@@ -56,9 +32,8 @@ const props = withDefaults(
|
|||||||
api: undefined,
|
api: undefined,
|
||||||
resultField: '',
|
resultField: '',
|
||||||
showDescription: false,
|
showDescription: false,
|
||||||
},
|
});
|
||||||
);
|
const emit = defineEmits(['change', 'update:value', 'delete', 'returnText']);
|
||||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
|
||||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||||
const isInnerOperate = ref<boolean>(false);
|
const isInnerOperate = ref<boolean>(false);
|
||||||
const { getStringAccept } = useUploadType({
|
const { getStringAccept } = useUploadType({
|
||||||
@@ -112,7 +87,7 @@ watch(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemove = async (file: UploadFile) => {
|
async function handleRemove(file: UploadFile) {
|
||||||
if (fileList.value) {
|
if (fileList.value) {
|
||||||
const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
||||||
index !== -1 && fileList.value.splice(index, 1);
|
index !== -1 && fileList.value.splice(index, 1);
|
||||||
@@ -122,9 +97,12 @@ const handleRemove = async (file: UploadFile) => {
|
|||||||
emit('change', value);
|
emit('change', value);
|
||||||
emit('delete', file);
|
emit('delete', file);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
async function beforeUpload(file: File) {
|
||||||
|
const fileContent = await file.text();
|
||||||
|
emit('returnText', fileContent);
|
||||||
|
|
||||||
const beforeUpload = async (file: File) => {
|
|
||||||
const { maxSize, accept } = props;
|
const { maxSize, accept } = props;
|
||||||
const isAct = checkFileType(file, accept);
|
const isAct = checkFileType(file, accept);
|
||||||
if (!isAct) {
|
if (!isAct) {
|
||||||
@@ -141,7 +119,7 @@ const beforeUpload = async (file: File) => {
|
|||||||
setTimeout(() => (isLtMsg.value = true), 1000);
|
setTimeout(() => (isLtMsg.value = true), 1000);
|
||||||
}
|
}
|
||||||
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
||||||
};
|
}
|
||||||
|
|
||||||
async function customRequest(info: UploadRequestOption<any>) {
|
async function customRequest(info: UploadRequestOption<any>) {
|
||||||
let { api } = props;
|
let { api } = props;
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* 默认图片类型
|
||||||
|
*/
|
||||||
|
export const defaultImageAccepts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
|
|
||||||
export function checkFileType(file: File, accepts: string[]) {
|
export function checkFileType(file: File, accepts: string[]) {
|
||||||
if (!accepts || accepts.length === 0) {
|
if (!accepts || accepts.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
@@ -7,11 +12,6 @@ export function checkFileType(file: File, accepts: string[]) {
|
|||||||
return reg.test(file.name);
|
return reg.test(file.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认图片类型
|
|
||||||
*/
|
|
||||||
export const defaultImageAccepts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
|
||||||
|
|
||||||
export function checkImgType(
|
export function checkImgType(
|
||||||
file: File,
|
file: File,
|
||||||
accepts: string[] = defaultImageAccepts,
|
accepts: string[] = defaultImageAccepts,
|
||||||
|
|||||||
@@ -2,15 +2,13 @@
|
|||||||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||||
|
|
||||||
import type { AxiosResponse } from '@vben/request';
|
import type { FileUploadProps } from './typing';
|
||||||
|
|
||||||
import type { UploadListType } from './typing';
|
|
||||||
|
|
||||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||||
|
|
||||||
import { ref, toRefs, watch } from 'vue';
|
import { ref, toRefs, watch } from 'vue';
|
||||||
|
|
||||||
import { CloudUpload } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
import { isFunction, isObject, isString } from '@vben/utils';
|
import { isFunction, isObject, isString } from '@vben/utils';
|
||||||
|
|
||||||
@@ -22,32 +20,7 @@ import { useUpload, useUploadType } from './use-upload';
|
|||||||
|
|
||||||
defineOptions({ name: 'ImageUpload', inheritAttrs: false });
|
defineOptions({ name: 'ImageUpload', inheritAttrs: false });
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||||
defineProps<{
|
|
||||||
// 根据后缀,或者其他
|
|
||||||
accept?: string[];
|
|
||||||
api?: (
|
|
||||||
file: File,
|
|
||||||
onUploadProgress?: AxiosProgressEvent,
|
|
||||||
) => Promise<AxiosResponse<any>>;
|
|
||||||
// 上传的目录
|
|
||||||
directory?: string;
|
|
||||||
disabled?: boolean;
|
|
||||||
helpText?: string;
|
|
||||||
listType?: UploadListType;
|
|
||||||
// 最大数量的文件,Infinity不限制
|
|
||||||
maxNumber?: number;
|
|
||||||
// 文件最大多少MB
|
|
||||||
maxSize?: number;
|
|
||||||
// 是否支持多选
|
|
||||||
multiple?: boolean;
|
|
||||||
// support xxx.xxx.xx
|
|
||||||
resultField?: string;
|
|
||||||
// 是否显示下面的描述
|
|
||||||
showDescription?: boolean;
|
|
||||||
value?: string | string[];
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
value: () => [],
|
value: () => [],
|
||||||
directory: undefined,
|
directory: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@@ -60,8 +33,7 @@ const props = withDefaults(
|
|||||||
api: undefined,
|
api: undefined,
|
||||||
resultField: '',
|
resultField: '',
|
||||||
showDescription: true,
|
showDescription: true,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
const emit = defineEmits(['change', 'update:value', 'delete']);
|
||||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||||
const isInnerOperate = ref<boolean>(false);
|
const isInnerOperate = ref<boolean>(false);
|
||||||
@@ -130,7 +102,7 @@ function getBase64<T extends ArrayBuffer | null | string>(file: File) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePreview = async (file: UploadFile) => {
|
async function handlePreview(file: UploadFile) {
|
||||||
if (!file.url && !file.preview) {
|
if (!file.url && !file.preview) {
|
||||||
file.preview = await getBase64<string>(file.originFileObj!);
|
file.preview = await getBase64<string>(file.originFileObj!);
|
||||||
}
|
}
|
||||||
@@ -141,9 +113,9 @@ const handlePreview = async (file: UploadFile) => {
|
|||||||
previewImage.value.slice(
|
previewImage.value.slice(
|
||||||
Math.max(0, previewImage.value.lastIndexOf('/') + 1),
|
Math.max(0, previewImage.value.lastIndexOf('/') + 1),
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleRemove = async (file: UploadFile) => {
|
async function handleRemove(file: UploadFile) {
|
||||||
if (fileList.value) {
|
if (fileList.value) {
|
||||||
const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
||||||
index !== -1 && fileList.value.splice(index, 1);
|
index !== -1 && fileList.value.splice(index, 1);
|
||||||
@@ -153,14 +125,14 @@ const handleRemove = async (file: UploadFile) => {
|
|||||||
emit('change', value);
|
emit('change', value);
|
||||||
emit('delete', file);
|
emit('delete', file);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
function handleCancel() {
|
||||||
previewOpen.value = false;
|
previewOpen.value = false;
|
||||||
previewTitle.value = '';
|
previewTitle.value = '';
|
||||||
};
|
}
|
||||||
|
|
||||||
const beforeUpload = async (file: File) => {
|
async function beforeUpload(file: File) {
|
||||||
const { maxSize, accept } = props;
|
const { maxSize, accept } = props;
|
||||||
const isAct = checkImgType(file, accept);
|
const isAct = checkImgType(file, accept);
|
||||||
if (!isAct) {
|
if (!isAct) {
|
||||||
@@ -177,7 +149,7 @@ const beforeUpload = async (file: File) => {
|
|||||||
setTimeout(() => (isLtMsg.value = true), 1000);
|
setTimeout(() => (isLtMsg.value = true), 1000);
|
||||||
}
|
}
|
||||||
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
||||||
};
|
}
|
||||||
|
|
||||||
async function customRequest(info: UploadRequestOption<any>) {
|
async function customRequest(info: UploadRequestOption<any>) {
|
||||||
let { api } = props;
|
let { api } = props;
|
||||||
@@ -242,13 +214,13 @@ function getValue() {
|
|||||||
v-if="fileList && fileList.length < maxNumber"
|
v-if="fileList && fileList.length < maxNumber"
|
||||||
class="flex flex-col items-center justify-center"
|
class="flex flex-col items-center justify-center"
|
||||||
>
|
>
|
||||||
<CloudUpload />
|
<IconifyIcon icon="lucide:cloud-upload" />
|
||||||
<div class="mt-2">{{ $t('ui.upload.imgUpload') }}</div>
|
<div class="mt-2">{{ $t('ui.upload.imgUpload') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</Upload>
|
</Upload>
|
||||||
<div
|
<div
|
||||||
v-if="showDescription"
|
v-if="showDescription"
|
||||||
class="mt-2 flex flex-wrap items-center text-[14px]"
|
class="mt-2 flex flex-wrap items-center text-sm"
|
||||||
>
|
>
|
||||||
请上传不超过
|
请上传不超过
|
||||||
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export { default as FileUpload } from './file-upload.vue';
|
export { default as FileUpload } from './file-upload.vue';
|
||||||
export { default as ImageUpload } from './image-upload.vue';
|
export { default as ImageUpload } from './image-upload.vue';
|
||||||
|
export { default as InputUpload } from './input-upload.vue';
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import type { AxiosResponse } from '@vben/request';
|
||||||
|
|
||||||
|
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||||
|
|
||||||
export enum UploadResultStatus {
|
export enum UploadResultStatus {
|
||||||
DONE = 'done',
|
DONE = 'done',
|
||||||
ERROR = 'error',
|
ERROR = 'error',
|
||||||
@@ -6,3 +10,28 @@ export enum UploadResultStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type UploadListType = 'picture' | 'picture-card' | 'text';
|
export type UploadListType = 'picture' | 'picture-card' | 'text';
|
||||||
|
|
||||||
|
export interface FileUploadProps {
|
||||||
|
// 根据后缀,或者其他
|
||||||
|
accept?: string[];
|
||||||
|
api?: (
|
||||||
|
file: File,
|
||||||
|
onUploadProgress?: AxiosProgressEvent,
|
||||||
|
) => Promise<AxiosResponse<any>>;
|
||||||
|
// 上传的目录
|
||||||
|
directory?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
helpText?: string;
|
||||||
|
listType?: UploadListType;
|
||||||
|
// 最大数量的文件,Infinity不限制
|
||||||
|
maxNumber?: number;
|
||||||
|
// 文件最大多少MB
|
||||||
|
maxSize?: number;
|
||||||
|
// 是否支持多选
|
||||||
|
multiple?: boolean;
|
||||||
|
// support xxx.xxx.xx
|
||||||
|
resultField?: string;
|
||||||
|
// 是否显示下面的描述
|
||||||
|
showDescription?: boolean;
|
||||||
|
value?: string | string[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,17 +80,17 @@ export function useUploadType({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
|
// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
|
||||||
export const useUpload = (directory?: string) => {
|
export function useUpload(directory?: string) {
|
||||||
// 后端上传地址
|
// 后端上传地址
|
||||||
const uploadUrl = getUploadUrl();
|
const uploadUrl = getUploadUrl();
|
||||||
// 是否使用前端直连上传
|
// 是否使用前端直连上传
|
||||||
const isClientUpload =
|
const isClientUpload =
|
||||||
UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE;
|
UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE;
|
||||||
// 重写ElUpload上传方法
|
// 重写ElUpload上传方法
|
||||||
const httpRequest = async (
|
async function httpRequest(
|
||||||
file: File,
|
file: File,
|
||||||
onUploadProgress?: AxiosProgressEvent,
|
onUploadProgress?: AxiosProgressEvent,
|
||||||
) => {
|
) {
|
||||||
// 模式一:前端上传
|
// 模式一:前端上传
|
||||||
if (isClientUpload) {
|
if (isClientUpload) {
|
||||||
// 1.1 生成文件名称
|
// 1.1 生成文件名称
|
||||||
@@ -114,20 +114,20 @@ export const useUpload = (directory?: string) => {
|
|||||||
// 模式二:后端上传
|
// 模式二:后端上传
|
||||||
return uploadFile({ file, directory }, onUploadProgress);
|
return uploadFile({ file, directory }, onUploadProgress);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uploadUrl,
|
uploadUrl,
|
||||||
httpRequest,
|
httpRequest,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得上传 URL
|
* 获得上传 URL
|
||||||
*/
|
*/
|
||||||
export const getUploadUrl = (): string => {
|
export function getUploadUrl(): string {
|
||||||
return `${apiURL}/infra/file/upload`;
|
return `${apiURL}/infra/file/upload`;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建文件信息
|
* 创建文件信息
|
||||||
@@ -135,7 +135,10 @@ export const getUploadUrl = (): string => {
|
|||||||
* @param vo 文件预签名信息
|
* @param vo 文件预签名信息
|
||||||
* @param file 文件
|
* @param file 文件
|
||||||
*/
|
*/
|
||||||
function createFile0(vo: InfraFileApi.FilePresignedUrlRespVO, file: File) {
|
function createFile0(
|
||||||
|
vo: InfraFileApi.FilePresignedUrlResp,
|
||||||
|
file: File,
|
||||||
|
): InfraFileApi.File {
|
||||||
const fileVO = {
|
const fileVO = {
|
||||||
configId: vo.configId,
|
configId: vo.configId,
|
||||||
url: vo.url,
|
url: vo.url,
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import type { TableToolbar } from '#/components/table-toolbar';
|
|||||||
|
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
// TODO @puhui999:这里的注释、目的写下;
|
/**
|
||||||
|
* vxe 原生工具栏挂载封装
|
||||||
|
* 解决每个组件使用 vxe-table 组件时都需要写一遍的问题
|
||||||
|
*/
|
||||||
export function useTableToolbar() {
|
export function useTableToolbar() {
|
||||||
const hiddenSearchBar = ref(false); // 隐藏搜索栏
|
const hiddenSearchBar = ref(false); // 隐藏搜索栏
|
||||||
const tableToolbarRef = ref<InstanceType<typeof TableToolbar>>();
|
const tableToolbarRef = ref<InstanceType<typeof TableToolbar>>();
|
||||||
@@ -15,7 +18,7 @@ export function useTableToolbar() {
|
|||||||
const table = tableRef.value;
|
const table = tableRef.value;
|
||||||
const tableToolbar = tableToolbarRef.value;
|
const tableToolbar = tableToolbarRef.value;
|
||||||
if (table && tableToolbar) {
|
if (table && tableToolbar) {
|
||||||
// TODO @puhui999:通过 nexttick 可以解决么?
|
// 延迟 1 秒,确保 toolbar 组件已经挂载
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
const toolbar = tableToolbar.getToolbarRef();
|
const toolbar = tableToolbar.getToolbarRef();
|
||||||
if (!toolbar) {
|
if (!toolbar) {
|
||||||
@@ -29,10 +32,9 @@ export function useTableToolbar() {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => tableRef.value,
|
() => tableRef.value,
|
||||||
(val) => {
|
async (val) => {
|
||||||
if (!val || isBound.value) return;
|
if (!val || isBound.value) return;
|
||||||
// TODO @puhui999:这里要处理下 promise 的告警么?
|
await bindTableToolbar();
|
||||||
bindTableToolbar();
|
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,12 +4,19 @@ import { computed } from 'vue';
|
|||||||
import { AuthPageLayout } from '@vben/layouts';
|
import { AuthPageLayout } from '@vben/layouts';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
const appName = computed(() => preferences.app.name);
|
const appName = computed(() => preferences.app.name);
|
||||||
const logo = computed(() => preferences.logo.source);
|
const logo = computed(() => preferences.logo.source);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthPageLayout :app-name="appName" :logo="logo">
|
<AuthPageLayout
|
||||||
|
:app-name="appName"
|
||||||
|
:logo="logo"
|
||||||
|
:page-description="$t('authentication.pageDesc')"
|
||||||
|
:page-title="$t('authentication.pageTitle')"
|
||||||
|
>
|
||||||
<!-- 自定义工具栏 -->
|
<!-- 自定义工具栏 -->
|
||||||
<!-- <template #toolbar></template> -->
|
<!-- <template #toolbar></template> -->
|
||||||
</AuthPageLayout>
|
</AuthPageLayout>
|
||||||
|
|||||||
@@ -1,20 +1,33 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { NotificationItem } from '@vben/layouts';
|
import type { NotificationItem } from '@vben/layouts';
|
||||||
|
|
||||||
|
import type { SystemTenantApi } from '#/api/system/tenant';
|
||||||
|
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { useAccess } from '@vben/access';
|
||||||
import { AuthenticationLoginExpiredModal, useVbenModal } from '@vben/common-ui';
|
import { AuthenticationLoginExpiredModal, useVbenModal } from '@vben/common-ui';
|
||||||
import { useWatermark } from '@vben/hooks';
|
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||||
import { AntdProfileOutlined } from '@vben/icons';
|
import { isTenantEnable, useTabs, useWatermark } from '@vben/hooks';
|
||||||
|
import {
|
||||||
|
AntdProfileOutlined,
|
||||||
|
BookOpenText,
|
||||||
|
CircleHelp,
|
||||||
|
MdiGithub,
|
||||||
|
} from '@vben/icons';
|
||||||
import {
|
import {
|
||||||
BasicLayout,
|
BasicLayout,
|
||||||
|
Help,
|
||||||
LockScreen,
|
LockScreen,
|
||||||
Notification,
|
Notification,
|
||||||
|
TenantDropdown,
|
||||||
UserDropdown,
|
UserDropdown,
|
||||||
} from '@vben/layouts';
|
} from '@vben/layouts';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
import { useAccessStore, useUserStore } from '@vben/stores';
|
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||||
import { formatDateTime } from '@vben/utils';
|
import { formatDateTime, openWindow } from '@vben/utils';
|
||||||
|
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUnreadNotifyMessageCount,
|
getUnreadNotifyMessageCount,
|
||||||
@@ -22,18 +35,18 @@ import {
|
|||||||
updateAllNotifyMessageRead,
|
updateAllNotifyMessageRead,
|
||||||
updateNotifyMessageRead,
|
updateNotifyMessageRead,
|
||||||
} from '#/api/system/notify/message';
|
} from '#/api/system/notify/message';
|
||||||
|
import { getSimpleTenantList } from '#/api/system/tenant';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { router } from '#/router';
|
import { router } from '#/router';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||||
|
|
||||||
import Help from './components/help.vue';
|
|
||||||
import TenantDropdown from './components/tenant-dropdown.vue';
|
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
|
const { hasAccessByCodes } = useAccess();
|
||||||
const { destroyWatermark, updateWatermark } = useWatermark();
|
const { destroyWatermark, updateWatermark } = useWatermark();
|
||||||
|
const { closeOtherTabs, refreshTab } = useTabs();
|
||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([]);
|
const notifications = ref<NotificationItem[]>([]);
|
||||||
const unreadCount = ref(0);
|
const unreadCount = ref(0);
|
||||||
@@ -142,10 +155,41 @@ function handleNotificationOpen(open: boolean) {
|
|||||||
handleNotificationGetUnreadCount();
|
handleNotificationGetUnreadCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 租户列表
|
||||||
|
const tenants = ref<SystemTenantApi.Tenant[]>([]);
|
||||||
|
const tenantEnable = computed(
|
||||||
|
() => hasAccessByCodes(['system:tenant:visit']) && isTenantEnable(),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 获取租户列表 */
|
||||||
|
async function handleGetTenantList() {
|
||||||
|
if (tenantEnable.value) {
|
||||||
|
tenants.value = await getSimpleTenantList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理租户切换 */
|
||||||
|
async function handleTenantChange(tenant: SystemTenantApi.Tenant) {
|
||||||
|
if (!tenant || !tenant.id) {
|
||||||
|
message.error('切换租户失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置访问租户 ID
|
||||||
|
accessStore.setVisitTenantId(tenant.id as number);
|
||||||
|
// 关闭其他标签页,只保留当前页
|
||||||
|
await closeOtherTabs();
|
||||||
|
// 刷新当前页面
|
||||||
|
await refreshTab();
|
||||||
|
// 提示切换成功
|
||||||
|
message.success(`切换当前租户为: ${tenant.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 初始化 ==========
|
// ========== 初始化 ==========
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 首次加载未读数量
|
// 首次加载未读数量
|
||||||
handleNotificationGetUnreadCount();
|
handleNotificationGetUnreadCount();
|
||||||
|
// 获取租户列表
|
||||||
|
handleGetTenantList();
|
||||||
// 轮询刷新未读数量
|
// 轮询刷新未读数量
|
||||||
setInterval(
|
setInterval(
|
||||||
() => {
|
() => {
|
||||||
@@ -198,7 +242,14 @@ watch(
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #header-right-1>
|
<template #header-right-1>
|
||||||
<TenantDropdown class="w-30 mr-2" />
|
<div v-if="tenantEnable">
|
||||||
|
<TenantDropdown
|
||||||
|
class="mr-2"
|
||||||
|
:tenant-list="tenants"
|
||||||
|
:visit-tenant-id="accessStore.visitTenantId"
|
||||||
|
@success="handleTenantChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<AuthenticationLoginExpiredModal
|
<AuthenticationLoginExpiredModal
|
||||||
|
|||||||
@@ -1,10 +1,248 @@
|
|||||||
// todo @芋艿:要不要共享
|
// todo @芋艿:要不要共享
|
||||||
/**
|
/**
|
||||||
* Created by 千通源码
|
* Created by 芋道源码
|
||||||
*
|
*
|
||||||
* 枚举类
|
* 枚举类
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 平台的枚举
|
||||||
|
*/
|
||||||
|
export const AiPlatformEnum = {
|
||||||
|
TONG_YI: 'TongYi', // 阿里
|
||||||
|
YI_YAN: 'YiYan', // 百度
|
||||||
|
DEEP_SEEK: 'DeepSeek', // DeepSeek
|
||||||
|
ZHI_PU: 'ZhiPu', // 智谱 AI
|
||||||
|
XING_HUO: 'XingHuo', // 讯飞
|
||||||
|
SiliconFlow: 'SiliconFlow', // 硅基流动
|
||||||
|
OPENAI: 'OpenAI',
|
||||||
|
Ollama: 'Ollama',
|
||||||
|
STABLE_DIFFUSION: 'StableDiffusion', // Stability AI
|
||||||
|
MIDJOURNEY: 'Midjourney', // Midjourney
|
||||||
|
SUNO: 'Suno', // Suno AI
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AiModelTypeEnum = {
|
||||||
|
CHAT: 1, // 聊天
|
||||||
|
IMAGE: 2, // 图像
|
||||||
|
VOICE: 3, // 音频
|
||||||
|
VIDEO: 4, // 视频
|
||||||
|
EMBEDDING: 5, // 向量
|
||||||
|
RERANK: 6, // 重排
|
||||||
|
};
|
||||||
|
export interface ImageModel {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
image?: string;
|
||||||
|
}
|
||||||
|
export const OtherPlatformEnum: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: AiPlatformEnum.TONG_YI,
|
||||||
|
name: '通义万相',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AiPlatformEnum.YI_YAN,
|
||||||
|
name: '百度千帆',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AiPlatformEnum.ZHI_PU,
|
||||||
|
name: '智谱 AI',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: AiPlatformEnum.SiliconFlow,
|
||||||
|
name: '硅基流动',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* AI 图像生成状态的枚举
|
||||||
|
*/
|
||||||
|
export const AiImageStatusEnum = {
|
||||||
|
IN_PROGRESS: 10, // 进行中
|
||||||
|
SUCCESS: 20, // 已完成
|
||||||
|
FAIL: 30, // 已失败
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* AI 音乐生成状态的枚举
|
||||||
|
*/
|
||||||
|
export const AiMusicStatusEnum = {
|
||||||
|
IN_PROGRESS: 10, // 进行中
|
||||||
|
SUCCESS: 20, // 已完成
|
||||||
|
FAIL: 30, // 已失败
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 写作类型的枚举
|
||||||
|
*/
|
||||||
|
export enum AiWriteTypeEnum {
|
||||||
|
WRITING = 1, // 撰写
|
||||||
|
REPLY, // 回复
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 【图片 UI】相关的枚举 ==========
|
||||||
|
|
||||||
|
export const ImageHotWords = [
|
||||||
|
'中国旗袍',
|
||||||
|
'古装美女',
|
||||||
|
'卡通头像',
|
||||||
|
'机甲战士',
|
||||||
|
'童话小屋',
|
||||||
|
'中国长城',
|
||||||
|
]; // 图片热词
|
||||||
|
|
||||||
|
export const ImageHotEnglishWords = [
|
||||||
|
'Chinese Cheongsam',
|
||||||
|
'Ancient Beauty',
|
||||||
|
'Cartoon Avatar',
|
||||||
|
'Mech Warrior',
|
||||||
|
'Fairy Tale Cottage',
|
||||||
|
'The Great Wall of China',
|
||||||
|
]; // 图片热词(英文)
|
||||||
|
|
||||||
|
export const StableDiffusionSamplers: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: 'DDIM',
|
||||||
|
name: 'DDIM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'DDPM',
|
||||||
|
name: 'DDPM',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_DPMPP_2M',
|
||||||
|
name: 'K_DPMPP_2M',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_DPMPP_2S_ANCESTRAL',
|
||||||
|
name: 'K_DPMPP_2S_ANCESTRAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_DPM_2',
|
||||||
|
name: 'K_DPM_2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_DPM_2_ANCESTRAL',
|
||||||
|
name: 'K_DPM_2_ANCESTRAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_EULER',
|
||||||
|
name: 'K_EULER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_EULER_ANCESTRAL',
|
||||||
|
name: 'K_EULER_ANCESTRAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_HEUN',
|
||||||
|
name: 'K_HEUN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'K_LMS',
|
||||||
|
name: 'K_LMS',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const StableDiffusionStylePresets: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: '3d-model',
|
||||||
|
name: '3d-model',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'analog-film',
|
||||||
|
name: 'analog-film',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'anime',
|
||||||
|
name: 'anime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cinematic',
|
||||||
|
name: 'cinematic',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'comic-book',
|
||||||
|
name: 'comic-book',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'digital-art',
|
||||||
|
name: 'digital-art',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'enhance',
|
||||||
|
name: 'enhance',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fantasy-art',
|
||||||
|
name: 'fantasy-art',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'isometric',
|
||||||
|
name: 'isometric',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'line-art',
|
||||||
|
name: 'line-art',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'low-poly',
|
||||||
|
name: 'low-poly',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'modeling-compound',
|
||||||
|
name: 'modeling-compound',
|
||||||
|
},
|
||||||
|
// neon-punk origami photographic pixel-art tile-texture
|
||||||
|
{
|
||||||
|
key: 'neon-punk',
|
||||||
|
name: 'neon-punk',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'origami',
|
||||||
|
name: 'origami',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'photographic',
|
||||||
|
name: 'photographic',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pixel-art',
|
||||||
|
name: 'pixel-art',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tile-texture',
|
||||||
|
name: 'tile-texture',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const StableDiffusionClipGuidancePresets: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: 'NONE',
|
||||||
|
name: 'NONE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FAST_BLUE',
|
||||||
|
name: 'FAST_BLUE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FAST_GREEN',
|
||||||
|
name: 'FAST_GREEN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SIMPLE',
|
||||||
|
name: 'SIMPLE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SLOW',
|
||||||
|
name: 'SLOW',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SLOWER',
|
||||||
|
name: 'SLOWER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SLOWEST',
|
||||||
|
name: 'SLOWEST',
|
||||||
|
},
|
||||||
|
];
|
||||||
// ========== COMMON 模块 ==========
|
// ========== COMMON 模块 ==========
|
||||||
// 全局通用状态枚举
|
// 全局通用状态枚举
|
||||||
export const CommonStatusEnum = {
|
export const CommonStatusEnum = {
|
||||||
@@ -92,7 +330,136 @@ export const InfraApiErrorLogProcessStatusEnum = {
|
|||||||
DONE: 1, // 已处理
|
DONE: 1, // 已处理
|
||||||
IGNORE: 2, // 已忽略
|
IGNORE: 2, // 已忽略
|
||||||
};
|
};
|
||||||
|
export interface ImageSize {
|
||||||
|
key: string;
|
||||||
|
name?: string;
|
||||||
|
style: string;
|
||||||
|
width: string;
|
||||||
|
height: string;
|
||||||
|
}
|
||||||
|
export const Dall3SizeList: ImageSize[] = [
|
||||||
|
{
|
||||||
|
key: '1024x1024',
|
||||||
|
name: '1:1',
|
||||||
|
width: '1024',
|
||||||
|
height: '1024',
|
||||||
|
style: 'width: 30px; height: 30px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '1024x1792',
|
||||||
|
name: '3:5',
|
||||||
|
width: '1024',
|
||||||
|
height: '1792',
|
||||||
|
style: 'width: 30px; height: 50px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '1792x1024',
|
||||||
|
name: '5:3',
|
||||||
|
width: '1792',
|
||||||
|
height: '1024',
|
||||||
|
style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Dall3Models: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: 'dall-e-3',
|
||||||
|
name: 'DALL·E 3',
|
||||||
|
image: `/static/imgs/ai/dall2.jpg`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dall-e-2',
|
||||||
|
name: 'DALL·E 2',
|
||||||
|
image: `/static/imgs/ai/dall3.jpg`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const Dall3StyleList: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: 'vivid',
|
||||||
|
name: '清晰',
|
||||||
|
image: `/static/imgs/ai/qingxi.jpg`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'natural',
|
||||||
|
name: '自然',
|
||||||
|
image: `/static/imgs/ai/ziran.jpg`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const MidjourneyModels: ImageModel[] = [
|
||||||
|
{
|
||||||
|
key: 'midjourney',
|
||||||
|
name: 'MJ',
|
||||||
|
image: 'https://bigpt8.com/pc/_nuxt/mj.34a61377.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'niji',
|
||||||
|
name: 'NIJI',
|
||||||
|
image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const MidjourneyVersions = [
|
||||||
|
{
|
||||||
|
value: '6.0',
|
||||||
|
label: 'v6.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '5.2',
|
||||||
|
label: 'v5.2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '5.1',
|
||||||
|
label: 'v5.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '5.0',
|
||||||
|
label: 'v5.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '4.0',
|
||||||
|
label: 'v4.0',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const NijiVersionList = [
|
||||||
|
{
|
||||||
|
value: '5',
|
||||||
|
label: 'v5',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const MidjourneySizeList: ImageSize[] = [
|
||||||
|
{
|
||||||
|
key: '1:1',
|
||||||
|
width: '1',
|
||||||
|
height: '1',
|
||||||
|
style: 'width: 30px; height: 30px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3:4',
|
||||||
|
width: '3',
|
||||||
|
height: '4',
|
||||||
|
style: 'width: 30px; height: 40px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4:3',
|
||||||
|
width: '4',
|
||||||
|
height: '3',
|
||||||
|
style: 'width: 40px; height: 30px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '9:16',
|
||||||
|
width: '9',
|
||||||
|
height: '16',
|
||||||
|
style: 'width: 30px; height: 50px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '16:9',
|
||||||
|
width: '16',
|
||||||
|
height: '9',
|
||||||
|
style: 'width: 50px; height: 30px;background-color: #dcdcdc;',
|
||||||
|
},
|
||||||
|
];
|
||||||
// ========== PAY 模块 ==========
|
// ========== PAY 模块 ==========
|
||||||
/**
|
/**
|
||||||
* 支付渠道枚举
|
* 支付渠道枚举
|
||||||
@@ -441,30 +808,6 @@ export const ErpBizType = {
|
|||||||
|
|
||||||
// ========== BPM 模块 ==========
|
// ========== BPM 模块 ==========
|
||||||
|
|
||||||
export const BpmModelType = {
|
|
||||||
BPMN: 10, // BPMN 设计器
|
|
||||||
SIMPLE: 20, // 简易设计器
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BpmModelFormType = {
|
|
||||||
NORMAL: 10, // 流程表单
|
|
||||||
CUSTOM: 20, // 业务表单
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BpmProcessInstanceStatus = {
|
|
||||||
NOT_START: -1, // 未开始
|
|
||||||
RUNNING: 1, // 审批中
|
|
||||||
APPROVE: 2, // 审批通过
|
|
||||||
REJECT: 3, // 审批不通过
|
|
||||||
CANCEL: 4, // 已取消
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BpmAutoApproveType = {
|
|
||||||
NONE: 0, // 不自动通过
|
|
||||||
APPROVE_ALL: 1, // 仅审批一次,后续重复的审批节点均自动通过
|
|
||||||
APPROVE_SEQUENT: 2, // 仅针对连续审批的节点自动通过
|
|
||||||
};
|
|
||||||
|
|
||||||
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
||||||
export enum BpmCandidateStrategyEnum {
|
export enum BpmCandidateStrategyEnum {
|
||||||
/**
|
/**
|
||||||
@@ -594,6 +937,40 @@ export enum BpmNodeTypeEnum {
|
|||||||
USER_TASK_NODE = 11,
|
USER_TASK_NODE = 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程任务操作按钮
|
||||||
|
*/
|
||||||
|
export enum BpmTaskOperationButtonTypeEnum {
|
||||||
|
/**
|
||||||
|
* 加签
|
||||||
|
*/
|
||||||
|
ADD_SIGN = 5,
|
||||||
|
/**
|
||||||
|
* 通过
|
||||||
|
*/
|
||||||
|
APPROVE = 1,
|
||||||
|
/**
|
||||||
|
* 抄送
|
||||||
|
*/
|
||||||
|
COPY = 7,
|
||||||
|
/**
|
||||||
|
* 委派
|
||||||
|
*/
|
||||||
|
DELEGATE = 4,
|
||||||
|
/**
|
||||||
|
* 拒绝
|
||||||
|
*/
|
||||||
|
REJECT = 2,
|
||||||
|
/**
|
||||||
|
* 退回
|
||||||
|
*/
|
||||||
|
RETURN = 6,
|
||||||
|
/**
|
||||||
|
* 转办
|
||||||
|
*/
|
||||||
|
TRANSFER = 3,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务状态枚举
|
* 任务状态枚举
|
||||||
*/
|
*/
|
||||||
@@ -667,3 +1044,147 @@ export enum BpmFieldPermissionType {
|
|||||||
*/
|
*/
|
||||||
WRITE = '2',
|
WRITE = '2',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程模型类型
|
||||||
|
*/
|
||||||
|
export const BpmModelType = {
|
||||||
|
BPMN: 10, // BPMN 设计器
|
||||||
|
SIMPLE: 20, // 简易设计器
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程模型表单类型
|
||||||
|
*/
|
||||||
|
export const BpmModelFormType = {
|
||||||
|
NORMAL: 10, // 流程表单
|
||||||
|
CUSTOM: 20, // 业务表单
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程实例状态
|
||||||
|
*/
|
||||||
|
export const BpmProcessInstanceStatus = {
|
||||||
|
NOT_START: -1, // 未开始
|
||||||
|
RUNNING: 1, // 审批中
|
||||||
|
APPROVE: 2, // 审批通过
|
||||||
|
REJECT: 3, // 审批不通过
|
||||||
|
CANCEL: 4, // 已取消
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动审批类型
|
||||||
|
*/
|
||||||
|
export const BpmAutoApproveType = {
|
||||||
|
NONE: 0, // 不自动通过
|
||||||
|
APPROVE_ALL: 1, // 仅审批一次,后续重复的审批节点均自动通过
|
||||||
|
APPROVE_SEQUENT: 2, // 仅针对连续审批的节点自动通过
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批操作按钮名称
|
||||||
|
*/
|
||||||
|
export const OPERATION_BUTTON_NAME = new Map<number, string>();
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.APPROVE, '通过');
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.REJECT, '拒绝');
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.TRANSFER, '转办');
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.DELEGATE, '委派');
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.ADD_SIGN, '加签');
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.RETURN, '退回');
|
||||||
|
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.COPY, '抄送');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程实例的变量枚举
|
||||||
|
*/
|
||||||
|
export enum ProcessVariableEnum {
|
||||||
|
/**
|
||||||
|
* 流程定义名称
|
||||||
|
*/
|
||||||
|
PROCESS_DEFINITION_NAME = 'PROCESS_DEFINITION_NAME',
|
||||||
|
/**
|
||||||
|
* 发起时间
|
||||||
|
*/
|
||||||
|
START_TIME = 'PROCESS_START_TIME',
|
||||||
|
/**
|
||||||
|
* 发起用户 ID
|
||||||
|
*/
|
||||||
|
START_USER_ID = 'PROCESS_START_USER_ID',
|
||||||
|
}
|
||||||
|
// ========== 【写作 UI】相关的枚举 ==========
|
||||||
|
|
||||||
|
/** 写作点击示例时的数据 */
|
||||||
|
export const WriteExample = {
|
||||||
|
write: {
|
||||||
|
prompt: 'vue',
|
||||||
|
data: 'Vue.js 是一种用于构建用户界面的渐进式 JavaScript 框架。它的核心库只关注视图层,易于上手,同时也便于与其他库或已有项目整合。\n\nVue.js 的特点包括:\n- 响应式的数据绑定:Vue.js 会自动将数据与 DOM 同步,使得状态管理变得更加简单。\n- 组件化:Vue.js 允许开发者通过小型、独立和通常可复用的组件构建大型应用。\n- 虚拟 DOM:Vue.js 使用虚拟 DOM 实现快速渲染,提高了性能。\n\n在 Vue.js 中,一个典型的应用结构可能包括:\n1. 根实例:每个 Vue 应用都需要一个根实例作为入口点。\n2. 组件系统:可以创建自定义的可复用组件。\n3. 指令:特殊的带有前缀 v- 的属性,为 DOM 元素提供特殊的行为。\n4. 插值:用于文本内容,将数据动态地插入到 HTML。\n5. 计算属性和侦听器:用于处理数据的复杂逻辑和响应数据变化。\n6. 条件渲染:根据条件决定元素的渲染。\n7. 列表渲染:用于显示列表数据。\n8. 事件处理:响应用户交互。\n9. 表单输入绑定:处理表单输入和验证。\n10. 组件生命周期钩子:在组件的不同阶段执行特定的函数。\n\nVue.js 还提供了官方的路由器 Vue Router 和状态管理库 Vuex,以支持构建复杂的单页应用(SPA)。\n\n在开发过程中,开发者通常会使用 Vue CLI,这是一个强大的命令行工具,用于快速生成 Vue 项目脚手架,集成了诸如 Babel、Webpack 等现代前端工具,以及热重载、代码检测等开发体验优化功能。\n\nVue.js 的生态系统还包括大量的第三方库和插件,如 Vuetify(UI 组件库)、Vue Test Utils(测试工具)等,这些都极大地丰富了 Vue.js 的开发生态。\n\n总的来说,Vue.js 是一个灵活、高效的前端框架,适合从小型项目到大型企业级应用的开发。它的易用性、灵活性和强大的社区支持使其成为许多开发者的首选框架之一。',
|
||||||
|
},
|
||||||
|
reply: {
|
||||||
|
originalContent: '领导,我想请假',
|
||||||
|
prompt: '不批',
|
||||||
|
data: '您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务,请及时与我联系。\n\n祝工作顺利。\n\n谢谢。',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== 【思维导图 UI】相关的枚举 ==========
|
||||||
|
|
||||||
|
/** 思维导图已有内容生成示例 */
|
||||||
|
export const MindMapContentExample = `# Java 技术栈
|
||||||
|
|
||||||
|
## 核心技术
|
||||||
|
### Java SE
|
||||||
|
### Java EE
|
||||||
|
|
||||||
|
## 框架
|
||||||
|
### Spring
|
||||||
|
#### Spring Boot
|
||||||
|
#### Spring MVC
|
||||||
|
#### Spring Data
|
||||||
|
### Hibernate
|
||||||
|
### MyBatis
|
||||||
|
|
||||||
|
## 构建工具
|
||||||
|
### Maven
|
||||||
|
### Gradle
|
||||||
|
|
||||||
|
## 版本控制
|
||||||
|
### Git
|
||||||
|
### SVN
|
||||||
|
|
||||||
|
## 测试工具
|
||||||
|
### JUnit
|
||||||
|
### Mockito
|
||||||
|
### Selenium
|
||||||
|
|
||||||
|
## 应用服务器
|
||||||
|
### Tomcat
|
||||||
|
### Jetty
|
||||||
|
### WildFly
|
||||||
|
|
||||||
|
## 数据库
|
||||||
|
### MySQL
|
||||||
|
### PostgreSQL
|
||||||
|
### Oracle
|
||||||
|
### MongoDB
|
||||||
|
|
||||||
|
## 消息队列
|
||||||
|
### Kafka
|
||||||
|
### RabbitMQ
|
||||||
|
### ActiveMQ
|
||||||
|
|
||||||
|
## 微服务
|
||||||
|
### Spring Cloud
|
||||||
|
### Dubbo
|
||||||
|
|
||||||
|
## 容器化
|
||||||
|
### Docker
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
## 云服务
|
||||||
|
### AWS
|
||||||
|
### Azure
|
||||||
|
### Google Cloud
|
||||||
|
|
||||||
|
## 开发工具
|
||||||
|
### IntelliJ IDEA
|
||||||
|
### Eclipse
|
||||||
|
### Visual Studio Code`;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { DefaultOptionType } from 'ant-design-vue/es/select';
|
|
||||||
// TODO @芋艿:后续再优化
|
// TODO @芋艿:后续再优化
|
||||||
// TODO @芋艿:可以共享么?
|
// TODO @芋艿:可以共享么?
|
||||||
|
|
||||||
|
import type { DictItem } from '#/store';
|
||||||
|
|
||||||
import { isObject } from '@vben/utils';
|
import { isObject } from '@vben/utils';
|
||||||
|
|
||||||
import { useDictStore } from '#/store';
|
import { useDictStore } from '#/store';
|
||||||
@@ -10,33 +11,103 @@ import { useDictStore } from '#/store';
|
|||||||
// 先临时移入到方法中
|
// 先临时移入到方法中
|
||||||
// const dictStore = useDictStore();
|
// const dictStore = useDictStore();
|
||||||
|
|
||||||
// TODO @dhb: antd 组件的 color 类型
|
/** AntD 组件的颜色类型 */
|
||||||
type ColorType = 'error' | 'info' | 'success' | 'warning';
|
type ColorType = 'error' | 'info' | 'success' | 'warning';
|
||||||
|
|
||||||
|
/** 字典值类型 */
|
||||||
|
type DictValueType = 'boolean' | 'number' | 'string';
|
||||||
|
|
||||||
|
/** 基础字典数据类型 */
|
||||||
export interface DictDataType {
|
export interface DictDataType {
|
||||||
dictType: string;
|
dictType?: string;
|
||||||
label: string;
|
label: string;
|
||||||
value: boolean | number | string;
|
value: boolean | number | string;
|
||||||
colorType: ColorType;
|
colorType?: string;
|
||||||
cssClass: string;
|
cssClass?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 数字类型字典数据 */
|
||||||
export interface NumberDictDataType extends DictDataType {
|
export interface NumberDictDataType extends DictDataType {
|
||||||
value: number;
|
value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 字符串类型字典数据 */
|
||||||
export interface StringDictDataType extends DictDataType {
|
export interface StringDictDataType extends DictDataType {
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 布尔类型字典数据 */
|
||||||
|
export interface BooleanDictDataType extends DictDataType {
|
||||||
|
value: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 字典缓存管理器 */
|
||||||
|
class DictCacheManager {
|
||||||
|
private cache = new Map<string, DictDataType[]>();
|
||||||
|
private maxCacheSize = 100; // 最大缓存数量
|
||||||
|
|
||||||
|
/** 清空缓存 */
|
||||||
|
clear(): void {
|
||||||
|
this.cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除指定字典类型的缓存 */
|
||||||
|
delete(dictType: string): void {
|
||||||
|
const keysToDelete = [];
|
||||||
|
for (const key of this.cache.keys()) {
|
||||||
|
if (key.startsWith(`${dictType}:`)) {
|
||||||
|
keysToDelete.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysToDelete.forEach((key) => this.cache.delete(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取缓存数据 */
|
||||||
|
get(dictType: string, valueType: DictValueType): DictDataType[] | undefined {
|
||||||
|
return this.cache.get(this.getCacheKey(dictType, valueType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置缓存数据 */
|
||||||
|
set(dictType: string, valueType: DictValueType, data: DictDataType[]): void {
|
||||||
|
const key = this.getCacheKey(dictType, valueType);
|
||||||
|
|
||||||
|
// 如果缓存数量超过限制,删除最早的缓存
|
||||||
|
if (this.cache.size >= this.maxCacheSize) {
|
||||||
|
const firstKey = this.cache.keys().next().value;
|
||||||
|
if (firstKey) {
|
||||||
|
this.cache.delete(firstKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache.set(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取缓存键 */
|
||||||
|
private getCacheKey(dictType: string, valueType: DictValueType): string {
|
||||||
|
return `${dictType}:${valueType}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 字典缓存实例 */
|
||||||
|
const dictCache = new DictCacheManager();
|
||||||
|
|
||||||
|
/** 值转换器映射 */
|
||||||
|
const valueConverters: Record<
|
||||||
|
DictValueType,
|
||||||
|
(value: any) => boolean | number | string
|
||||||
|
> = {
|
||||||
|
boolean: (value: any) => `${value}` === 'true',
|
||||||
|
number: (value: any) => Number.parseInt(`${value}`, 10),
|
||||||
|
string: (value: any) => `${value}`,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取字典标签
|
* 获取字典标签
|
||||||
*
|
|
||||||
* @param dictType 字典类型
|
* @param dictType 字典类型
|
||||||
* @param value 字典值
|
* @param value 字典值
|
||||||
* @returns 字典标签
|
* @returns 字典标签
|
||||||
*/
|
*/
|
||||||
function getDictLabel(dictType: string, value: any) {
|
function getDictLabel(dictType: string, value: any): string {
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
const dictObj = dictStore.getDictData(dictType, value);
|
const dictObj = dictStore.getDictData(dictType, value);
|
||||||
return isObject(dictObj) ? dictObj.label : '';
|
return isObject(dictObj) ? dictObj.label : '';
|
||||||
@@ -44,111 +115,80 @@ function getDictLabel(dictType: string, value: any) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取字典对象
|
* 获取字典对象
|
||||||
*
|
|
||||||
* @param dictType 字典类型
|
* @param dictType 字典类型
|
||||||
* @param value 字典值
|
* @param value 字典值
|
||||||
* @returns 字典对象
|
* @returns 字典对象
|
||||||
*/
|
*/
|
||||||
function getDictObj(dictType: string, value: any) {
|
function getDictObj(dictType: string, value: any): DictItem | null {
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
const dictObj = dictStore.getDictData(dictType, value);
|
const dictObj = dictStore.getDictData(dictType, value);
|
||||||
return isObject(dictObj) ? dictObj : null;
|
return isObject(dictObj) ? dictObj : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取字典数组 用于select radio 等
|
* 获取字典数组 - 优化版本,支持缓存和泛型
|
||||||
*
|
|
||||||
* @param dictType 字典类型
|
* @param dictType 字典类型
|
||||||
* @param valueType 字典值类型,默认 string 类型
|
* @param valueType 字典值类型,默认 string 类型
|
||||||
* @returns 字典数组
|
* @returns 字典数组
|
||||||
*/
|
*/
|
||||||
// TODO @puhui999:貌似可以定义一个类型?不使用 any[]
|
function getDictOptions<T extends DictValueType = 'string'>(
|
||||||
function getDictOptions(
|
|
||||||
dictType: string,
|
dictType: string,
|
||||||
valueType: 'boolean' | 'number' | 'string' = 'string',
|
valueType: T = 'string' as T,
|
||||||
): any[] {
|
): T extends 'number'
|
||||||
|
? NumberDictDataType[]
|
||||||
|
: T extends 'boolean'
|
||||||
|
? BooleanDictDataType[]
|
||||||
|
: StringDictDataType[] {
|
||||||
|
// 检查缓存
|
||||||
|
const cachedData = dictCache.get(dictType, valueType);
|
||||||
|
if (cachedData) {
|
||||||
|
return cachedData as any;
|
||||||
|
}
|
||||||
|
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
const dictOpts = dictStore.getDictOptions(dictType);
|
const dictOpts = dictStore.getDictOptions(dictType);
|
||||||
const dictOptions: DefaultOptionType = [];
|
|
||||||
if (dictOpts.length > 0) {
|
if (dictOpts.length === 0) {
|
||||||
let dictValue: boolean | number | string = '';
|
return [] as any;
|
||||||
dictOpts.forEach((d) => {
|
|
||||||
switch (valueType) {
|
|
||||||
case 'boolean': {
|
|
||||||
dictValue = `${d.value}` === 'true';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'number': {
|
|
||||||
dictValue = Number.parseInt(`${d.value}`);
|
const converter = valueConverters[valueType];
|
||||||
break;
|
const dictOptions: DictDataType[] = dictOpts.map((d: DictItem) => ({
|
||||||
}
|
value: converter(d.value),
|
||||||
case 'string': {
|
|
||||||
dictValue = `${d.value}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// No default
|
|
||||||
}
|
|
||||||
dictOptions.push({
|
|
||||||
value: dictValue,
|
|
||||||
label: d.label,
|
label: d.label,
|
||||||
});
|
colorType: d.colorType,
|
||||||
});
|
cssClass: d.cssClass,
|
||||||
}
|
}));
|
||||||
return dictOptions.length > 0 ? dictOptions : [];
|
|
||||||
|
// 缓存结果
|
||||||
|
dictCache.set(dictType, valueType, dictOptions);
|
||||||
|
|
||||||
|
return dictOptions as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @dhb52:下面的一系列方法,看看能不能复用 getDictOptions 方法
|
/**
|
||||||
export const getIntDictOptions = (dictType: string): NumberDictDataType[] => {
|
* 清空字典缓存
|
||||||
// 获得通用的 DictDataType 列表
|
*/
|
||||||
const dictOptions = getDictOptions(dictType) as DictDataType[];
|
export const clearDictCache = (): void => {
|
||||||
// 转换成 number 类型的 NumberDictDataType 类型
|
dictCache.clear();
|
||||||
// why 需要特殊转换:避免 IDEA 在 v-for="dict in getIntDictOptions(...)" 时,el-option 的 key 会告警
|
|
||||||
const dictOption: NumberDictDataType[] = [];
|
|
||||||
dictOptions.forEach((dict: DictDataType) => {
|
|
||||||
dictOption.push({
|
|
||||||
...dict,
|
|
||||||
value: Number.parseInt(`${dict.value}`),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return dictOption;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO @dhb52:下面的一系列方法,看看能不能复用 getDictOptions 方法
|
/**
|
||||||
export const getStrDictOptions = (dictType: string) => {
|
* 删除指定字典类型的缓存
|
||||||
// 获得通用的 DictDataType 列表
|
* @param dictType 字典类型
|
||||||
const dictOptions = getDictOptions(dictType) as DictDataType[];
|
*/
|
||||||
// 转换成 string 类型的 StringDictDataType 类型
|
export const deleteDictCache = (dictType: string): void => {
|
||||||
// why 需要特殊转换:避免 IDEA 在 v-for="dict in getStrDictOptions(...)" 时,el-option 的 key 会告警
|
dictCache.delete(dictType);
|
||||||
const dictOption: StringDictDataType[] = [];
|
|
||||||
dictOptions.forEach((dict: DictDataType) => {
|
|
||||||
dictOption.push({
|
|
||||||
...dict,
|
|
||||||
value: `${dict.value}`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return dictOption;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO @dhb52:下面的一系列方法,看看能不能复用 getDictOptions 方法
|
|
||||||
export const getBoolDictOptions = (dictType: string) => {
|
|
||||||
const dictOption: DictDataType[] = [];
|
|
||||||
const dictOptions = getDictOptions(dictType) as DictDataType[];
|
|
||||||
dictOptions.forEach((dict: DictDataType) => {
|
|
||||||
dictOption.push({
|
|
||||||
...dict,
|
|
||||||
value: `${dict.value}` === 'true',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return dictOption;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 字典类型枚举 - 按模块分组和排序 */
|
||||||
enum DICT_TYPE {
|
enum DICT_TYPE {
|
||||||
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式
|
||||||
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态
|
||||||
|
AI_MODEL_TYPE = 'ai_model_type', // AI 模型类型
|
||||||
AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态
|
AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态
|
||||||
// ========== AI - 人工智能模块 ==========
|
// ========== AI - 人工智能模块 ==========
|
||||||
AI_PLATFORM = 'ai_platform', // AI 平台
|
AI_PLATFORM = 'ai_platform', // AI 平台
|
||||||
|
|
||||||
AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式
|
AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式
|
||||||
AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言
|
AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言
|
||||||
AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度
|
AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度
|
||||||
@@ -282,4 +322,12 @@ enum DICT_TYPE {
|
|||||||
TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型
|
TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型
|
||||||
USER_TYPE = 'user_type',
|
USER_TYPE = 'user_type',
|
||||||
}
|
}
|
||||||
export { DICT_TYPE, getDictLabel, getDictObj, getDictOptions };
|
|
||||||
|
export {
|
||||||
|
type ColorType,
|
||||||
|
DICT_TYPE,
|
||||||
|
type DictValueType,
|
||||||
|
getDictLabel,
|
||||||
|
getDictObj,
|
||||||
|
getDictOptions,
|
||||||
|
};
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
import { isRef } from 'vue';
|
import { isRef } from 'vue';
|
||||||
|
|
||||||
// 编码表单 Conf
|
// 编码表单 Conf
|
||||||
export const encodeConf = (designerRef: object) => {
|
export const encodeConf = (designerRef: any) => {
|
||||||
// @ts-ignore designerRef.value is dynamically added by form-create-designer
|
|
||||||
return JSON.stringify(designerRef.value.getOption());
|
return JSON.stringify(designerRef.value.getOption());
|
||||||
};
|
};
|
||||||
|
|
||||||
// 编码表单 Fields
|
// 编码表单 Fields
|
||||||
export const encodeFields = (designerRef: object) => {
|
export const encodeFields = (designerRef: any) => {
|
||||||
// @ts-ignore designerRef.value is dynamically added by form-create-designer
|
|
||||||
const rule = JSON.parse(designerRef.value.getJson());
|
const rule = JSON.parse(designerRef.value.getJson());
|
||||||
const fields: string[] = [];
|
const fields: string[] = [];
|
||||||
rule.forEach((item: unknown) => {
|
rule.forEach((item: unknown) => {
|
||||||
@@ -32,33 +30,29 @@ export const decodeFields = (fields: string[]) => {
|
|||||||
|
|
||||||
// 设置表单的 Conf 和 Fields,适用 FcDesigner 场景
|
// 设置表单的 Conf 和 Fields,适用 FcDesigner 场景
|
||||||
export const setConfAndFields = (
|
export const setConfAndFields = (
|
||||||
designerRef: object,
|
designerRef: any,
|
||||||
conf: string,
|
conf: string,
|
||||||
fields: string,
|
fields: string | string[],
|
||||||
) => {
|
) => {
|
||||||
// @ts-ignore designerRef.value is dynamically added by form-create-designer
|
|
||||||
designerRef.value.setOption(JSON.parse(conf));
|
designerRef.value.setOption(JSON.parse(conf));
|
||||||
// @ts-ignore designerRef.value is dynamically added by form-create-designer
|
// 处理 fields 参数类型,确保传入 decodeFields 的是 string[] 类型
|
||||||
designerRef.value.setRule(decodeFields(fields));
|
const fieldsArray = Array.isArray(fields) ? fields : [fields];
|
||||||
|
designerRef.value.setRule(decodeFields(fieldsArray));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 设置表单的 Conf 和 Fields,适用 form-create 场景
|
// 设置表单的 Conf 和 Fields,适用 form-create 场景
|
||||||
export const setConfAndFields2 = (
|
export const setConfAndFields2 = (
|
||||||
detailPreview: object,
|
detailPreview: any,
|
||||||
conf: string,
|
conf: string,
|
||||||
fields: string[],
|
fields: string[],
|
||||||
value?: object,
|
value?: any,
|
||||||
) => {
|
) => {
|
||||||
if (isRef(detailPreview)) {
|
if (isRef(detailPreview)) {
|
||||||
// @ts-ignore detailPreview.value is dynamically added by form-create-designer
|
|
||||||
detailPreview = detailPreview.value;
|
detailPreview = detailPreview.value;
|
||||||
}
|
}
|
||||||
// @ts-ignore detailPreview properties are dynamically added by form-create-designer
|
|
||||||
detailPreview.option = JSON.parse(conf);
|
detailPreview.option = JSON.parse(conf);
|
||||||
// @ts-ignore detailPreview properties are dynamically added by form-create-designer
|
|
||||||
detailPreview.rule = decodeFields(fields);
|
detailPreview.rule = decodeFields(fields);
|
||||||
if (value) {
|
if (value) {
|
||||||
// @ts-ignore detailPreview properties are dynamically added by form-create-designer
|
|
||||||
detailPreview.value = value;
|
detailPreview.value = value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* 将毫秒,转换成时间字符串。例如说,xx 分钟
|
|
||||||
*
|
|
||||||
* @param ms 毫秒
|
|
||||||
* @returns {string} 字符串
|
|
||||||
*/
|
|
||||||
// TODO @xingyu:这个要融合到哪里去 date 么?
|
|
||||||
export function formatPast2(ms: number): string {
|
|
||||||
// 定义时间单位常量,便于维护
|
|
||||||
const SECOND = 1000;
|
|
||||||
const MINUTE = 60 * SECOND;
|
|
||||||
const HOUR = 60 * MINUTE;
|
|
||||||
const DAY = 24 * HOUR;
|
|
||||||
|
|
||||||
// 计算各时间单位
|
|
||||||
const day = Math.floor(ms / DAY);
|
|
||||||
const hour = Math.floor((ms % DAY) / HOUR);
|
|
||||||
const minute = Math.floor((ms % HOUR) / MINUTE);
|
|
||||||
const second = Math.floor((ms % MINUTE) / SECOND);
|
|
||||||
|
|
||||||
// 根据时间长短返回不同格式
|
|
||||||
if (day > 0) {
|
|
||||||
return `${day} 天${hour} 小时 ${minute} 分钟`;
|
|
||||||
}
|
|
||||||
if (hour > 0) {
|
|
||||||
return `${hour} 小时 ${minute} 分钟`;
|
|
||||||
}
|
|
||||||
if (minute > 0) {
|
|
||||||
return `${minute} 分钟`;
|
|
||||||
}
|
|
||||||
return second > 0 ? `${second} 秒` : `${0} 秒`;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
export * from './constants';
|
export * from './constants';
|
||||||
export * from './dict';
|
export * from './dict';
|
||||||
export * from './formatTime';
|
|
||||||
export * from './formCreate';
|
export * from './formCreate';
|
||||||
export * from './rangePickerProps';
|
export * from './rangePickerProps';
|
||||||
export * from './routerHelper';
|
export * from './routerHelper';
|
||||||
export * from './validator';
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { defineAsyncComponent } from 'vue';
|
import { defineAsyncComponent } from 'vue';
|
||||||
|
|
||||||
const modules = import.meta.glob('../views/**/*.{vue,tsx}');
|
const modules = import.meta.glob('../views/**/*.{vue,tsx}');
|
||||||
// TODO @xingyu:这个要不要融合到哪个 router util 里?
|
|
||||||
/**
|
/**
|
||||||
* 注册一个异步组件
|
* 注册一个异步组件
|
||||||
* @param componentPath 例:/bpm/oa/leave/detail
|
* @param componentPath 例:/bpm/oa/leave/detail
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
// 参数校验,对标 Hutool 的 Validator 工具类
|
|
||||||
|
|
||||||
/** 手机号正则表达式(中国) */
|
|
||||||
const MOBILE_REGEX = /(?:0|86|\+86)?1[3-9]\d{9}/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证是否为手机号码(中国)
|
|
||||||
*
|
|
||||||
* @param value 值
|
|
||||||
* @returns 是否为手机号码(中国)
|
|
||||||
*/
|
|
||||||
export function isMobile(value?: null | string): boolean {
|
|
||||||
if (!value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return MOBILE_REGEX.test(value);
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ const authStore = useAuthStore();
|
|||||||
const activeName = ref('basicInfo');
|
const activeName = ref('basicInfo');
|
||||||
|
|
||||||
/** 加载个人信息 */
|
/** 加载个人信息 */
|
||||||
const profile = ref<SystemUserProfileApi.UserProfileRespVO>();
|
const profile = ref<SystemUserProfileApi.UserProfileResp>();
|
||||||
async function loadProfile() {
|
async function loadProfile() {
|
||||||
profile.value = await getUserProfile();
|
profile.value = await getUserProfile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { updateUserProfile } from '#/api/system/user/profile';
|
|||||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
profile?: SystemUserProfileApi.UserProfileRespVO;
|
profile?: SystemUserProfileApi.UserProfileResp;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'success'): void;
|
(e: 'success'): void;
|
||||||
@@ -77,7 +77,7 @@ async function handleSubmit(values: Recordable<any>) {
|
|||||||
try {
|
try {
|
||||||
formApi.setLoading(true);
|
formApi.setLoading(true);
|
||||||
// 提交表单
|
// 提交表单
|
||||||
await updateUserProfile(values as SystemUserProfileApi.UpdateProfileReqVO);
|
await updateUserProfile(values as SystemUserProfileApi.UpdateProfileReq);
|
||||||
// 关闭并提示
|
// 关闭并提示
|
||||||
emit('success');
|
emit('success');
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success($t('ui.actionMessage.operationSuccess'));
|
||||||
@@ -101,7 +101,7 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mt-16px md:w-full lg:w-1/2 2xl:w-2/5">
|
<div class="mt-4 md:w-full lg:w-1/2 2xl:w-2/5">
|
||||||
<Form />
|
<Form />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { CropperAvatar } from '#/components/cropper';
|
|||||||
import { useUpload } from '#/components/upload/use-upload';
|
import { useUpload } from '#/components/upload/use-upload';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
profile?: SystemUserProfileApi.UserProfileRespVO;
|
profile?: SystemUserProfileApi.UserProfileResp;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ async function handleSubmit(values: Recordable<any>) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mt-[16px] md:w-full lg:w-1/2 2xl:w-2/5">
|
<div class="mt-4 md:w-full lg:w-1/2 2xl:w-2/5">
|
||||||
<Form />
|
<Form />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -181,9 +181,7 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
<div class="flex flex-1 items-center justify-between">
|
<div class="flex flex-1 items-center justify-between">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<h4
|
<h4 class="mb-1 text-sm text-black/85 dark:text-white/85">
|
||||||
class="mb-[4px] text-[14px] text-black/85 dark:text-white/85"
|
|
||||||
>
|
|
||||||
{{ getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type) }}
|
{{ getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type) }}
|
||||||
</h4>
|
</h4>
|
||||||
<span class="text-black/45 dark:text-white/45">
|
<span class="text-black/45 dark:text-white/45">
|
||||||
@@ -191,9 +189,9 @@ onMounted(() => {
|
|||||||
{{ item.socialUser?.nickname || item.socialUser?.openid }}
|
{{ item.socialUser?.nickname || item.socialUser?.openid }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
绑定{{
|
绑定
|
||||||
getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type)
|
{{ getDictLabel(DICT_TYPE.SYSTEM_SOCIAL_TYPE, item.type) }}
|
||||||
}}账号
|
账号
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
import type { DescriptionItemSchema } from '#/components/description';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { JsonViewer } from '@vben/common-ui';
|
||||||
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 列表的搜索表单 */
|
/** 列表的搜索表单 */
|
||||||
export function useGridFormSchema(): VbenFormSchema[] {
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -70,24 +72,19 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns<T = InfraApiAccessLogApi.ApiAccessLog>(
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
onActionClick: OnActionClickFn<T>,
|
|
||||||
): VxeTableGridOptions['columns'] {
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '日志编号',
|
title: '日志编号',
|
||||||
minWidth: 100,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'userId',
|
field: 'userId',
|
||||||
title: '用户编号',
|
title: '用户编号',
|
||||||
minWidth: 100,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'userType',
|
field: 'userType',
|
||||||
title: '用户类型',
|
title: '用户类型',
|
||||||
minWidth: 120,
|
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.USER_TYPE },
|
props: { type: DICT_TYPE.USER_TYPE },
|
||||||
@@ -96,34 +93,28 @@ export function useGridColumns<T = InfraApiAccessLogApi.ApiAccessLog>(
|
|||||||
{
|
{
|
||||||
field: 'applicationName',
|
field: 'applicationName',
|
||||||
title: '应用名',
|
title: '应用名',
|
||||||
minWidth: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'requestMethod',
|
field: 'requestMethod',
|
||||||
title: '请求方法',
|
title: '请求方法',
|
||||||
minWidth: 80,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'requestUrl',
|
field: 'requestUrl',
|
||||||
title: '请求地址',
|
title: '请求地址',
|
||||||
minWidth: 300,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'beginTime',
|
field: 'beginTime',
|
||||||
title: '请求时间',
|
title: '请求时间',
|
||||||
minWidth: 180,
|
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'duration',
|
field: 'duration',
|
||||||
title: '执行时长',
|
title: '执行时长',
|
||||||
minWidth: 120,
|
formatter: ({ cellValue }) => `${cellValue} ms`,
|
||||||
formatter: ({ row }) => `${row.duration} ms`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'resultCode',
|
field: 'resultCode',
|
||||||
title: '操作结果',
|
title: '操作结果',
|
||||||
minWidth: 150,
|
|
||||||
formatter: ({ row }) => {
|
formatter: ({ row }) => {
|
||||||
return row.resultCode === 0 ? '成功' : `失败(${row.resultMsg})`;
|
return row.resultCode === 0 ? '成功' : `失败(${row.resultMsg})`;
|
||||||
},
|
},
|
||||||
@@ -131,43 +122,122 @@ export function useGridColumns<T = InfraApiAccessLogApi.ApiAccessLog>(
|
|||||||
{
|
{
|
||||||
field: 'operateModule',
|
field: 'operateModule',
|
||||||
title: '操作模块',
|
title: '操作模块',
|
||||||
minWidth: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operateName',
|
field: 'operateName',
|
||||||
title: '操作名',
|
title: '操作名',
|
||||||
minWidth: 220,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operateType',
|
field: 'operateType',
|
||||||
title: '操作类型',
|
title: '操作类型',
|
||||||
minWidth: 120,
|
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.INFRA_OPERATE_TYPE },
|
props: { type: DICT_TYPE.INFRA_OPERATE_TYPE },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
minWidth: 80,
|
width: 80,
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
cellRender: {
|
slots: { default: 'actions' },
|
||||||
attrs: {
|
},
|
||||||
nameField: 'id',
|
];
|
||||||
nameTitle: 'API访问日志',
|
}
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
/** 详情页的字段 */
|
||||||
name: 'CellOperation',
|
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||||
options: [
|
return [
|
||||||
{
|
{
|
||||||
code: 'detail',
|
field: 'id',
|
||||||
text: '详情',
|
label: '日志编号',
|
||||||
show: hasAccessByCodes(['infra:api-access-log:query']),
|
},
|
||||||
},
|
{
|
||||||
],
|
field: 'traceId',
|
||||||
},
|
label: '链路追踪',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'applicationName',
|
||||||
|
label: '应用名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userId',
|
||||||
|
label: '用户Id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userType',
|
||||||
|
label: '用户类型',
|
||||||
|
content: (data) => {
|
||||||
|
return h(DictTag, {
|
||||||
|
type: DICT_TYPE.USER_TYPE,
|
||||||
|
value: data.userType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userIp',
|
||||||
|
label: '用户IP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userAgent',
|
||||||
|
label: '用户UA',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'requestMethod',
|
||||||
|
label: '请求信息',
|
||||||
|
content: (data) => {
|
||||||
|
return `${data.requestMethod} ${data.requestUrl}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'requestParams',
|
||||||
|
label: '请求参数',
|
||||||
|
content: (data) => {
|
||||||
|
return h(JsonViewer, {
|
||||||
|
value: data.requestParams,
|
||||||
|
previewMode: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'responseBody',
|
||||||
|
label: '请求结果',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'beginTime',
|
||||||
|
label: '请求时间',
|
||||||
|
content: (data) => {
|
||||||
|
return `${formatDateTime(data?.beginTime)} ~ ${formatDateTime(data?.endTime)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'duration',
|
||||||
|
label: '请求耗时',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'resultCode',
|
||||||
|
label: '操作结果',
|
||||||
|
content: (data) => {
|
||||||
|
return data.resultCode === 0
|
||||||
|
? '成功'
|
||||||
|
: `失败 | ${data.resultCode} | ${data.resultMsg}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operateModule',
|
||||||
|
label: '操作模块',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operateName',
|
||||||
|
label: '操作名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'operateType',
|
||||||
|
label: '操作类型',
|
||||||
|
content: (data) =>
|
||||||
|
h(DictTag, {
|
||||||
|
type: DICT_TYPE.INFRA_OPERATE_TYPE,
|
||||||
|
value: data?.operateType,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download } from '@vben/icons';
|
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||||
|
|
||||||
import { Button } from 'ant-design-vue';
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
|
||||||
import {
|
import {
|
||||||
exportApiAccessLog,
|
exportApiAccessLog,
|
||||||
getApiAccessLogPage,
|
getApiAccessLogPage,
|
||||||
} from '#/api/infra/api-access-log';
|
} from '#/api/infra/api-access-log';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useGridColumns, useGridFormSchema } from './data';
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
@@ -33,35 +26,22 @@ function onRefresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 导出表格 */
|
||||||
async function onExport() {
|
async function handleExport() {
|
||||||
const data = await exportApiAccessLog(await gridApi.formApi.getValues());
|
const data = await exportApiAccessLog(await gridApi.formApi.getValues());
|
||||||
downloadFileFromBlobPart({ fileName: 'API 访问日志.xls', source: data });
|
downloadFileFromBlobPart({ fileName: 'API 访问日志.xls', source: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查看 API 访问日志详情 */
|
/** 查看 API 访问日志详情 */
|
||||||
function onDetail(row: InfraApiAccessLogApi.ApiAccessLog) {
|
function handleDetail(row: InfraApiAccessLogApi.ApiAccessLog) {
|
||||||
detailModalApi.setData(row).open();
|
detailModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
|
||||||
function onActionClick({
|
|
||||||
code,
|
|
||||||
row,
|
|
||||||
}: OnActionClickParams<InfraApiAccessLogApi.ApiAccessLog>) {
|
|
||||||
switch (code) {
|
|
||||||
case 'detail': {
|
|
||||||
onDetail(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useGridFormSchema(),
|
schema: useGridFormSchema(),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick),
|
columns: useGridColumns(),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
@@ -95,15 +75,30 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
<DetailModal @success="onRefresh" />
|
<DetailModal @success="onRefresh" />
|
||||||
<Grid table-title="API 访问日志列表">
|
<Grid table-title="API 访问日志列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button
|
<TableAction
|
||||||
type="primary"
|
:actions="[
|
||||||
class="ml-2"
|
{
|
||||||
@click="onExport"
|
label: $t('ui.actionTitle.export'),
|
||||||
v-access:code="['infra:api-access-log:export']"
|
type: 'primary',
|
||||||
>
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
<Download class="size-5" />
|
auth: ['infra:api-access-log:export'],
|
||||||
{{ $t('ui.actionTitle.export') }}
|
onClick: handleExport,
|
||||||
</Button>
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.detail'),
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.VIEW,
|
||||||
|
auth: ['infra:api-access-log:query'],
|
||||||
|
onClick: handleDetail.bind(null, row),
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import type { InfraApiAccessLogApi } from '#/api/infra/api-access-log';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { formatDateTime } from '@vben/utils';
|
|
||||||
|
|
||||||
import { Descriptions } from 'ant-design-vue';
|
import { useDescription } from '#/components/description';
|
||||||
|
|
||||||
import { DictTag } from '#/components/dict-tag';
|
import { useDetailSchema } from '../data';
|
||||||
import { DICT_TYPE } from '#/utils';
|
|
||||||
|
|
||||||
const formData = ref<InfraApiAccessLogApi.ApiAccessLog>();
|
const formData = ref<InfraApiAccessLogApi.ApiAccessLog>();
|
||||||
|
|
||||||
@@ -32,6 +30,15 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [Description] = useDescription({
|
||||||
|
componentProps: {
|
||||||
|
bordered: true,
|
||||||
|
column: 1,
|
||||||
|
class: 'mx-4',
|
||||||
|
},
|
||||||
|
schema: useDetailSchema(),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -41,66 +48,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
:show-cancel-button="false"
|
:show-cancel-button="false"
|
||||||
:show-confirm-button="false"
|
:show-confirm-button="false"
|
||||||
>
|
>
|
||||||
<Descriptions
|
<Description :data="formData" />
|
||||||
bordered
|
|
||||||
:column="1"
|
|
||||||
size="middle"
|
|
||||||
class="mx-4"
|
|
||||||
:label-style="{ width: '110px' }"
|
|
||||||
>
|
|
||||||
<Descriptions.Item label="日志编号">
|
|
||||||
{{ formData?.id }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="链路追踪">
|
|
||||||
{{ formData?.traceId }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="应用名">
|
|
||||||
{{ formData?.applicationName }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户信息">
|
|
||||||
{{ formData?.userId }}
|
|
||||||
<DictTag :type="DICT_TYPE.USER_TYPE" :value="formData?.userType" />
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户IP">
|
|
||||||
{{ formData?.userIp }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户UA">
|
|
||||||
{{ formData?.userAgent }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求信息">
|
|
||||||
{{ formData?.requestMethod }} {{ formData?.requestUrl }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求参数">
|
|
||||||
{{ formData?.requestParams }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求结果">
|
|
||||||
{{ formData?.responseBody }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求时间">
|
|
||||||
{{ formatDateTime(formData?.beginTime || '') }} ~
|
|
||||||
{{ formatDateTime(formData?.endTime || '') }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求耗时">
|
|
||||||
{{ formData?.duration }} ms
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="操作结果">
|
|
||||||
<div v-if="formData?.resultCode === 0">正常</div>
|
|
||||||
<div v-else-if="formData && formData?.resultCode > 0">
|
|
||||||
失败 | {{ formData?.resultCode }} | {{ formData?.resultMsg }}
|
|
||||||
</div>
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="操作模块">
|
|
||||||
{{ formData?.operateModule }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="操作名">
|
|
||||||
{{ formData?.operateName }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="操作类型">
|
|
||||||
<DictTag
|
|
||||||
:type="DICT_TYPE.INFRA_OPERATE_TYPE"
|
|
||||||
:value="formData?.operateType"
|
|
||||||
/>
|
|
||||||
</Descriptions.Item>
|
|
||||||
</Descriptions>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
import type { DescriptionItemSchema } from '#/components/description';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { JsonViewer } from '@vben/common-ui';
|
||||||
|
import { formatDateTime } from '@vben/utils';
|
||||||
|
|
||||||
|
import { DictTag } from '#/components/dict-tag';
|
||||||
import {
|
import {
|
||||||
DICT_TYPE,
|
DICT_TYPE,
|
||||||
getDictOptions,
|
getDictOptions,
|
||||||
@@ -11,8 +15,6 @@ import {
|
|||||||
InfraApiErrorLogProcessStatusEnum,
|
InfraApiErrorLogProcessStatusEnum,
|
||||||
} from '#/utils';
|
} from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 列表的搜索表单 */
|
/** 列表的搜索表单 */
|
||||||
export function useGridFormSchema(): VbenFormSchema[] {
|
export function useGridFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -71,24 +73,19 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns<T = InfraApiErrorLogApi.ApiErrorLog>(
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
onActionClick: OnActionClickFn<T>,
|
|
||||||
): VxeTableGridOptions['columns'] {
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '日志编号',
|
title: '日志编号',
|
||||||
minWidth: 100,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'userId',
|
field: 'userId',
|
||||||
title: '用户编号',
|
title: '用户编号',
|
||||||
minWidth: 100,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'userType',
|
field: 'userType',
|
||||||
title: '用户类型',
|
title: '用户类型',
|
||||||
minWidth: 120,
|
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.USER_TYPE },
|
props: { type: DICT_TYPE.USER_TYPE },
|
||||||
@@ -97,78 +94,135 @@ export function useGridColumns<T = InfraApiErrorLogApi.ApiErrorLog>(
|
|||||||
{
|
{
|
||||||
field: 'applicationName',
|
field: 'applicationName',
|
||||||
title: '应用名',
|
title: '应用名',
|
||||||
minWidth: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'requestMethod',
|
field: 'requestMethod',
|
||||||
title: '请求方法',
|
title: '请求方法',
|
||||||
minWidth: 80,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'requestUrl',
|
field: 'requestUrl',
|
||||||
title: '请求地址',
|
title: '请求地址',
|
||||||
minWidth: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'exceptionTime',
|
field: 'exceptionTime',
|
||||||
title: '异常发生时间',
|
title: '异常发生时间',
|
||||||
minWidth: 180,
|
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'exceptionName',
|
field: 'exceptionName',
|
||||||
title: '异常名',
|
title: '异常名',
|
||||||
minWidth: 180,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'processStatus',
|
field: 'processStatus',
|
||||||
title: '处理状态',
|
title: '处理状态',
|
||||||
minWidth: 120,
|
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS },
|
props: { type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
minWidth: 200,
|
width: 200,
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
cellRender: {
|
slots: { default: 'actions' },
|
||||||
attrs: {
|
|
||||||
nameField: 'id',
|
|
||||||
nameTitle: 'API错误日志',
|
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
},
|
||||||
name: 'CellOperation',
|
];
|
||||||
options: [
|
}
|
||||||
|
|
||||||
|
/** 详情页的字段 */
|
||||||
|
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
code: 'detail',
|
field: 'id',
|
||||||
text: '详情',
|
label: '日志编号',
|
||||||
show: hasAccessByCodes(['infra:api-error-log:query']),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'done',
|
field: 'traceId',
|
||||||
text: '已处理',
|
label: '链路追踪',
|
||||||
show: (row: InfraApiErrorLogApi.ApiErrorLog) => {
|
},
|
||||||
return (
|
{
|
||||||
row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT &&
|
field: 'applicationName',
|
||||||
hasAccessByCodes(['infra:api-error-log:update-status'])
|
label: '应用名',
|
||||||
);
|
},
|
||||||
|
{
|
||||||
|
field: 'userId',
|
||||||
|
label: '用户Id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'userType',
|
||||||
|
label: '用户类型',
|
||||||
|
content: (data) => {
|
||||||
|
return h(DictTag, {
|
||||||
|
type: DICT_TYPE.USER_TYPE,
|
||||||
|
value: data.userType,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ignore',
|
field: 'userIp',
|
||||||
text: '已忽略',
|
label: '用户IP',
|
||||||
show: (row: InfraApiErrorLogApi.ApiErrorLog) => {
|
},
|
||||||
return (
|
{
|
||||||
row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT &&
|
field: 'userAgent',
|
||||||
hasAccessByCodes(['infra:api-error-log:update-status'])
|
label: '用户UA',
|
||||||
);
|
},
|
||||||
|
{
|
||||||
|
field: 'requestMethod',
|
||||||
|
label: '请求信息',
|
||||||
|
content: (data) => {
|
||||||
|
return `${data.requestMethod} ${data.requestUrl}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
|
field: 'requestParams',
|
||||||
|
label: '请求参数',
|
||||||
|
content: (data) => {
|
||||||
|
return h(JsonViewer, {
|
||||||
|
value: data.requestParams,
|
||||||
|
previewMode: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'exceptionTime',
|
||||||
|
label: '异常时间',
|
||||||
|
content: (data) => {
|
||||||
|
return formatDateTime(data?.exceptionTime) as string;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'exceptionName',
|
||||||
|
label: '异常名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'exceptionStackTrace',
|
||||||
|
label: '异常堆栈',
|
||||||
|
content: (data) => {
|
||||||
|
return h(JsonViewer, {
|
||||||
|
value: data.exceptionStackTrace,
|
||||||
|
previewMode: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'processStatus',
|
||||||
|
label: '处理状态',
|
||||||
|
content: (data) => {
|
||||||
|
return h(DictTag, {
|
||||||
|
type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS,
|
||||||
|
value: data?.processStatus,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'processUserId',
|
||||||
|
label: '处理人',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'processTime',
|
||||||
|
label: '处理时间',
|
||||||
|
content: (data) => {
|
||||||
|
return formatDateTime(data?.processTime) as string;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
||||||
|
|
||||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download } from '@vben/icons';
|
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
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 {
|
import {
|
||||||
exportApiErrorLog,
|
exportApiErrorLog,
|
||||||
getApiErrorLogPage,
|
getApiErrorLogPage,
|
||||||
updateApiErrorLogStatus,
|
updateApiErrorLogStatus,
|
||||||
} from '#/api/infra/api-error-log';
|
} from '#/api/infra/api-error-log';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { InfraApiErrorLogProcessStatusEnum } from '#/utils';
|
import { InfraApiErrorLogProcessStatusEnum } from '#/utils';
|
||||||
|
|
||||||
@@ -35,18 +30,18 @@ function onRefresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 导出表格 */
|
||||||
async function onExport() {
|
async function handleExport() {
|
||||||
const data = await exportApiErrorLog(await gridApi.formApi.getValues());
|
const data = await exportApiErrorLog(await gridApi.formApi.getValues());
|
||||||
downloadFileFromBlobPart({ fileName: 'API 错误日志.xls', source: data });
|
downloadFileFromBlobPart({ fileName: 'API 错误日志.xls', source: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查看 API 错误日志详情 */
|
/** 查看 API 错误日志详情 */
|
||||||
function onDetail(row: InfraApiErrorLogApi.ApiErrorLog) {
|
function handleDetail(row: InfraApiErrorLogApi.ApiErrorLog) {
|
||||||
detailModalApi.setData(row).open();
|
detailModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 处理已处理 / 已忽略的操作 */
|
/** 处理已处理 / 已忽略的操作 */
|
||||||
async function onProcess(id: number, processStatus: number) {
|
async function handleProcess(id: number, processStatus: number) {
|
||||||
confirm({
|
confirm({
|
||||||
content: `确认标记为${InfraApiErrorLogProcessStatusEnum.DONE ? '已处理' : '已忽略'}?`,
|
content: `确认标记为${InfraApiErrorLogProcessStatusEnum.DONE ? '已处理' : '已忽略'}?`,
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
@@ -57,33 +52,12 @@ async function onProcess(id: number, processStatus: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
|
||||||
function onActionClick({
|
|
||||||
code,
|
|
||||||
row,
|
|
||||||
}: OnActionClickParams<InfraApiErrorLogApi.ApiErrorLog>) {
|
|
||||||
switch (code) {
|
|
||||||
case 'detail': {
|
|
||||||
onDetail(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'done': {
|
|
||||||
onProcess(row.id, InfraApiErrorLogProcessStatusEnum.DONE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ignore': {
|
|
||||||
onProcess(row.id, InfraApiErrorLogProcessStatusEnum.IGNORE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useGridFormSchema(),
|
schema: useGridFormSchema(),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick),
|
columns: useGridColumns(),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
@@ -117,15 +91,54 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
<DetailModal @success="onRefresh" />
|
<DetailModal @success="onRefresh" />
|
||||||
<Grid table-title="API 错误日志列表">
|
<Grid table-title="API 错误日志列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button
|
<TableAction
|
||||||
type="primary"
|
:actions="[
|
||||||
class="ml-2"
|
{
|
||||||
@click="onExport"
|
label: $t('ui.actionTitle.export'),
|
||||||
v-access:code="['infra:api-error-log:export']"
|
type: 'primary',
|
||||||
>
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
<Download class="size-5" />
|
auth: ['infra:api-error-log:export'],
|
||||||
{{ $t('ui.actionTitle.export') }}
|
onClick: handleExport,
|
||||||
</Button>
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.detail'),
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.VIEW,
|
||||||
|
auth: ['infra:api-error-log:query'],
|
||||||
|
onClick: handleDetail.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已处理',
|
||||||
|
type: 'link',
|
||||||
|
auth: ['infra:api-error-log:update-status'],
|
||||||
|
ifShow:
|
||||||
|
row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT,
|
||||||
|
onClick: handleProcess.bind(
|
||||||
|
null,
|
||||||
|
row.id,
|
||||||
|
InfraApiErrorLogProcessStatusEnum.DONE,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '已忽略',
|
||||||
|
type: 'link',
|
||||||
|
auth: ['infra:api-error-log:update-status'],
|
||||||
|
ifShow:
|
||||||
|
row.processStatus === InfraApiErrorLogProcessStatusEnum.INIT,
|
||||||
|
onClick: handleProcess.bind(
|
||||||
|
null,
|
||||||
|
row.id,
|
||||||
|
InfraApiErrorLogProcessStatusEnum.IGNORE,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -4,12 +4,10 @@ import type { InfraApiErrorLogApi } from '#/api/infra/api-error-log';
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { formatDateTime } from '@vben/utils';
|
|
||||||
|
|
||||||
import { Descriptions, Input } from 'ant-design-vue';
|
import { useDescription } from '#/components/description';
|
||||||
|
|
||||||
import { DictTag } from '#/components/dict-tag';
|
import { useDetailSchema } from '../data';
|
||||||
import { DICT_TYPE } from '#/utils';
|
|
||||||
|
|
||||||
const formData = ref<InfraApiErrorLogApi.ApiErrorLog>();
|
const formData = ref<InfraApiErrorLogApi.ApiErrorLog>();
|
||||||
|
|
||||||
@@ -32,6 +30,15 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [Description] = useDescription({
|
||||||
|
componentProps: {
|
||||||
|
bordered: true,
|
||||||
|
column: 1,
|
||||||
|
class: 'mx-4',
|
||||||
|
},
|
||||||
|
schema: useDetailSchema(),
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -41,63 +48,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
:show-cancel-button="false"
|
:show-cancel-button="false"
|
||||||
:show-confirm-button="false"
|
:show-confirm-button="false"
|
||||||
>
|
>
|
||||||
<Descriptions
|
<Description :data="formData" />
|
||||||
bordered
|
|
||||||
:column="1"
|
|
||||||
size="middle"
|
|
||||||
class="mx-4"
|
|
||||||
:label-style="{ width: '110px' }"
|
|
||||||
>
|
|
||||||
<Descriptions.Item label="日志编号">
|
|
||||||
{{ formData?.id }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="链路追踪">
|
|
||||||
{{ formData?.traceId }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="应用名">
|
|
||||||
{{ formData?.applicationName }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户编号">
|
|
||||||
{{ formData?.userId }}
|
|
||||||
<DictTag :type="DICT_TYPE.USER_TYPE" :value="formData?.userType" />
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户IP">
|
|
||||||
{{ formData?.userIp }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="用户UA">
|
|
||||||
{{ formData?.userAgent }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求信息">
|
|
||||||
{{ formData?.requestMethod }} {{ formData?.requestUrl }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="请求参数">
|
|
||||||
{{ formData?.requestParams }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="异常时间">
|
|
||||||
{{ formatDateTime(formData?.exceptionTime || '') }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="异常名">
|
|
||||||
{{ formData?.exceptionName }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item v-if="formData?.exceptionStackTrace" label="异常堆栈">
|
|
||||||
<Input.TextArea
|
|
||||||
:value="formData?.exceptionStackTrace"
|
|
||||||
:auto-size="{ maxRows: 20 }"
|
|
||||||
readonly
|
|
||||||
/>
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label="处理状态">
|
|
||||||
<DictTag
|
|
||||||
:type="DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS"
|
|
||||||
:value="formData?.processStatus"
|
|
||||||
/>
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item v-if="formData?.processUserId" label="处理人">
|
|
||||||
{{ formData?.processUserId }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item v-if="formData?.processTime" label="处理时间">
|
|
||||||
{{ formatDateTime(formData?.processTime || '') }}
|
|
||||||
</Descriptions.Item>
|
|
||||||
</Descriptions>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -60,34 +60,35 @@ const formData = ref(''); // 表单数据
|
|||||||
useFormCreateDesigner(designer); // 表单设计器增强
|
useFormCreateDesigner(designer); // 表单设计器增强
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const openModel = (title: string) => {
|
function openModel(title: string) {
|
||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
dialogTitle.value = title;
|
dialogTitle.value = title;
|
||||||
modalApi.open();
|
modalApi.open();
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 生成 JSON */
|
/** 生成 JSON */
|
||||||
const showJson = () => {
|
function showJson() {
|
||||||
openModel('生成 JSON');
|
openModel('生成 JSON');
|
||||||
formType.value = 0;
|
formType.value = 0;
|
||||||
formData.value = designer.value.getRule();
|
formData.value = designer.value.getRule();
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 生成 Options */
|
/** 生成 Options */
|
||||||
const showOption = () => {
|
function showOption() {
|
||||||
openModel('生成 Options');
|
openModel('生成 Options');
|
||||||
formType.value = 1;
|
formType.value = 1;
|
||||||
formData.value = designer.value.getOption();
|
formData.value = designer.value.getOption();
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 生成组件 */
|
/** 生成组件 */
|
||||||
const showTemplate = () => {
|
function showTemplate() {
|
||||||
openModel('生成组件');
|
openModel('生成组件');
|
||||||
formType.value = 2;
|
formType.value = 2;
|
||||||
formData.value = makeTemplate();
|
formData.value = makeTemplate();
|
||||||
};
|
}
|
||||||
|
|
||||||
const makeTemplate = () => {
|
/** 生成组件 */
|
||||||
|
function makeTemplate() {
|
||||||
const rule = designer.value.getRule();
|
const rule = designer.value.getRule();
|
||||||
const opt = designer.value.getOption();
|
const opt = designer.value.getOption();
|
||||||
return `<template>
|
return `<template>
|
||||||
@@ -111,10 +112,10 @@ const makeTemplate = () => {
|
|||||||
}
|
}
|
||||||
init()
|
init()
|
||||||
<\/script>`;
|
<\/script>`;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 复制 */
|
/** 复制 */
|
||||||
const copy = async (text: string) => {
|
async function copy(text: string) {
|
||||||
const textToCopy = JSON.stringify(text, null, 2);
|
const textToCopy = JSON.stringify(text, null, 2);
|
||||||
const { copy, copied, isSupported } = useClipboard({ source: textToCopy });
|
const { copy, copied, isSupported } = useClipboard({ source: textToCopy });
|
||||||
if (isSupported) {
|
if (isSupported) {
|
||||||
@@ -125,12 +126,10 @@ const copy = async (text: string) => {
|
|||||||
} else {
|
} else {
|
||||||
message.error('复制失败');
|
message.error('复制失败');
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/** 代码高亮 */
|
||||||
* 代码高亮
|
function highlightedCode(code: string) {
|
||||||
*/
|
|
||||||
const highlightedCode = (code: string) => {
|
|
||||||
// 处理语言和代码
|
// 处理语言和代码
|
||||||
let language = 'json';
|
let language = 'json';
|
||||||
if (formType.value === 2) {
|
if (formType.value === 2) {
|
||||||
@@ -143,7 +142,7 @@ const highlightedCode = (code: string) => {
|
|||||||
// 高亮
|
// 高亮
|
||||||
const result = hljs.highlight(code, { language, ignoreIllegals: true });
|
const result = hljs.highlight(code, { language, ignoreIllegals: true });
|
||||||
return result.value || ' ';
|
return result.value || ' ';
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import type { Recordable } from '@vben/types';
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
import type { SystemMenuApi } from '#/api/system/menu';
|
import type { SystemMenuApi } from '#/api/system/menu';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { handleTree } from '@vben/utils';
|
import { handleTree } from '@vben/utils';
|
||||||
|
|
||||||
@@ -16,8 +15,6 @@ import { getMenuList } from '#/api/system/menu';
|
|||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 导入数据库表的表单 */
|
/** 导入数据库表的表单 */
|
||||||
export function useImportTableFormSchema(): VbenFormSchema[] {
|
export function useImportTableFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -350,7 +347,7 @@ export function useGenerationInfoSubTableFormSchema(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '一对一',
|
label: '一对一',
|
||||||
value: 'false',
|
value: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -393,83 +390,43 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns<T = InfraCodegenApi.CodegenTable>(
|
export function useGridColumns(
|
||||||
onActionClick: OnActionClickFn<T>,
|
|
||||||
getDataSourceConfigName?: (dataSourceConfigId: number) => string | undefined,
|
getDataSourceConfigName?: (dataSourceConfigId: number) => string | undefined,
|
||||||
): VxeTableGridOptions['columns'] {
|
): VxeTableGridOptions['columns'] {
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'dataSourceConfigId',
|
field: 'dataSourceConfigId',
|
||||||
title: '数据源',
|
title: '数据源',
|
||||||
minWidth: 120,
|
formatter: ({ cellValue }) => getDataSourceConfigName?.(cellValue) || '-',
|
||||||
formatter: (row) => getDataSourceConfigName?.(row.cellValue) || '-',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'tableName',
|
field: 'tableName',
|
||||||
title: '表名称',
|
title: '表名称',
|
||||||
minWidth: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'tableComment',
|
field: 'tableComment',
|
||||||
title: '表描述',
|
title: '表描述',
|
||||||
minWidth: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'className',
|
field: 'className',
|
||||||
title: '实体',
|
title: '实体',
|
||||||
minWidth: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
minWidth: 180,
|
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'updateTime',
|
field: 'updateTime',
|
||||||
title: '更新时间',
|
title: '更新时间',
|
||||||
minWidth: 180,
|
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
width: 300,
|
width: 280,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
align: 'center',
|
slots: { default: 'actions' },
|
||||||
cellRender: {
|
|
||||||
attrs: {
|
|
||||||
nameField: 'tableName',
|
|
||||||
nameTitle: '代码生成',
|
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
|
||||||
name: 'CellOperation',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
code: 'preview',
|
|
||||||
text: '预览',
|
|
||||||
show: hasAccessByCodes(['infra:codegen:preview']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'edit',
|
|
||||||
show: hasAccessByCodes(['infra:codegen:update']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'delete',
|
|
||||||
show: hasAccessByCodes(['infra:codegen:delete']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'sync',
|
|
||||||
text: '同步',
|
|
||||||
show: hasAccessByCodes(['infra:codegen:update']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'generate',
|
|
||||||
text: '生成代码',
|
|
||||||
show: hasAccessByCodes(['infra:codegen:download']),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const columnInfoRef = ref<InstanceType<typeof ColumnInfo>>();
|
|||||||
const generateInfoRef = ref<InstanceType<typeof GenerationInfo>>();
|
const generateInfoRef = ref<InstanceType<typeof GenerationInfo>>();
|
||||||
|
|
||||||
/** 获取详情数据 */
|
/** 获取详情数据 */
|
||||||
const getDetail = async () => {
|
async function getDetail() {
|
||||||
const id = route.query.id as any;
|
const id = route.query.id as any;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return;
|
return;
|
||||||
@@ -42,10 +42,10 @@ const getDetail = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 提交表单 */
|
/** 提交表单 */
|
||||||
const submitForm = async () => {
|
async function submitForm() {
|
||||||
// 表单验证
|
// 表单验证
|
||||||
const basicInfoValid = await basicInfoRef.value?.validate();
|
const basicInfoValid = await basicInfoRef.value?.validate();
|
||||||
if (!basicInfoValid) {
|
if (!basicInfoValid) {
|
||||||
@@ -61,7 +61,6 @@ const submitForm = async () => {
|
|||||||
// 提交表单
|
// 提交表单
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.updating'),
|
content: $t('ui.actionMessage.updating'),
|
||||||
duration: 0,
|
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
@@ -74,32 +73,35 @@ const submitForm = async () => {
|
|||||||
columns,
|
columns,
|
||||||
});
|
});
|
||||||
// 关闭并提示
|
// 关闭并提示
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.operationSuccess'),
|
||||||
|
key: 'action_key_msg',
|
||||||
|
});
|
||||||
close();
|
close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存失败', error);
|
console.error('保存失败', error);
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
const tabs = useTabs();
|
const tabs = useTabs();
|
||||||
/** 返回列表 */
|
/** 返回列表 */
|
||||||
const close = () => {
|
function close() {
|
||||||
tabs.closeCurrentTab();
|
tabs.closeCurrentTab();
|
||||||
router.push('/infra/codegen');
|
router.push('/infra/codegen');
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 下一步 */
|
/** 下一步 */
|
||||||
const nextStep = async () => {
|
function nextStep() {
|
||||||
currentStep.value += 1;
|
currentStep.value += 1;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 上一步 */
|
/** 上一步 */
|
||||||
const prevStep = () => {
|
function prevStep() {
|
||||||
if (currentStep.value > 0) {
|
if (currentStep.value > 0) {
|
||||||
currentStep.value -= 1;
|
currentStep.value -= 1;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 步骤配置 */
|
/** 步骤配置 */
|
||||||
const steps = [
|
const steps = [
|
||||||
@@ -120,13 +122,11 @@ getDetail();
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height v-loading="loading">
|
<Page auto-content-height v-loading="loading">
|
||||||
<div
|
<div class="bg-card flex h-[95%] flex-col rounded-md p-4">
|
||||||
class="flex h-[95%] flex-col rounded-md bg-white p-4 dark:bg-[#1f1f1f] dark:text-gray-300"
|
|
||||||
>
|
|
||||||
<Steps
|
<Steps
|
||||||
type="navigation"
|
type="navigation"
|
||||||
v-model:current="currentStep"
|
v-model:current="currentStep"
|
||||||
class="mb-8 rounded shadow-sm dark:bg-[#141414]"
|
class="mb-8 rounded shadow-sm"
|
||||||
>
|
>
|
||||||
<Steps.Step
|
<Steps.Step
|
||||||
v-for="(step, index) in steps"
|
v-for="(step, index) in steps"
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||||
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Plus } from '@vben/icons';
|
import { isEmpty } 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 {
|
import {
|
||||||
deleteCodegenTable,
|
deleteCodegenTable,
|
||||||
|
deleteCodegenTableList,
|
||||||
downloadCodegen,
|
downloadCodegen,
|
||||||
getCodegenTablePage,
|
getCodegenTablePage,
|
||||||
syncCodegenFromDB,
|
syncCodegenFromDB,
|
||||||
} from '#/api/infra/codegen';
|
} from '#/api/infra/codegen';
|
||||||
import { getDataSourceConfigList } from '#/api/infra/data-source-config';
|
import { getDataSourceConfigList } from '#/api/infra/data-source-config';
|
||||||
import { DocAlert } from '#/components/doc-alert';
|
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useGridColumns, useGridFormSchema } from './data';
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
@@ -57,22 +54,22 @@ function onRefresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 导入表格 */
|
/** 导入表格 */
|
||||||
function onImport() {
|
function handleImport() {
|
||||||
importModalApi.open();
|
importModalApi.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 预览代码 */
|
/** 预览代码 */
|
||||||
function onPreview(row: InfraCodegenApi.CodegenTable) {
|
function handlePreview(row: InfraCodegenApi.CodegenTable) {
|
||||||
previewModalApi.setData(row).open();
|
previewModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑表格 */
|
/** 编辑表格 */
|
||||||
function onEdit(row: InfraCodegenApi.CodegenTable) {
|
function handleEdit(row: InfraCodegenApi.CodegenTable) {
|
||||||
router.push({ name: 'InfraCodegenEdit', query: { id: row.id } });
|
router.push({ name: 'InfraCodegenEdit', query: { id: row.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除代码生成配置 */
|
/** 删除代码生成配置 */
|
||||||
async function onDelete(row: InfraCodegenApi.CodegenTable) {
|
async function handleDelete(row: InfraCodegenApi.CodegenTable) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.tableName]),
|
content: $t('ui.actionMessage.deleting', [row.tableName]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
@@ -87,16 +84,43 @@ async function onDelete(row: InfraCodegenApi.CodegenTable) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 同步数据库 */
|
const checkedIds = ref<number[]>([]);
|
||||||
async function onSync(row: InfraCodegenApi.CodegenTable) {
|
function handleRowCheckboxChange({
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
records: InfraCodegenApi.CodegenTable[];
|
||||||
|
}) {
|
||||||
|
checkedIds.value = records.map((item) => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 批量删除代码生成配置 */
|
||||||
|
async function handleDeleteBatch() {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.updating', [row.tableName]),
|
content: $t('ui.actionMessage.deleting'),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
key: 'action_process_msg',
|
key: 'action_process_msg',
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
|
await deleteCodegenTableList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 同步数据库 */
|
||||||
|
async function handleSync(row: InfraCodegenApi.CodegenTable) {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.updating', [row.tableName]),
|
||||||
|
key: 'action_key_msg',
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
await syncCodegenFromDB(row.id);
|
await syncCodegenFromDB(row.id);
|
||||||
message.success($t('ui.actionMessage.updateSuccess', [row.tableName]));
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.updateSuccess', [row.tableName]),
|
||||||
|
key: 'action_key_msg',
|
||||||
|
});
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
@@ -104,11 +128,10 @@ async function onSync(row: InfraCodegenApi.CodegenTable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 生成代码 */
|
/** 生成代码 */
|
||||||
async function onGenerate(row: InfraCodegenApi.CodegenTable) {
|
async function handleGenerate(row: InfraCodegenApi.CodegenTable) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: '正在生成代码...',
|
content: '正在生成代码...',
|
||||||
duration: 0,
|
key: 'action_key_msg',
|
||||||
key: 'action_process_msg',
|
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const res = await downloadCodegen(row.id);
|
const res = await downloadCodegen(row.id);
|
||||||
@@ -119,47 +142,21 @@ async function onGenerate(row: InfraCodegenApi.CodegenTable) {
|
|||||||
link.download = `codegen-${row.className}.zip`;
|
link.download = `codegen-${row.className}.zip`;
|
||||||
link.click();
|
link.click();
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
message.success('代码生成成功');
|
message.success({
|
||||||
|
content: '代码生成成功',
|
||||||
|
key: 'action_key_msg',
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
|
||||||
function onActionClick({
|
|
||||||
code,
|
|
||||||
row,
|
|
||||||
}: OnActionClickParams<InfraCodegenApi.CodegenTable>) {
|
|
||||||
switch (code) {
|
|
||||||
case 'delete': {
|
|
||||||
onDelete(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'edit': {
|
|
||||||
onEdit(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'generate': {
|
|
||||||
onGenerate(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'preview': {
|
|
||||||
onPreview(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'sync': {
|
|
||||||
onSync(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useGridFormSchema(),
|
schema: useGridFormSchema(),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick, getDataSourceConfigName),
|
columns: useGridColumns(getDataSourceConfigName),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
@@ -175,12 +172,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: { code: 'query' },
|
refresh: { code: 'query' },
|
||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<InfraCodegenApi.CodegenTable>,
|
} as VxeTableGridOptions<InfraCodegenApi.CodegenTable>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 获取数据源配置列表 */
|
/** 获取数据源配置列表 */
|
||||||
@@ -217,14 +219,70 @@ initDataSourceConfig();
|
|||||||
<PreviewModal />
|
<PreviewModal />
|
||||||
<Grid table-title="代码生成列表">
|
<Grid table-title="代码生成列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button
|
<TableAction
|
||||||
type="primary"
|
:actions="[
|
||||||
@click="onImport"
|
{
|
||||||
v-access:code="['infra:codegen:create']"
|
label: $t('ui.actionTitle.import'),
|
||||||
>
|
type: 'primary',
|
||||||
<Plus class="size-5" />
|
icon: ACTION_ICON.ADD,
|
||||||
导入
|
auth: ['infra:codegen:create'],
|
||||||
</Button>
|
onClick: handleImport,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '批量删除',
|
||||||
|
type: 'primary',
|
||||||
|
danger: true,
|
||||||
|
disabled: isEmpty(checkedIds),
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['infra:codegen:delete'],
|
||||||
|
onClick: handleDeleteBatch,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '预览',
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.VIEW,
|
||||||
|
auth: ['infra:codegen:preview'],
|
||||||
|
onClick: handlePreview.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '生成代码',
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
|
auth: ['infra:codegen:download'],
|
||||||
|
onClick: handleGenerate.bind(null, row),
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:drop-down-actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.edit'),
|
||||||
|
type: 'link',
|
||||||
|
auth: ['infra:codegen:update'],
|
||||||
|
onClick: handleEdit.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '同步',
|
||||||
|
type: 'link',
|
||||||
|
auth: ['infra:codegen:update'],
|
||||||
|
onClick: handleSync.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.delete'),
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
auth: ['infra:codegen:delete'],
|
||||||
|
popConfirm: {
|
||||||
|
title: $t('ui.actionMessage.deleteConfirm', [row.tableName]),
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ function updateTreeSchema(): void {
|
|||||||
treeFormApi.setState({
|
treeFormApi.setState({
|
||||||
schema: useGenerationInfoTreeFormSchema(props.columns),
|
schema: useGenerationInfoTreeFormSchema(props.columns),
|
||||||
});
|
});
|
||||||
|
// 树表信息回显
|
||||||
|
treeFormApi.setValues(props.table as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更新主子表信息表单 schema */
|
/** 更新主子表信息表单 schema */
|
||||||
@@ -76,6 +78,8 @@ function updateSubSchema(): void {
|
|||||||
subFormApi.setState({
|
subFormApi.setState({
|
||||||
schema: useGenerationInfoSubTableFormSchema(props.columns, tables.value),
|
schema: useGenerationInfoSubTableFormSchema(props.columns, tables.value),
|
||||||
});
|
});
|
||||||
|
// 主子表信息回显
|
||||||
|
subFormApi.setValues(props.table as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取合并的表单值 */
|
/** 获取合并的表单值 */
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const emit = defineEmits<{
|
|||||||
(e: 'success'): void;
|
(e: 'success'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const formData = reactive<InfraCodegenApi.CodegenCreateListReqVO>({
|
const formData = reactive<InfraCodegenApi.CodegenCreateListReq>({
|
||||||
dataSourceConfigId: 0,
|
dataSourceConfigId: 0,
|
||||||
tableNames: [], // 已选择的表列表
|
tableNames: [], // 已选择的表列表
|
||||||
});
|
});
|
||||||
@@ -96,15 +96,17 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
// 2. 提交请求
|
// 2. 提交请求
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: '导入中...',
|
content: '导入中...',
|
||||||
duration: 0,
|
key: 'action_key_msg',
|
||||||
key: 'import_loading',
|
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await createCodegenList(formData);
|
await createCodegenList(formData);
|
||||||
// 关闭并提示
|
// 关闭并提示
|
||||||
await modalApi.close();
|
await modalApi.close();
|
||||||
emit('success');
|
emit('success');
|
||||||
message.success($t('ui.actionMessage.operationSuccess'));
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.operationSuccess'),
|
||||||
|
key: 'action_key_msg',
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
modalApi.unlock();
|
modalApi.unlock();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const activeKey = ref<string>('');
|
|||||||
|
|
||||||
/** 代码地图 */
|
/** 代码地图 */
|
||||||
const codeMap = ref<Map<string, string>>(new Map<string, string>());
|
const codeMap = ref<Map<string, string>>(new Map<string, string>());
|
||||||
const setCodeMap = (key: string, lang: string, code: string) => {
|
function setCodeMap(key: string, lang: string, code: string) {
|
||||||
// 处理可能的缩进问题,特别是对Java文件
|
// 处理可能的缩进问题,特别是对Java文件
|
||||||
const trimmedCode = code.trimStart();
|
const trimmedCode = code.trimStart();
|
||||||
// 如果已有缓存则不重新构建
|
// 如果已有缓存则不重新构建
|
||||||
@@ -59,8 +59,10 @@ const setCodeMap = (key: string, lang: string, code: string) => {
|
|||||||
} catch {
|
} catch {
|
||||||
codeMap.value.set(key, trimmedCode);
|
codeMap.value.set(key, trimmedCode);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
const removeCodeMapKey = (targetKey: any) => {
|
|
||||||
|
/** 删除代码地图 */
|
||||||
|
function removeCodeMapKey(targetKey: any) {
|
||||||
// 只有一个代码视图时不允许删除
|
// 只有一个代码视图时不允许删除
|
||||||
if (codeMap.value.size === 1) {
|
if (codeMap.value.size === 1) {
|
||||||
return;
|
return;
|
||||||
@@ -68,10 +70,10 @@ const removeCodeMapKey = (targetKey: any) => {
|
|||||||
if (codeMap.value.has(targetKey)) {
|
if (codeMap.value.has(targetKey)) {
|
||||||
codeMap.value.delete(targetKey);
|
codeMap.value.delete(targetKey);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 复制代码 */
|
/** 复制代码 */
|
||||||
const copyCode = async () => {
|
async function copyCode() {
|
||||||
const { copy } = useClipboard();
|
const { copy } = useClipboard();
|
||||||
const file = previewFiles.value.find(
|
const file = previewFiles.value.find(
|
||||||
(item) => item.filePath === activeKey.value,
|
(item) => item.filePath === activeKey.value,
|
||||||
@@ -80,10 +82,10 @@ const copyCode = async () => {
|
|||||||
await copy(file.code);
|
await copy(file.code);
|
||||||
message.success('复制成功');
|
message.success('复制成功');
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 文件节点点击事件 */
|
/** 文件节点点击事件 */
|
||||||
const handleNodeClick = (_: any[], e: any) => {
|
function handleNodeClick(_: any[], e: any) {
|
||||||
if (!e.node.isLeaf) return;
|
if (!e.node.isLeaf) return;
|
||||||
|
|
||||||
activeKey.value = e.node.key;
|
activeKey.value = e.node.key;
|
||||||
@@ -100,10 +102,10 @@ const handleNodeClick = (_: any[], e: any) => {
|
|||||||
|
|
||||||
const lang = file.filePath.split('.').pop() || '';
|
const lang = file.filePath.split('.').pop() || '';
|
||||||
setCodeMap(activeKey.value, lang, file.code);
|
setCodeMap(activeKey.value, lang, file.code);
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 处理文件树 */
|
/** 处理文件树 */
|
||||||
const handleFiles = (data: InfraCodegenApi.CodegenPreview[]): FileNode[] => {
|
function handleFiles(data: InfraCodegenApi.CodegenPreview[]): FileNode[] {
|
||||||
const exists: Record<string, boolean> = {};
|
const exists: Record<string, boolean> = {};
|
||||||
const files: FileNode[] = [];
|
const files: FileNode[] = [];
|
||||||
|
|
||||||
@@ -176,17 +178,17 @@ const handleFiles = (data: InfraCodegenApi.CodegenPreview[]): FileNode[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 构建树形结构 */
|
/** 构建树形结构 */
|
||||||
const buildTree = (parentKey: string): FileNode[] => {
|
function buildTree(parentKey: string): FileNode[] {
|
||||||
return files
|
return files
|
||||||
.filter((file) => file.parentKey === parentKey)
|
.filter((file) => file.parentKey === parentKey)
|
||||||
.map((file) => ({
|
.map((file) => ({
|
||||||
...file,
|
...file,
|
||||||
children: buildTree(file.key),
|
children: buildTree(file.key),
|
||||||
}));
|
}));
|
||||||
};
|
}
|
||||||
|
|
||||||
return buildTree('/');
|
return buildTree('/');
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 模态框实例 */
|
/** 模态框实例 */
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { InfraConfigApi } from '#/api/infra/config';
|
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
|
||||||
|
|
||||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 新增/修改的表单 */
|
/** 新增/修改的表单 */
|
||||||
export function useFormSchema(): VbenFormSchema[] {
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -122,39 +117,32 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns<T = InfraConfigApi.Config>(
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
onActionClick: OnActionClickFn<T>,
|
|
||||||
): VxeTableGridOptions['columns'] {
|
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '参数主键',
|
title: '参数主键',
|
||||||
minWidth: 100,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'category',
|
field: 'category',
|
||||||
title: '参数分类',
|
title: '参数分类',
|
||||||
minWidth: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '参数名称',
|
title: '参数名称',
|
||||||
minWidth: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'key',
|
field: 'key',
|
||||||
title: '参数键名',
|
title: '参数键名',
|
||||||
minWidth: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'value',
|
field: 'value',
|
||||||
title: '参数键值',
|
title: '参数键值',
|
||||||
minWidth: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'visible',
|
field: 'visible',
|
||||||
title: '是否可见',
|
title: '是否可见',
|
||||||
minWidth: 100,
|
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
||||||
@@ -163,7 +151,6 @@ export function useGridColumns<T = InfraConfigApi.Config>(
|
|||||||
{
|
{
|
||||||
field: 'type',
|
field: 'type',
|
||||||
title: '系统内置',
|
title: '系统内置',
|
||||||
minWidth: 100,
|
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.INFRA_CONFIG_TYPE },
|
props: { type: DICT_TYPE.INFRA_CONFIG_TYPE },
|
||||||
@@ -172,38 +159,17 @@ export function useGridColumns<T = InfraConfigApi.Config>(
|
|||||||
{
|
{
|
||||||
field: 'remark',
|
field: 'remark',
|
||||||
title: '备注',
|
title: '备注',
|
||||||
minWidth: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
minWidth: 180,
|
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
minWidth: 130,
|
width: 160,
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
cellRender: {
|
slots: { default: 'actions' },
|
||||||
attrs: {
|
|
||||||
nameField: 'name',
|
|
||||||
nameTitle: '参数',
|
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
|
||||||
name: 'CellOperation',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
code: 'edit',
|
|
||||||
show: hasAccessByCodes(['infra:config:update']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'delete',
|
|
||||||
show: hasAccessByCodes(['infra:config:delete']),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { InfraConfigApi } from '#/api/infra/config';
|
import type { InfraConfigApi } from '#/api/infra/config';
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download, Plus } from '@vben/icons';
|
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||||
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 { deleteConfig, exportConfig, getConfigPage } from '#/api/infra/config';
|
import {
|
||||||
|
deleteConfig,
|
||||||
|
deleteConfigList,
|
||||||
|
exportConfig,
|
||||||
|
getConfigPage,
|
||||||
|
} from '#/api/infra/config';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
|
|
||||||
import { useGridColumns, useGridFormSchema } from './data';
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
@@ -29,51 +32,62 @@ function onRefresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 导出表格 */
|
||||||
async function onExport() {
|
async function handleExport() {
|
||||||
const data = await exportConfig(await gridApi.formApi.getValues());
|
const data = await exportConfig(await gridApi.formApi.getValues());
|
||||||
downloadFileFromBlobPart({ fileName: '参数配置.xls', source: data });
|
downloadFileFromBlobPart({ fileName: '参数配置.xls', source: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 创建参数 */
|
/** 创建参数 */
|
||||||
function onCreate() {
|
function handleCreate() {
|
||||||
formModalApi.setData(null).open();
|
formModalApi.setData(null).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑参数 */
|
/** 编辑参数 */
|
||||||
function onEdit(row: InfraConfigApi.Config) {
|
function handleEdit(row: InfraConfigApi.Config) {
|
||||||
formModalApi.setData(row).open();
|
formModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除参数 */
|
/** 删除参数 */
|
||||||
async function onDelete(row: InfraConfigApi.Config) {
|
async function handleDelete(row: InfraConfigApi.Config) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
key: 'action_process_msg',
|
key: 'action_key_msg',
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteConfig(row.id as number);
|
await deleteConfig(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();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
const checkedIds = ref<number[]>([]);
|
||||||
function onActionClick({
|
function handleRowCheckboxChange({
|
||||||
code,
|
records,
|
||||||
row,
|
}: {
|
||||||
}: OnActionClickParams<InfraConfigApi.Config>) {
|
records: InfraConfigApi.Config[];
|
||||||
switch (code) {
|
}) {
|
||||||
case 'delete': {
|
checkedIds.value = records.map((item) => item.id as number);
|
||||||
onDelete(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'edit': {
|
|
||||||
onEdit(row);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除参数 */
|
||||||
|
async function handleDeleteBatch() {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteConfigList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +96,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
schema: useGridFormSchema(),
|
schema: useGridFormSchema(),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick),
|
columns: useGridColumns(),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
@@ -98,12 +112,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
keyField: 'id',
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
refresh: { code: 'query' },
|
refresh: { code: 'query' },
|
||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<InfraConfigApi.Config>,
|
} as VxeTableGridOptions<InfraConfigApi.Config>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -112,23 +131,57 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="onRefresh" />
|
||||||
<Grid table-title="参数列表">
|
<Grid table-title="参数列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button
|
<TableAction
|
||||||
type="primary"
|
:actions="[
|
||||||
@click="onCreate"
|
{
|
||||||
v-access:code="['infra:config:create']"
|
label: $t('ui.actionTitle.create', ['参数列表']),
|
||||||
>
|
type: 'primary',
|
||||||
<Plus class="size-5" />
|
icon: ACTION_ICON.ADD,
|
||||||
{{ $t('ui.actionTitle.create', ['参数']) }}
|
auth: ['infra:config:create'],
|
||||||
</Button>
|
onClick: handleCreate,
|
||||||
<Button
|
},
|
||||||
type="primary"
|
{
|
||||||
class="ml-2"
|
label: $t('ui.actionTitle.export'),
|
||||||
@click="onExport"
|
type: 'primary',
|
||||||
v-access:code="['infra:config:export']"
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
>
|
auth: ['infra:config:export'],
|
||||||
<Download class="size-5" />
|
onClick: handleExport,
|
||||||
{{ $t('ui.actionTitle.export') }}
|
},
|
||||||
</Button>
|
{
|
||||||
|
label: '批量删除',
|
||||||
|
type: 'primary',
|
||||||
|
danger: true,
|
||||||
|
disabled: isEmpty(checkedIds),
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['infra:config:delete'],
|
||||||
|
onClick: handleDeleteBatch,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</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>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 新增/修改的表单 */
|
/** 新增/修改的表单 */
|
||||||
export function useFormSchema(): VbenFormSchema[] {
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
@@ -58,62 +53,34 @@ export function useFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns<T = InfraDataSourceConfigApi.DataSourceConfig>(
|
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||||
onActionClick: OnActionClickFn<T>,
|
|
||||||
): VxeTableGridOptions['columns'] {
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '主键编号',
|
title: '主键编号',
|
||||||
minWidth: 100,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
title: '数据源名称',
|
title: '数据源名称',
|
||||||
minWidth: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'url',
|
field: 'url',
|
||||||
title: '数据源连接',
|
title: '数据源连接',
|
||||||
minWidth: 300,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'username',
|
field: 'username',
|
||||||
title: '用户名',
|
title: '用户名',
|
||||||
minWidth: 120,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'createTime',
|
field: 'createTime',
|
||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
minWidth: 180,
|
|
||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
minWidth: 130,
|
width: 160,
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
cellRender: {
|
slots: { default: 'actions' },
|
||||||
attrs: {
|
|
||||||
nameField: 'name',
|
|
||||||
nameTitle: '数据源',
|
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
|
||||||
name: 'CellOperation',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
code: 'edit',
|
|
||||||
show: hasAccessByCodes(['infra:data-source-config:update']),
|
|
||||||
disabled: (row: any) => row.id === 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'delete',
|
|
||||||
show: hasAccessByCodes(['infra:data-source-config:delete']),
|
|
||||||
disabled: (row: any) => row.id === 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
import type { InfraDataSourceConfigApi } from '#/api/infra/data-source-config';
|
||||||
|
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
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 {
|
import {
|
||||||
deleteDataSourceConfig,
|
deleteDataSourceConfig,
|
||||||
getDataSourceConfigList,
|
getDataSourceConfigList,
|
||||||
@@ -28,51 +24,37 @@ const [FormModal, formModalApi] = useVbenModal({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 创建数据源 */
|
/** 创建数据源 */
|
||||||
function onCreate() {
|
function handleCreate() {
|
||||||
formModalApi.setData(null).open();
|
formModalApi.setData(null).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑数据源 */
|
/** 编辑数据源 */
|
||||||
function onEdit(row: InfraDataSourceConfigApi.DataSourceConfig) {
|
function handleEdit(row: InfraDataSourceConfigApi.DataSourceConfig) {
|
||||||
formModalApi.setData(row).open();
|
formModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除数据源 */
|
/** 删除数据源 */
|
||||||
async function onDelete(row: InfraDataSourceConfigApi.DataSourceConfig) {
|
async function handleDelete(row: InfraDataSourceConfigApi.DataSourceConfig) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
key: 'action_process_msg',
|
key: 'action_key_msg',
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteDataSourceConfig(row.id as number);
|
await deleteDataSourceConfig(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',
|
||||||
|
});
|
||||||
await handleLoadData();
|
await handleLoadData();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
|
||||||
function onActionClick({
|
|
||||||
code,
|
|
||||||
row,
|
|
||||||
}: OnActionClickParams<InfraDataSourceConfigApi.DataSourceConfig>) {
|
|
||||||
switch (code) {
|
|
||||||
case 'delete': {
|
|
||||||
onDelete(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'edit': {
|
|
||||||
onEdit(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick),
|
columns: useGridColumns(),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
rowConfig: {
|
rowConfig: {
|
||||||
@@ -94,11 +76,6 @@ async function handleLoadData() {
|
|||||||
await gridApi.query();
|
await gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 刷新表格 */
|
|
||||||
async function onRefresh() {
|
|
||||||
await handleLoadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
handleLoadData();
|
handleLoadData();
|
||||||
@@ -107,17 +84,44 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<FormModal @success="onRefresh" />
|
<FormModal @success="handleLoadData" />
|
||||||
<Grid table-title="数据源列表">
|
<Grid table-title="数据源列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button
|
<TableAction
|
||||||
type="primary"
|
:actions="[
|
||||||
@click="onCreate"
|
{
|
||||||
v-access:code="['infra:data-source-config:create']"
|
label: $t('ui.actionTitle.create', ['数据源']),
|
||||||
>
|
type: 'primary',
|
||||||
<Plus class="size-5" />
|
icon: ACTION_ICON.ADD,
|
||||||
{{ $t('ui.actionTitle.create', ['数据源']) }}
|
auth: ['infra:data-source-config:create'],
|
||||||
</Button>
|
onClick: handleCreate,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.edit'),
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['infra:data-source-config:update'],
|
||||||
|
onClick: handleEdit.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.delete'),
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['infra:data-source-config:delete'],
|
||||||
|
popConfirm: {
|
||||||
|
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal :title="getTitle">
|
<Modal class="w-2/5" :title="getTitle">
|
||||||
<Form class="mx-4" />
|
<Form class="mx-4" />
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
|
||||||
|
|
||||||
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 新增/修改的表单 */
|
/** 新增/修改的表单 */
|
||||||
export function useFormSchema(): VbenFormSchema[] {
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -99,10 +95,9 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns(
|
export function useGridColumns(): VxeTableGridOptions<Demo01ContactApi.Demo01Contact>['columns'] {
|
||||||
onActionClick?: OnActionClickFn<Demo01ContactApi.Demo01Contact>,
|
|
||||||
): VxeTableGridOptions<Demo01ContactApi.Demo01Contact>['columns'] {
|
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
@@ -145,32 +140,10 @@ export function useGridColumns(
|
|||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
minWidth: 200,
|
width: 160,
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
// TODO @puhui999:headerAlign 要使用 headerAlign: 'center' 么?看着现在分成了 align 和 headerAlign 两种
|
slots: { default: 'actions' },
|
||||||
headerAlign: 'center',
|
|
||||||
showOverflow: false,
|
|
||||||
cellRender: {
|
|
||||||
attrs: {
|
|
||||||
nameField: 'id',
|
|
||||||
nameTitle: '示例联系人',
|
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
|
||||||
name: 'CellOperation',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
code: 'edit',
|
|
||||||
show: hasAccessByCodes(['infra:demo01-contact:update']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'delete',
|
|
||||||
show: hasAccessByCodes(['infra:demo01-contact:delete']),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download, Plus } from '@vben/icons';
|
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||||
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 {
|
import {
|
||||||
deleteDemo01Contact,
|
deleteDemo01Contact,
|
||||||
|
deleteDemo01ContactList,
|
||||||
exportDemo01Contact,
|
exportDemo01Contact,
|
||||||
getDemo01ContactPage,
|
getDemo01ContactPage,
|
||||||
} from '#/api/infra/demo/demo01';
|
} from '#/api/infra/demo/demo01';
|
||||||
@@ -35,17 +32,17 @@ function onRefresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 创建示例联系人 */
|
/** 创建示例联系人 */
|
||||||
function onCreate() {
|
function handleCreate() {
|
||||||
formModalApi.setData({}).open();
|
formModalApi.setData({}).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑示例联系人 */
|
/** 编辑示例联系人 */
|
||||||
function onEdit(row: Demo01ContactApi.Demo01Contact) {
|
function handleEdit(row: Demo01ContactApi.Demo01Contact) {
|
||||||
formModalApi.setData(row).open();
|
formModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除示例联系人 */
|
/** 删除示例联系人 */
|
||||||
async function onDelete(row: Demo01ContactApi.Demo01Contact) {
|
async function handleDelete(row: Demo01ContactApi.Demo01Contact) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
@@ -55,32 +52,40 @@ async function onDelete(row: Demo01ContactApi.Demo01Contact) {
|
|||||||
await deleteDemo01Contact(row.id as number);
|
await deleteDemo01Contact(row.id as number);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 批量删除示例联系人 */
|
||||||
async function onExport() {
|
async function handleDeleteBatch() {
|
||||||
const data = await exportDemo01Contact(await gridApi.formApi.getValues());
|
const hideLoading = message.loading({
|
||||||
downloadFileFromBlobPart({ fileName: '示例联系人.xls', source: data });
|
content: $t('ui.actionMessage.deleting'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteDemo01ContactList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
const checkedIds = ref<number[]>([]);
|
||||||
function onActionClick({
|
function handleRowCheckboxChange({
|
||||||
code,
|
records,
|
||||||
row,
|
}: {
|
||||||
}: OnActionClickParams<Demo01ContactApi.Demo01Contact>) {
|
records: Demo01ContactApi.Demo01Contact[];
|
||||||
switch (code) {
|
}) {
|
||||||
case 'delete': {
|
checkedIds.value = records.map((item) => item.id);
|
||||||
onDelete(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'edit': {
|
|
||||||
onEdit(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 导出表格 */
|
||||||
|
async function handleExport() {
|
||||||
|
const data = await exportDemo01Contact(await gridApi.formApi.getValues());
|
||||||
|
downloadFileFromBlobPart({ fileName: '示例联系人.xls', source: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
@@ -88,7 +93,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
schema: useGridFormSchema(),
|
schema: useGridFormSchema(),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick),
|
columns: useGridColumns(),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
pagerConfig: {
|
pagerConfig: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -113,6 +118,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<Demo01ContactApi.Demo01Contact>,
|
} as VxeTableGridOptions<Demo01ContactApi.Demo01Contact>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -122,23 +131,57 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<Grid table-title="示例联系人列表">
|
<Grid table-title="示例联系人列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button
|
<TableAction
|
||||||
:icon="h(Plus)"
|
:actions="[
|
||||||
type="primary"
|
{
|
||||||
@click="onCreate"
|
label: $t('ui.actionTitle.create', ['示例联系人']),
|
||||||
v-access:code="['infra:demo01-contact:create']"
|
type: 'primary',
|
||||||
>
|
icon: ACTION_ICON.ADD,
|
||||||
{{ $t('ui.actionTitle.create', ['示例联系人']) }}
|
auth: ['infra:demo01-contact:create'],
|
||||||
</Button>
|
onClick: handleCreate,
|
||||||
<Button
|
},
|
||||||
:icon="h(Download)"
|
{
|
||||||
type="primary"
|
label: $t('ui.actionTitle.export'),
|
||||||
class="ml-2"
|
type: 'primary',
|
||||||
@click="onExport"
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
v-access:code="['infra:demo01-contact:export']"
|
auth: ['infra:demo01-contact:export'],
|
||||||
>
|
onClick: handleExport,
|
||||||
{{ $t('ui.actionTitle.export') }}
|
},
|
||||||
</Button>
|
{
|
||||||
|
label: '批量删除',
|
||||||
|
type: 'primary',
|
||||||
|
danger: true,
|
||||||
|
disabled: isEmpty(checkedIds),
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['infra:demo01-contact:delete'],
|
||||||
|
onClick: handleDeleteBatch,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: $t('common.edit'),
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['infra:demo01-contact:update'],
|
||||||
|
onClick: handleEdit.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.delete'),
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['infra:demo01-contact:delete'],
|
||||||
|
popConfirm: {
|
||||||
|
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import type { VbenFormSchema } from '#/adapter/form';
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
||||||
|
|
||||||
import { useAccess } from '@vben/access';
|
|
||||||
import { handleTree } from '@vben/utils';
|
import { handleTree } from '@vben/utils';
|
||||||
|
|
||||||
import { getDemo02CategoryList } from '#/api/infra/demo/demo02';
|
import { getDemo02CategoryList } from '#/api/infra/demo/demo02';
|
||||||
import { getRangePickerDefaultProps } from '#/utils';
|
import { getRangePickerDefaultProps } from '#/utils';
|
||||||
|
|
||||||
const { hasAccessByCodes } = useAccess();
|
|
||||||
|
|
||||||
/** 新增/修改的表单 */
|
/** 新增/修改的表单 */
|
||||||
export function useFormSchema(): VbenFormSchema[] {
|
export function useFormSchema(): VbenFormSchema[] {
|
||||||
return [
|
return [
|
||||||
@@ -89,9 +86,7 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 列表的字段 */
|
/** 列表的字段 */
|
||||||
export function useGridColumns(
|
export function useGridColumns(): VxeTableGridOptions<Demo02CategoryApi.Demo02Category>['columns'] {
|
||||||
onActionClick?: OnActionClickFn<Demo02CategoryApi.Demo02Category>,
|
|
||||||
): VxeTableGridOptions<Demo02CategoryApi.Demo02Category>['columns'] {
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
@@ -116,39 +111,10 @@ export function useGridColumns(
|
|||||||
formatter: 'formatDateTime',
|
formatter: 'formatDateTime',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'operation',
|
|
||||||
title: '操作',
|
title: '操作',
|
||||||
minWidth: 200,
|
width: 220,
|
||||||
align: 'center',
|
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
headerAlign: 'center',
|
slots: { default: 'actions' },
|
||||||
showOverflow: false,
|
|
||||||
cellRender: {
|
|
||||||
attrs: {
|
|
||||||
nameField: 'id',
|
|
||||||
nameTitle: '示例分类',
|
|
||||||
onClick: onActionClick,
|
|
||||||
},
|
|
||||||
name: 'CellOperation',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
code: 'append',
|
|
||||||
text: '新增下级',
|
|
||||||
show: hasAccessByCodes(['infra:demo02-category:create']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'edit',
|
|
||||||
show: hasAccessByCodes(['infra:demo02-category:update']),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'delete',
|
|
||||||
show: hasAccessByCodes(['infra:demo02-category:delete']),
|
|
||||||
disabled: (row: Demo02CategoryApi.Demo02Category) => {
|
|
||||||
return !!(row.children && row.children.length > 0);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
OnActionClickParams,
|
|
||||||
VxeTableGridOptions,
|
|
||||||
} from '#/adapter/vxe-table';
|
|
||||||
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
||||||
|
|
||||||
import { h, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download, Plus } from '@vben/icons';
|
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
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 {
|
import {
|
||||||
deleteDemo02Category,
|
deleteDemo02Category,
|
||||||
exportDemo02Category,
|
exportDemo02Category,
|
||||||
@@ -42,69 +38,50 @@ function onRefresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 导出表格 */
|
||||||
async function onExport() {
|
async function handleExport() {
|
||||||
const data = await exportDemo02Category(await gridApi.formApi.getValues());
|
const data = await exportDemo02Category(await gridApi.formApi.getValues());
|
||||||
downloadFileFromBlobPart({ fileName: '示例分类.xls', source: data });
|
downloadFileFromBlobPart({ fileName: '示例分类.xls', source: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 创建示例分类 */
|
/** 创建示例分类 */
|
||||||
function onCreate() {
|
function handleCreate() {
|
||||||
formModalApi.setData(null).open();
|
formModalApi.setData(null).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑示例分类 */
|
/** 编辑示例分类 */
|
||||||
function onEdit(row: Demo02CategoryApi.Demo02Category) {
|
function handleEdit(row: Demo02CategoryApi.Demo02Category) {
|
||||||
formModalApi.setData(row).open();
|
formModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增下级示例分类 */
|
/** 新增下级示例分类 */
|
||||||
function onAppend(row: Demo02CategoryApi.Demo02Category) {
|
function handleAppend(row: Demo02CategoryApi.Demo02Category) {
|
||||||
formModalApi.setData({ parentId: row.id }).open();
|
formModalApi.setData({ parentId: row.id }).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除示例分类 */
|
/** 删除示例分类 */
|
||||||
async function onDelete(row: Demo02CategoryApi.Demo02Category) {
|
async function handleDelete(row: Demo02CategoryApi.Demo02Category) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||||
duration: 0,
|
key: 'action_key_msg',
|
||||||
key: 'action_process_msg',
|
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await deleteDemo02Category(row.id as number);
|
await deleteDemo02Category(row.id as number);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
message.success({
|
||||||
|
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||||
|
key: 'action_key_msg',
|
||||||
|
});
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
|
||||||
function onActionClick({
|
|
||||||
code,
|
|
||||||
row,
|
|
||||||
}: OnActionClickParams<Demo02CategoryApi.Demo02Category>) {
|
|
||||||
switch (code) {
|
|
||||||
case 'append': {
|
|
||||||
onAppend(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'delete': {
|
|
||||||
onDelete(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'edit': {
|
|
||||||
onEdit(row);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
formOptions: {
|
formOptions: {
|
||||||
schema: useGridFormSchema(),
|
schema: useGridFormSchema(),
|
||||||
},
|
},
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useGridColumns(onActionClick),
|
columns: useGridColumns(),
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
treeConfig: {
|
treeConfig: {
|
||||||
parentField: 'parentId',
|
parentField: 'parentId',
|
||||||
@@ -141,26 +118,60 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
|
|
||||||
<Grid table-title="示例分类列表">
|
<Grid table-title="示例分类列表">
|
||||||
<template #toolbar-tools>
|
<template #toolbar-tools>
|
||||||
<Button @click="toggleExpand" class="mr-2">
|
<TableAction
|
||||||
{{ isExpanded ? '收缩' : '展开' }}
|
:actions="[
|
||||||
</Button>
|
{
|
||||||
<Button
|
label: isExpanded ? '收缩' : '展开',
|
||||||
:icon="h(Plus)"
|
type: 'primary',
|
||||||
type="primary"
|
onClick: toggleExpand,
|
||||||
@click="onCreate"
|
},
|
||||||
v-access:code="['infra:demo02-category:create']"
|
{
|
||||||
>
|
label: $t('ui.actionTitle.create', ['菜单']),
|
||||||
{{ $t('ui.actionTitle.create', ['示例分类']) }}
|
type: 'primary',
|
||||||
</Button>
|
icon: ACTION_ICON.ADD,
|
||||||
<Button
|
auth: ['infra:demo02-category:create'],
|
||||||
:icon="h(Download)"
|
onClick: handleCreate,
|
||||||
type="primary"
|
},
|
||||||
class="ml-2"
|
{
|
||||||
@click="onExport"
|
label: $t('ui.actionTitle.export'),
|
||||||
v-access:code="['infra:demo02-category:export']"
|
type: 'primary',
|
||||||
>
|
icon: ACTION_ICON.DOWNLOAD,
|
||||||
{{ $t('ui.actionTitle.export') }}
|
auth: ['infra:demo02-category:export'],
|
||||||
</Button>
|
onClick: handleExport,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<TableAction
|
||||||
|
:actions="[
|
||||||
|
{
|
||||||
|
label: '新增下级',
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.ADD,
|
||||||
|
auth: ['infra:demo02-category:create'],
|
||||||
|
onClick: handleAppend.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.edit'),
|
||||||
|
type: 'link',
|
||||||
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['infra:demo02-category:update'],
|
||||||
|
onClick: handleEdit.bind(null, row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('common.delete'),
|
||||||
|
type: 'link',
|
||||||
|
danger: true,
|
||||||
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['infra:demo02-category:delete'],
|
||||||
|
popConfirm: {
|
||||||
|
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||||
|
confirm: handleDelete.bind(null, row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export function useGridColumns(
|
|||||||
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Student>,
|
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Student>,
|
||||||
): VxeTableGridOptions<Demo03StudentApi.Demo03Student>['columns'] {
|
): VxeTableGridOptions<Demo03StudentApi.Demo03Student>['columns'] {
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
@@ -149,7 +150,6 @@ export function useGridColumns(
|
|||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
headerAlign: 'center',
|
|
||||||
showOverflow: false,
|
showOverflow: false,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -254,6 +254,7 @@ export function useDemo03CourseGridColumns(
|
|||||||
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Course>,
|
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Course>,
|
||||||
): VxeTableGridOptions<Demo03StudentApi.Demo03Course>['columns'] {
|
): VxeTableGridOptions<Demo03StudentApi.Demo03Course>['columns'] {
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
@@ -286,7 +287,7 @@ export function useDemo03CourseGridColumns(
|
|||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
headerAlign: 'center',
|
|
||||||
showOverflow: false,
|
showOverflow: false,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -391,6 +392,7 @@ export function useDemo03GradeGridColumns(
|
|||||||
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Grade>,
|
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Grade>,
|
||||||
): VxeTableGridOptions<Demo03StudentApi.Demo03Grade>['columns'] {
|
): VxeTableGridOptions<Demo03StudentApi.Demo03Grade>['columns'] {
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
title: '编号',
|
title: '编号',
|
||||||
@@ -423,7 +425,7 @@ export function useDemo03GradeGridColumns(
|
|||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
headerAlign: 'center',
|
|
||||||
showOverflow: false,
|
showOverflow: false,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
attrs: {
|
attrs: {
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
|||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download, Plus } from '@vben/icons';
|
import { Download, Plus, Trash2 } from '@vben/icons';
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { Button, message, Tabs } from 'ant-design-vue';
|
import { Button, message, Tabs } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import {
|
import {
|
||||||
deleteDemo03Student,
|
deleteDemo03Student,
|
||||||
|
deleteDemo03StudentList,
|
||||||
exportDemo03Student,
|
exportDemo03Student,
|
||||||
getDemo03StudentPage,
|
getDemo03StudentPage,
|
||||||
} from '#/api/infra/demo/demo03/erp';
|
} from '#/api/infra/demo/demo03/erp';
|
||||||
@@ -61,11 +62,36 @@ async function onDelete(row: Demo03StudentApi.Demo03Student) {
|
|||||||
await deleteDemo03Student(row.id as number);
|
await deleteDemo03Student(row.id as number);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生 */
|
||||||
|
async function onDeleteBatch() {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteDemo03StudentList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkedIds = ref<number[]>([]);
|
||||||
|
function handleRowCheckboxChange({
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
records: Demo03StudentApi.Demo03Grade[];
|
||||||
|
}) {
|
||||||
|
checkedIds.value = records.map((item) => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 导出表格 */
|
||||||
async function onExport() {
|
async function onExport() {
|
||||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||||
@@ -124,6 +150,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
cellClick: ({ row }: { row: Demo03StudentApi.Demo03Student }) => {
|
cellClick: ({ row }: { row: Demo03StudentApi.Demo03Student }) => {
|
||||||
selectDemo03Student.value = row;
|
selectDemo03Student.value = row;
|
||||||
},
|
},
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -152,6 +180,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
>
|
>
|
||||||
{{ $t('ui.actionTitle.export') }}
|
{{ $t('ui.actionTitle.export') }}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
:icon="h(Trash2)"
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
class="ml-2"
|
||||||
|
:disabled="isEmpty(checkedIds)"
|
||||||
|
@click="onDeleteBatch"
|
||||||
|
v-access:code="['infra:demo03-student:delete']"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,18 @@ import type {
|
|||||||
} from '#/adapter/vxe-table';
|
} from '#/adapter/vxe-table';
|
||||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||||
|
|
||||||
import { h, nextTick, watch } from 'vue';
|
import { h, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { Plus } from '@vben/icons';
|
import { Plus, Trash2 } from '@vben/icons';
|
||||||
|
import { isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import {
|
import {
|
||||||
deleteDemo03Course,
|
deleteDemo03Course,
|
||||||
|
deleteDemo03CourseList,
|
||||||
getDemo03CoursePage,
|
getDemo03CoursePage,
|
||||||
} from '#/api/infra/demo/demo03/erp';
|
} from '#/api/infra/demo/demo03/erp';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -59,11 +61,36 @@ async function onDelete(row: Demo03StudentApi.Demo03Course) {
|
|||||||
await deleteDemo03Course(row.id as number);
|
await deleteDemo03Course(row.id as number);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生课程 */
|
||||||
|
async function onDeleteBatch() {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteDemo03CourseList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkedIds = ref<number[]>([]);
|
||||||
|
function handleRowCheckboxChange({
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
records: Demo03StudentApi.Demo03Course[];
|
||||||
|
}) {
|
||||||
|
checkedIds.value = records.map((item) => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
/** 表格操作按钮的回调函数 */
|
||||||
function onActionClick({
|
function onActionClick({
|
||||||
code,
|
code,
|
||||||
@@ -115,6 +142,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
isHover: true,
|
isHover: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<Demo03StudentApi.Demo03Course>,
|
} as VxeTableGridOptions<Demo03StudentApi.Demo03Course>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
@@ -148,6 +179,17 @@ watch(
|
|||||||
>
|
>
|
||||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
:icon="h(Trash2)"
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
class="ml-2"
|
||||||
|
:disabled="isEmpty(checkedIds)"
|
||||||
|
@click="onDeleteBatch"
|
||||||
|
v-access:code="['infra:demo03-student:delete']"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,16 +5,18 @@ import type {
|
|||||||
} from '#/adapter/vxe-table';
|
} from '#/adapter/vxe-table';
|
||||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||||
|
|
||||||
import { h, nextTick, watch } from 'vue';
|
import { h, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
import { Plus } from '@vben/icons';
|
import { Plus, Trash2 } from '@vben/icons';
|
||||||
|
import { isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import {
|
import {
|
||||||
deleteDemo03Grade,
|
deleteDemo03Grade,
|
||||||
|
deleteDemo03GradeList,
|
||||||
getDemo03GradePage,
|
getDemo03GradePage,
|
||||||
} from '#/api/infra/demo/demo03/erp';
|
} from '#/api/infra/demo/demo03/erp';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
@@ -59,11 +61,36 @@ async function onDelete(row: Demo03StudentApi.Demo03Grade) {
|
|||||||
await deleteDemo03Grade(row.id as number);
|
await deleteDemo03Grade(row.id as number);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生班级 */
|
||||||
|
async function onDeleteBatch() {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteDemo03GradeList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkedIds = ref<number[]>([]);
|
||||||
|
function handleRowCheckboxChange({
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
records: Demo03StudentApi.Demo03Grade[];
|
||||||
|
}) {
|
||||||
|
checkedIds.value = records.map((item) => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
/** 表格操作按钮的回调函数 */
|
/** 表格操作按钮的回调函数 */
|
||||||
function onActionClick({
|
function onActionClick({
|
||||||
code,
|
code,
|
||||||
@@ -115,6 +142,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
isHover: true,
|
isHover: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<Demo03StudentApi.Demo03Grade>,
|
} as VxeTableGridOptions<Demo03StudentApi.Demo03Grade>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
@@ -148,6 +179,17 @@ watch(
|
|||||||
>
|
>
|
||||||
{{ $t('ui.actionTitle.create', ['学生班级']) }}
|
{{ $t('ui.actionTitle.create', ['学生班级']) }}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
:icon="h(Trash2)"
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
class="ml-2"
|
||||||
|
:disabled="isEmpty(checkedIds)"
|
||||||
|
@click="onDeleteBatch"
|
||||||
|
v-access:code="['infra:demo03-student:delete']"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
formData.value = undefined;
|
formData.value = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
let data = modalApi.getData<Demo03StudentApi.Demo03Student>();
|
let data = modalApi.getData<Demo03StudentApi.Demo03Student>();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ export function useGridColumns(
|
|||||||
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Student>,
|
onActionClick?: OnActionClickFn<Demo03StudentApi.Demo03Student>,
|
||||||
): VxeTableGridOptions<Demo03StudentApi.Demo03Student>['columns'] {
|
): VxeTableGridOptions<Demo03StudentApi.Demo03Student>['columns'] {
|
||||||
return [
|
return [
|
||||||
|
{ type: 'checkbox', width: 40 },
|
||||||
{ type: 'expand', width: 80, slots: { content: 'expand_content' } },
|
{ type: 'expand', width: 80, slots: { content: 'expand_content' } },
|
||||||
{
|
{
|
||||||
field: 'id',
|
field: 'id',
|
||||||
@@ -150,7 +151,6 @@ export function useGridColumns(
|
|||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
headerAlign: 'center',
|
|
||||||
showOverflow: false,
|
showOverflow: false,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -199,7 +199,6 @@ export function useDemo03CourseGridEditColumns(
|
|||||||
minWidth: 60,
|
minWidth: 60,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
headerAlign: 'center',
|
|
||||||
showOverflow: false,
|
showOverflow: false,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
attrs: {
|
attrs: {
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
|||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import { Download, Plus } from '@vben/icons';
|
import { Download, Plus, Trash2 } from '@vben/icons';
|
||||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
import { Button, message, Tabs } from 'ant-design-vue';
|
import { Button, message, Tabs } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import {
|
import {
|
||||||
deleteDemo03Student,
|
deleteDemo03Student,
|
||||||
|
deleteDemo03StudentList,
|
||||||
exportDemo03Student,
|
exportDemo03Student,
|
||||||
getDemo03StudentPage,
|
getDemo03StudentPage,
|
||||||
} from '#/api/infra/demo/demo03/inner';
|
} from '#/api/infra/demo/demo03/inner';
|
||||||
@@ -60,11 +61,36 @@ async function onDelete(row: Demo03StudentApi.Demo03Student) {
|
|||||||
await deleteDemo03Student(row.id as number);
|
await deleteDemo03Student(row.id as number);
|
||||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||||
onRefresh();
|
onRefresh();
|
||||||
} catch {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 批量删除学生 */
|
||||||
|
async function onDeleteBatch() {
|
||||||
|
const hideLoading = message.loading({
|
||||||
|
content: $t('ui.actionMessage.deleting'),
|
||||||
|
duration: 0,
|
||||||
|
key: 'action_process_msg',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await deleteDemo03StudentList(checkedIds.value);
|
||||||
|
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||||
|
onRefresh();
|
||||||
|
} finally {
|
||||||
|
hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkedIds = ref<number[]>([]);
|
||||||
|
function handleRowCheckboxChange({
|
||||||
|
records,
|
||||||
|
}: {
|
||||||
|
records: Demo03StudentApi.Demo03Student[];
|
||||||
|
}) {
|
||||||
|
checkedIds.value = records.map((item) => item.id);
|
||||||
|
}
|
||||||
|
|
||||||
/** 导出表格 */
|
/** 导出表格 */
|
||||||
async function onExport() {
|
async function onExport() {
|
||||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||||
@@ -118,6 +144,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<Demo03StudentApi.Demo03Student>,
|
} as VxeTableGridOptions<Demo03StudentApi.Demo03Student>,
|
||||||
|
gridEvents: {
|
||||||
|
checkboxAll: handleRowCheckboxChange,
|
||||||
|
checkboxChange: handleRowCheckboxChange,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -155,6 +185,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
>
|
>
|
||||||
{{ $t('ui.actionTitle.export') }}
|
{{ $t('ui.actionTitle.export') }}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
:icon="h(Trash2)"
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
class="ml-2"
|
||||||
|
:disabled="isEmpty(checkedIds)"
|
||||||
|
@click="onDeleteBatch"
|
||||||
|
v-access:code="['infra:demo03-student:delete']"
|
||||||
|
>
|
||||||
|
批量删除
|
||||||
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ const props = defineProps<{
|
|||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useDemo03CourseGridColumns(),
|
columns: useDemo03CourseGridColumns(),
|
||||||
height: 'auto',
|
|
||||||
rowConfig: {
|
|
||||||
keyField: 'id',
|
|
||||||
isHover: true,
|
|
||||||
},
|
|
||||||
pagerConfig: {
|
pagerConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<Demo03StudentApi.Demo03Student>,
|
height: '600px',
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<Demo03StudentApi.Demo03Course>,
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const [Form, formApi] = useVbenForm({
|
const [Form, formApi] = useVbenForm({
|
||||||
|
commonConfig: {
|
||||||
|
componentProps: {
|
||||||
|
class: 'w-full',
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-2',
|
||||||
|
labelWidth: 80,
|
||||||
|
},
|
||||||
layout: 'horizontal',
|
layout: 'horizontal',
|
||||||
schema: useDemo03GradeFormSchema(),
|
schema: useDemo03GradeFormSchema(),
|
||||||
showDefaultActions: false,
|
showDefaultActions: false,
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ const props = defineProps<{
|
|||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: useDemo03GradeGridColumns(),
|
columns: useDemo03GradeGridColumns(),
|
||||||
height: 'auto',
|
|
||||||
rowConfig: {
|
|
||||||
keyField: 'id',
|
|
||||||
isHover: true,
|
|
||||||
},
|
|
||||||
pagerConfig: {
|
pagerConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<Demo03StudentApi.Demo03Student>,
|
height: '600px',
|
||||||
|
rowConfig: {
|
||||||
|
keyField: 'id',
|
||||||
|
isHover: true,
|
||||||
|
},
|
||||||
|
} as VxeTableGridOptions<Demo03StudentApi.Demo03Grade>,
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
// 提交表单
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
||||||
// 拼接子表的数据
|
// 拼接子表的数据
|
||||||
data.demo03courses = demo03CourseFormRef.value?.getData();
|
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
||||||
data.demo03grade = await demo03GradeFormRef.value?.getValues();
|
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
||||||
try {
|
try {
|
||||||
await (formData.value?.id
|
await (formData.value?.id
|
||||||
? updateDemo03Student(data)
|
? updateDemo03Student(data)
|
||||||
@@ -80,7 +80,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
formData.value = undefined;
|
formData.value = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
let data = modalApi.getData<Demo03StudentApi.Demo03Student>();
|
let data = modalApi.getData<Demo03StudentApi.Demo03Student>();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user