!11 follow后端发布
* docs: readme * fix: missing formPath * chore: 去除锁定的esbuild版本 * perf: 去除debug组件 * perf: 参数键值 自动高度 * refactor: 代码生成配置页面重构 去除步骤条 * perf: 移除文件 * docs: 文件夹说明 * chore: 移除一些配置项 * chore: 注释优化 * refactor: 移除ele和naive目录 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * perf: request support to set how to return response (#5436) * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * refactor: 登录超时的i18n * fix: requestClient缺失i18n内容 * refactor: 优化oss下载进度提示 * feat: 下载进度loading * fix: antd button icon style (#5421) * feat: oss下载进度(已下载的KB 无法作为进度显示 total返回为null) * fix: 下载文件时(responseType === 'blob')需要判断下载失败(返回json而非二进制)的情况 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * refactor: 新增后跳转到未发布流程 * fix: same name route * chore: 调整为部署json类型 * fix: mouse events ignored on modal loading (#5409) * docs: update docs (#5408) * refactor: 移除已经弃用的方法 * refactor: follow官方handleRangeTimeValue更新 * chore: 删除文件夹(前端路由需要的) * chore: 修改本地路由写法(新版)/新增本地菜单图标 * fix: form update state error before form mounted (#5406) * fix: demos route fixed (#5405) * chore: 不使用基础布局(仅在顶级生效) * feat: modal state locked on submitting (#5401) * chore: 修改zIndex * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * refactor: fix popup component zIndex (#5397) * style: element plus loading style fixed (#5393) * perf: improve fieldMappingTime to support format function (#5392) * fix: hide root route in breadcrumb * feat: support set default props for drawer and modal (#5390) * fix: root router config fixed (#5389) * fix: 修改Vxe默认zIndex为995 解决右上角全屏后modal/drawer(zIndex: 1000)被遮挡 * feat: add `noBasicLayout` in route meta (#5386) * chore: wechat image * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * chore: 改为全局参数配置 去除局部参数 * fix: spinner may stop playing animation after dismiss (#5365) * style: popover bgColor is too close to common (#5364) * docs: version update * docs: changelog * chore: 文件上传 描述 * ci: retry deploy while faild * feat: 文件上传 进度条+提示文字 * feat: 文件上传 进度条 * feat: 上传文件格式说明 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into warmflow * fix: useEcharts return invalid instance (#5360) * feat: popup component support overlay blur effect (#5359) * feat: improve `tippy` demo (#5357) * feat: integrate new component `Tippy` with demo (#5355) * chore: 优化表格图片显示 * perf: add nested modal demo (#5353) * chore: 默认显示右边的滚动条 防止出现滚动条被挤压 * perf: modal and drawer api support chain calls (#5351) * feat: allow close tab when mouse middle button click (#5347) * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into warmflow * refactor: 重构显示total的逻辑 * chore: 调整高度自适应代码 * chore: vxe升级4.10.0版本(锁定) * fix: 添加失效的option * fix: 需要为数组 * fix: locale switching logic correction (#5344) * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into warmflow * chore: 导入类型优化 解决eslint报红 * refactor: type/注释优化 去除大量any * fix: vxeGrid init without search form (#5342) * chore: 锁屏默认false 关闭该功能 * chore: 调整接口 * chore: update deps * fix: primaryColor calculation (#5337) * fix: form valid-error style in naive (#5336) * fix: form `fieldMappingTime` improve and `modelPropName` support (#5335) * fix: code lint * fix: form `fieldMappingTime` is not working (#5333) * chore: 选人组件样式 * fix: download from url triggered twice sometimes (#5319) * chore: 优化代码 * chore: 动态类名(无效)改为style * refactor: 字典相关功能重构 采用一个Map储存字典(之前为两个Map) * feat: 字典支持number类型存储 * chore: 调整样式 * chore: 修改选中border为1px * chore: 字段 * chore: 改为新窗口打开(适用于pdf/图片)而非直接下载 * chore: 更新样式 * chore: 更新字段 * chore: 改为computed * chore: 跳转到未发布流程tab * chore: 优化样式 * docs: readme * fix: name重复导致的404 * Merge branch 'dev' of https://gitee.com/dapppp/ruoyi-plus-vben5 into warmflow * chore: 使用legacy来保证copy的兼容性 * chore: 去除log 添加说明 * chore: 优化代码 * feat: 节点关联/节点独立的切换逻辑 * chore: remove logic * chore: vxe可编辑表格demo * chore: 不允许在按钮下添加数据 * docs: changelog * fix: wrong code * chore: 移除测试菜单 * chore: 优化代码 * refactor: 租户套餐菜单替换为新版 * refactor: 使用新版菜单勾选 * chore: 点行会勾选/取消全部权限 点权限不会勾选行 * chore: 全屏引导+样式优化 * chore: 调整间距 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * feat: useEcharts exports echarts instance#5294 (#5299) * chore: update quick-start.md (#5303) * chore: updateCheckedNumber * refactor: 优化代码 * chore: 优化代码 * chore: 优化样式 * chore: keys依赖于menu 需要先加载menu * chore: 菜单加载完毕再显示 * feat: 新的菜单选择组件(beta) * chore: $t * chore: 测试菜单页面 * chore: 优化代码 * feat: 对ossId回显的支持 * chore: 只获取一次默认密码而非每次打开modal都获取 * fix: vben select placeholder color (#5286) * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * perf: format code with better style (#5283) * chore: 工作流演示站 * fix: sidebar preferences fixed (#5276) * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * fix: breadcrumb setting not valid for `header-sidebar-nav` layout (#5275) * fix: header logo may not be collapsed in `header-sidebar-nav` layout (#5274) * feat: new layout `sidebar nav with full header` (#5270) * feat: drawer close icon placement (#5269) * docs: update dialog and drawer docs * feat: drawer support destroy on close * feat: drawer support `onOpened` & `onClosed` * feat: modal support destroy on close * fix: wrong boolean * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * chore: 调整tab位置 * chore: 删除历史流程 改为tab切换 * fix: header-mixed layout side-menu active (#5265) * feat: header mixed layout (#5263) * chore: release 5.5.2 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * chore: downgrade vue-tsc version * feat: header menu align support (#5256) * chore: update deps * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * chore: add apiSelect remote search demo (#5246) * chore: 审批改为description而非disabled的表单 * chore: 改为ts * chore: 错误的conetnt * refactor: 终止/转办/委托支持填写意见 * chore: 第一次拿到的是readonly的数据 如果需要修改 需要cloneDeep * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * fix: grid form submit button locale switch (#5205) * chore: 调整驳回 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * fix: build error (#5199) * fix: esbuild自动升级导致运行/打包报错 * fix: esbuild自动升级导致运行/打包报错 * chore: 流程定义 激活改为switch * chore: 流程申请支持上传文件 * chore: title 审批通过 * fix: vxeGrid top padding (#5193) * fix: 表格排序翻页会丢失排序参数 * chore: 去除log打印 * chore: 流程监控 待办任务 * chore: 我发起的 * chore: 去除已经移除的菜单页面 * chore: 我的已办 * chore: 页面优化 * chore: 重置tooltip * feat: 我的抄送搜索/优化重复触发的接口 * feat: 流程定义 历史 * chore: 修改分类 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * fix: grid tools in toolbar config not working as expected (#5190) * feat: add `resizable` and `ColPage` component (#5188) * chore: 条件 * chore: break-all * feat: 流程分类 搜索 * chore: 弹窗关闭后仍然显示表单浮层 * chore: 选人组件的样式 * chore: 搜索的样式 * chore: 漏掉的导入 * chore: 最大显示的头像数量 超过显示为省略号头像 * fix: 选人的一些问题 * Merge branch 'warmflow' of https://gitee.com/dapppp/ruoyi-plus-vben5 i… * chore: 没有更多数据了 * fix: sidebar header height (#5183) * chore: 搜索表单布局+申请人 * fix: remove the overlap caused by border-b (#5160) * docs: fix typos (#5169) * fix: resolve eslint errors as well as TS type errors (#5172) * chore: enter提交表单 * chore: 修改文案 * chore: 默认全部展开 * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * feat: page content class override (#5179) * fix: sidebar style on focus (#5178) * fix: 抄送选人 最右侧已选中删除item无效 * feat: 复制 * chore: 昵称过长的显示 * chore: 默认选中第一个 * chore: 修改relative位置 * chore: 搜索 * feat: 我的待办 - 搜索条件 * chore: 流程监控 - 待办任务页面的id不唯一 改为前端处理 * feat: 修改办理人 * chore: 流程干预 - 加签/减签 * chore: avatar大小 * chore: 抄送需要手动添加createByName * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * chore: 审批通过 抄送 * feat: 流程实例-流程预览 * chore: spell * chore: clientid * chore: 分类条件 * chore: 修改办理人 * chore: 更改postMessage参数 * chore: 内嵌iframe高度根据表单高度调整 * chore: 流程详情 * feat: 抄送选择 * chore: 调整分类树 * fix: user homePath no effect sometimes (#5166) * feat: form compact mode support (#5165) * fix: form auto submit no effect when showDefaultActions is false (#5163) * chore: 修改width * feat: 待办任务 * feat: 我的抄送 * chore: 流程定义 样式 * chore: 退回后重新申请 * chore: 请假申请布局 * chore: 请假申请-并行会签网关 * chore: 分类去除根目录 * chore: 详情modal(未完成) * chore: 请假申请根据不同状态显示按钮 * chore: 流程删除/撤销 * chore: 审批完成后刷新当前页 * feat: 选人组件(未完成) 加签减签 * docs: fix docs-link and add `EllipsisText` docs (#5158) * chore: 新窗口打开文件 * chore: 审批通过 * chore: 使用useEventListener替换原生 * chore: 字段错误 * chore: iframe通信 加载完毕后才显示表单 解决卡顿问题 * chore: 审批终止/驳回 * chore: 附件图标 * chore: process_running显示按钮 * chore: label错误 * chore: 保存的事件 * chore: 需要加上clientId * Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev * feat: form `colon` support (#5156) * chore: 完善请假申请 * feat: improve code login demo (#5154) * chore: 客户端管理 行高自适应 * chore: 内嵌表单的路径 * chore: 修改avatar背景色 * chore: 注释 * chore: activePath * chore: leave表单 * chore: 修改请假demo路径 * chore: categoryId * chore: 我的已办 * chore: 我发起的 * chore: loading * chore: 历史版本 * chore: 完善task api * chore: 隐藏'菜单加载中' * chore: missing import * feat: add demo for modify menu badge data * chore: 流程实例 * chore: 审批附件 * chore: 我的待办 提取公共组件 * chore: 流程部署 * chore: 新增/编辑/导出xml * chore: 流程定义(除历史版本) * feat: `autoActivateChild` support more layout mode (#5148) * feat: auto activate subMenu on select root menu (#5147) * fix: `disabledOnChangeListener` not work in form (#5146) * fix: login expired modal z-index (#5145) * feat: user-dropdown support `hover` trigger (#5143) * fix: pinInput value synchronous update (#5142) * fix: vxeGrid default sort data no effect in first query (#5141) * fix: vscode debug profile (#5140) * fix: form component events bind (#5137) * chore: 在线用户样式 开启虚拟滚动 * chore: 去掉个人中心 在线设备的分页 * chore: 去掉在线用户的分页 * chore: changelog * refactor: 获取字典的方法 提取公共函数 减少冗余代码 * fix: element plus validate failed style (#5130) * chore: 使用私有桶的提示 * feat: tabbar support mouse wheel vertical (#5129) * fix: form support `disabledOnInputListener` (#5127) * fix: form submission not appropriate (#5126) * Merge branch 'main' of https://gitee.com/dapppp/ruoyi-plus-vben5 into dev * chore: release 5.5.1 * feat: table search form visible control (#5121) * chore: 需要隐藏菜单 * chore: 我的待办 & 请假 * chore: 流程定义(未完成) * chore: 流程定义(开发中) * Merge branch 'main' of https://gitee.com/dapppp/ruoyi-plus-vben5 into dev * Merge branch 'main' of https://gitee.com/dapppp/ruoyi-plus-vben5 into dev * Merge branch 'main' of https://gitee.com/dapppp/ruoyi-plus-vben5 into dev * chore: version * chore: 锁定vxe-table版本 4.9.8版本存在样式问题 * chore: 暂时锁定cspell版本 * refactor: 由于不能输入 需要使用watch监听 * chore: https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IB7ANL * chore: 移除冗余代码 * chore: 组件卸载时移除emitter * fix: the route path did not synchronize with the page (#4947) * style: typo (#4948) * chore: 替换为commonDownloadExcel * fix: 左边部门树错误emit导致会调用两次列表api * chore: label样式 * chore: 改为Textarea * chore: 滚动条宽度 * chore: 审批样式 * chore: 部门及以下或本人数据权限 * Merge branch 'main' of https://gitee.com/dapppp/ruoyi-plus-vben5 into dev * chore: 个人中心强退设备接口路径
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-antd",
|
||||
"version": "1.1.3",
|
||||
"version": "1.2.0",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -3,9 +3,10 @@
|
||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||
*/
|
||||
|
||||
import type { Component, SetupContext } from 'vue';
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
|
||||
import type { Component, SetupContext } from 'vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import { h, type Ref } from 'vue';
|
||||
import type { VxeGridPropTypes } from '@vben/plugins/vxe-table';
|
||||
|
||||
import {
|
||||
setupVbenVxeTable,
|
||||
useVbenVxeGrid,
|
||||
type VxeGridDefines,
|
||||
} from '@vben/plugins/vxe-table';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { Button, Image } from 'ant-design-vue';
|
||||
|
||||
@@ -43,7 +41,7 @@ setupVbenVxeTable({
|
||||
// 鼠标移入行显示 hover 样式
|
||||
isHover: true,
|
||||
// 点击行高亮
|
||||
isCurrent: true,
|
||||
isCurrent: false,
|
||||
},
|
||||
columnConfig: {
|
||||
// 可拖拽列宽
|
||||
@@ -105,21 +103,6 @@ export { useVbenVxeGrid };
|
||||
|
||||
export type * from '@vben/plugins/vxe-table';
|
||||
|
||||
/**
|
||||
* 通用的表格复选框是否选中事件
|
||||
* @deprecated 使用vxeCheckboxChecked代替
|
||||
* @param checked 是否选中
|
||||
* @returns function
|
||||
*/
|
||||
export function tableCheckboxEvent(checked: Ref<boolean>) {
|
||||
const event: (params: VxeGridDefines.CheckboxChangeEventParams) => void = (
|
||||
params,
|
||||
) => {
|
||||
checked.value = params.$table.getCheckboxRecords().length > 0;
|
||||
};
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断vxe-table的复选框是否选中
|
||||
* @param tableApi api
|
||||
@@ -132,22 +115,21 @@ export function vxeCheckboxChecked(
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的vxe-table排序事件 支持单/多字段排序
|
||||
* @param tableApi api
|
||||
* @param sortParams 排序参数
|
||||
* 通用的 排序参数添加到请求参数中
|
||||
* @param params 请求参数
|
||||
* @param sortList vxe-table的排序参数
|
||||
*/
|
||||
export function vxeSortEvent(
|
||||
tableApi: ReturnType<typeof useVbenVxeGrid>[1],
|
||||
sortParams: VxeGridDefines.SortChangeEventParams,
|
||||
export function addSortParams(
|
||||
params: Record<string, any>,
|
||||
sortList: VxeGridPropTypes.ProxyAjaxQuerySortCheckedParams[],
|
||||
) {
|
||||
const { sortList } = sortParams;
|
||||
// 这里是排序取消 length为0 就不传参数了
|
||||
// 这里是排序取消 length为0 就不添加参数了
|
||||
if (sortList.length === 0) {
|
||||
tableApi.query();
|
||||
return;
|
||||
}
|
||||
// 支持单/多字段排序
|
||||
const orderByColumn = sortList.map((item) => item.field).join(',');
|
||||
const isAsc = sortList.map((item) => item.order).join(',');
|
||||
tableApi.query({ orderByColumn, isAsc });
|
||||
params.orderByColumn = orderByColumn;
|
||||
params.isAsc = isAsc;
|
||||
}
|
||||
|
1
apps/web-antd/src/api/common.d.ts
vendored
1
apps/web-antd/src/api/common.d.ts
vendored
@@ -38,4 +38,5 @@ export interface PageQuery {
|
||||
orderByColumn?: string;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ export interface MenuMeta {
|
||||
* @param name 菜单名
|
||||
* @param path 菜单路径
|
||||
* @param hidden 是否隐藏
|
||||
* @param component 组件名称 Laout
|
||||
* @param component 组件名称 Layout
|
||||
* @param alwaysShow 总是显示
|
||||
* @param query 路由参数(json形式)
|
||||
* @param meta 路由信息
|
||||
|
@@ -1,12 +1,27 @@
|
||||
import type { AxiosRequestConfig } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* Axios上传进度事件
|
||||
*/
|
||||
export type AxiosProgressEvent = AxiosRequestConfig['onUploadProgress'];
|
||||
|
||||
/**
|
||||
* 通过单文件上传接口
|
||||
* @param file 上传的文件
|
||||
* @param onUploadProgress 上传进度事件 非必传
|
||||
* @returns 上传结果
|
||||
*/
|
||||
export function uploadApi(file: Blob | File) {
|
||||
return requestClient.upload('/resource/oss/upload', { file });
|
||||
export function uploadApi(
|
||||
file: Blob | File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) {
|
||||
return requestClient.upload(
|
||||
'/resource/oss/upload',
|
||||
{ file },
|
||||
{ onUploadProgress, timeout: 60_000 },
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 默认上传结果
|
||||
|
@@ -17,6 +17,11 @@ export function onlineDeviceList() {
|
||||
return requestClient.get<PageResult<OnlineUser>>(Api.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* 这里的分页参数无效 返回的是全部的分页
|
||||
* @param params 请求参数
|
||||
* @returns 结果
|
||||
*/
|
||||
export function onlineList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<OnlineUser>>(Api.onlineList, { params });
|
||||
}
|
||||
@@ -36,5 +41,5 @@ export function forceLogout(tokenId: string) {
|
||||
* @returns void
|
||||
*/
|
||||
export function forceLogout2(tokenId: string) {
|
||||
return requestClient.postWithMsg<void>(`${Api.root}/${tokenId}`);
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/myself/${tokenId}`);
|
||||
}
|
||||
|
@@ -12,20 +12,36 @@ enum Api {
|
||||
root = '/monitor/operlog',
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志分页
|
||||
* @param params 查询参数
|
||||
* @returns 分页结果
|
||||
*/
|
||||
export function operLogList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<OperationLog>>(Api.operLogList, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
* @param operIds id/ids
|
||||
*/
|
||||
export function operLogDelete(operIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${operIds}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空全部分页日志
|
||||
*/
|
||||
export function operLogClean() {
|
||||
return requestClient.deleteWithMsg<void>(Api.operLogClean);
|
||||
}
|
||||
|
||||
export function operLogExport(data: any) {
|
||||
/**
|
||||
* 导出操作日志
|
||||
* @param data 查询参数
|
||||
*/
|
||||
export function operLogExport(data: Partial<OperationLog>) {
|
||||
return commonExport(Api.operLogExport, data);
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* 该文件可自行根据业务逻辑进行调整
|
||||
*/
|
||||
|
||||
import type { HttpResponse } from '@vben/request';
|
||||
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
@@ -134,14 +135,14 @@ function createRequestClient(baseURL: string) {
|
||||
});
|
||||
|
||||
// 通用的错误处理, 如果没有进入上面的错误处理逻辑,就会进入这里
|
||||
// 主要处理http状态码不为200的情况 必须放在在下面的响应拦截器之前
|
||||
// 主要处理http状态码不为200(如网络异常/离线)的情况 必须放在在下面的响应拦截器之前
|
||||
client.addResponseInterceptor(
|
||||
errorMessageResponseInterceptor((msg: string) => message.error(msg)),
|
||||
);
|
||||
|
||||
client.addResponseInterceptor<HttpResponse>({
|
||||
fulfilled: (response) => {
|
||||
const encryptKey = (response.headers || {})['encrypt-key'];
|
||||
fulfilled: async (response) => {
|
||||
const encryptKey = (response.headers ?? {})['encrypt-key'];
|
||||
if (encryptKey) {
|
||||
/** RSA私钥解密 拿到解密秘钥的base64 */
|
||||
const base64Str = encryptUtil.decrypt(encryptKey);
|
||||
@@ -164,39 +165,57 @@ function createRequestClient(baseURL: string) {
|
||||
// 不进行任何处理,直接返回
|
||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||
if (!isTransformResponse) {
|
||||
return response.data;
|
||||
/**
|
||||
* 需要判断下载二进制的情况 正常是返回二进制 报错会返回json
|
||||
* 当type为blob且content-type为application/json时 则判断已经下载出错
|
||||
*/
|
||||
if (
|
||||
response.config.responseType === 'blob' &&
|
||||
response.headers['content-type']?.includes?.('application/json')
|
||||
) {
|
||||
// 这时候的data为blob类型
|
||||
const blob = response.data as unknown as Blob;
|
||||
// 拿到字符串转json对象
|
||||
response.data = JSON.parse(await blob.text());
|
||||
// 然后按正常逻辑执行下面的代码(判断业务状态码)
|
||||
} else {
|
||||
// 其他情况 直接返回
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
const axiosResponseData = response.data;
|
||||
if (!axiosResponseData) {
|
||||
throw new Error($t('fallback.http.apiRequestFailed'));
|
||||
throw new Error($t('http.apiRequestFailed'));
|
||||
}
|
||||
|
||||
// ruoyi-plus没有采用严格的{code, msg, data}模式
|
||||
// 后端并没有采用严格的{code, msg, data}模式
|
||||
const { code, data, msg, ...other } = axiosResponseData;
|
||||
|
||||
// 这里逻辑可以根据项目进行修改
|
||||
// 业务状态码为200则请求成功
|
||||
const hasSuccess = Reflect.has(axiosResponseData, 'code') && code === 200;
|
||||
if (hasSuccess) {
|
||||
let successMsg = msg;
|
||||
|
||||
if (isNull(successMsg) || isEmpty(successMsg)) {
|
||||
successMsg = $t(`fallback.http.operationSuccess`);
|
||||
successMsg = $t(`http.operationSuccess`);
|
||||
}
|
||||
|
||||
if (response.config.successMessageMode === 'modal') {
|
||||
Modal.success({
|
||||
content: successMsg,
|
||||
title: $t('fallback.http.successTip'),
|
||||
title: $t('http.successTip'),
|
||||
});
|
||||
} else if (response.config.successMessageMode === 'message') {
|
||||
message.success(successMsg);
|
||||
}
|
||||
// 分页情况下为code msg rows total 并没有data字段
|
||||
// 如果有data 直接返回data 没有data将剩余参数(...other)封装为data返回
|
||||
// 需要考虑data为null的情况(比如查询为空)
|
||||
// 需要考虑data为null的情况(比如查询为空) 所以这里直接判断undefined
|
||||
if (data !== undefined) {
|
||||
return data;
|
||||
}
|
||||
// 没有data 将其他参数包装为data
|
||||
return other;
|
||||
}
|
||||
// 在此处根据自己项目的实际情况对不同的code执行不同的操作
|
||||
@@ -209,7 +228,7 @@ function createRequestClient(baseURL: string) {
|
||||
return;
|
||||
}
|
||||
isLogoutProcessing = true;
|
||||
const _msg = '登录超时, 请重新登录';
|
||||
const _msg = $t('http.loginTimeout');
|
||||
const userStore = useAuthStore();
|
||||
userStore.logout().finally(() => {
|
||||
message.error(_msg);
|
||||
@@ -230,13 +249,13 @@ function createRequestClient(baseURL: string) {
|
||||
if (response.config.errorMessageMode === 'modal') {
|
||||
Modal.error({
|
||||
content: timeoutMsg,
|
||||
title: $t('fallback.http.errorTip'),
|
||||
title: $t('http.errorTip'),
|
||||
});
|
||||
} else if (response.config.errorMessageMode === 'message') {
|
||||
message.error(timeoutMsg);
|
||||
}
|
||||
|
||||
throw new Error(timeoutMsg || $t('fallback.http.apiRequestFailed'));
|
||||
throw new Error(timeoutMsg || $t('http.apiRequestFailed'));
|
||||
},
|
||||
});
|
||||
|
||||
|
@@ -12,30 +12,60 @@ enum Api {
|
||||
root = '/system/client',
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询客户端分页列表
|
||||
* @param params 请求参数
|
||||
* @returns 列表
|
||||
*/
|
||||
export function clientList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<Client>>(Api.clientList, { params });
|
||||
}
|
||||
|
||||
export function clientExport(data: any) {
|
||||
/**
|
||||
* 导出客户端excel
|
||||
* @param data 请求参数
|
||||
*/
|
||||
export function clientExport(data: Partial<Client>) {
|
||||
return commonExport(Api.clientExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端详情
|
||||
* @param id id
|
||||
* @returns 详情
|
||||
*/
|
||||
export function clientInfo(id: ID) {
|
||||
return requestClient.get<Client>(`${Api.root}/${id}`);
|
||||
}
|
||||
|
||||
export function clientAdd(data: any) {
|
||||
/**
|
||||
* 客户端新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function clientAdd(data: Partial<Client>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function clientUpdate(data: any) {
|
||||
/**
|
||||
* 客户端修改
|
||||
* @param data 参数
|
||||
*/
|
||||
export function clientUpdate(data: Partial<Client>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端状态修改
|
||||
* @param data 状态
|
||||
*/
|
||||
export function clientChangeStatus(data: any) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
return requestClient.putWithMsg<void>(Api.clientChangeStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端删除
|
||||
* @param ids id集合
|
||||
*/
|
||||
export function clientRemove(ids: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${ids}`);
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${ids}`);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { Config } from './model';
|
||||
import type { SysConfig } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
@@ -13,15 +13,24 @@ enum Api {
|
||||
root = '/system/config',
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统参数分页列表
|
||||
* @param params 请求参数
|
||||
* @returns 列表
|
||||
*/
|
||||
export function configList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<Config>>(Api.configList, { params });
|
||||
return requestClient.get<PageResult<SysConfig>>(Api.configList, { params });
|
||||
}
|
||||
|
||||
export function configInfo(configId: ID) {
|
||||
return requestClient.get<Config>(`${Api.root}/${configId}`);
|
||||
return requestClient.get<SysConfig>(`${Api.root}/${configId}`);
|
||||
}
|
||||
|
||||
export function configExport(data: any) {
|
||||
/**
|
||||
* 导出
|
||||
* @param data 参数
|
||||
*/
|
||||
export function configExport(data: Partial<SysConfig>) {
|
||||
return commonExport(Api.configExport, data);
|
||||
}
|
||||
|
||||
@@ -33,14 +42,26 @@ export function configRefreshCache() {
|
||||
return requestClient.deleteWithMsg<void>(Api.configRefreshCache);
|
||||
}
|
||||
|
||||
export function configUpdate(data: any) {
|
||||
/**
|
||||
* 更新系统配置
|
||||
* @param data 参数
|
||||
*/
|
||||
export function configUpdate(data: Partial<SysConfig>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function configAdd(data: any) {
|
||||
/**
|
||||
* 新增系统配置
|
||||
* @param data 参数
|
||||
*/
|
||||
export function configAdd(data: Partial<SysConfig>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置
|
||||
* @param configIds ids
|
||||
*/
|
||||
export function configRemove(configIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${configIds}`);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export interface Config {
|
||||
export interface SysConfig {
|
||||
configId: number;
|
||||
configName: string;
|
||||
configKey: string;
|
||||
|
@@ -10,7 +10,11 @@ enum Api {
|
||||
root = '/system/dept',
|
||||
}
|
||||
|
||||
export function deptList(params?: any) {
|
||||
/**
|
||||
* 部门列表
|
||||
* @returns list
|
||||
*/
|
||||
export function deptList(params?: { deptName?: string; status?: string }) {
|
||||
return requestClient.get<Dept[]>(Api.deptList, { params });
|
||||
}
|
||||
|
||||
@@ -23,15 +27,28 @@ export function deptNodeList(deptId: ID) {
|
||||
return requestClient.get<Dept[]>(`${Api.deptNodeInfo}/${deptId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门详情
|
||||
* @param deptId 部门id
|
||||
* @returns 部门信息
|
||||
*/
|
||||
export function deptInfo(deptId: ID) {
|
||||
return requestClient.get<Dept>(`${Api.root}/${deptId}`);
|
||||
}
|
||||
|
||||
export function deptAdd(data: any) {
|
||||
/**
|
||||
* 部门新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function deptAdd(data: Partial<Dept>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function deptUpdate(data: any) {
|
||||
/**
|
||||
* 部门更新
|
||||
* @param data 参数
|
||||
*/
|
||||
export function deptUpdate(data: Partial<Dept>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
|
@@ -34,7 +34,7 @@ export function dictDataList(params?: PageQuery) {
|
||||
* @param data 表单参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function dictDataExport(data: any) {
|
||||
export function dictDataExport(data: Partial<DictData>) {
|
||||
return commonExport(Api.dictDataExport, data);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function dictDataRemove(dictIds: IDS) {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictDataAdd(data: any) {
|
||||
export function dictDataAdd(data: Partial<DictData>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export function dictDataAdd(data: any) {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictDataUpdate(data: any) {
|
||||
export function dictDataUpdate(data: Partial<DictData>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,7 @@ export function dictTypeList(params?: PageQuery) {
|
||||
* @param data 表单参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function dictTypeExport(data: any) {
|
||||
export function dictTypeExport(data: Partial<DictType>) {
|
||||
return commonExport(Api.dictTypeExport, data);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export function refreshDictTypeCache() {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictTypeAdd(data: any) {
|
||||
export function dictTypeAdd(data: Partial<DictType>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export function dictTypeAdd(data: any) {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictTypeUpdate(data: any) {
|
||||
export function dictTypeUpdate(data: Partial<DictType>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ export function dictTypeInfo(dictId: ID) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个在ele用到 v5用不上
|
||||
* 下拉框 返回值和list一样
|
||||
* @returns options
|
||||
*/
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { Menu, MenuOption, MenuResp } from './model';
|
||||
import type { Menu, MenuOption, MenuQuery, MenuResp } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
|
||||
@@ -12,22 +12,44 @@ enum Api {
|
||||
tenantPackageMenuTreeselect = '/system/menu/tenantPackageMenuTreeselect',
|
||||
}
|
||||
|
||||
export function menuList(params?: any) {
|
||||
/**
|
||||
* 菜单列表
|
||||
* @param params 参数
|
||||
* @returns 列表
|
||||
*/
|
||||
export function menuList(params?: MenuQuery) {
|
||||
return requestClient.get<Menu[]>(Api.menuList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单详情
|
||||
* @param menuId 菜单id
|
||||
* @returns 菜单详情
|
||||
*/
|
||||
export function menuInfo(menuId: ID) {
|
||||
return requestClient.get<Menu>(`${Api.root}/${menuId}`);
|
||||
}
|
||||
|
||||
export function menuAdd(data: any) {
|
||||
/**
|
||||
* 菜单新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function menuAdd(data: Partial<Menu>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function menuUpdate(data: any) {
|
||||
/**
|
||||
* 菜单更新
|
||||
* @param data 参数
|
||||
*/
|
||||
export function menuUpdate(data: Partial<Menu>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单删除
|
||||
* @param menuIds ids
|
||||
*/
|
||||
export function menuRemove(menuIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${menuIds}`);
|
||||
}
|
||||
|
11
apps/web-antd/src/api/system/menu/model.d.ts
vendored
11
apps/web-antd/src/api/system/menu/model.d.ts
vendored
@@ -33,6 +33,8 @@ export interface MenuOption {
|
||||
weight: number;
|
||||
children: MenuOption[];
|
||||
key: string; // 实际上不存在 ide报错
|
||||
menuType: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,3 +46,12 @@ export interface MenuResp {
|
||||
checkedKeys: number[];
|
||||
menus: MenuOption[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单表单查询
|
||||
*/
|
||||
export interface MenuQuery {
|
||||
menuName?: string;
|
||||
visible?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
@@ -9,22 +9,44 @@ enum Api {
|
||||
root = '/system/notice',
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告分页
|
||||
* @param params 分页参数
|
||||
* @returns 分页结果
|
||||
*/
|
||||
export function noticeList(params?: PageQuery) {
|
||||
return requestClient.get<Notice[]>(Api.noticeList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告详情
|
||||
* @param noticeId id
|
||||
* @returns 详情
|
||||
*/
|
||||
export function noticeInfo(noticeId: ID) {
|
||||
return requestClient.get<Notice>(`${Api.root}/${noticeId}`);
|
||||
}
|
||||
|
||||
export function noticeAdd(data: any) {
|
||||
/**
|
||||
* 通知公告新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function noticeAdd(data: Partial<Notice>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告更新
|
||||
* @param data 参数
|
||||
*/
|
||||
export function noticeUpdate(data: any) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告删除
|
||||
* @param noticeIds ids
|
||||
*/
|
||||
export function noticeRemove(noticeIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${noticeIds}`);
|
||||
}
|
||||
|
@@ -21,12 +21,12 @@ export function ossConfigInfo(ossConfigId: ID) {
|
||||
}
|
||||
|
||||
// 添加新的OSS配置
|
||||
export function ossConfigAdd(data: any) {
|
||||
export function ossConfigAdd(data: Partial<OssConfig>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
// 更新现有的OSS配置
|
||||
export function ossConfigUpdate(data: any) {
|
||||
export function ossConfigUpdate(data: Partial<OssConfig>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { AxiosRequestConfig } from '@vben/request';
|
||||
|
||||
import type { OssFile } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
@@ -13,20 +15,30 @@ enum Api {
|
||||
root = '/resource/oss',
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件list
|
||||
* @param params 参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function ossList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<OssFile>>(Api.ossList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文件信息 返回为数组
|
||||
* @param ossIds id数组
|
||||
* @returns 信息数组
|
||||
*/
|
||||
export function ossInfo(ossIds: IDS) {
|
||||
return requestClient.get<OssFile>(`${Api.ossInfo}/${ossIds}`);
|
||||
return requestClient.get<OssFile[]>(`${Api.ossInfo}/${ossIds}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @deprecated 使用apps/web-antd/src/api/core/upload.ts uploadApi方法
|
||||
* @param file 文件
|
||||
* @returns void
|
||||
*/
|
||||
export function ossUpload(file: any) {
|
||||
export function ossUpload(file: Blob | File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return requestClient.postWithMsg(Api.ossUpload, formData, {
|
||||
@@ -38,16 +50,26 @@ export function ossUpload(file: any) {
|
||||
/**
|
||||
* 下载文件 返回为二进制
|
||||
* @param ossId ossId
|
||||
* @param onDownloadProgress 下载进度(可选)
|
||||
* @returns blob
|
||||
*/
|
||||
export function ossDownload(ossId: ID) {
|
||||
export function ossDownload(
|
||||
ossId: ID,
|
||||
onDownloadProgress?: AxiosRequestConfig['onDownloadProgress'],
|
||||
) {
|
||||
return requestClient.get<Blob>(`${Api.ossDownload}/${ossId}`, {
|
||||
responseType: 'blob',
|
||||
timeout: 30 * 1000,
|
||||
isTransformResponse: false,
|
||||
onDownloadProgress,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param ossIds id数组
|
||||
* @returns void
|
||||
*/
|
||||
export function ossRemove(ossIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${ossIds}`);
|
||||
}
|
||||
|
@@ -21,26 +21,56 @@ export function postList(params?: PageQuery) {
|
||||
return requestClient.get<Post[]>(Api.postList, { params });
|
||||
}
|
||||
|
||||
export function postExport(data: any) {
|
||||
/**
|
||||
* 导出岗位信息
|
||||
* @param data 请求参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function postExport(data: Partial<Post>) {
|
||||
return commonExport(Api.postExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询岗位信息
|
||||
* @param postId id
|
||||
* @returns 岗位信息
|
||||
*/
|
||||
export function postInfo(postId: ID) {
|
||||
return requestClient.get<Post>(`${Api.root}/${postId}`);
|
||||
}
|
||||
|
||||
export function postAdd(data: any) {
|
||||
/**
|
||||
* 岗位新增
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function postAdd(data: Partial<Post>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function postUpdate(data: any) {
|
||||
/**
|
||||
* 岗位更新
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function postUpdate(data: Partial<Post>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 岗位删除
|
||||
* @param postIds ids
|
||||
* @returns void
|
||||
*/
|
||||
export function postRemove(postIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${postIds}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门id获取岗位下拉列表
|
||||
* @param deptId 部门id
|
||||
* @returns 岗位
|
||||
*/
|
||||
export function postOptionSelect(deptId: ID) {
|
||||
return requestClient.get<Post[]>(Api.postSelect, { params: { deptId } });
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import type { User } from '../user/model';
|
||||
import type { DeptResp, Role } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
@@ -20,30 +21,65 @@ enum Api {
|
||||
root = '/system/role',
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询角色分页列表
|
||||
* @param params 搜索条件
|
||||
* @returns 分页列表
|
||||
*/
|
||||
export function roleList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<Role>>(Api.roleList, { params });
|
||||
}
|
||||
|
||||
export function roleExport(data: any) {
|
||||
/**
|
||||
* 导出角色信息
|
||||
* @param data 查询参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function roleExport(data: Partial<Role>) {
|
||||
return commonExport(Api.roleExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询角色信息
|
||||
* @param roleId 角色id
|
||||
* @returns 角色信息
|
||||
*/
|
||||
export function roleInfo(roleId: ID) {
|
||||
return requestClient.get<Role>(`${Api.root}/${roleId}`);
|
||||
}
|
||||
|
||||
export function roleAdd(data: any) {
|
||||
/**
|
||||
* 角色新增
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function roleAdd(data: Partial<Role>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function roleUpdate(data: any) {
|
||||
/**
|
||||
* 角色更新
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function roleUpdate(data: Partial<Role>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function roleChangeStatus(data: any) {
|
||||
/**
|
||||
* 修改角色状态
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function roleChangeStatus(data: Partial<Role>) {
|
||||
return requestClient.putWithMsg<void>(Api.roleChangeStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色删除
|
||||
* @param roleIds ids
|
||||
* @returns void
|
||||
*/
|
||||
export function roleRemove(roleIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${roleIds}`);
|
||||
}
|
||||
@@ -57,12 +93,20 @@ export function roleDataScope(data: any) {
|
||||
return requestClient.putWithMsg<void>(Api.roleDataScope, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 全局并没有用到这个方法
|
||||
*/
|
||||
export function roleOptionSelect(params?: any) {
|
||||
return requestClient.get(Api.roleOptionSelect, { params });
|
||||
}
|
||||
|
||||
export function roleAllocatedList(params: any) {
|
||||
return requestClient.get(Api.roleAllocatedList, { params });
|
||||
/**
|
||||
* 已分配角色的用户分页
|
||||
* @param params 请求参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function roleAllocatedList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<User>>(Api.roleAllocatedList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,28 +115,26 @@ export function roleAllocatedList(params: any) {
|
||||
* @returns void
|
||||
*/
|
||||
export function roleUnallocatedList(params: any) {
|
||||
return requestClient.get(Api.roleUnallocatedList, { params });
|
||||
return requestClient.get<PageResult<User>>(Api.roleUnallocatedList, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消授权
|
||||
* @param data {userId: 2, roleId: "2"}
|
||||
* 取消用户角色授权
|
||||
* @returns void
|
||||
*/
|
||||
export function roleAuthCancel(data: any) {
|
||||
export function roleAuthCancel(data: { roleId: ID; userId: ID }) {
|
||||
return requestClient.putWithMsg<void>(Api.roleAuthCancel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取消授权
|
||||
* @param roleId
|
||||
* @param userIds
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 用户ID集合
|
||||
* @returns void
|
||||
*/
|
||||
export function roleAuthCancelAll(
|
||||
roleId: number | string,
|
||||
userIds: number[] | string[],
|
||||
) {
|
||||
export function roleAuthCancelAll(roleId: ID, userIds: IDS) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`${Api.roleAuthCancelAll}?roleId=${roleId}&userIds=${userIds.join(',')}`,
|
||||
);
|
||||
@@ -100,21 +142,18 @@ export function roleAuthCancelAll(
|
||||
|
||||
/**
|
||||
* 批量授权用户
|
||||
* @param roleId
|
||||
* @param userIds
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 用户ID集合
|
||||
* @returns void
|
||||
*/
|
||||
export function roleSelectAll(
|
||||
roleId: number | string,
|
||||
userIds: number[] | string[],
|
||||
) {
|
||||
export function roleSelectAll(roleId: ID, userIds: IDS) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`${Api.roleAuthSelectAll}?roleId=${roleId}&userIds=${userIds.join(',')}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门树
|
||||
* 根据角色id获取部门树
|
||||
* @param roleId 角色id
|
||||
* @returns DeptResp
|
||||
*/
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import type { SocialInfo } from './model';
|
||||
|
||||
import type { ID } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
enum Api {
|
||||
@@ -15,6 +17,9 @@ export function socialList() {
|
||||
return requestClient.get<SocialInfo[]>(Api.socialList);
|
||||
}
|
||||
|
||||
export function socialInfo(id: number | string) {
|
||||
/**
|
||||
* @deprecated 并没有用到这个方法
|
||||
*/
|
||||
export function socialInfo(id: ID) {
|
||||
return requestClient.get(`${Api.root}/${id}`);
|
||||
}
|
||||
|
@@ -13,37 +13,75 @@ enum Api {
|
||||
root = '/system/tenant/package',
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户套餐分页列表
|
||||
* @param params 请求参数
|
||||
* @returns 分页列表
|
||||
*/
|
||||
export function packageList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TenantPackage>>(Api.packageList, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 下拉框
|
||||
/**
|
||||
* 租户套餐下拉框
|
||||
* @returns 下拉框
|
||||
*/
|
||||
export function packageSelectList() {
|
||||
return requestClient.get<TenantPackage[]>(Api.packageSelectList);
|
||||
}
|
||||
|
||||
export function packageExport(data: any) {
|
||||
/**
|
||||
* 租户套餐导出
|
||||
* @param data 参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function packageExport(data: Partial<TenantPackage>) {
|
||||
return commonExport(Api.packageExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户套餐信息
|
||||
* @param id id
|
||||
* @returns 信息
|
||||
*/
|
||||
export function packageInfo(id: ID) {
|
||||
return requestClient.get<TenantPackage>(`${Api.root}/${id}`);
|
||||
}
|
||||
|
||||
export function packageAdd(data: any) {
|
||||
/**
|
||||
* 租户套餐新增
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function packageAdd(data: Partial<TenantPackage>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function packageUpdate(data: any) {
|
||||
/**
|
||||
* 租户套餐更新
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function packageUpdate(data: Partial<TenantPackage>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function packageChangeStatus(data: any) {
|
||||
return requestClient.putWithMsg(Api.packageChangeStatus, data);
|
||||
/**
|
||||
* 租户套餐状态变更
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function packageChangeStatus(data: Partial<TenantPackage>) {
|
||||
return requestClient.putWithMsg<void>(Api.packageChangeStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户套餐移除
|
||||
* @param ids ids
|
||||
* @returns void
|
||||
*/
|
||||
export function packageRemove(ids: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${ids}`);
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${ids}`);
|
||||
}
|
||||
|
@@ -16,14 +16,29 @@ enum Api {
|
||||
tenantSyncPackage = '/system/tenant/syncTenantPackage',
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户分页列表
|
||||
* @param params 参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function tenantList(params?: PageQuery) {
|
||||
return requestClient.get<Tenant[]>(Api.tenantList, { params });
|
||||
}
|
||||
|
||||
export function tenantExport(data: any) {
|
||||
/**
|
||||
* 租户导出
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantExport(data: Partial<Tenant>) {
|
||||
return commonExport(Api.tenantExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户信息
|
||||
* @param id id
|
||||
* @returns 租户信息
|
||||
*/
|
||||
export function tenantInfo(id: ID) {
|
||||
return requestClient.get<Tenant>(`${Api.root}/${id}`);
|
||||
}
|
||||
@@ -33,18 +48,33 @@ export function tenantInfo(id: ID) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantAdd(data: any) {
|
||||
export function tenantAdd(data: Partial<Tenant>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data, { encrypt: true });
|
||||
}
|
||||
|
||||
export function tenantUpdate(data: any) {
|
||||
/**
|
||||
* 租户更新
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantUpdate(data: Partial<Tenant>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function tenantStatusChange(data: any) {
|
||||
/**
|
||||
* 租户状态更新
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantStatusChange(data: Partial<Tenant>) {
|
||||
return requestClient.putWithMsg(Api.tenantStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户删除
|
||||
* @param ids ids
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantRemove(ids: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${ids}`);
|
||||
}
|
||||
@@ -70,17 +100,12 @@ export function tenantDynamicClear() {
|
||||
* 租户套餐同步
|
||||
* @param tenantId 租户id
|
||||
* @param packageId 套餐id
|
||||
* @param showMsg 是否显示成功信息
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantSyncPackage(
|
||||
tenantId: string,
|
||||
packageId: string,
|
||||
showMsg = true,
|
||||
) {
|
||||
export function tenantSyncPackage(tenantId: string, packageId: string) {
|
||||
return requestClient.get<void>(Api.tenantSyncPackage, {
|
||||
params: { packageId, tenantId },
|
||||
successMessageMode: showMsg ? 'message' : 'none',
|
||||
successMessageMode: 'message',
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,7 @@ export interface Tenant {
|
||||
id: number;
|
||||
intro: string;
|
||||
licenseNumber?: any;
|
||||
packageId?: string;
|
||||
packageId: string;
|
||||
remark?: string;
|
||||
status: string;
|
||||
tenantId: string;
|
||||
|
@@ -38,7 +38,7 @@ export function userList(params?: PageQuery) {
|
||||
* @param data data
|
||||
* @returns blob
|
||||
*/
|
||||
export function userExport(data: any) {
|
||||
export function userExport(data: Partial<User>) {
|
||||
return commonExport(Api.userExport, data);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export function findUserInfo(userId?: ID) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function userAdd(data: any) {
|
||||
export function userAdd(data: Partial<User>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export function userAdd(data: any) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function userUpdate(data: any) {
|
||||
export function userUpdate(data: Partial<User>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function userUpdate(data: any) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function userStatusChange(data: any) {
|
||||
export function userStatusChange(data: Partial<User>) {
|
||||
return requestClient.putWithMsg<void>(Api.userStatusChange, data);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { GenInfo } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
import type { ID, IDS, PageQuery } from '#/api/common';
|
||||
|
||||
import { ContentTypeEnum } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
@@ -19,7 +19,7 @@ enum Api {
|
||||
syncDb = '/tool/gen/synchDb',
|
||||
}
|
||||
// 查询代码生成列表
|
||||
export function generatedList(params: any) {
|
||||
export function generatedList(params?: PageQuery) {
|
||||
return requestClient.get(Api.generatedList, { params });
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function genInfo(tableId: ID) {
|
||||
}
|
||||
|
||||
// 查询数据库列表
|
||||
export function readyToGenList(params: any) {
|
||||
export function readyToGenList(params?: PageQuery) {
|
||||
return requestClient.get(Api.readyToGenList, { params });
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ export function editSave(data: any) {
|
||||
export function genRemove(tableIds: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${tableIds}`);
|
||||
}
|
||||
|
||||
// 预览代码
|
||||
export function previewCode(tableId: ID) {
|
||||
return requestClient.get<{ [key: string]: string }>(
|
||||
|
@@ -1,9 +1,22 @@
|
||||
import type { CategoryForm, CategoryQuery, CategoryVO } from './model';
|
||||
import type {
|
||||
CategoryForm,
|
||||
CategoryQuery,
|
||||
CategoryTree,
|
||||
CategoryVO,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取流程分类树列表
|
||||
* @returns tree
|
||||
*/
|
||||
export function categoryTree() {
|
||||
return requestClient.get<CategoryTree[]>('/workflow/category/categoryTree');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询流程分类列表
|
||||
* @param params
|
||||
|
@@ -30,6 +30,7 @@ export interface CategoryVO {
|
||||
* 子对象
|
||||
*/
|
||||
children: CategoryVO[];
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface CategoryForm extends BaseEntity {
|
||||
@@ -85,3 +86,12 @@ export interface CategoryQuery {
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
export interface CategoryTree {
|
||||
id: number;
|
||||
parentId: number;
|
||||
label: string;
|
||||
weight: number;
|
||||
children: CategoryTree[];
|
||||
key: string;
|
||||
}
|
||||
|
155
apps/web-antd/src/api/workflow/definition/index.ts
Normal file
155
apps/web-antd/src/api/workflow/definition/index.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import type { ProcessDefinition } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 全部的流程定义
|
||||
* @param params 查询参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function workflowDefinitionList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<ProcessDefinition>>(
|
||||
'/workflow/definition/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未发布的流程定义
|
||||
* @param params 查询参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function unPublishList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<ProcessDefinition>>(
|
||||
'/workflow/definition/unPublishList',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史流程定义列表
|
||||
* @param flowCode
|
||||
* @returns ProcessDefinition[]
|
||||
*/
|
||||
export function getHisListByKey(flowCode: string) {
|
||||
return requestClient.get<ProcessDefinition[]>(
|
||||
`/workflow/definition/getHisListByKey/${flowCode}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义详细信息
|
||||
* @param id id
|
||||
* @returns ProcessDefinition
|
||||
*/
|
||||
export function workflowDefinitionInfo(id: ID) {
|
||||
return requestClient.get<ProcessDefinition>(`/workflow/definition/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增流程定义
|
||||
* @param data
|
||||
*/
|
||||
export function workflowDefinitionAdd(data: any) {
|
||||
return requestClient.postWithMsg<void>('/workflow/definition', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新流程定义
|
||||
* @param data
|
||||
*/
|
||||
export function workflowDefinitionUpdate(data: any) {
|
||||
return requestClient.putWithMsg<void>('/workflow/definition', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布流程定义
|
||||
* @param id id
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionPublish(id: ID) {
|
||||
return requestClient.putWithMsg<boolean>(
|
||||
`/workflow/definition/publish/${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消发布流程定义
|
||||
* @param id id
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionUnPublish(id: ID) {
|
||||
return requestClient.putWithMsg<boolean>(
|
||||
`/workflow/definition/unPublish/${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程定义
|
||||
* @param ids idList
|
||||
*/
|
||||
export function workflowDefinitionDelete(ids: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/workflow/definition/${ids}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制流程定义
|
||||
* @param id id
|
||||
*/
|
||||
export function workflowDefinitionCopy(id: ID) {
|
||||
return requestClient.postWithMsg<void>(`/workflow/definition/copy/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入流程定义
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionImport(data: {
|
||||
category: ID;
|
||||
file: Blob | File;
|
||||
}) {
|
||||
return requestClient.postWithMsg<boolean>(
|
||||
'/workflow/definition/importDef',
|
||||
data,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' } },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出流程定义
|
||||
* @param id id
|
||||
* @returns blob
|
||||
*/
|
||||
export function workflowDefinitionExport(id: ID) {
|
||||
return requestClient.postWithMsg<Blob>(
|
||||
`/workflow/definition/exportDef/${id}`,
|
||||
{},
|
||||
{
|
||||
responseType: 'blob',
|
||||
isTransformResponse: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义xml字符串
|
||||
* @param id id
|
||||
* @returns xml
|
||||
*/
|
||||
export function workflowDefinitionXml(id: ID) {
|
||||
return requestClient.get<string>(`/workflow/definition/xmlString/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活/挂起流程定义
|
||||
* @param id 流程定义id
|
||||
* @param active 激活/挂起
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionActive(id: ID, active: boolean) {
|
||||
return requestClient.putWithMsg<boolean>(
|
||||
`/workflow/definition/active/${id}?active=${active}`,
|
||||
);
|
||||
}
|
19
apps/web-antd/src/api/workflow/definition/model.d.ts
vendored
Normal file
19
apps/web-antd/src/api/workflow/definition/model.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface ProcessDefinition {
|
||||
id: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag: string;
|
||||
flowCode: string;
|
||||
flowName: string;
|
||||
category: string;
|
||||
categoryName: string;
|
||||
version: string;
|
||||
isPublish: number;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
activityStatus: number;
|
||||
listenerType?: any;
|
||||
listenerPath?: any;
|
||||
ext?: any;
|
||||
}
|
120
apps/web-antd/src/api/workflow/instance/index.ts
Normal file
120
apps/web-antd/src/api/workflow/instance/index.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type { TaskInfo } from '../task/model';
|
||||
import type { FlowInfoResponse } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* @param businessId 业务ID
|
||||
* @returns TaskInfo
|
||||
*/
|
||||
export function getTaskByBusinessId(businessId: string) {
|
||||
return requestClient.get<TaskInfo>(
|
||||
`/workflow/instance/getInfo/${businessId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询正在运行的流程实例
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function pageByRunning(params?: PageQuery) {
|
||||
return requestClient.get('/workflow/instance/pageByRunning', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* pageByFinish
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function pageByFinish(params?: PageQuery) {
|
||||
return requestClient.get('/workflow/instance/pageByFinish', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照业务id删除流程实例
|
||||
* @param businessIds 业务id
|
||||
*/
|
||||
export function deleteByBusinessIds(businessIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(
|
||||
`/workflow/instance/deleteByBusinessIds${businessIds}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照实例id删除流程实例
|
||||
* @param instanceIds 实例id
|
||||
*/
|
||||
export function deleteByInstanceIds(instanceIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(
|
||||
`/workflow/instance/deleteByInstanceIds/${instanceIds}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销流程
|
||||
* @param data
|
||||
*/
|
||||
export function cancelProcessApply(data: { businessId: ID; message?: string }) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
'/workflow/instance/cancelProcessApply',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活/挂起流程实例
|
||||
* @param instanceId
|
||||
* @param active
|
||||
*/
|
||||
export function workflowInstanceActive(instanceId: ID, active: boolean) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`/workflow/instance/active/${instanceId}?active=${active}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录人发起的流程实例
|
||||
* @param params
|
||||
* @returns PageResult<Flow>
|
||||
*/
|
||||
export function pageByCurrent(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/instance/pageByCurrent',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程图,流程记录
|
||||
* @param businessId 业务标识
|
||||
* @returns 流程图,流程记录
|
||||
*/
|
||||
export function flowInfo(businessId: string) {
|
||||
return requestClient.get<FlowInfoResponse>(
|
||||
`/workflow/instance/flowImage/${businessId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程变量
|
||||
* @param instanceId
|
||||
* @returns Map<string,any>
|
||||
*/
|
||||
export function instanceVariable(instanceId: string) {
|
||||
return requestClient.get<Record<string, any>>(
|
||||
`/workflow/instance/variable/${instanceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 作废流程
|
||||
*/
|
||||
export function workflowInstanceInvalid(data: {
|
||||
comment?: string;
|
||||
id: string;
|
||||
}) {
|
||||
return requestClient.postWithMsg<void>('/workflow/instance/invalid', data);
|
||||
}
|
41
apps/web-antd/src/api/workflow/instance/model.d.ts
vendored
Normal file
41
apps/web-antd/src/api/workflow/instance/model.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
export interface Flow {
|
||||
id: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag: string;
|
||||
definitionId: string;
|
||||
flowName?: any;
|
||||
instanceId: string;
|
||||
taskId: string;
|
||||
cooperateType: number;
|
||||
cooperateTypeName: string;
|
||||
businessId?: any;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
nodeType: number;
|
||||
targetNodeCode: string;
|
||||
targetNodeName: string;
|
||||
approver: string;
|
||||
approveName: string;
|
||||
collaborator?: any;
|
||||
permissionList?: any;
|
||||
skipType: string;
|
||||
flowStatus: string;
|
||||
flowTaskStatus?: any;
|
||||
flowStatusName?: any;
|
||||
message: string;
|
||||
ext: null | string;
|
||||
createBy?: any;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
flowCode?: any;
|
||||
version?: any;
|
||||
runDuration: string;
|
||||
nickName?: any;
|
||||
}
|
||||
|
||||
export interface FlowInfoResponse {
|
||||
image: string;
|
||||
list: Flow[];
|
||||
}
|
158
apps/web-antd/src/api/workflow/task/index.ts
Normal file
158
apps/web-antd/src/api/workflow/task/index.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import type {
|
||||
CompleteTaskReqData,
|
||||
StartWorkFlowReqData,
|
||||
TaskInfo,
|
||||
TaskOperationData,
|
||||
TaskOperationType,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 启动任务
|
||||
* @param data
|
||||
*/
|
||||
export function startWorkFlow(data: StartWorkFlowReqData) {
|
||||
return requestClient.post<{
|
||||
processInstanceId: string;
|
||||
taskId: string;
|
||||
}>('/workflow/task/startWorkFlow', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理任务
|
||||
* @param data
|
||||
*/
|
||||
export function completeTask(data: CompleteTaskReqData) {
|
||||
return requestClient.postWithMsg<void>('/workflow/task/completeTask', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的待办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByTaskWait(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByTaskWait',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的已办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByTaskFinish(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByTaskFinish',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有待办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByAllTaskWait(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByAllTaskWait',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByAllTaskFinish(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByAllTaskFinish',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的抄送
|
||||
* @param params
|
||||
*/
|
||||
export function pageByTaskCopy(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByTaskCopy',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据taskId查询代表任务
|
||||
* @param taskId 任务id
|
||||
* @returns info
|
||||
*/
|
||||
export function getTaskByTaskId(taskId: string) {
|
||||
return requestClient.get<TaskInfo>(`/workflow/task/getTask/${taskId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止任务
|
||||
*/
|
||||
export function terminationTask(data: { comment?: string; taskId: string }) {
|
||||
return requestClient.postWithMsg<void>(
|
||||
'/workflow/task/terminationTask',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务操作
|
||||
* @param taskOperationData 参数
|
||||
* @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
*/
|
||||
export function taskOperation(
|
||||
taskOperationData: TaskOperationData,
|
||||
taskOperation: TaskOperationType,
|
||||
) {
|
||||
return requestClient.postWithMsg<void>(
|
||||
`/workflow/task/taskOperation/${taskOperation}`,
|
||||
taskOperationData,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务办理人
|
||||
* @param taskIdList 任务id
|
||||
* @param userId 办理人id
|
||||
*/
|
||||
export function updateAssignee(taskIdList: IDS, userId: ID) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`/workflow/task/updateAssignee/${userId}`,
|
||||
taskIdList,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 驳回审批
|
||||
* @param data 参数
|
||||
*/
|
||||
export function backProcess(data: any) {
|
||||
return requestClient.postWithMsg<void>('/workflow/task/backProcess', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可驳回节点
|
||||
* @param definitionId 流程定义ID
|
||||
* @param nodeCode 当前节点编码
|
||||
*/
|
||||
export function getBackTaskNode(definitionId: string, nodeCode: string) {
|
||||
return requestClient.get<{ nodeCode: string; nodeName: string }[]>(
|
||||
`/workflow/task/getBackTaskNode/${definitionId}/${nodeCode}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务的所有办理人
|
||||
* @param taskId 任务id
|
||||
*/
|
||||
export function currentTaskAllUser(taskId: ID) {
|
||||
return requestClient.get<any>(`/workflow/task/currentTaskAllUser/${taskId}`);
|
||||
}
|
74
apps/web-antd/src/api/workflow/task/model.d.ts
vendored
Normal file
74
apps/web-antd/src/api/workflow/task/model.d.ts
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
export interface TaskInfo {
|
||||
id: string;
|
||||
categoryName: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag?: any;
|
||||
definitionId: string;
|
||||
instanceId: string;
|
||||
flowName: string;
|
||||
businessId: string;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
nodeType: number;
|
||||
permissionList?: any;
|
||||
userList?: any;
|
||||
formCustom: string;
|
||||
formPath?: any;
|
||||
flowCode: string;
|
||||
version: string;
|
||||
flowStatus: string;
|
||||
flowStatusName: string;
|
||||
assigneeIds: string;
|
||||
assigneeNames: string;
|
||||
processedBy: string;
|
||||
type: string;
|
||||
nodeRatio?: string;
|
||||
createBy: string;
|
||||
createByName: string;
|
||||
targetNodeName?: string;
|
||||
}
|
||||
|
||||
export interface CompleteTaskReqData {
|
||||
messageType: string[];
|
||||
flowCopyList: { userId: string; userName: string }[];
|
||||
taskId: ID;
|
||||
taskVariables: Record<string, any>;
|
||||
variables: any;
|
||||
// 附件ID 1,2,3,4形式
|
||||
fileId?: string;
|
||||
}
|
||||
|
||||
export interface StartWorkFlowReqData {
|
||||
/**
|
||||
* 业务ID
|
||||
*/
|
||||
businessId: ID;
|
||||
/**
|
||||
* flowCode
|
||||
*/
|
||||
flowCode: string;
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
variables: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface TaskOperationData {
|
||||
message?: string;
|
||||
taskId: ID;
|
||||
// 单个操作人
|
||||
userId?: ID;
|
||||
// 多个操作人
|
||||
userIds?: IDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
*/
|
||||
export type TaskOperationType =
|
||||
| 'addSignature'
|
||||
| 'delegateTask'
|
||||
| 'reductionSignature'
|
||||
| 'transferTask';
|
@@ -1,6 +1,7 @@
|
||||
import { createApp, watchEffect } from 'vue';
|
||||
|
||||
import { registerAccessDirective } from '@vben/access';
|
||||
import { initTippy } from '@vben/common-ui';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { initStores } from '@vben/stores';
|
||||
import '@vben/styles';
|
||||
@@ -19,6 +20,15 @@ async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
// });
|
||||
// // 设置抽屉的默认配置
|
||||
// setDefaultDrawerProps({
|
||||
// zIndex: 1020,
|
||||
// });
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 全局组件
|
||||
@@ -33,6 +43,9 @@ async function bootstrap(namespace: string) {
|
||||
// 安装权限指令
|
||||
registerAccessDirective(app);
|
||||
|
||||
// 初始化 tippy
|
||||
initTippy(app);
|
||||
|
||||
// 配置路由及路由守卫
|
||||
app.use(router);
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
export { default as CropperImage } from './src/cropper.vue';
|
||||
|
||||
export { default as CropperAvatar } from './src/cropper-avatar.vue';
|
||||
export { default as CropperImage } from './src/cropper.vue';
|
||||
export type { Cropper } from './src/typing';
|
||||
|
@@ -1,18 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
type CSSProperties,
|
||||
type PropType,
|
||||
ref,
|
||||
unref,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import type { ButtonProps } from 'ant-design-vue';
|
||||
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
|
||||
import { computed, ref, unref, watch, watchEffect } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t as t } from '@vben/locales';
|
||||
|
||||
import { type ButtonProps, message } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import cropperModal from './cropper-modal.vue';
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import type { CropendResult, Cropper } from './typing';
|
||||
|
||||
import { type PropType, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t as t } from '@vben/locales';
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
|
||||
import { computed, onMounted, onUnmounted, ref, unref, useAttrs } from 'vue';
|
||||
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
|
@@ -2,19 +2,11 @@
|
||||
import type { CardSize } from 'ant-design-vue/es/card/Card';
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
|
||||
|
||||
import type { CSSProperties, PropType, Slots } from 'vue';
|
||||
|
||||
import type { DescInstance, DescItem, DescriptionProps } from './typing';
|
||||
|
||||
import {
|
||||
computed,
|
||||
type CSSProperties,
|
||||
defineComponent,
|
||||
type PropType,
|
||||
ref,
|
||||
type Slots,
|
||||
toRefs,
|
||||
unref,
|
||||
useAttrs,
|
||||
} from 'vue';
|
||||
import { computed, defineComponent, ref, toRefs, unref, useAttrs } from 'vue';
|
||||
|
||||
import { Card, Descriptions } from 'ant-design-vue';
|
||||
import { get, isFunction } from 'lodash-es';
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
|
||||
import type { JSX } from 'vue/jsx-runtime';
|
||||
|
||||
import type { CSSProperties, VNode } from 'vue';
|
||||
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
export interface DescItem {
|
||||
labelMinWidth?: number;
|
||||
contentMinWidth?: number;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { type VNode } from 'vue';
|
||||
import type { VNode } from 'vue';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<!-- eslint-disable eqeqeq -->
|
||||
<script setup lang="ts">
|
||||
import type { DictData } from '#/api/system/dict/dict-data-model';
|
||||
|
||||
@@ -17,7 +18,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
});
|
||||
|
||||
const color = computed<string>(() => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const current = props.dicts.find((item) => item.dictValue == props.value);
|
||||
const listClass = current?.listClass ?? '';
|
||||
// 是否为默认的颜色
|
||||
@@ -31,13 +31,11 @@ const color = computed<string>(() => {
|
||||
});
|
||||
|
||||
const cssClass = computed<string>(() => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const current = props.dicts.find((item) => item.dictValue == props.value);
|
||||
return current?.cssClass ?? '';
|
||||
});
|
||||
|
||||
const label = computed<number | string>(() => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const current = props.dicts.find((item) => item.dictValue == props.value);
|
||||
return current?.dictLabel ?? 'unknown';
|
||||
});
|
||||
|
@@ -1 +1,2 @@
|
||||
export { default as OptionsTag } from './src/options-tag.vue';
|
||||
export { default as TableSwitch } from './src/table-switch.vue';
|
||||
|
21
apps/web-antd/src/components/table/src/options-tag.vue
Normal file
21
apps/web-antd/src/components/table/src/options-tag.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script setup lang="tsx">
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'OptionsTag' });
|
||||
|
||||
const props = defineProps<{
|
||||
options: { color?: string; label: string; value: number | string }[];
|
||||
value: number | string;
|
||||
}>();
|
||||
|
||||
const found = computed(() =>
|
||||
props.options.find((item) => item.value === props.value),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tag v-if="found" :color="found.color">{{ found.label }}</Tag>
|
||||
<span v-else>未知</span>
|
||||
</template>
|
@@ -2,6 +2,10 @@
|
||||
import type { IPropTypes } from '@tinymce/tinymce-vue/lib/cjs/main/ts/components/EditorPropTypes';
|
||||
import type { Editor as EditorType } from 'tinymce/tinymce';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import type { UploadResult } from '#/api/core/upload';
|
||||
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
@@ -9,7 +13,6 @@ import {
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted,
|
||||
type PropType,
|
||||
ref,
|
||||
unref,
|
||||
useAttrs,
|
||||
@@ -22,7 +25,7 @@ import { buildShortUUID } from '@vben/utils';
|
||||
import Editor from '@tinymce/tinymce-vue';
|
||||
import { isNumber } from 'lodash-es';
|
||||
|
||||
import { uploadApi, type UploadResult } from '#/api/core/upload';
|
||||
import { uploadApi } from '#/api/core/upload';
|
||||
|
||||
import { bindHandlers } from './helper';
|
||||
import ImgUpload from './img-upload.vue';
|
||||
|
@@ -4,9 +4,9 @@ const validEvents = new Set([
|
||||
'onBeforeAddUndo',
|
||||
'onBeforeExecCommand',
|
||||
'onBeforeGetContent',
|
||||
'onBeforePaste',
|
||||
'onBeforeRenderUI',
|
||||
'onBeforeSetContent',
|
||||
'onBeforePaste',
|
||||
'onBlur',
|
||||
'onChange',
|
||||
'onClearUndos',
|
||||
@@ -42,8 +42,8 @@ const validEvents = new Set([
|
||||
'onMouseOver',
|
||||
'onMouseUp',
|
||||
'onNodeChange',
|
||||
'onObjectResizeStart',
|
||||
'onObjectResized',
|
||||
'onObjectResizeStart',
|
||||
'onObjectSelected',
|
||||
'onPaste',
|
||||
'onPostProcess',
|
||||
|
@@ -1 +1,2 @@
|
||||
export { default as MenuSelectTable } from './src/menu-select-table.vue';
|
||||
export { default as TreeSelectPanel } from './src/tree-select-panel.vue';
|
||||
|
85
apps/web-antd/src/components/tree/src/data.tsx
Normal file
85
apps/web-antd/src/components/tree/src/data.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { ID } from '#/api/common';
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { h, markRaw } from 'vue';
|
||||
|
||||
import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons';
|
||||
|
||||
export interface Permission {
|
||||
checked: boolean;
|
||||
id: ID;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface MenuPermissionOption extends MenuOption {
|
||||
permissions: Permission[];
|
||||
}
|
||||
|
||||
const menuTypes = {
|
||||
C: { icon: markRaw(MenuIcon), value: '菜单' },
|
||||
F: { icon: markRaw(OkButtonIcon), value: '按钮' },
|
||||
M: { icon: markRaw(FolderIcon), value: '目录' },
|
||||
};
|
||||
|
||||
export const nodeOptions = [
|
||||
{ label: '节点关联', value: true },
|
||||
{ label: '节点独立', value: false },
|
||||
];
|
||||
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
{
|
||||
type: 'checkbox',
|
||||
title: '菜单名称',
|
||||
field: 'label',
|
||||
treeNode: true,
|
||||
headerAlign: 'left',
|
||||
align: 'left',
|
||||
width: 230,
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
field: 'icon',
|
||||
width: 80,
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
if (row?.icon === '#') {
|
||||
return '';
|
||||
}
|
||||
return (
|
||||
<span class={'flex justify-center'}>
|
||||
<VbenIcon icon={row.icon} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
field: 'menuType',
|
||||
width: 80,
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
const current = menuTypes[row.menuType as 'C' | 'F' | 'M'];
|
||||
if (!current) {
|
||||
return '未知';
|
||||
}
|
||||
return (
|
||||
<span class="flex items-center justify-center gap-1">
|
||||
{h(current.icon, { class: 'size-[18px]' })}
|
||||
<span>{current.value}</span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
field: 'permissions',
|
||||
headerAlign: 'left',
|
||||
align: 'left',
|
||||
slots: {
|
||||
default: 'permissions',
|
||||
},
|
||||
},
|
||||
];
|
133
apps/web-antd/src/components/tree/src/helper.tsx
Normal file
133
apps/web-antd/src/components/tree/src/helper.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import type { MenuPermissionOption } from './data';
|
||||
|
||||
import type { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { eachTree, treeToList } from '@vben/utils';
|
||||
|
||||
import { difference, isEmpty, isUndefined } from 'lodash-es';
|
||||
|
||||
/**
|
||||
* 权限列设置是否全选
|
||||
* @param record 行记录
|
||||
* @param checked 是否选中
|
||||
*/
|
||||
export function setPermissionsChecked(
|
||||
record: MenuPermissionOption,
|
||||
checked: boolean,
|
||||
) {
|
||||
if (record?.permissions?.length > 0) {
|
||||
// 全部设置为选中
|
||||
record.permissions.forEach((permission) => {
|
||||
permission.checked = checked;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前行 & 所有子节点选中状态
|
||||
* @param record 行
|
||||
* @param checked 是否选中
|
||||
*/
|
||||
export function rowAndChildrenChecked(
|
||||
record: MenuPermissionOption,
|
||||
checked: boolean,
|
||||
) {
|
||||
// 当前行选中
|
||||
setPermissionsChecked(record, checked);
|
||||
// 所有子节点选中
|
||||
record?.children?.forEach?.((permission) => {
|
||||
rowAndChildrenChecked(permission as MenuPermissionOption, checked);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* void方法 会直接修改原始数据
|
||||
* 将树结构转为 tree+permissions结构
|
||||
* @param menus 后台返回的menu
|
||||
*/
|
||||
export function menusWithPermissions(menus: MenuOption[]) {
|
||||
eachTree(menus, (item: MenuPermissionOption) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
/**
|
||||
* 所有为按钮的节点提取出来
|
||||
* 需要注意 这里需要过滤目录下直接是按钮的情况item.menuType !== 'M'
|
||||
* 将按钮往children添加而非加到permissions
|
||||
*/
|
||||
const permissions = item.children.filter(
|
||||
(child: MenuOption) => child.menuType === 'F' && item.menuType !== 'M',
|
||||
);
|
||||
// 取差集
|
||||
const diffCollection = difference(item.children, permissions);
|
||||
// 更新后的children 即去除按钮
|
||||
item.children = diffCollection;
|
||||
|
||||
// permissions作为字段添加到item
|
||||
const permissionsArr = permissions.map((permission) => {
|
||||
return {
|
||||
id: permission.id,
|
||||
label: permission.label,
|
||||
checked: false,
|
||||
};
|
||||
});
|
||||
item.permissions = permissionsArr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表格选中
|
||||
* @param checkedKeys 选中的keys
|
||||
* @param menus 菜单 转换后的菜单
|
||||
* @param tableApi api
|
||||
* @param association 是否节点关联
|
||||
*/
|
||||
export function setTableChecked(
|
||||
checkedKeys: (number | string)[],
|
||||
menus: MenuPermissionOption[],
|
||||
tableApi: ReturnType<typeof useVbenVxeGrid>['1'],
|
||||
association: boolean,
|
||||
) {
|
||||
// tree转list
|
||||
const menuList: MenuPermissionOption[] = treeToList(menus);
|
||||
// 拿到勾选的行数据
|
||||
let checkedRows = menuList.filter((item) => checkedKeys.includes(item.id));
|
||||
|
||||
/**
|
||||
* 节点独立切换到节点关联 只需要最末尾的数据 即children为空
|
||||
*/
|
||||
if (!association) {
|
||||
checkedRows = checkedRows.filter(
|
||||
(item) => isUndefined(item.children) || isEmpty(item.children),
|
||||
);
|
||||
}
|
||||
|
||||
// 设置行选中 & permissions选中
|
||||
checkedRows.forEach((item) => {
|
||||
tableApi.grid.setCheckboxRow(item, true);
|
||||
if (item?.permissions?.length > 0) {
|
||||
item.permissions.forEach((permission) => {
|
||||
if (checkedKeys.includes(permission.id)) {
|
||||
permission.checked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 节点独立切换到节点关联
|
||||
* 勾选后还需要过滤权限没有任何勾选的情况 这时候取消行的勾选
|
||||
*/
|
||||
if (!association) {
|
||||
const emptyRows = checkedRows.filter((item) => {
|
||||
if (isUndefined(item.permissions) || isEmpty(item.permissions)) {
|
||||
return false;
|
||||
}
|
||||
return item.permissions.every(
|
||||
(permission) => permission.checked === false,
|
||||
);
|
||||
});
|
||||
// 设置为不选中
|
||||
tableApi.grid.setCheckboxRow(emptyRows, false);
|
||||
}
|
||||
}
|
62
apps/web-antd/src/components/tree/src/hook.tsx
Normal file
62
apps/web-antd/src/components/tree/src/hook.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { TourProps } from 'ant-design-vue';
|
||||
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { Tour } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 全屏引导
|
||||
* @returns value
|
||||
*/
|
||||
export function useFullScreenGuide() {
|
||||
const open = ref(false);
|
||||
/**
|
||||
* 是否已读 只显示一次
|
||||
*/
|
||||
const read = useLocalStorage('menu_select_fullscreen_read', false);
|
||||
|
||||
function openGuide() {
|
||||
if (!read.value) {
|
||||
open.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function closeGuide() {
|
||||
open.value = false;
|
||||
read.value = true;
|
||||
}
|
||||
|
||||
const steps: TourProps['steps'] = [
|
||||
{
|
||||
title: '提示',
|
||||
description: '点击这里可以全屏',
|
||||
target: () =>
|
||||
document.querySelector(
|
||||
'div#menu-select-table .vxe-tools--operate > button[title="全屏"]',
|
||||
)!,
|
||||
},
|
||||
];
|
||||
|
||||
const FullScreenGuide = defineComponent({
|
||||
name: 'FullScreenGuide',
|
||||
inheritAttrs: false,
|
||||
setup() {
|
||||
return () => (
|
||||
<Tour
|
||||
onClose={closeGuide}
|
||||
open={open.value}
|
||||
steps={steps}
|
||||
zIndex={9999}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
FullScreenGuide,
|
||||
openGuide,
|
||||
closeGuide,
|
||||
};
|
||||
}
|
412
apps/web-antd/src/components/tree/src/menu-select-table.vue
Normal file
412
apps/web-antd/src/components/tree/src/menu-select-table.vue
Normal file
@@ -0,0 +1,412 @@
|
||||
<!--
|
||||
不兼容也不会兼容一些错误用法
|
||||
比如: 菜单下放目录 菜单下放菜单
|
||||
比如: 按钮下放目录 按钮下放菜单 按钮下放按钮
|
||||
-->
|
||||
<script setup lang="tsx">
|
||||
import type { RadioChangeEvent } from 'ant-design-vue';
|
||||
|
||||
import type { MenuPermissionOption } from './data';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
|
||||
import { cloneDeep, findGroupParentIds } from '@vben/utils';
|
||||
|
||||
import { Alert, Checkbox, RadioGroup, Space } from 'ant-design-vue';
|
||||
import { uniq } from 'lodash-es';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
import { columns, nodeOptions } from './data';
|
||||
import {
|
||||
menusWithPermissions,
|
||||
rowAndChildrenChecked,
|
||||
setPermissionsChecked,
|
||||
setTableChecked,
|
||||
} from './helper';
|
||||
import { useFullScreenGuide } from './hook';
|
||||
|
||||
defineOptions({
|
||||
name: 'MenuSelectTable',
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
checkedKeys: (number | string)[];
|
||||
defaultExpandAll?: boolean;
|
||||
menus: MenuOption[];
|
||||
}>(),
|
||||
{
|
||||
/**
|
||||
* 是否默认展开全部
|
||||
*/
|
||||
defaultExpandAll: true,
|
||||
/**
|
||||
* 注意这里不是双向绑定 需要调用getCheckedKeys实例方法来获取真正选中的节点
|
||||
*/
|
||||
checkedKeys: () => [],
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 是否节点关联
|
||||
*/
|
||||
const association = defineModel('association', {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
});
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
checkboxConfig: {
|
||||
// checkbox显示的字段
|
||||
labelField: 'label',
|
||||
// 是否严格模式 即节点不关联
|
||||
checkStrictly: !association.value,
|
||||
},
|
||||
size: 'small',
|
||||
columns,
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbarConfig: {
|
||||
// 自定义列
|
||||
custom: false,
|
||||
// 最大化
|
||||
zoom: true,
|
||||
// 刷新
|
||||
refresh: false,
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: false,
|
||||
isCurrent: false,
|
||||
keyField: 'id',
|
||||
},
|
||||
/**
|
||||
* 开启虚拟滚动
|
||||
* 数据量小可以选择关闭
|
||||
* 如果遇到样式问题(空白、错位 滚动等)可以选择关闭虚拟滚动
|
||||
*/
|
||||
scrollY: {
|
||||
enabled: true,
|
||||
gt: 0,
|
||||
},
|
||||
treeConfig: {
|
||||
parentField: 'parentId',
|
||||
rowField: 'id',
|
||||
transform: false,
|
||||
},
|
||||
// 溢出换行显示
|
||||
showOverflow: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于界面显示选中的数量
|
||||
*/
|
||||
const checkedNum = ref(0);
|
||||
/**
|
||||
* 更新选中的数量
|
||||
*/
|
||||
function updateCheckedNumber() {
|
||||
checkedNum.value = getCheckedKeys().length;
|
||||
}
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
// 勾选事件
|
||||
checkboxChange: (params) => {
|
||||
// 选中还是取消选中
|
||||
const checked = params.checked;
|
||||
// 行
|
||||
const record = params.row;
|
||||
if (association.value) {
|
||||
// 节点关联
|
||||
// 设置所有子节点选中状态
|
||||
rowAndChildrenChecked(record, checked);
|
||||
} else {
|
||||
// 节点独立
|
||||
// 点行会勾选/取消全部权限 点权限不会勾选行
|
||||
setPermissionsChecked(record, checked);
|
||||
}
|
||||
updateCheckedNumber();
|
||||
},
|
||||
// 全选事件
|
||||
checkboxAll: (params) => {
|
||||
const records = params.$grid.getData();
|
||||
records.forEach((item) => {
|
||||
rowAndChildrenChecked(item, params.checked);
|
||||
});
|
||||
updateCheckedNumber();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 设置表格选中
|
||||
* @param menus menu
|
||||
* @param keys 选中的key
|
||||
* @param triggerOnchange 节点独立情况 不需要触发onChange(false)
|
||||
*/
|
||||
function setCheckedByKeys(
|
||||
menus: MenuPermissionOption[],
|
||||
keys: (number | string)[],
|
||||
triggerOnchange: boolean,
|
||||
) {
|
||||
menus.forEach((item) => {
|
||||
// 设置行选中
|
||||
if (keys.includes(item.id)) {
|
||||
tableApi.grid.setCheckboxRow(item, true);
|
||||
}
|
||||
// 设置权限columns选中
|
||||
if (item.permissions && item.permissions.length > 0) {
|
||||
// 遍历 设置勾选
|
||||
item.permissions.forEach((permission) => {
|
||||
if (keys.includes(permission.id)) {
|
||||
permission.checked = true;
|
||||
// 手动触发onChange来选中 节点独立情况不需要处理
|
||||
triggerOnchange && handlePermissionChange(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 设置children选中
|
||||
if (item.children && item.children.length > 0) {
|
||||
setCheckedByKeys(item.children as any, keys, triggerOnchange);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { FullScreenGuide, openGuide } = useFullScreenGuide();
|
||||
onMounted(() => {
|
||||
/**
|
||||
* 加载表格数据 转为指定结构
|
||||
*/
|
||||
watch(
|
||||
() => props.menus,
|
||||
async (menus) => {
|
||||
const clonedMenus = cloneDeep(menus);
|
||||
menusWithPermissions(clonedMenus);
|
||||
// console.log(clonedMenus);
|
||||
await tableApi.grid.loadData(clonedMenus);
|
||||
// 展开全部 默认true
|
||||
if (props.defaultExpandAll) {
|
||||
await nextTick();
|
||||
setExpandOrCollapse(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 节点关联变动 更新表格勾选效果
|
||||
*/
|
||||
watch(association, (value) => {
|
||||
tableApi.setGridOptions({
|
||||
checkboxConfig: {
|
||||
checkStrictly: !value,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* checkedKeys依赖menus
|
||||
* 要注意加载顺序
|
||||
* !!!要在外部确保menus先加载!!!
|
||||
*/
|
||||
watch(
|
||||
() => props.checkedKeys,
|
||||
(value) => {
|
||||
const allCheckedKeys = uniq([...value]);
|
||||
// 获取表格data 如果checkedKeys在menus的watch之前触发 这里会拿到空 导致勾选异常
|
||||
const records = tableApi.grid.getData();
|
||||
setCheckedByKeys(records, allCheckedKeys, association.value);
|
||||
updateCheckedNumber();
|
||||
|
||||
// 全屏引导
|
||||
setTimeout(openGuide, 1000);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// 缓存上次(切换节点关系前)选中的keys
|
||||
const lastCheckedKeys = shallowRef<(number | string)[]>([]);
|
||||
/**
|
||||
* 节点关联变动 事件
|
||||
*/
|
||||
async function handleAssociationChange(e: RadioChangeEvent) {
|
||||
lastCheckedKeys.value = getCheckedKeys();
|
||||
// 清空全部permissions选中
|
||||
const records = tableApi.grid.getData();
|
||||
records.forEach((item) => {
|
||||
rowAndChildrenChecked(item, false);
|
||||
});
|
||||
// 需要清空全部勾选
|
||||
await tableApi.grid.clearCheckboxRow();
|
||||
// 滚动到顶部
|
||||
await tableApi.grid.scrollTo(0, 0);
|
||||
|
||||
// 节点切换 不同的选中
|
||||
setTableChecked(lastCheckedKeys.value, records, tableApi, !e.target.value);
|
||||
|
||||
updateCheckedNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部展开/折叠
|
||||
* @param expand 是否展开
|
||||
*/
|
||||
function setExpandOrCollapse(expand: boolean) {
|
||||
tableApi.grid?.setAllTreeExpand(expand);
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限列表 checkbox勾选的事件
|
||||
* @param row 行
|
||||
*/
|
||||
function handlePermissionChange(row: any) {
|
||||
// 节点关联
|
||||
if (association.value) {
|
||||
const checkedPermissions = row.permissions.filter(
|
||||
(item: any) => item.checked === true,
|
||||
);
|
||||
// 有一条选中 则整个行选中
|
||||
if (checkedPermissions.length > 0) {
|
||||
tableApi.grid.setCheckboxRow(row, true);
|
||||
}
|
||||
// 无任何选中 则整个行不选中
|
||||
if (checkedPermissions.length === 0) {
|
||||
tableApi.grid.setCheckboxRow(row, false);
|
||||
}
|
||||
}
|
||||
// 节点独立 不处理
|
||||
updateCheckedNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取勾选的key
|
||||
* @param records 行记录列表
|
||||
* @param addCurrent 是否添加当前行的id
|
||||
*/
|
||||
function getKeys(records: MenuPermissionOption[], addCurrent: boolean) {
|
||||
const allKeys: (number | string)[] = [];
|
||||
records.forEach((item) => {
|
||||
// 处理children
|
||||
if (item.children && item.children.length > 0) {
|
||||
const keys = getKeys(item.children as MenuPermissionOption[], addCurrent);
|
||||
allKeys.push(...keys);
|
||||
} else {
|
||||
// 当前行的id
|
||||
addCurrent && allKeys.push(item.id);
|
||||
// 当前行权限id 获取已经选中的
|
||||
if (item.permissions && item.permissions.length > 0) {
|
||||
const ids = item.permissions
|
||||
.filter((m) => m.checked === true)
|
||||
.map((m) => m.id);
|
||||
allKeys.push(...ids);
|
||||
}
|
||||
}
|
||||
});
|
||||
return uniq(allKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选中的key
|
||||
*/
|
||||
function getCheckedKeys() {
|
||||
// 节点关联
|
||||
if (association.value) {
|
||||
const records = tableApi?.grid?.getCheckboxRecords?.() ?? [];
|
||||
// 子节点
|
||||
const nodeKeys = getKeys(records, true);
|
||||
// 所有父节点
|
||||
const parentIds = findGroupParentIds(props.menus, nodeKeys as number[]);
|
||||
// 拼接 去重
|
||||
const realKeys = uniq([...parentIds, ...nodeKeys]);
|
||||
return realKeys;
|
||||
}
|
||||
// 节点独立
|
||||
|
||||
// 勾选的行
|
||||
const records = tableApi?.grid?.getCheckboxRecords?.() ?? [];
|
||||
// 全部数据 用于获取permissions
|
||||
const allRecords = tableApi?.grid?.getData?.() ?? [];
|
||||
// 表格已经选中的行ids
|
||||
const checkedIds = records.map((item) => item.id);
|
||||
// 所有已经勾选权限的ids
|
||||
const permissionIds = getKeys(allRecords, false);
|
||||
// 合并 去重
|
||||
const allIds = uniq([...checkedIds, ...permissionIds]);
|
||||
return allIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暴露给外部使用 获取已选中的key
|
||||
*/
|
||||
defineExpose({
|
||||
getCheckedKeys,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col" id="menu-select-table">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<RadioGroup
|
||||
v-model:value="association"
|
||||
:options="nodeOptions"
|
||||
button-style="solid"
|
||||
option-type="button"
|
||||
@change="handleAssociationChange"
|
||||
/>
|
||||
<Alert class="mx-2" type="info">
|
||||
<template #message>
|
||||
<div>
|
||||
已选中
|
||||
<span class="text-primary mx-1 font-semibold">
|
||||
{{ checkedNum }}
|
||||
</span>
|
||||
个节点
|
||||
</div>
|
||||
</template>
|
||||
</Alert>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="setExpandOrCollapse(false)">
|
||||
{{ $t('pages.common.collapse') }}
|
||||
</a-button>
|
||||
<a-button @click="setExpandOrCollapse(true)">
|
||||
{{ $t('pages.common.expand') }}
|
||||
</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
<template #permissions="{ row }">
|
||||
<div class="flex flex-wrap gap-x-3 gap-y-1">
|
||||
<Checkbox
|
||||
v-for="permission in row.permissions"
|
||||
:key="permission.id"
|
||||
v-model:checked="permission.checked"
|
||||
@change="() => handlePermissionChange(row)"
|
||||
>
|
||||
{{ permission.label }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 全屏引导 -->
|
||||
<FullScreenGuide />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.ant-alert) {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
</style>
|
@@ -3,7 +3,9 @@ import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
|
||||
import type { DataNode } from 'ant-design-vue/es/tree';
|
||||
import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
|
||||
|
||||
import { computed, nextTick, onMounted, type PropType, ref, watch } from 'vue';
|
||||
import type { PropType, SetupContext } from 'vue';
|
||||
|
||||
import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue';
|
||||
|
||||
import { findGroupParentIds, treeToList } from '@vben/utils';
|
||||
|
||||
@@ -108,8 +110,8 @@ const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
* @param info info.halfCheckedKeys为父节点的ID
|
||||
*/
|
||||
type CheckedState<T = number | string> =
|
||||
| { checked: T[]; halfChecked: T[] }
|
||||
| T[];
|
||||
| T[]
|
||||
| { checked: T[]; halfChecked: T[] };
|
||||
function handleChecked(checkedStateKeys: CheckedState, info: CheckInfo) {
|
||||
// 数组的话为节点关联
|
||||
if (Array.isArray(checkedStateKeys)) {
|
||||
@@ -157,6 +159,8 @@ onMounted(async () => {
|
||||
expandedKeys.value = allKeys.value;
|
||||
}
|
||||
});
|
||||
|
||||
const slots = useSlots() as SetupContext['slots'];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -205,7 +209,7 @@ onMounted(async () => {
|
||||
@check="handleChecked"
|
||||
>
|
||||
<template
|
||||
v-for="slotName in Object.keys($slots)"
|
||||
v-for="slotName in Object.keys(slots)"
|
||||
:key="slotName"
|
||||
#[slotName]="data"
|
||||
>
|
||||
|
@@ -2,6 +2,10 @@
|
||||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||
|
||||
import type { AxiosResponse } from '@vben/request';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api';
|
||||
|
||||
import { ref, toRefs, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
@@ -26,7 +30,10 @@ const props = withDefaults(
|
||||
* 需自行改造 ./helper/checkFileType方法
|
||||
*/
|
||||
accept?: string[];
|
||||
api?: (...args: any[]) => Promise<any>;
|
||||
api?: (
|
||||
file: Blob | File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
// 最大数量的文件,Infinity不限制
|
||||
@@ -38,6 +45,10 @@ const props = withDefaults(
|
||||
// support xxx.xxx.xx
|
||||
// 返回的字段 默认url
|
||||
resultField?: 'fileName' | 'ossId' | 'url' | string;
|
||||
/**
|
||||
* 是否显示下面的描述
|
||||
*/
|
||||
showDescription?: boolean;
|
||||
value?: string[];
|
||||
}>(),
|
||||
{
|
||||
@@ -50,6 +61,7 @@ const props = withDefaults(
|
||||
multiple: false,
|
||||
api: uploadApi,
|
||||
resultField: '',
|
||||
showDescription: true,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
||||
@@ -144,12 +156,19 @@ async function customRequest(info: UploadRequestOption<any>) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api?.(info.file);
|
||||
// 进度条事件
|
||||
const progressEvent: AxiosProgressEvent = (e) => {
|
||||
const percent = Math.trunc((e.loaded / e.total!) * 100);
|
||||
info.onProgress!({ percent });
|
||||
};
|
||||
const res = await api?.(info.file as File, progressEvent);
|
||||
/**
|
||||
* 由getValue处理 传对象过去
|
||||
* 直接传string(id)会被转为Number
|
||||
* 内部的逻辑由requestClient.upload处理 这里不用判断业务状态码 不符合会自动reject
|
||||
*/
|
||||
info.onSuccess!(res);
|
||||
message.success($t('component.upload.uploadSuccess'));
|
||||
// 获取
|
||||
const value = getValue();
|
||||
isInnerOperate.value = true;
|
||||
@@ -191,6 +210,7 @@ function getValue() {
|
||||
:max-count="maxNumber"
|
||||
:multiple="multiple"
|
||||
list-type="text"
|
||||
:progress="{ showInfo: true }"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<div v-if="fileList && fileList.length < maxNumber">
|
||||
@@ -199,6 +219,13 @@ function getValue() {
|
||||
{{ $t('component.upload.upload') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-if="showDescription" class="mt-2 flex flex-wrap items-center">
|
||||
请上传不超过
|
||||
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
||||
的
|
||||
<div class="text-primary mx-1 font-bold">{{ accept.join('/') }}</div>
|
||||
格式文件
|
||||
</div>
|
||||
</Upload>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -2,15 +2,20 @@
|
||||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||
|
||||
import type { AxiosResponse } from '@vben/request';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api';
|
||||
|
||||
import { ref, toRefs, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { message, Modal, Upload } from 'ant-design-vue';
|
||||
import { isArray, isFunction, isObject, isString } from 'lodash-es';
|
||||
import { isArray, isFunction, isObject, isString, uniqueId } from 'lodash-es';
|
||||
|
||||
import { uploadApi } from '#/api';
|
||||
import { ossInfo } from '#/api/system/oss';
|
||||
|
||||
import { checkImageFileType, defaultImageAccept } from './helper';
|
||||
import { UploadResultStatus } from './typing';
|
||||
@@ -24,7 +29,10 @@ const props = withDefaults(
|
||||
* 包括拓展名(不带点) 文件头(image/png等 不包括泛写法即image/*)
|
||||
*/
|
||||
accept?: string[];
|
||||
api?: (...args: any[]) => Promise<any>;
|
||||
api?: (
|
||||
file: Blob | File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
@@ -37,7 +45,11 @@ const props = withDefaults(
|
||||
multiple?: boolean;
|
||||
// support xxx.xxx.xx
|
||||
// 返回的字段 默认url
|
||||
resultField?: 'fileName' | 'ossId' | 'url' | string;
|
||||
resultField?: 'fileName' | 'ossId' | 'url';
|
||||
/**
|
||||
* 是否显示下面的描述
|
||||
*/
|
||||
showDescription?: boolean;
|
||||
value?: string | string[];
|
||||
}>(),
|
||||
{
|
||||
@@ -50,7 +62,8 @@ const props = withDefaults(
|
||||
accept: () => defaultImageAccept,
|
||||
multiple: false,
|
||||
api: uploadApi,
|
||||
resultField: '',
|
||||
resultField: 'url',
|
||||
showDescription: true,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
||||
@@ -74,7 +87,7 @@ const isFirstRender = ref<boolean>(true);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(v) => {
|
||||
async (v) => {
|
||||
if (isInnerOperate.value) {
|
||||
isInnerOperate.value = false;
|
||||
return;
|
||||
@@ -90,19 +103,40 @@ watch(
|
||||
}
|
||||
// 直接赋值 可能为string | string[]
|
||||
value = v;
|
||||
fileList.value = _fileList.map((item, i) => {
|
||||
if (item && isString(item)) {
|
||||
return {
|
||||
uid: `${-i}`,
|
||||
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
|
||||
status: 'done',
|
||||
url: item,
|
||||
};
|
||||
} else if (item && isObject(item)) {
|
||||
return item;
|
||||
const withUrlList: UploadProps['fileList'] = [];
|
||||
for (const item of _fileList) {
|
||||
// ossId情况
|
||||
if (props.resultField === 'ossId') {
|
||||
const resp = await ossInfo([item]);
|
||||
if (item && isString(item)) {
|
||||
withUrlList.push({
|
||||
uid: item, // ossId作为uid 方便getValue获取
|
||||
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
|
||||
status: 'done',
|
||||
url: resp?.[0]?.url,
|
||||
});
|
||||
} else if (item && isObject(item)) {
|
||||
withUrlList.push({
|
||||
...(item as any),
|
||||
uid: item,
|
||||
url: resp?.[0]?.url,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 非ossId情况
|
||||
if (item && isString(item)) {
|
||||
withUrlList.push({
|
||||
uid: uniqueId(),
|
||||
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
|
||||
status: 'done',
|
||||
url: item,
|
||||
});
|
||||
} else if (item && isObject(item)) {
|
||||
withUrlList.push(item);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}) as UploadProps['fileList'];
|
||||
}
|
||||
fileList.value = withUrlList;
|
||||
}
|
||||
if (!isFirstRender.value) {
|
||||
emit('change', value);
|
||||
@@ -182,12 +216,19 @@ async function customRequest(info: UploadRequestOption<any>) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api?.(info.file);
|
||||
// 进度条事件
|
||||
const progressEvent: AxiosProgressEvent = (e) => {
|
||||
const percent = Math.trunc((e.loaded / e.total!) * 100);
|
||||
info.onProgress!({ percent });
|
||||
};
|
||||
const res = await api?.(info.file as File, progressEvent);
|
||||
/**
|
||||
* 由getValue处理 传对象过去
|
||||
* 直接传string(id)会被转为Number
|
||||
* 内部的逻辑由requestClient.upload处理 这里不用判断业务状态码 不符合会自动reject
|
||||
*/
|
||||
info.onSuccess!(res);
|
||||
message.success($t('component.upload.uploadSuccess'));
|
||||
// 获取
|
||||
const value = getValue();
|
||||
isInnerOperate.value = true;
|
||||
@@ -200,12 +241,17 @@ async function customRequest(info: UploadRequestOption<any>) {
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
console.log(fileList.value);
|
||||
const list = (fileList.value || [])
|
||||
.filter((item) => item?.status === UploadResultStatus.DONE)
|
||||
.map((item: any) => {
|
||||
if (item?.response && props?.resultField) {
|
||||
return item?.response?.[props.resultField];
|
||||
}
|
||||
// ossId兼容 uid为ossId直接返回
|
||||
if (props.resultField === 'ossId' && item.uid) {
|
||||
return item.uid;
|
||||
}
|
||||
// 适用于已经有图片 回显的情况 会默认在init处理为{url: 'xx'}
|
||||
if (item?.url) {
|
||||
return item.url;
|
||||
@@ -237,6 +283,7 @@ function getValue() {
|
||||
:list-type="listType"
|
||||
:max-count="maxNumber"
|
||||
:multiple="multiple"
|
||||
:progress="{ showInfo: true }"
|
||||
@preview="handlePreview"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
@@ -245,6 +292,16 @@ function getValue() {
|
||||
<div style="margin-top: 8px">{{ $t('component.upload.upload') }}</div>
|
||||
</div>
|
||||
</Upload>
|
||||
<div
|
||||
v-if="showDescription"
|
||||
class="mt-2 flex flex-wrap items-center text-[14px]"
|
||||
>
|
||||
请上传不超过
|
||||
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
||||
的
|
||||
<div class="text-primary mx-1 font-bold">{{ accept.join('/') }}</div>
|
||||
格式文件
|
||||
</div>
|
||||
<Modal
|
||||
:footer="null"
|
||||
:open="previewOpen"
|
||||
|
@@ -15,7 +15,7 @@ export interface FileItem {
|
||||
percent: number;
|
||||
file: File;
|
||||
status?: UploadResultStatus;
|
||||
response?: { fileName: string; ossId: string; url: string } | Recordable<any>;
|
||||
response?: Recordable<any> | { fileName: string; ossId: string; url: string };
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { computed, unref } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { computed, unref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
export function useUploadType({
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
||||
import type { Locale } from 'ant-design-vue/es/locale';
|
||||
|
||||
import type { App } from 'vue';
|
||||
|
||||
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import {
|
||||
@@ -30,7 +32,8 @@ const localesMap = loadLocalesMapFromDir(
|
||||
*/
|
||||
async function loadMessages(lang: SupportedLanguagesType) {
|
||||
const [appLocaleMessages] = await Promise.all([
|
||||
localesMap[lang](),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
localesMap[lang]!(),
|
||||
loadThirdPartyMessage(lang),
|
||||
]);
|
||||
return appLocaleMessages.default;
|
||||
|
7
apps/web-antd/src/locales/langs/en-US/http.json
Normal file
7
apps/web-antd/src/locales/langs/en-US/http.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"apiRequestFailed": "Operation failed",
|
||||
"operationSuccess": "Operation Success",
|
||||
"successTip": "Success Tip",
|
||||
"errorTip": "Error Tip",
|
||||
"loginTimeout": "Login timeout, please log in again"
|
||||
}
|
7
apps/web-antd/src/locales/langs/zh-CN/http.json
Normal file
7
apps/web-antd/src/locales/langs/zh-CN/http.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"apiRequestFailed": "请求出错,请稍候重试",
|
||||
"operationSuccess": "操作成功",
|
||||
"successTip": "成功提示",
|
||||
"errorTip": "错误提示",
|
||||
"loginTimeout": "登录超时, 请重新登录"
|
||||
}
|
@@ -4,13 +4,15 @@ import type {
|
||||
RouteRecordStringComponent,
|
||||
} from '@vben/types';
|
||||
|
||||
import type { Menu } from '#/api';
|
||||
|
||||
import { generateAccessible } from '@vben/access';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { getAllMenusApi, type Menu } from '#/api';
|
||||
import { getAllMenusApi } from '#/api';
|
||||
import { BasicLayout, IFrameView } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
|
@@ -54,7 +54,9 @@ function setupAccessGuard(router: Router) {
|
||||
if (coreRouteNames.includes(to.name as string)) {
|
||||
if (to.path === LOGIN_PATH && accessStore.accessToken) {
|
||||
return decodeURIComponent(
|
||||
(to.query?.redirect as string) || DEFAULT_HOME_PATH,
|
||||
(to.query?.redirect as string) ||
|
||||
userStore.userInfo?.homePath ||
|
||||
DEFAULT_HOME_PATH,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
@@ -72,7 +74,10 @@ function setupAccessGuard(router: Router) {
|
||||
return {
|
||||
path: LOGIN_PATH,
|
||||
// 如不需要,直接删除 query
|
||||
query: { redirect: encodeURIComponent(to.fullPath) },
|
||||
query:
|
||||
to.fullPath === DEFAULT_HOME_PATH
|
||||
? {}
|
||||
: { redirect: encodeURIComponent(to.fullPath) },
|
||||
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||
replace: true,
|
||||
};
|
||||
@@ -102,7 +107,10 @@ function setupAccessGuard(router: Router) {
|
||||
accessStore.setAccessMenus(accessibleMenus);
|
||||
accessStore.setAccessRoutes(accessibleRoutes);
|
||||
accessStore.setIsAccessChecked(true);
|
||||
const redirectPath = (from.query.redirect ?? to.fullPath) as string;
|
||||
const redirectPath = (from.query.redirect ??
|
||||
(to.path === DEFAULT_HOME_PATH
|
||||
? userInfo.homePath || DEFAULT_HOME_PATH
|
||||
: to.fullPath)) as string;
|
||||
|
||||
return {
|
||||
...router.resolve(decodeURIComponent(redirectPath)),
|
||||
|
@@ -2,7 +2,7 @@ import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||
|
||||
import { AuthPageLayout } from '#/layouts';
|
||||
import { AuthPageLayout, BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
import Login from '#/views/_core/authentication/login.vue';
|
||||
|
||||
@@ -21,13 +21,21 @@ const fallbackNotFoundRoute: RouteRecordRaw = {
|
||||
|
||||
/** 基本路由,这些路由是必须存在的 */
|
||||
const coreRoutes: RouteRecordRaw[] = [
|
||||
/**
|
||||
* 根路由
|
||||
* 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。
|
||||
* 此路由必须存在,且不应修改
|
||||
*/
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
title: 'Root',
|
||||
},
|
||||
name: 'Root',
|
||||
path: '/',
|
||||
redirect: DEFAULT_HOME_PATH,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/_core/social-callback/index.vue'),
|
||||
|
@@ -5,105 +5,83 @@ import { $t } from '@vben/locales';
|
||||
/**
|
||||
* 该文件放非后台返回的路由 比如个人中心 等需要跳转显示的页面
|
||||
*/
|
||||
|
||||
/**
|
||||
* 个人中心
|
||||
*/
|
||||
const profileRoute: RouteRecordStringComponent[] = [
|
||||
const localRoutes: RouteRecordStringComponent[] = [
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
component: '/_core/profile/index',
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
hideInMenu: true,
|
||||
icon: 'mingcute:profile-line',
|
||||
title: $t('ui.widgets.profile'),
|
||||
hideInMenu: true,
|
||||
},
|
||||
name: 'Profile',
|
||||
path: '/',
|
||||
redirect: '/profile',
|
||||
children: [
|
||||
{
|
||||
component: '/_core/profile/index',
|
||||
meta: {
|
||||
icon: 'mingcute:profile-line',
|
||||
keepAlive: true,
|
||||
title: $t('ui.widgets.profile'),
|
||||
},
|
||||
name: 'ProfileIndex',
|
||||
path: '/profile',
|
||||
},
|
||||
],
|
||||
path: '/profile',
|
||||
},
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
component: '/system/oss-config/index',
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
hideInMenu: true,
|
||||
activePath: '/system/oss',
|
||||
icon: 'ant-design:setting-outlined',
|
||||
title: 'oss配置',
|
||||
hideInMenu: true,
|
||||
},
|
||||
name: 'OssConfig',
|
||||
path: '/',
|
||||
redirect: '/system/oss-config',
|
||||
children: [
|
||||
{
|
||||
component: '/system/oss-config/index',
|
||||
meta: {
|
||||
activePath: '/system/oss',
|
||||
icon: 'ant-design:setting-outlined',
|
||||
keepAlive: true,
|
||||
title: 'oss配置',
|
||||
},
|
||||
name: 'OssConfigIndex',
|
||||
path: '/system/oss-config',
|
||||
},
|
||||
],
|
||||
path: '/system/oss-config',
|
||||
},
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
component: '/tool/gen/edit-gen',
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
activePath: '/tool/gen',
|
||||
icon: 'tabler:code',
|
||||
title: '生成配置',
|
||||
hideInMenu: true,
|
||||
title: '修改生成配置',
|
||||
},
|
||||
name: 'GenConfig',
|
||||
path: '/',
|
||||
redirect: '/code-gen/edit',
|
||||
children: [
|
||||
{
|
||||
component: '/tool/gen/edit-gen',
|
||||
meta: {
|
||||
activePath: '/tool/gen',
|
||||
icon: 'tabler:code',
|
||||
keepAlive: true,
|
||||
title: '生成配置',
|
||||
},
|
||||
name: 'GenConfigIndex',
|
||||
path: '/code-gen/edit/:tableId',
|
||||
},
|
||||
],
|
||||
path: '/code-gen/edit/:tableId',
|
||||
},
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
component: '/system/role-assign/index',
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
hideInMenu: true,
|
||||
activePath: '/system/role',
|
||||
icon: 'eos-icons:role-binding-outlined',
|
||||
title: '分配角色',
|
||||
hideInMenu: true,
|
||||
},
|
||||
name: 'RoleAssign',
|
||||
path: '/',
|
||||
redirect: '/system/role-assign',
|
||||
children: [
|
||||
{
|
||||
component: '/system/role-assign/index',
|
||||
meta: {
|
||||
activePath: '/system/role',
|
||||
icon: 'eos-icons:role-binding-outlined',
|
||||
keepAlive: true,
|
||||
title: '分配角色',
|
||||
},
|
||||
name: 'RoleAssignIndex',
|
||||
path: '/system/role-assign/:roleId',
|
||||
},
|
||||
],
|
||||
path: '/system/role-assign/:roleId',
|
||||
},
|
||||
{
|
||||
component: '/workflow/components/flow-designer',
|
||||
meta: {
|
||||
activePath: '/workflow/processDefinition',
|
||||
icon: 'fluent-mdl2:flow',
|
||||
title: '流程设计',
|
||||
hideInMenu: true,
|
||||
},
|
||||
name: 'WorkflowDesigner',
|
||||
path: '/workflow/designer',
|
||||
},
|
||||
{
|
||||
component: 'workflow/leave/leave-form',
|
||||
meta: {
|
||||
icon: 'flat-color-icons:leave',
|
||||
title: '请假申请',
|
||||
activePath: '/demo/leave',
|
||||
hideInMenu: true,
|
||||
},
|
||||
name: 'WorkflowLeaveIndex',
|
||||
path: '/workflow/leaveEdit/index',
|
||||
},
|
||||
// 这里是iframe使用的 去掉外层的BasicLayout
|
||||
{
|
||||
component: 'workflow/leave/leave-form',
|
||||
meta: {
|
||||
title: '请假申请',
|
||||
hideInMenu: true,
|
||||
// 不使用基础布局(仅在顶级生效)
|
||||
noBasicLayout: true,
|
||||
},
|
||||
name: 'WorkflowLeaveInner',
|
||||
path: '/workflow/leaveEdit/index/iframe',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -116,6 +94,8 @@ export const localMenuList: RouteRecordStringComponent[] = [
|
||||
meta: {
|
||||
order: -1,
|
||||
title: 'page.dashboard.title',
|
||||
// 不使用基础布局(仅在顶级生效)
|
||||
noBasicLayout: true,
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
@@ -152,25 +132,14 @@ export const localMenuList: RouteRecordStringComponent[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
component: '/_core/about/index',
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
icon: 'lucide:copyright',
|
||||
order: 9999,
|
||||
title: $t('demos.vben.about'),
|
||||
},
|
||||
name: 'About',
|
||||
path: '/about',
|
||||
children: [
|
||||
{
|
||||
component: '/_core/about/index',
|
||||
meta: {
|
||||
title: $t('demos.vben.about'),
|
||||
},
|
||||
name: 'VbenAbout',
|
||||
path: '/vben-admin/about',
|
||||
},
|
||||
],
|
||||
path: '/vben-admin/about',
|
||||
},
|
||||
...profileRoute,
|
||||
...localRoutes,
|
||||
];
|
||||
|
@@ -1,18 +1,16 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
icon: 'lucide:layout-dashboard',
|
||||
order: -1,
|
||||
title: $t('page.dashboard.title'),
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
path: '/dashboard',
|
||||
children: [
|
||||
{
|
||||
name: 'Analytics',
|
||||
|
@@ -1,30 +0,0 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
icon: 'ic:baseline-view-in-ar',
|
||||
keepAlive: true,
|
||||
order: 1000,
|
||||
title: $t('demos.title'),
|
||||
},
|
||||
name: 'Demos',
|
||||
path: '/demos',
|
||||
children: [
|
||||
{
|
||||
meta: {
|
||||
title: $t('demos.antd'),
|
||||
},
|
||||
name: 'AntDesignDemos',
|
||||
path: '/demos/ant-design',
|
||||
component: () => import('#/views/demos/antd/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@@ -8,16 +8,15 @@ import {
|
||||
VBEN_NAIVE_PREVIEW_URL,
|
||||
} from '@vben/constants';
|
||||
|
||||
import { BasicLayout, IFrameView } from '#/layouts';
|
||||
import { IFrameView } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
icon: VBEN_LOGO_URL,
|
||||
order: 9999,
|
||||
order: 9998,
|
||||
title: $t('demos.vben.title'),
|
||||
},
|
||||
name: 'VbenProject',
|
||||
@@ -76,6 +75,16 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'VbenAbout',
|
||||
path: '/vben-admin/about',
|
||||
component: () => import('#/views/_core/about/index.vue'),
|
||||
meta: {
|
||||
icon: 'lucide:copyright',
|
||||
title: $t('demos.vben.about'),
|
||||
order: 9999,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { DictData } from '#/api/system/dict/dict-data-model';
|
||||
|
||||
import { reactive } from 'vue';
|
||||
@@ -6,29 +7,36 @@ import { defineStore } from 'pinia';
|
||||
|
||||
/**
|
||||
* antd使用 select和radio通用
|
||||
* 本质上是对DictData的拓展
|
||||
*/
|
||||
export interface Option {
|
||||
export interface DictOption extends DictData {
|
||||
disabled?: boolean;
|
||||
label: string;
|
||||
value: string;
|
||||
value: number | string;
|
||||
}
|
||||
|
||||
export function dictToOptions(data: DictData[]): Option[] {
|
||||
/**
|
||||
* 将字典数据转为Options
|
||||
* @param data 字典数据
|
||||
* @param formatNumber 是否需要将value格式化为number类型
|
||||
* @returns options
|
||||
*/
|
||||
export function dictToOptions(
|
||||
data: DictData[],
|
||||
formatNumber = false,
|
||||
): DictOption[] {
|
||||
return data.map((item) => ({
|
||||
...item,
|
||||
label: item.dictLabel,
|
||||
value: item.dictValue,
|
||||
value: formatNumber ? Number(item.dictValue) : item.dictValue,
|
||||
}));
|
||||
}
|
||||
|
||||
export const useDictStore = defineStore('app-dict', () => {
|
||||
/**
|
||||
* 一般是dictTag使用
|
||||
* select radio checkbox等使用 只能为固定格式{label, value}
|
||||
*/
|
||||
const dictMap = reactive(new Map<string, DictData[]>());
|
||||
/**
|
||||
* select radio radioButton使用 只能为固定格式(Option)
|
||||
*/
|
||||
const dictOptionsMap = reactive(new Map<string, Option[]>());
|
||||
const dictOptionsMap = reactive(new Map<string, DictOption[]>());
|
||||
/**
|
||||
* 添加一个字典请求状态的缓存
|
||||
*
|
||||
@@ -40,30 +48,17 @@ export const useDictStore = defineStore('app-dict', () => {
|
||||
new Map<string, Promise<DictData[] | void>>(),
|
||||
);
|
||||
|
||||
function getDict(dictName: string): DictData[] {
|
||||
if (!dictName) return [];
|
||||
// 没有key 添加一个空数组
|
||||
if (!dictMap.has(dictName)) {
|
||||
dictMap.set(dictName, reactive([]));
|
||||
}
|
||||
// 这里拿到的就不可能为空了
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return dictMap.get(dictName)!;
|
||||
}
|
||||
|
||||
function getDictOptions(dictName: string): Option[] {
|
||||
function getDictOptions(dictName: string): DictOption[] {
|
||||
if (!dictName) return [];
|
||||
// 没有key 添加一个空数组
|
||||
if (!dictOptionsMap.has(dictName)) {
|
||||
dictOptionsMap.set(dictName, []);
|
||||
}
|
||||
// 这里拿到的就不可能为空了
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return dictOptionsMap.get(dictName)!;
|
||||
}
|
||||
|
||||
function resetCache() {
|
||||
dictMap.clear();
|
||||
dictOptionsMap.clear();
|
||||
}
|
||||
|
||||
@@ -76,19 +71,20 @@ export const useDictStore = defineStore('app-dict', () => {
|
||||
* 否则 直接set
|
||||
*
|
||||
*/
|
||||
function setDictInfo(dictName: string, dictValue: DictData[]) {
|
||||
if (dictMap.has(dictName) && dictMap.get(dictName)?.length === 0) {
|
||||
dictMap.get(dictName)?.push(...dictValue);
|
||||
} else {
|
||||
dictMap.set(dictName, dictValue);
|
||||
}
|
||||
function setDictInfo(
|
||||
dictName: string,
|
||||
dictValue: DictData[],
|
||||
formatNumber = false,
|
||||
) {
|
||||
if (
|
||||
dictOptionsMap.has(dictName) &&
|
||||
dictOptionsMap.get(dictName)?.length === 0
|
||||
) {
|
||||
dictOptionsMap.get(dictName)?.push(...dictToOptions(dictValue));
|
||||
dictOptionsMap
|
||||
.get(dictName)
|
||||
?.push(...dictToOptions(dictValue, formatNumber));
|
||||
} else {
|
||||
dictOptionsMap.set(dictName, dictToOptions(dictValue));
|
||||
dictOptionsMap.set(dictName, dictToOptions(dictValue, formatNumber));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +96,8 @@ export const useDictStore = defineStore('app-dict', () => {
|
||||
|
||||
return {
|
||||
$reset,
|
||||
dictMap,
|
||||
dictOptionsMap,
|
||||
dictRequestCache,
|
||||
getDict,
|
||||
getDictOptions,
|
||||
resetCache,
|
||||
setDictInfo,
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import type { TenantOption } from '#/api/core/auth';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import {
|
||||
tenantList as tenantListApi,
|
||||
type TenantOption,
|
||||
} from '#/api/core/auth';
|
||||
import { tenantList as tenantListApi } from '#/api/core/auth';
|
||||
|
||||
/**
|
||||
* 用于超级管理员切换租户
|
||||
|
@@ -1,22 +1,31 @@
|
||||
import type { DictData } from '#/api/system/dict/dict-data-model';
|
||||
|
||||
import { dictDataInfo } from '#/api/system/dict/dict-data';
|
||||
import { type Option, useDictStore } from '#/store/dict';
|
||||
import { useDictStore } from '#/store/dict';
|
||||
|
||||
/**
|
||||
* 抽取公共逻辑的基础方法
|
||||
* @param dictName 字典名称
|
||||
* @param dataGetter 获取字典数据的函数
|
||||
* @param formatNumber 是否格式化字典value为number类型
|
||||
* @returns 数据
|
||||
*/
|
||||
function fetchAndCacheDictData<T>(
|
||||
dictName: string,
|
||||
dataGetter: () => T[],
|
||||
formatNumber = false,
|
||||
): T[] {
|
||||
const { dictRequestCache, setDictInfo } = useDictStore();
|
||||
// 有调用方决定如何获取数据
|
||||
const dataList = dataGetter();
|
||||
|
||||
// todo 重复代码的封装
|
||||
export function getDict(dictName: string): DictData[] {
|
||||
const { dictRequestCache, getDict, setDictInfo } = useDictStore();
|
||||
// 这里拿到
|
||||
const dictList = getDict(dictName);
|
||||
// 检查请求状态缓存
|
||||
if (dictList.length === 0 && !dictRequestCache.has(dictName)) {
|
||||
if (dataList.length === 0 && !dictRequestCache.has(dictName)) {
|
||||
dictRequestCache.set(
|
||||
dictName,
|
||||
dictDataInfo(dictName)
|
||||
.then((resp) => {
|
||||
// 缓存到store 这样就不用重复获取了
|
||||
// 内部处理了push的逻辑 这里不用push
|
||||
setDictInfo(dictName, resp);
|
||||
setDictInfo(dictName, resp, formatNumber);
|
||||
})
|
||||
.finally(() => {
|
||||
// 移除请求状态缓存
|
||||
@@ -25,40 +34,37 @@ export function getDict(dictName: string): DictData[] {
|
||||
* 会导致if一直进入逻辑导致接口无限刷新
|
||||
* 在这里dictList为空时 不删除缓存
|
||||
*/
|
||||
if (dictList.length > 0) {
|
||||
if (dataList.length > 0) {
|
||||
dictRequestCache.delete(dictName);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
return dictList;
|
||||
return dataList;
|
||||
}
|
||||
|
||||
export function getDictOptions(dictName: string): Option[] {
|
||||
const { dictRequestCache, getDictOptions, setDictInfo } = useDictStore();
|
||||
const dictOptionList = getDictOptions(dictName);
|
||||
// 检查请求状态缓存
|
||||
if (dictOptionList.length === 0 && !dictRequestCache.has(dictName)) {
|
||||
dictRequestCache.set(
|
||||
dictName,
|
||||
dictDataInfo(dictName)
|
||||
.then((resp) => {
|
||||
// 缓存到store 这样就不用重复获取了
|
||||
// 内部处理了push的逻辑 这里不用push
|
||||
setDictInfo(dictName, resp);
|
||||
})
|
||||
.finally(() => {
|
||||
// 移除请求状态缓存
|
||||
/**
|
||||
* 这里主要判断字典item为空的情况(无奈兼容 不给字典item本来就是错误用法)
|
||||
* 会导致if一直进入逻辑导致接口五线刷新
|
||||
* 在这里dictList为空时 不删除缓存
|
||||
*/
|
||||
if (dictOptionList.length > 0) {
|
||||
dictRequestCache.delete(dictName);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
return dictOptionList;
|
||||
/**
|
||||
* 这里是提供给渲染标签使用的方法
|
||||
* @deprecated 使用getDictOptions代替 于下个版本删除
|
||||
* @param dictName 字典名称
|
||||
* @returns 字典信息
|
||||
*/
|
||||
export function getDict(dictName: string) {
|
||||
const { getDictOptions } = useDictStore();
|
||||
return fetchAndCacheDictData(dictName, () => getDictOptions(dictName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般是Select, Radio, Checkbox等组件使用
|
||||
* @param dictName 字典名称
|
||||
* @param formatNumber 是否格式化字典value为number类型
|
||||
* @returns Options数组
|
||||
*/
|
||||
export function getDictOptions(dictName: string, formatNumber = false) {
|
||||
const { getDictOptions } = useDictStore();
|
||||
return fetchAndCacheDictData(
|
||||
dictName,
|
||||
() => getDictOptions(dictName),
|
||||
formatNumber,
|
||||
);
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import { $t } from '@vben/locales';
|
||||
import { cloneDeep, formatDate } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { isFunction } from 'lodash-es';
|
||||
|
||||
import { dataURLtoBlob, urlToBase64 } from './base64Conver';
|
||||
|
||||
@@ -54,37 +55,42 @@ function handleRangeTimeValue(
|
||||
|
||||
fieldMappingTime.forEach(
|
||||
([field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD']) => {
|
||||
if (
|
||||
values[field] === null &&
|
||||
values[startTimeKey] &&
|
||||
values[endTimeKey]
|
||||
) {
|
||||
if (startTimeKey && endTimeKey && values[field] === null) {
|
||||
Reflect.deleteProperty(values, startTimeKey);
|
||||
Reflect.deleteProperty(values, endTimeKey);
|
||||
return;
|
||||
// delete values[startTimeKey];
|
||||
// delete values[endTimeKey];
|
||||
}
|
||||
|
||||
if (!values[field]) {
|
||||
Reflect.deleteProperty(values, field);
|
||||
// delete values[field];
|
||||
return;
|
||||
}
|
||||
|
||||
const [startTime, endTime] = values[field];
|
||||
const [startTimeFormat, endTimeFormat] = Array.isArray(format)
|
||||
? format
|
||||
: [format, format];
|
||||
|
||||
values[startTimeKey] = startTime
|
||||
? formatDate(startTime, startTimeFormat)
|
||||
: undefined;
|
||||
values[endTimeKey] = endTime
|
||||
? formatDate(endTime, endTimeFormat)
|
||||
: undefined;
|
||||
if (format === null) {
|
||||
values[startTimeKey] = startTime;
|
||||
values[endTimeKey] = endTime;
|
||||
} else if (isFunction(format)) {
|
||||
values[startTimeKey] = format(startTime, startTimeKey);
|
||||
values[endTimeKey] = format(endTime, endTimeKey);
|
||||
} else {
|
||||
const [startTimeFormat, endTimeFormat] = Array.isArray(format)
|
||||
? format
|
||||
: [format, format];
|
||||
|
||||
values[startTimeKey] = startTime
|
||||
? formatDate(startTime, startTimeFormat)
|
||||
: undefined;
|
||||
values[endTimeKey] = endTime
|
||||
? formatDate(endTime, endTimeFormat)
|
||||
: undefined;
|
||||
}
|
||||
// delete values[field];
|
||||
Reflect.deleteProperty(values, field);
|
||||
},
|
||||
);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
|
31
apps/web-antd/src/utils/file/index.ts
Normal file
31
apps/web-antd/src/utils/file/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 计算文件大小并以适当单位表示
|
||||
*
|
||||
* 此函数接收一个表示文件大小的数字(以字节为单位),并返回一个格式化后的字符串,
|
||||
* 该字符串表示文件的大小,以最适合的单位(B, KB, MB, GB, TB)表示
|
||||
*
|
||||
* @param size 文件大小,以字节为单位
|
||||
* @param isInteger 是否返回整数大小,默认为false如果设置为true,
|
||||
* 则返回的大小将不包含小数部分;如果为false,则根据单位的不同,
|
||||
* 返回最多3位小数的大小
|
||||
* @returns 格式化后的文件大小字符串,如"4.5KB"或"3MB"
|
||||
*/
|
||||
export function calculateFileSize(size: number, isInteger = false) {
|
||||
// 定义文件大小的单位数组
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
// 定义换算基数,1KB = 1024B,1MB = 1024KB,以此类推
|
||||
const base = 1024;
|
||||
|
||||
// 初始化单位索引,初始值为0,即默认单位为B
|
||||
let unitIndex = 0;
|
||||
// 当文件大小大于等于基数且单位索引未超出单位数组范围时,循环进行单位转换
|
||||
while (size >= base && unitIndex < units.length - 1) {
|
||||
size /= base;
|
||||
unitIndex++;
|
||||
}
|
||||
|
||||
// 根据是否需要整数大小,确定输出的精度
|
||||
const precision = isInteger ? 0 : Math.min(unitIndex, 3);
|
||||
// 返回格式化后的文件大小字符串
|
||||
return `${size.toFixed(precision)}${units[unitIndex]}`;
|
||||
}
|
@@ -1,8 +1,9 @@
|
||||
import type { ModalFuncProps } from 'ant-design-vue';
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import { Alert, Form, Input, Modal, type ModalFuncProps } from 'ant-design-vue';
|
||||
import { Alert, Form, Input, Modal } from 'ant-design-vue';
|
||||
import { isFunction } from 'lodash-es';
|
||||
|
||||
export interface ConfirmModalProps extends Omit<ModalFuncProps, 'visible'> {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import type { Component as ComponentType } from 'vue';
|
||||
|
||||
import type { DictData } from '#/api/system/dict/dict-data-model';
|
||||
|
||||
import { type Component as ComponentType, h } from 'vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { JsonPreview } from '@vben/common-ui';
|
||||
import {
|
||||
@@ -29,7 +31,7 @@ import { Tag } from 'ant-design-vue';
|
||||
|
||||
import { DictTag } from '#/components/dict';
|
||||
|
||||
import { getDict } from './dict';
|
||||
import { getDictOptions } from './dict';
|
||||
|
||||
/**
|
||||
* 渲染标签
|
||||
@@ -50,7 +52,10 @@ function renderTag(text: string, color?: string) {
|
||||
*/
|
||||
export function renderTags(tags: string[], wrap = false, gap = 1) {
|
||||
return (
|
||||
<div class={['flex', `gap-${gap}`, wrap ? 'flex-col' : 'flex-row']}>
|
||||
<div
|
||||
class={['flex', wrap ? 'flex-col' : 'flex-row']}
|
||||
style={{ gap: `${gap}px` }}
|
||||
>
|
||||
{tags.map((tag, index) => {
|
||||
return <div key={index}>{renderTag(tag)}</div>;
|
||||
})}
|
||||
@@ -133,7 +138,10 @@ export function renderDictTags(
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
return (
|
||||
<div class={['flex', `gap-${gap}`, wrap ? 'flex-col' : 'flex-row']}>
|
||||
<div
|
||||
class={['flex', wrap ? 'flex-col' : 'flex-row']}
|
||||
style={{ gap: `${gap}px` }}
|
||||
>
|
||||
{value.map((item, index) => {
|
||||
return <div key={index}>{renderDictTag(item, dicts)}</div>;
|
||||
})}
|
||||
@@ -148,7 +156,7 @@ export function renderDictTags(
|
||||
* @returns tag
|
||||
*/
|
||||
export function renderDict(value: string, dictName: string) {
|
||||
const dictInfo = getDict(dictName);
|
||||
const dictInfo = getDictOptions(dictName);
|
||||
return renderDictTag(value, dictInfo);
|
||||
}
|
||||
export function renderIconSpan(
|
||||
@@ -205,12 +213,8 @@ export function renderOsIcon(os: string, center = false) {
|
||||
if (os.toLocaleLowerCase().includes('windows')) {
|
||||
current = osOptions[0];
|
||||
}
|
||||
if (current) {
|
||||
return renderIconSpan(current.icon, os, center, '5px');
|
||||
}
|
||||
// 返回默认
|
||||
const defaultIcon = DefaultOsIcon;
|
||||
return renderIconSpan(defaultIcon, os, center, '5px');
|
||||
const icon = current ? current.icon : DefaultOsIcon;
|
||||
return renderIconSpan(icon, os, center, '5px');
|
||||
}
|
||||
|
||||
export function renderBrowserIcon(browser: string, center = false) {
|
||||
@@ -220,10 +224,6 @@ export function renderBrowserIcon(browser: string, center = false) {
|
||||
const current = browserOptions.find((item) =>
|
||||
browser.toLocaleLowerCase().includes(item.value),
|
||||
);
|
||||
if (current) {
|
||||
return renderIconSpan(current.icon, browser, center, '5px');
|
||||
}
|
||||
// 返回默认
|
||||
const defaultIcon = DefaultBrowserIcon;
|
||||
return renderIconSpan(defaultIcon, browser, center, '5px');
|
||||
const icon = current ? current.icon : DefaultBrowserIcon;
|
||||
return renderIconSpan(icon, browser, center, '5px');
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LoginCodeParams, VbenFormSchema } from '@vben/common-ui';
|
||||
|
||||
import type { TenantResp } from '#/api';
|
||||
|
||||
import { computed, onMounted, ref, useTemplateRef } from 'vue';
|
||||
|
||||
import { AuthenticationCodeLogin, z } from '@vben/common-ui';
|
||||
@@ -8,13 +10,14 @@ import { $t } from '@vben/locales';
|
||||
|
||||
import { Alert, message } from 'ant-design-vue';
|
||||
|
||||
import { tenantList, type TenantResp } from '#/api';
|
||||
import { tenantList } from '#/api';
|
||||
import { sendSmsCode } from '#/api/core/captcha';
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
defineOptions({ name: 'CodeLogin' });
|
||||
|
||||
const loading = ref(false);
|
||||
const CODE_LENGTH = 6;
|
||||
|
||||
const tenantInfo = ref<TenantResp>({
|
||||
tenantEnabled: false,
|
||||
@@ -98,7 +101,9 @@ const formSchema = computed((): VbenFormSchema[] => {
|
||||
},
|
||||
fieldName: 'code',
|
||||
label: $t('authentication.code'),
|
||||
rules: z.string().min(1, { message: $t('authentication.codeTip') }),
|
||||
rules: z.string().length(CODE_LENGTH, {
|
||||
message: $t('authentication.codeTip', [CODE_LENGTH]),
|
||||
}),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
@@ -1,6 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
|
||||
|
||||
import type { TenantResp } from '#/api';
|
||||
import type { CaptchaResponse } from '#/api/core/captcha';
|
||||
|
||||
import { computed, onMounted, ref, useTemplateRef } from 'vue';
|
||||
|
||||
import { AuthenticationLogin, z } from '@vben/common-ui';
|
||||
@@ -8,8 +11,8 @@ import { $t } from '@vben/locales';
|
||||
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
import { tenantList, type TenantResp } from '#/api';
|
||||
import { captchaImage, type CaptchaResponse } from '#/api/core/captcha';
|
||||
import { tenantList } from '#/api';
|
||||
import { captchaImage } from '#/api/core/captcha';
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import OAuthLogin from './oauth-login.vue';
|
||||
|
@@ -1,7 +1,11 @@
|
||||
<script setup lang="tsx">
|
||||
import type { VxeGridProps } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { BindItem } from '../../oauth-common';
|
||||
|
||||
import { computed, ref, unref } from 'vue';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '@vben/plugins/vxe-table';
|
||||
import { useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
@@ -16,7 +20,7 @@ import {
|
||||
import { authUnbinding } from '#/api';
|
||||
import { socialList } from '#/api/system/social';
|
||||
|
||||
import { accountBindList, type BindItem } from '../../oauth-common';
|
||||
import { accountBindList } from '../../oauth-common';
|
||||
|
||||
/**
|
||||
* 没有传递action事件则不支持绑定 弹出默认提示
|
||||
@@ -103,7 +107,6 @@ const gridOptions: VxeGridProps = {
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
isCurrent: false,
|
||||
keyField: 'id',
|
||||
},
|
||||
|
@@ -1,16 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { forceLogout2, onlineDeviceList } from '#/api/monitor/online';
|
||||
import { columns } from '#/views/monitor/online/data';
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
columns,
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async () => {
|
||||
@@ -19,7 +23,6 @@ const gridOptions: VxeGridProps = {
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'tokenId',
|
||||
},
|
||||
};
|
||||
|
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AuthApi } from '#/api';
|
||||
|
||||
import { onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
@@ -7,7 +9,7 @@ import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { type AuthApi, authCallback } from '#/api';
|
||||
import { authCallback } from '#/api';
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import { accountBindList } from '../oauth-common';
|
||||
|
@@ -1,18 +0,0 @@
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { Fallback } from '@vben/common-ui';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CommonSkeleton',
|
||||
setup() {
|
||||
return () => (
|
||||
<div class="flex h-[600px] w-full items-center justify-center">
|
||||
<Fallback
|
||||
description="等待后端重构工作流后开发"
|
||||
status="coming-soon"
|
||||
title="等待开发"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
@@ -1,11 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
@@ -1,11 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
@@ -1,11 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
@@ -1,11 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
@@ -1,11 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const chartRef = ref<EchartsUIType>();
|
||||
const { renderEcharts } = useEcharts(chartRef);
|
||||
|
@@ -1,19 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { getPopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
tableCheckboxEvent,
|
||||
useVbenVxeGrid,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { downloadExcel } from '#/utils/file/download';
|
||||
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import { commonDownloadExcel } from '#/utils/file/download';
|
||||
|
||||
import { demoExport, demoList, demoRemove } from './api';
|
||||
import { columns, querySchema } from './data';
|
||||
@@ -61,10 +60,6 @@ const checked = ref(false);
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: tableCheckboxEvent(checked),
|
||||
checkboxAll: tableCheckboxEvent(checked),
|
||||
},
|
||||
});
|
||||
|
||||
const [DemoModal, modalApi] = useVbenModal({
|
||||
@@ -113,7 +108,7 @@ function handleMultiDelete() {
|
||||
<a-button
|
||||
v-access:code="['system:demo:export']"
|
||||
@click="
|
||||
downloadExcel(
|
||||
commonDownloadExcel(
|
||||
demoExport,
|
||||
'测试单数据',
|
||||
tableApi.formApi.form.values,
|
||||
@@ -123,7 +118,7 @@ function handleMultiDelete() {
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="!checked"
|
||||
:disabled="!vxeCheckboxChecked(tableApi)"
|
||||
danger
|
||||
type="primary"
|
||||
v-access:code="['system:demo:remove']"
|
||||
|
@@ -1,14 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { getPopupContainer, listToTree } from '@vben/utils';
|
||||
|
||||
import { Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
import { treeList, treeRemove } from './api';
|
||||
import { columns, querySchema } from './data';
|
||||
@@ -51,7 +54,6 @@ const gridOptions: VxeGridProps = {
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'id',
|
||||
},
|
||||
|
||||
|
@@ -1,66 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, Card, message, notification, Space } from 'ant-design-vue';
|
||||
|
||||
type NotificationType = 'error' | 'info' | 'success' | 'warning';
|
||||
|
||||
function info() {
|
||||
message.info('How many roads must a man walk down');
|
||||
}
|
||||
|
||||
function error() {
|
||||
message.error({
|
||||
content: 'Once upon a time you dressed so fine',
|
||||
duration: 2500,
|
||||
});
|
||||
}
|
||||
|
||||
function warning() {
|
||||
message.warning('How many roads must a man walk down');
|
||||
}
|
||||
function success() {
|
||||
message.success('Cause you walked hand in hand With another man in my place');
|
||||
}
|
||||
|
||||
function notify(type: NotificationType) {
|
||||
notification[type]({
|
||||
duration: 2500,
|
||||
message: '说点啥呢',
|
||||
type,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page
|
||||
description="支持多语言,主题功能集成切换等"
|
||||
title="Ant Design Vue组件使用演示"
|
||||
>
|
||||
<Card class="mb-5" title="按钮">
|
||||
<Space>
|
||||
<Button>Default</Button>
|
||||
<Button type="primary"> Primary </Button>
|
||||
<Button> Info </Button>
|
||||
<Button danger> Error </Button>
|
||||
</Space>
|
||||
</Card>
|
||||
<Card class="mb-5" title="Message">
|
||||
<Space>
|
||||
<Button @click="info"> 信息 </Button>
|
||||
<Button danger @click="error"> 错误 </Button>
|
||||
<Button @click="warning"> 警告 </Button>
|
||||
<Button @click="success"> 成功 </Button>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card class="mb-5" title="Notification">
|
||||
<Space>
|
||||
<Button @click="notify('info')"> 信息 </Button>
|
||||
<Button danger @click="notify('error')"> 错误 </Button>
|
||||
<Button @click="notify('warning')"> 警告 </Button>
|
||||
<Button @click="notify('success')"> 成功 </Button>
|
||||
</Space>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
@@ -1,20 +1,18 @@
|
||||
<script lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import { defineComponent, onActivated, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
export default defineComponent({
|
||||
components: { EchartsUI },
|
||||
props: {
|
||||
data: {
|
||||
default: () => [],
|
||||
type: Array,
|
||||
type: Array as PropType<{ name: string; value: string }[]>,
|
||||
},
|
||||
},
|
||||
setup(props, { expose }) {
|
||||
@@ -41,6 +39,7 @@ export default defineComponent({
|
||||
*/
|
||||
onActivated(() => resize(false));
|
||||
|
||||
type EChartsOption = Parameters<typeof renderEcharts>['0'];
|
||||
function setEchartsOption(data: any[]) {
|
||||
const option: EChartsOption = {
|
||||
series: [
|
||||
|
@@ -1,13 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||
|
||||
import { defineComponent, onActivated, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import {
|
||||
EchartsUI,
|
||||
type EchartsUIType,
|
||||
useEcharts,
|
||||
} from '@vben/plugins/echarts';
|
||||
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
|
||||
export default defineComponent({
|
||||
components: { EchartsUI },
|
||||
@@ -38,6 +34,13 @@ export default defineComponent({
|
||||
// 从其他页面切换回来会有一个奇怪的动画效果 需要调用resize
|
||||
onActivated(resize);
|
||||
|
||||
/**
|
||||
* 获取最近的十的幂次
|
||||
* 该函数用于寻找大于给定数字num的最近的10的幂次
|
||||
* 主要解决的问题是确定一个数附近较大的十的幂次,这在某些算法中很有用
|
||||
*
|
||||
* @param num {number} 输入的数字,用于寻找最近的十的幂次
|
||||
*/
|
||||
function getNearestPowerOfTen(num: number) {
|
||||
let power = 10;
|
||||
while (power <= num) {
|
||||
@@ -46,6 +49,7 @@ export default defineComponent({
|
||||
return power;
|
||||
}
|
||||
|
||||
type EChartsOption = Parameters<typeof renderEcharts>['0'];
|
||||
function setEchartsOption(value: string) {
|
||||
// x10
|
||||
const formattedValue = Math.floor(Number.parseFloat(value));
|
||||
|
@@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import type { RedisInfo } from '#/api/monitor/cache';
|
||||
import type { DescItem } from '#/components/description';
|
||||
|
||||
import { onMounted, type PropType, watch } from 'vue';
|
||||
import { onMounted, watch } from 'vue';
|
||||
|
||||
import {
|
||||
type DescItem,
|
||||
Description,
|
||||
useDescription,
|
||||
} from '#/components/description';
|
||||
import { Description, useDescription } from '#/components/description';
|
||||
|
||||
interface IRedisInfo extends RedisInfo {
|
||||
dbSize: string;
|
||||
|
10
apps/web-antd/src/views/monitor/cache/index.vue
vendored
10
apps/web-antd/src/views/monitor/cache/index.vue
vendored
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { RedisInfo } from '#/api/monitor/cache';
|
||||
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
@@ -6,13 +8,16 @@ import { CommandLineIcon, MemoryIcon, RedisIcon } from '@vben/icons';
|
||||
|
||||
import { Button, Card, Col, Row } from 'ant-design-vue';
|
||||
|
||||
import { redisCacheInfo, type RedisInfo } from '#/api/monitor/cache';
|
||||
import { redisCacheInfo } from '#/api/monitor/cache';
|
||||
|
||||
import { CommandChart, MemoryChart, RedisDescription } from './components';
|
||||
|
||||
const baseSpan = { lg: 12, md: 24, sm: 24, xl: 12, xs: 24 };
|
||||
|
||||
const chartData = reactive<{ command: any[]; memory: string }>({
|
||||
const chartData = reactive<{
|
||||
command: { name: string; value: string }[];
|
||||
memory: string;
|
||||
}>({
|
||||
command: [],
|
||||
memory: '0',
|
||||
});
|
||||
@@ -39,6 +44,7 @@ async function loadInfo() {
|
||||
chartData.memory = usedMemory;
|
||||
// 命令统计
|
||||
chartData.command = ret.commandStats;
|
||||
console.log(chartData.command);
|
||||
// redis信息
|
||||
redisInfo.value = { ...ret.info, dbSize: String(ret.dbSize) };
|
||||
} catch (error) {
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import type { VNode } from 'vue';
|
||||
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { DescItem } from '#/components/description';
|
||||
|
||||
import type { VNode } from 'vue';
|
||||
|
||||
import { DictEnum } from '@vben/constants';
|
||||
|
||||
import { type FormSchemaGetter } from '#/adapter/form';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import { renderBrowserIcon, renderDict, renderOsIcon } from '#/utils/render';
|
||||
|
||||
@@ -68,7 +68,9 @@ export const columns: VxeGridProps['columns'] = [
|
||||
field: 'os',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
// Windows 10 or Windows Server 2016 太长了 分割一下 详情依旧能看到详细的
|
||||
/**
|
||||
* Windows 10 or Windows Server 2016 太长了 分割一下 详情依旧能看到详细的
|
||||
*/
|
||||
let value = row.os;
|
||||
if (value) {
|
||||
const split = value.split(' or ');
|
||||
|
@@ -1,19 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { LoginLog } from '#/api/monitor/logininfo/model';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridDefines,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
|
||||
import {
|
||||
loginInfoClean,
|
||||
loginInfoExport,
|
||||
@@ -71,7 +69,6 @@ const gridOptions: VxeGridProps = {
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'infoId',
|
||||
},
|
||||
id: 'monitor-logininfo-index',
|
||||
@@ -82,9 +79,9 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
checkboxChange: (e: VxeGridDefines.CheckboxChangeEventParams) => {
|
||||
checkboxChange: (e) => {
|
||||
const records = e.$grid.getCheckboxRecords();
|
||||
canUnlock.value = records.length === 1 && records[0]?.status === '1';
|
||||
canUnlock.value = records.length === 1 && records[0]!.status === '1';
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -93,7 +90,7 @@ const [LoginInfoModal, modalApi] = useVbenModal({
|
||||
connectedComponent: loginInfoModal,
|
||||
});
|
||||
|
||||
function handlePreview(record: Recordable<any>) {
|
||||
function handlePreview(record: LoginLog) {
|
||||
modalApi.setData(record);
|
||||
modalApi.open();
|
||||
}
|
||||
@@ -107,14 +104,14 @@ function handleClear() {
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete(row: Recordable<any>) {
|
||||
await loginInfoRemove(row.infoId);
|
||||
async function handleDelete(row: LoginLog) {
|
||||
await loginInfoRemove([row.infoId]);
|
||||
await tableApi.query();
|
||||
}
|
||||
|
||||
function handleMultiDelete() {
|
||||
const rows = tableApi.grid.getCheckboxRecords();
|
||||
const ids = rows.map((row: any) => row.infoId);
|
||||
const ids = rows.map((row: LoginLog) => row.infoId);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
okType: 'danger',
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import type { VNode } from 'vue';
|
||||
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { type FormSchemaGetter } from '#/adapter/form';
|
||||
import { renderBrowserIcon, renderOsIcon } from '#/utils/render';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
|
@@ -1,12 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
|
||||
import { Page, type VbenFormProps } from '@vben/common-ui';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { OnlineUser } from '#/api/monitor/online/model';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid, type VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { forceLogout, onlineList } from '#/api/monitor/online';
|
||||
|
||||
import { columns, querySchema } from './data';
|
||||
@@ -22,24 +27,30 @@ const formOptions: VbenFormProps = {
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
||||
};
|
||||
|
||||
const onlineCount = ref(0);
|
||||
const gridOptions: VxeGridProps = {
|
||||
columns,
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await onlineList({
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
query: async (_, formValues = {}) => {
|
||||
const resp = await onlineList({
|
||||
...formValues,
|
||||
});
|
||||
onlineCount.value = resp.total;
|
||||
return resp;
|
||||
},
|
||||
},
|
||||
},
|
||||
scrollY: {
|
||||
enabled: true,
|
||||
gt: 0,
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'tokenId',
|
||||
},
|
||||
id: 'monitor-online-index',
|
||||
@@ -47,7 +58,7 @@ const gridOptions: VxeGridProps = {
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions });
|
||||
|
||||
async function handleForceOffline(row: Recordable<any>) {
|
||||
async function handleForceOffline(row: OnlineUser) {
|
||||
await forceLogout(row.tokenId);
|
||||
await tableApi.query();
|
||||
}
|
||||
@@ -55,7 +66,16 @@ async function handleForceOffline(row: Recordable<any>) {
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable table-title="在线用户列表">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<div class="mr-1 pl-1 text-[1rem]">
|
||||
<div>
|
||||
在线用户列表 (共
|
||||
<span class="text-primary font-bold">{{ onlineCount }}</span>
|
||||
人在线)
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Popconfirm
|
||||
:get-popup-container="getVxePopupContainer"
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { DescItem } from '#/components/description';
|
||||
|
||||
@@ -5,7 +6,6 @@ import { DictEnum } from '@vben/constants';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
import { type FormSchemaGetter } from '#/adapter/form';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import {
|
||||
renderDict,
|
||||
|
@@ -1,18 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { VbenFormProps } from '@vben/common-ui';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { PageQuery } from '#/api/common';
|
||||
import type { OperationLog } from '#/api/monitor/operlog/model';
|
||||
|
||||
import { Page, useVbenDrawer, type VbenFormProps } from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { Modal, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
addSortParams,
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
vxeSortEvent,
|
||||
} from '#/adapter/vxe-table';
|
||||
import {
|
||||
operLogClean,
|
||||
@@ -60,18 +61,19 @@ const gridOptions: VxeGridProps<OperationLog> = {
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues = {}) => {
|
||||
const params: any = {
|
||||
query: async ({ page, sorts }, formValues = {}) => {
|
||||
const params: PageQuery = {
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
};
|
||||
// 添加排序参数
|
||||
addSortParams(params, sorts);
|
||||
return await operLogList(params);
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
keyField: 'operId',
|
||||
},
|
||||
sortConfig: {
|
||||
@@ -87,7 +89,8 @@ const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
sortChange: (sortParams) => vxeSortEvent(tableApi, sortParams),
|
||||
// 排序 重新请求接口
|
||||
sortChange: () => tableApi.query(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -99,7 +102,7 @@ const [OperationPreviewDrawer, drawerApi] = useVbenDrawer({
|
||||
* 预览
|
||||
* @param record 操作日志记录
|
||||
*/
|
||||
function handlePreview(record: Recordable<any>) {
|
||||
function handlePreview(record: OperationLog) {
|
||||
drawerApi.setData({ record });
|
||||
drawerApi.open();
|
||||
}
|
||||
@@ -120,7 +123,7 @@ function handleClear() {
|
||||
*/
|
||||
async function handleDelete() {
|
||||
const rows = tableApi.grid.getCheckboxRecords();
|
||||
const ids = rows.map((row: any) => row.operId);
|
||||
const ids = rows.map((row: OperationLog) => row.operId);
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
okType: 'danger',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user