--添加测试模块
This commit is contained in:
@ -10,7 +10,7 @@ VITE_PUBLIC_PATH = /
|
||||
# 如果接口地址匹配到,则会转发到http://localhost:3000,防止本地出现跨域问题
|
||||
# 可以有多个,注意多个不能换行,否则代理将会失效
|
||||
#VITE_PROXY = [["/api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
|
||||
VITE_PROXY=[["/api","http://back.ges.bjgastx.com"]]
|
||||
VITE_PROXY=[["/api","http://127.0.0.1:8090"]]
|
||||
|
||||
# 是否删除Console.log
|
||||
VITE_DROP_CONSOLE = false
|
||||
|
||||
@ -12,6 +12,6 @@ VOLUME ["/etc/nginx/nginx.conf", "/usr/share/nginx/html"]
|
||||
|
||||
CMD ["nginx","-g","daemon off;"]
|
||||
|
||||
# docker build -t itc-web-arm64:1.0.0 .
|
||||
# docker build -f Dockerfile-arm64 -t itc-web-arm64:1.0.0 .
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "itc-framework",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"author": {
|
||||
"name": "ITC",
|
||||
"email": "gdyditc@geg.com.cn",
|
||||
|
||||
11
src/App.vue
11
src/App.vue
@ -55,7 +55,7 @@
|
||||
return '关闭提示';
|
||||
};
|
||||
|
||||
if(!getAppEnvConfig().VITE_GLOB_CLOSE_ALERT_DISABLED){
|
||||
if(!import.meta.env.VITE_GLOB_CLOSE_ALERT_DISABLED){
|
||||
window.addEventListener('beforeunload', beforeUnloadHandler);
|
||||
}
|
||||
|
||||
@ -79,3 +79,12 @@
|
||||
line-height: 35px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.vben-default-layout {
|
||||
height: 100%;
|
||||
}
|
||||
.vben-layout-content {
|
||||
flex: 1!important;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
110
src/api/system/Testfrom3/index.ts
Normal file
110
src/api/system/Testfrom3/index.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { Testfrom3PageModel, Testfrom3PageParams, Testfrom3PageResult } from './model/Testfrom3Model';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { ErrorMessageMode } from '/#/axios';
|
||||
|
||||
enum Api {
|
||||
Page = '/system/testfrom3/page',
|
||||
List = '/system/testfrom3/list',
|
||||
Info = '/system/testfrom3/info',
|
||||
Testfrom3 = '/system/testfrom3',
|
||||
|
||||
|
||||
Export = '/system/testfrom3/export',
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 查询Testfrom3分页列表
|
||||
*/
|
||||
export async function getTestfrom3Page(params: Testfrom3PageParams, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get<Testfrom3PageResult>(
|
||||
{
|
||||
url: Api.Page,
|
||||
params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取Testfrom3信息
|
||||
*/
|
||||
export async function getTestfrom3(id: String, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get<Testfrom3PageModel>(
|
||||
{
|
||||
url: Api.Info,
|
||||
params: { id },
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 新增Testfrom3
|
||||
*/
|
||||
export async function addTestfrom3(testfrom3: Recordable, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post<boolean>(
|
||||
{
|
||||
url: Api.Testfrom3,
|
||||
params: testfrom3,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 更新Testfrom3
|
||||
*/
|
||||
export async function updateTestfrom3(testfrom3: Recordable, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.put<boolean>(
|
||||
{
|
||||
url: Api.Testfrom3,
|
||||
params: testfrom3,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除Testfrom3(批量删除)
|
||||
*/
|
||||
export async function deleteTestfrom3(ids: string[], mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.delete<boolean>(
|
||||
{
|
||||
url: Api.Testfrom3,
|
||||
data: ids,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @description: 导出Testfrom3
|
||||
*/
|
||||
export async function exportTestfrom3(
|
||||
params?: object,
|
||||
mode: ErrorMessageMode = 'modal'
|
||||
) {
|
||||
return defHttp.download(
|
||||
{
|
||||
url: Api.Export,
|
||||
method: 'GET',
|
||||
params,
|
||||
responseType: 'blob',
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
32
src/api/system/Testfrom3/model/Testfrom3Model.ts
Normal file
32
src/api/system/Testfrom3/model/Testfrom3Model.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
|
||||
|
||||
/**
|
||||
* @description: Testfrom3分页参数 模型
|
||||
*/
|
||||
export interface Testfrom3PageParams extends BasicPageParams {
|
||||
danXingWenBen5518: string;
|
||||
|
||||
danXingWenBen5841: string;
|
||||
|
||||
jiShuZuJian6835: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Testfrom3分页返回值模型
|
||||
*/
|
||||
export interface Testfrom3PageModel {
|
||||
id: string;
|
||||
|
||||
danXingWenBen5518: string;
|
||||
|
||||
danXingWenBen5841: string;
|
||||
|
||||
jiShuZuJian6835: string;
|
||||
}
|
||||
|
||||
0;
|
||||
|
||||
/**
|
||||
* @description: Testfrom3分页返回值结构
|
||||
*/
|
||||
export type Testfrom3PageResult = BasicFetchResult<Testfrom3PageModel>;
|
||||
@ -3,9 +3,11 @@ import { ErrorMessageMode } from '/#/axios';
|
||||
|
||||
enum Api {
|
||||
ExportDatas= '/system/dataMigration/exportDatas',
|
||||
ImportDatas= '/system/dataMigration/importDatas',
|
||||
DownloadDatas='/system/dataMigration/downloadDatas',
|
||||
LogList='/system/dataMigration/logList',
|
||||
LogDetails='/system/dataMigration/logDetails',
|
||||
GetUploadedData='/system/dataMigration/getUploadedData'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,6 +26,38 @@ export async function exportDatas(params, mode: ErrorMessageMode = 'modal') {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 系统配置迁移-导入资源
|
||||
*/
|
||||
export async function importDatas(params, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post(
|
||||
{
|
||||
url: Api.ImportDatas+"/"+params.dirName,
|
||||
data:params.data,
|
||||
timeout: 5 * 60 * 1000,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取上传的数据
|
||||
*/
|
||||
export async function getUploadedData(params,mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get<[]>(
|
||||
{
|
||||
url: Api.GetUploadedData,
|
||||
params
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description: 根据uuid(目录名称)下载数据
|
||||
*/
|
||||
|
||||
@ -1,13 +1,27 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
import { ErrorMessageMode } from '/#/axios';
|
||||
import { FilePageListParams, FilePageListSearchModel, FilePageListResultModel } from './model';
|
||||
import { FilePageListParams, FilePageListSearchModel, FilePageListResultModel, ZipFilesModel, FileModel } from './model';
|
||||
enum Api {
|
||||
File = '/system/file',
|
||||
Info = '/system/file/info',
|
||||
InfoByDownloadUrl = '/system/file/info-byDownloadUrl',
|
||||
List = '/system/file',
|
||||
Page = '/system/file/page',
|
||||
DeleteFile = '/system/file/delete-single',
|
||||
ZipFiles = '/system/file/package-files',
|
||||
}
|
||||
|
||||
export async function getInfoByDownloadUrl(params: {id: string}, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get<FileModel>(
|
||||
{
|
||||
url: Api.InfoByDownloadUrl,
|
||||
params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,6 +87,18 @@ export async function getFileList(params: FilePageListParams, mode: ErrorMessage
|
||||
);
|
||||
}
|
||||
|
||||
export async function getZipFiles(params, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get<ZipFilesModel>(
|
||||
{
|
||||
url: Api.ZipFiles,
|
||||
params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function getAppToken(params, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get(
|
||||
{
|
||||
|
||||
@ -34,6 +34,17 @@ export interface FileModel {
|
||||
fileType: string;
|
||||
downloadCount: number;
|
||||
remark: string;
|
||||
fileUrlFixed: string; //加签后的url
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传打包 返回模型
|
||||
*/
|
||||
export interface ZipFilesModel {
|
||||
url: string;
|
||||
type: string;
|
||||
msg: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@ export interface LoginParams {
|
||||
password: string;
|
||||
tenantCode: string;
|
||||
deviceType?: number;
|
||||
captchaCode: string;
|
||||
captchaCode?: string;
|
||||
}
|
||||
|
||||
export interface RoleInfo {
|
||||
|
||||
@ -7,8 +7,6 @@ enum Api {
|
||||
List = '/system/systemConfig/list',
|
||||
Info = '/system/systemConfig/info',
|
||||
XjrSystemConfig = '/system/systemConfig',
|
||||
|
||||
|
||||
Export = '/system/systemConfig/export',
|
||||
}
|
||||
|
||||
@ -108,3 +106,4 @@ export async function exportXjrSystemConfig(
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { XjrNoticePageModel, XjrNoticePageParams, XjrNoticePageResult } from './model/SystemNoticeModel';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { ErrorMessageMode } from '/#/axios';
|
||||
import { BasicPageParams } from "/@/api/model/baseModel";
|
||||
|
||||
enum Api {
|
||||
Page = '/system/systemNotice/page',
|
||||
List = '/system/systemNotice/list',
|
||||
Info = '/system/systemNotice/info',
|
||||
XjrNotice = '/system/systemNotice',
|
||||
|
||||
|
||||
QueryLoginUserNotices = '/system/systemNotice/queryLoginUserNotices',
|
||||
SetNoticeUserIsRead = '/system/systemNotice/setNoticeUserIsRead'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,3 +86,35 @@ export async function deleteXjrNotice(ids: string[], mode: ErrorMessageMode = 'm
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询登录用户系统通知
|
||||
* @param params
|
||||
* @param mode
|
||||
*/
|
||||
export async function queryLoginUserNotices(params: BasicPageParams, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post(
|
||||
{
|
||||
url: Api.QueryLoginUserNotices,
|
||||
params: params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
showLoading: false
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户消息为已读状态
|
||||
* @param ids
|
||||
* @param mode
|
||||
*/
|
||||
export async function setSystemNoticeRead(ids, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post(
|
||||
{ url: Api.SetNoticeUserIsRead, params: {noticeIds: ids} },
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ enum Api {
|
||||
Status = '/system/tenant/status',
|
||||
Authorize = '/system/tenant/authorize',
|
||||
Switch = '/system/switch-tenant',
|
||||
Export = '/system/tenant/export',
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,3 +192,25 @@ export async function changeTenant(tenantCode: String, mode: ErrorMessageMode =
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:导出租户
|
||||
*/
|
||||
export async function exportTenant(
|
||||
params?: object,
|
||||
mode: ErrorMessageMode = 'modal'
|
||||
) {
|
||||
return defHttp.download(
|
||||
{
|
||||
url: Api.Export,
|
||||
params:{
|
||||
id:params.id
|
||||
},
|
||||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -30,6 +30,8 @@ enum Api {
|
||||
Online = '/organization/user/online-users/page',
|
||||
OffOnline = '/organization/user/offline-users',
|
||||
Profile = '/organization/user/get-user-organization-info',
|
||||
GetUserNamePrefix = '/organization/user/getUserNamePrefix',
|
||||
getUserByDepartTree = '/organization/user/get-user-department-tree'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,3 +309,33 @@ export async function getUserProfile(mode: ErrorMessageMode = 'modal') {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取XjrSystemConfig用户名前缀
|
||||
*/
|
||||
export async function getUserNamePrefix(mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.get(
|
||||
{
|
||||
url: Api.GetUserNamePrefix
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode
|
||||
}
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @description: 将用户转化为departTree形式
|
||||
*/
|
||||
export async function getUserByDepartTree(ids, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post(
|
||||
{
|
||||
url: Api.getUserByDepartTree,
|
||||
params: {
|
||||
ids: ids
|
||||
},
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -38,6 +38,8 @@ enum Api {
|
||||
Withdraw = '/workflow/execute/my-task/withdraw',
|
||||
SetAssignee = '/workflow/execute/set-assignee',
|
||||
withdrawNode = '/workflow/execute/withdraw-node',
|
||||
AddTask = '/workflow/adminOperation/add-task',
|
||||
SubTask = '/workflow/adminOperation/sub-task',
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,6 +274,24 @@ export async function postSetSign(
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @description: 加签减签
|
||||
*/
|
||||
|
||||
export async function postSetSignV2(
|
||||
data,
|
||||
mode: ErrorMessageMode = 'modal',
|
||||
) {
|
||||
return defHttp.post<SubmittingProcessData>(
|
||||
{
|
||||
url: Api.SetSign,
|
||||
params: data,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @description: 转办
|
||||
*/
|
||||
@ -439,3 +459,33 @@ export async function getDrawNode(processId, mode: ErrorMessageMode = 'modal') {
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @description: 加签
|
||||
*/
|
||||
|
||||
export async function postAddTask(params, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post<boolean>(
|
||||
{
|
||||
url: Api.AddTask,
|
||||
params: params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @description: 减签
|
||||
*/
|
||||
|
||||
export async function postSubTask(params, mode: ErrorMessageMode = 'modal') {
|
||||
return defHttp.post<boolean>(
|
||||
{
|
||||
url: Api.SubTask,
|
||||
params: params,
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
4
src/components/CustomComponent/index.ts
Normal file
4
src/components/CustomComponent/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
import customComponent from './src/CustomComponent.vue';
|
||||
export const CustomComponent = withInstall(customComponent);
|
||||
|
||||
60
src/components/CustomComponent/src/CustomComponent.vue
Normal file
60
src/components/CustomComponent/src/CustomComponent.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<component :is="component" v-model:value="modelValue" v-bind="props"/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch, defineAsyncComponent, onMounted} from 'vue';
|
||||
const props = defineProps({
|
||||
defaultValue: {
|
||||
type: Object
|
||||
},
|
||||
value: {
|
||||
type: Object
|
||||
},
|
||||
component: Object,
|
||||
formData: Object, //主表
|
||||
row: Object, //子表
|
||||
});
|
||||
const modelValue = ref(null);
|
||||
const component = ref(
|
||||
defineAsyncComponent({
|
||||
loader: () => props.component
|
||||
})
|
||||
);
|
||||
const emit = defineEmits(['update:value', 'change', 'blur']);
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
watch(
|
||||
() => props.defaultValue,
|
||||
(val) => {
|
||||
if(val){
|
||||
emit('update:value', val);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
modelValue.value = val;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
modelValue,
|
||||
(val) => {
|
||||
emit('update:value', val);
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
@ -37,7 +37,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('绑定字段')" required>
|
||||
<a-select v-model:value="item.bindField" :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select v-model:value="item.bindField" showSearch :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select-option v-for="(field, idx) in tableField" :value="field.name" :key="idx" />
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="(data.type == 'input' && !data.options.isSave) || (!noHaveField.includes(data.type) && data.type !== 'input')" :label="t('绑定字段')">
|
||||
<a-select v-model:value="data.bindField" :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select v-model:value="data.bindField" showSearch :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select-option v-for="(field, idx) in fieldsInfo" :key="idx" :value="field.name">
|
||||
{{ field.name }}
|
||||
<span>
|
||||
@ -467,6 +467,15 @@
|
||||
<a-form-item v-if="hasKey('span') && (!data.isSubFormChild || !data.isSingleFormChild)" label="标签宽度">
|
||||
<a-input-number v-model:value="data.options.span" :max="24" :min="0" addonAfter="/ 24" @change="handleSpanChange" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="hasKey('mode')" label="模式选择">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="data.options.mode"
|
||||
>
|
||||
<a-select-option value="">单选</a-select-option>
|
||||
<a-select-option value="multiple">多选</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="hasKey('range')" :label="t('双滑块模式')">
|
||||
<a-switch v-model:checked="data.options.range" @change="handleSliderModeChange" />
|
||||
|
||||
@ -247,6 +247,7 @@ export const advanceComponents = [
|
||||
showSearch: false,
|
||||
clearable: false,
|
||||
disabled: false,
|
||||
mode: 'multiple',
|
||||
staticOptions: [
|
||||
{
|
||||
key: 1,
|
||||
|
||||
@ -24,6 +24,7 @@ import SelectDepartment from './components/SelectDepartment.vue';
|
||||
import SelectDepartmentV2 from './components/SelectDepartmentV2.vue';
|
||||
import SelectUser from './components/SelectUser.vue';
|
||||
import SelectUserV2 from './components/SelectUserV2.vue';
|
||||
import SelectUserShowTree from './components/SelectUserShowTree.vue';
|
||||
import CommonInfo from './components/CommonInfo.vue';
|
||||
import SelectArea from './components/SelectArea.vue';
|
||||
import AutoCodeRule from './components/AutoCodeRule.vue';
|
||||
@ -66,6 +67,7 @@ import { XjrDatePicker } from '/@/components/DatePicker';
|
||||
import { Slider } from '/@/components/Slider';
|
||||
import { CodeTextArea } from '/@/components/Input';
|
||||
import { OneForOne } from '/@/components/OneForOne';
|
||||
import { CustomComponent } from '/@/components/CustomComponent';
|
||||
import SubForm from './components/SubFormV2.vue';
|
||||
import ErpApply from './components/ErpApply.vue';
|
||||
import ErpUpload from './components/ErpUpload.vue';
|
||||
@ -102,6 +104,7 @@ componentMap.set('Rate', Rate);
|
||||
componentMap.set('DeptTree', SelectDepartment);
|
||||
componentMap.set('Dept', SelectDepartmentV2);
|
||||
componentMap.set('User', SelectUserV2);
|
||||
componentMap.set('UserTree', SelectUserShowTree);
|
||||
componentMap.set('Info', CommonInfo);
|
||||
componentMap.set('Area', SelectArea);
|
||||
componentMap.set('SubForm', SubForm);
|
||||
@ -145,6 +148,7 @@ componentMap.set('ErpApply', ErpApply);
|
||||
componentMap.set('ErpUpload', ErpUpload);
|
||||
componentMap.set('ErpCheck', ErpCheck);
|
||||
componentMap.set('AutoComplete', AutoComplete);
|
||||
componentMap.set('CustomComponent', CustomComponent);
|
||||
|
||||
export function add(compName: ComponentType, component: Component) {
|
||||
componentMap.set(compName, component);
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="field-readonly">
|
||||
<div :class="isWordWrap ? '' : 'field-readonly'" :title="fieldValue">
|
||||
<div v-if="schema.component === 'RichTextEditor'" v-html="htmlValue"> </div>
|
||||
{{ fieldValue }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: String,
|
||||
@ -17,6 +17,14 @@
|
||||
const textComponents = ['Input', 'AutoCodeRule', 'DatePicker', 'TimePicker', 'Text', 'InputTextArea', 'InputNumber'];
|
||||
const fieldValue = ref(genFieldValue(props.value));
|
||||
const htmlValue = ref(getHtmlValue(props.value));
|
||||
const isWordWrap = computed(() => {
|
||||
const schema = props.schema;
|
||||
const { componentProps } = schema;
|
||||
if (componentProps?.wordWrap) {
|
||||
return componentProps.wordWrap;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
|
||||
function parseRangeVal(val, component) {
|
||||
if (component !== 'RangePicker' || !val) {
|
||||
@ -75,8 +83,10 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: pre-wrap;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="depart-select" style="width: 100%" @click="show">
|
||||
<a-input v-model:value="departNames" :bordered="bordered" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
|
||||
<div v-if="disabled && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="departNames">
|
||||
{{departNames}}
|
||||
</div>
|
||||
<a-input v-model:value="departNames" v-if="disabledShowBorder || !disabled" :bordered="bordered" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
|
||||
<template v-if="prefix" #prefix>
|
||||
<Icon :icon="prefix" />
|
||||
</template>
|
||||
@ -81,7 +84,16 @@
|
||||
justCompany: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
wordWrap:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledShowBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formData:Object
|
||||
});
|
||||
const selectedNodes = ref([]);
|
||||
const loading = ref(true);
|
||||
@ -181,7 +193,11 @@
|
||||
visible.value = true;
|
||||
loading.value = true;
|
||||
if(props.defaultDeptField) {
|
||||
defaultDepts.value = props.row[props.defaultDeptField]
|
||||
if(props.row) {
|
||||
defaultDepts.value = props.row[props.defaultDeptField];
|
||||
}else{
|
||||
defaultDepts.value = props.formData[props.defaultDeptField];
|
||||
}
|
||||
}
|
||||
if (props.value) {
|
||||
if(props.isArray && !props.value.length) {
|
||||
@ -227,6 +243,11 @@
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
.choose-dep-box {
|
||||
display: flex;
|
||||
height: 95%;
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<div class="user-select-list">
|
||||
<div class="user-select-item" v-for="item in data" @click="selectItem(item)">
|
||||
<template v-for="item in data">
|
||||
<div class="user-select-item" @click="selectItem(item)" :class="isDisabledItem(item) ? 'disabled-item' : ''">
|
||||
<div class="user-select-item-left" v-if="showDep && item.departmentPathName">
|
||||
{{ `${item.name}(${item.departmentPathName})` }}
|
||||
</div>
|
||||
<div class="user-select-item-left" v-else>
|
||||
{{ `${item.name}` }}
|
||||
</div>
|
||||
<div class="user-select-item-right">
|
||||
<div class="user-select-item-right" v-if="!isDisabledItem(item)">
|
||||
<div class="select-circle" :class="item.selected ? 'selected' : ''" v-if="!viewList">
|
||||
<check-outlined v-if="item.selected" />
|
||||
</div>
|
||||
@ -16,6 +17,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="empty-box" v-if="!data.length">
|
||||
<a-empty :image="simpleImage">
|
||||
<template #description>
|
||||
@ -58,8 +61,18 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledSelectList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
})
|
||||
function isDisabledItem(item) {
|
||||
return props.disabledSelectList.includes(item.id)
|
||||
}
|
||||
function selectItem(item) {
|
||||
if(isDisabledItem(item)) {
|
||||
return
|
||||
}
|
||||
emits('selectId', item.id)
|
||||
}
|
||||
function delItem(item) {
|
||||
@ -83,6 +96,9 @@ function delItem(item) {
|
||||
// margin: auto;
|
||||
}
|
||||
}
|
||||
.disabled-item {
|
||||
background: #e3e3e3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -102,11 +118,14 @@ function delItem(item) {
|
||||
overflow: hidden; //超出隐藏
|
||||
text-overflow: ellipsis; //文本超出时显示省略号
|
||||
white-space: nowrap; //设置文本不换行
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-select-item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
|
||||
.select-circle {
|
||||
width: 16px;
|
||||
|
||||
143
src/components/Form/src/components/SelectUserShowTree.vue
Normal file
143
src/components/Form/src/components/SelectUserShowTree.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="selectUser-show-tree">
|
||||
<div class="content">
|
||||
<div class="user-list" v-if="treeData.length" :style="{height: height, width: width}">
|
||||
<a-tree v-model:expandedKeys="expandedKeys" :treeData="treeData" :key="treeKey" :fieldNames="{
|
||||
children: 'children',
|
||||
title: 'name',
|
||||
key: 'id'
|
||||
}">
|
||||
</a-tree>
|
||||
</div>
|
||||
<a-button style="margin-top: 10px" @click="showDialog" v-if="!disabled">选择用户</a-button>
|
||||
</div>
|
||||
<SelectUserV2
|
||||
ref="selectUser"
|
||||
v-model:value="selected"
|
||||
v-bind="filteredProps"
|
||||
justDialog
|
||||
@change="changeSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import SelectUserV2 from './SelectUserV2.vue'
|
||||
import { getUserByDepartTree } from '/@/api/system/user';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const emits = defineEmits(['update:value', 'change']);
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
prefix: String,
|
||||
suffix: String,
|
||||
placeholder: String,
|
||||
readonly: Boolean,
|
||||
disabled: Boolean,
|
||||
size: String,
|
||||
justDialog: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
row: Object, // 行数据,在表格里用的到
|
||||
sepTextField: String, // 将文本表示存储在独立字段
|
||||
onlyUserCompany: { // 仅在登录用户公司范围内筛选
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
buttonShow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultDeptField: { // 默认选择公司范围key值
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
justCompany: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isOnlyCurrentDepartment: { // 仅当前部门,无下级部门
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
// 过滤掉value属性,避免冲突
|
||||
const filteredProps = computed(() => {
|
||||
const { value, ...rest } = props;
|
||||
return rest;
|
||||
});
|
||||
const treeKey = ref(1)
|
||||
|
||||
const selected = ref(props.value || [])
|
||||
const selectUser = ref()
|
||||
|
||||
const expandedKeys = ref([])
|
||||
|
||||
const treeData = ref([])
|
||||
|
||||
const changeSelect = async (ids, options) => {
|
||||
emits('update:value', ids)
|
||||
emits('change', ids, options)
|
||||
}
|
||||
|
||||
async function getTreeData(list) {
|
||||
let tree = await getUserByDepartTree(list)
|
||||
expandedKeys.value = []
|
||||
setTreeData(tree)
|
||||
treeData.value = tree
|
||||
treeKey.value = Date.now()
|
||||
}
|
||||
|
||||
const setTreeData = (list, indexList = []) => {
|
||||
list.forEach((item, index) => {
|
||||
let list = cloneDeep(indexList)
|
||||
list.push(index)
|
||||
item.id = list.join('-')
|
||||
expandedKeys.value.push(item.id)
|
||||
if(item.children && item.children.length){
|
||||
setTreeData(item.children, cloneDeep(list))
|
||||
} else if (item.users.length) {
|
||||
item.children = item.users
|
||||
}
|
||||
})
|
||||
}
|
||||
const showDialog = () => {
|
||||
selectUser.value.show()
|
||||
}
|
||||
// 监听props.value变化
|
||||
watch(() => props.value, (newVal) => {
|
||||
selected.value = newVal
|
||||
if(selected.value) {
|
||||
getTreeData(selected.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.selectUser-show-tree {
|
||||
.content{
|
||||
.user-list {
|
||||
overflow: auto;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div :class="{ disabled }" class="form-select-user" @click="show">
|
||||
<a-button type="primary" v-if="buttonShow">{{ t('添加人员') }}</a-button>
|
||||
<a-input v-model:value="userNames" v-if="!buttonShow" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
|
||||
<a-button type="primary" v-if="buttonShow && !justDialog">{{ t('添加人员') }}</a-button>
|
||||
<div v-if="disabled && !buttonShow && !justDialog && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="userNames">
|
||||
{{userNames}}
|
||||
</div>
|
||||
<a-input v-model:value="userNames" v-if="!buttonShow && !justDialog && (disabledShowBorder || !disabled)" :placeholder="placeholder" :size="size" readonly>
|
||||
<template v-if="prefix" #prefix>
|
||||
<Icon :icon="prefix" />
|
||||
</template>
|
||||
@ -9,7 +12,7 @@
|
||||
<Icon :icon="suffix" />
|
||||
</template>
|
||||
</a-input>
|
||||
<ModalPanel :title="t('选择人员')" :visible="visible" :width="900" class="select-user-model" @close="close" @submit="submit">
|
||||
<ModalPanel :title="t('选择人员')" :visible="visible" :width="900" class="select-user-model" @close="close" @submit="submit" :confirmLoading="confirmLoading">
|
||||
<div class="select-user">
|
||||
<div class="select-user-left">
|
||||
<a-tabs v-model:activeKey="activeKey" :tabBarGutter="20">
|
||||
@ -29,20 +32,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-select-box">
|
||||
<div class="select-depart-all" @click="selectAll">
|
||||
<div class="select-depart-all" @click="selectAll" v-if="multiple">
|
||||
<span>全选</span>
|
||||
<div class="select-circle" :class="departAllSelected ? 'selected' : ''" v-if="!viewList">
|
||||
<check-outlined v-if="departAllSelected" />
|
||||
</div>
|
||||
</div>
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchDepartMemberList" emptyDescription="暂无人员" @selectId="changeDepMemberSelect"></SelectUserListV2>
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchDepartMemberList" emptyDescription="暂无人员" @selectId="changeDepMemberSelect" :disabledSelectList="disabledSelectList"></SelectUserListV2>
|
||||
<div v-if="false" class="user-select-pagination">
|
||||
<a-pagination v-model:current="searchDepartMemberParams.limit" :pageSize="searchDepartMemberParams.size" :total="searchDepartMemberTotal" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="activeKey === 'allPerson'" class="all-user-select-box">
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchAllMemberList" @selectId="changeMemberSelect" show-dep></SelectUserListV2>
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchAllMemberList" @selectId="changeMemberSelect" show-dep :disabledSelectList="disabledSelectList"></SelectUserListV2>
|
||||
<div v-if="searchAllMemberTotal > 25" class="all-user-select-pagination">
|
||||
<a-form-item label="" name="pagination">
|
||||
<a-pagination
|
||||
@ -64,14 +67,14 @@
|
||||
<!-- <div class="selected-user-title sub-title">
|
||||
已选列表
|
||||
</div> -->
|
||||
<SelectUserListV2 :data="selectedMemberList" canDel emptyDescription="暂无已选择人员,<br> 请从左侧添加人员" viewList @delId="delMember"></SelectUserListV2>
|
||||
<SelectUserListV2 :data="selectedMemberList" canDel emptyDescription="暂无已选择人员,<br> 请从左侧添加人员" viewList @delId="delMember" :disabledSelectList="disabledSelectList"></SelectUserListV2>
|
||||
</div>
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { watch, ref } from 'vue';
|
||||
import { watch, ref, defineExpose } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ModalPanel } from '/@/components/ModalPanel/index';
|
||||
@ -104,6 +107,10 @@
|
||||
readonly: Boolean,
|
||||
disabled: Boolean,
|
||||
size: String,
|
||||
justDialog: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@ -129,6 +136,26 @@
|
||||
isOnlyCurrentDepartment: { // 仅当前部门,无下级部门
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
submitClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabledShowBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
wordWrap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
lastSelectedDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
let timeoutId = null;
|
||||
@ -136,6 +163,7 @@
|
||||
const searchPlaceholder = '请输入姓名或工号搜索';
|
||||
const defaultDepts = ref('')
|
||||
const departAllSelected = ref(false)
|
||||
const disabledSelectList = ref([])
|
||||
if(props.onlyUserCompany) {
|
||||
const userStore = useUserStore();
|
||||
const userInfo = userStore.getUserInfo;
|
||||
@ -180,6 +208,9 @@
|
||||
watch(
|
||||
props,
|
||||
async () => {
|
||||
if(props.lastSelectedDisabled) {
|
||||
disabledSelectList.value = props.value.split(',')
|
||||
}
|
||||
if (props.value && !valChanged.value && props.sepTextField) {
|
||||
const idArr = props.value.split(',');
|
||||
let valStr = props.row[camelCaseString(props.sepTextField)];
|
||||
@ -196,7 +227,7 @@
|
||||
resetMemberList = cloneDeep(initValue);
|
||||
return;
|
||||
}
|
||||
if (props.value && !resetMemberList.length) {
|
||||
if (props.value && !Array.isArray(props.value) && !resetMemberList.length) {
|
||||
const list = await getUserMulti(props.value);
|
||||
selectedMemberList.value = list;
|
||||
resetMemberList = cloneDeep(list);
|
||||
@ -282,11 +313,17 @@
|
||||
async function getUserList(params) {
|
||||
return await getUserPageListNew(params);
|
||||
}
|
||||
function isDisabledItem(item) {
|
||||
return disabledSelectList.value.includes(item.id)
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
departAllSelected.value = !departAllSelected.value
|
||||
if(departAllSelected.value) {
|
||||
searchDepartMemberList.value.forEach(item => {
|
||||
if(isDisabledItem(item)) {
|
||||
return
|
||||
}
|
||||
if(!item.selected) {
|
||||
selectedMemberList.value.push(item)
|
||||
}
|
||||
@ -294,6 +331,9 @@
|
||||
})
|
||||
} else {
|
||||
searchDepartMemberList.value.forEach(item => {
|
||||
if(isDisabledItem(item)) {
|
||||
return
|
||||
}
|
||||
if(item.selected) {
|
||||
selectedMemberList.value = selectedMemberList.value.filter(m => m.id !== item.id)
|
||||
}
|
||||
@ -380,8 +420,10 @@
|
||||
emits('selectedId', ids);
|
||||
emits('change', ids, selectedMemberList.value);
|
||||
valChanged.value = false;
|
||||
if(props.submitClose) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
selectedMemberList.value = cloneDeep(resetMemberList);
|
||||
@ -390,6 +432,10 @@
|
||||
searchDepartMemberList.value = [];
|
||||
searchAllMemberList.value = [];
|
||||
}
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
.select-user-model {
|
||||
@ -407,6 +453,11 @@
|
||||
justify-content: space-between;
|
||||
color: rgba(144, 147, 153, 0.7);
|
||||
}
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
|
||||
.select-user {
|
||||
display: flex;
|
||||
|
||||
@ -52,24 +52,35 @@
|
||||
|
||||
<template v-else-if="column.key !== 'index'">
|
||||
<component
|
||||
v-show="column.width!==0"
|
||||
:is="componentMap.get(column.componentType)"
|
||||
:key="column.dataIndex + record['_key_']"
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:title="readonlySupport(column.componentType) ? record[column.dataIndex] : ''"
|
||||
:bordered="showComponentBorder"
|
||||
:index="index"
|
||||
:mainKey="mainKey"
|
||||
:row="record"
|
||||
v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)"
|
||||
v-bind="{...getComponentsProps(column.componentProps, column.dataIndex, record, index), placeholder: getComponentsProps(column.componentProps, column.dataIndex, record, index).disabled ? '' : getComponentsProps(column.componentProps, column.dataIndex, record, index).placeholder}"
|
||||
@blur="onFieldBlur(column, record, index)"
|
||||
@change="onFieldChange(column, record, index)"
|
||||
/>
|
||||
</template>
|
||||
</FormItem>
|
||||
</template>
|
||||
<template v-if="column.key === 'action' && !disabled">
|
||||
<template v-if="column.key === 'action'">
|
||||
<template v-if="column.actions?.length > 0">
|
||||
<div class="tag-wrapper">
|
||||
<template v-for="action in column.actions">
|
||||
<a-tag class="custom-tag" v-if="showButton(action.show,{record,records:data,formModel,disabled})" @click="action.type === 'delete' ? remove(record) : action.click({record,records:data,formModel,disabled})" :color="action?.color ? action.color : 'blue'">{{action.label}}</a-tag>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="!disabled">
|
||||
<MinusCircleOutlined style="padding-bottom: 20px" @click="remove(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<div class="tbl-toolbar">
|
||||
@ -92,6 +103,7 @@
|
||||
:isSubFormUse="true"
|
||||
:params="{ itemId }"
|
||||
popupType="preload"
|
||||
:backPagination="backPagination"
|
||||
@submit="renderSubFormList"
|
||||
/>
|
||||
</FormItemRest>
|
||||
@ -182,6 +194,8 @@
|
||||
},
|
||||
// 是否开启分页
|
||||
showPagination: Boolean,
|
||||
//后端分页
|
||||
backPagination: Boolean,
|
||||
/**
|
||||
* 选数据按钮名称
|
||||
*/
|
||||
@ -197,7 +211,8 @@
|
||||
//add after hooks
|
||||
addAfter: Function,
|
||||
//表头合并数据
|
||||
multipleHeads: { type: Array as PropType<MutipleHeadInfo[]> }
|
||||
multipleHeads: { type: Array as PropType<MutipleHeadInfo[]> },
|
||||
backPagination: { type: Boolean }
|
||||
});
|
||||
const data = ref<Recordable[]>([]);
|
||||
|
||||
@ -207,14 +222,18 @@
|
||||
const headColums = ref<MutipleHeadInfo[]>([]); // 多表头
|
||||
const originHeads = ref<MutipleHeadInfo[]>([]); // 多表头源数据
|
||||
const columns = ref<SubFormColumn[]>(props.columns);
|
||||
const allColumns = ref([])
|
||||
|
||||
// 注入表单数据
|
||||
const formModel = inject<any>('formModel', null);
|
||||
function readonlySupport(name) {
|
||||
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker)$/.test(name);
|
||||
}
|
||||
|
||||
const onFieldChange = (column, row, rowIndex) => {
|
||||
const evt = column?.componentProps?.events?.change;
|
||||
if (evt) {
|
||||
evt({ column, row, rowIndex, formModel });
|
||||
evt({ column, row, rowIndex, formModel, columns: headColums.length > 0 ? headColums : columns });
|
||||
}
|
||||
};
|
||||
|
||||
@ -235,7 +254,7 @@
|
||||
}
|
||||
|
||||
function addDataKey(rows) {
|
||||
rows.forEach((row) => {
|
||||
rows?.forEach((row) => {
|
||||
if (!row['_key_']) {
|
||||
row['_key_'] = buildUUID();
|
||||
}
|
||||
@ -255,6 +274,7 @@
|
||||
});
|
||||
}
|
||||
setColWidth(columns);
|
||||
allColumns.value = cloneDeep(columns.value)
|
||||
columns.value = filterColum(columns.value);
|
||||
nextTick(() => {
|
||||
//处理多表头
|
||||
@ -271,6 +291,7 @@
|
||||
watch(
|
||||
() => props.columns,
|
||||
(val) => {
|
||||
allColumns.value = cloneDeep(val)
|
||||
columns.value = filterColum(val);
|
||||
setColWidth(columns);
|
||||
}
|
||||
@ -382,11 +403,20 @@
|
||||
emit('update:value', unref(data));
|
||||
};
|
||||
|
||||
const showButton=(show,obj)=>{
|
||||
if(show===undefined||show===null||show===''||show===true||show==='true'){
|
||||
return true;
|
||||
}else if(isFunction(show)){
|
||||
return show(obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const renderSubFormList = async (list) => {
|
||||
list?.forEach((x) => {
|
||||
const dataObj = {};
|
||||
columns.value?.map((item) => {
|
||||
if (!item?.dataIndex) return;
|
||||
allColumns.value?.map((item) => {
|
||||
if (!item?.dataIndex || !item.componentProps.isShow) return;
|
||||
dataObj[item.dataIndex as string] = item.componentProps?.prestrainField ? x[item.componentProps.prestrainField] : null;
|
||||
});
|
||||
|
||||
@ -652,4 +682,14 @@
|
||||
.tbl-toolbar {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tag-wrapper{
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.custom-tag{
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -168,6 +168,10 @@
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* 按钮组 [{name: '按钮', click: function(){}}]
|
||||
*/
|
||||
buttons: Array,
|
||||
// 是否开启分页
|
||||
showPagination: Boolean,
|
||||
/**
|
||||
@ -197,6 +201,7 @@
|
||||
const headColums = ref<MutipleHeadInfo[]>([]); // 多表头
|
||||
const originHeads = ref<MutipleHeadInfo[]>([]); // 多表头源数据
|
||||
const columns = ref<SubFormColumn[]>(props.columns);
|
||||
const allColumns = ref([])
|
||||
|
||||
// 注入表单数据
|
||||
const formModel = inject<any>('formModel', null);
|
||||
@ -224,6 +229,9 @@
|
||||
}
|
||||
|
||||
function addDataKey(rows) {
|
||||
if (!rows) {
|
||||
return [];
|
||||
}
|
||||
rows.forEach((row) => {
|
||||
if (!row['_key_']) {
|
||||
row['_key_'] = Math.random();
|
||||
@ -244,6 +252,7 @@
|
||||
});
|
||||
}
|
||||
setColWidth(columns);
|
||||
allColumns.value = cloneDeep(columns.value)
|
||||
columns.value = filterColum(columns.value);
|
||||
nextTick(() => {
|
||||
//处理多表头
|
||||
@ -260,6 +269,7 @@
|
||||
watch(
|
||||
() => props.columns,
|
||||
(val) => {
|
||||
allColumns.value = cloneDeep(val)
|
||||
columns.value = filterColum(val);
|
||||
setColWidth(columns);
|
||||
}
|
||||
@ -327,6 +337,29 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
const removeById = (id) => {
|
||||
data.value = deleteNodeById(data.value, id);
|
||||
};
|
||||
function deleteNodeById(tree, idToDelete) {
|
||||
function deepCloneAndFilter(nodes) {
|
||||
return nodes.map((node) => {
|
||||
if (node.id === idToDelete) {
|
||||
return null; // 当前节点要删除
|
||||
}
|
||||
|
||||
// 递归处理 children
|
||||
|
||||
const newChildren = node.children ? deepCloneAndFilter(node.children) : [];
|
||||
|
||||
return {
|
||||
...node,
|
||||
children: newChildren.length ? newChildren : undefined
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
return deepCloneAndFilter(tree);
|
||||
}
|
||||
|
||||
const add = () => {
|
||||
//给各个组件赋默认值
|
||||
@ -364,17 +397,25 @@
|
||||
}
|
||||
};
|
||||
|
||||
const remove = (index) => {
|
||||
const remove = (record) => {
|
||||
let index;
|
||||
if (typeof record === 'number' || typeof record === 'string') {
|
||||
index = Number(record);
|
||||
} else {
|
||||
index = data.value.findIndex((r) => r._key_ === record._key_);
|
||||
}
|
||||
if (index !== -1 && index < data.value.length) {
|
||||
data.value.splice(index, 1);
|
||||
emit('change', unref(data));
|
||||
emit('update:value', unref(data));
|
||||
}
|
||||
};
|
||||
|
||||
const renderSubFormList = async (list) => {
|
||||
list?.forEach((x) => {
|
||||
const dataObj = {};
|
||||
columns.value?.map((item) => {
|
||||
if (!item?.dataIndex) return;
|
||||
allColumns.value?.map((item) => {
|
||||
if (!item?.dataIndex || !item.componentProps.isShow) return;
|
||||
dataObj[item.dataIndex as string] = item.componentProps?.prestrainField ? x[item.componentProps.prestrainField] : null;
|
||||
});
|
||||
|
||||
@ -562,7 +603,8 @@
|
||||
data,
|
||||
headColums,
|
||||
columns,
|
||||
renderSubFormList
|
||||
renderSubFormList,
|
||||
removeById
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
</a-upload>
|
||||
</div>
|
||||
<a-upload
|
||||
:file-list="fileList"
|
||||
:file-list="fileListWithHeader"
|
||||
:maxCount="maxNumber"
|
||||
:accept="accept"
|
||||
:name="name"
|
||||
@ -75,16 +75,46 @@
|
||||
</div>
|
||||
|
||||
<template #itemRender="{ file, actions }">
|
||||
<template v-if="file.__header&&showDownloadIcon">
|
||||
<div class="file-list-header" style="display: flex; align-items: center; padding: 4px 0;">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isAllSelected"
|
||||
@change="toggleSelectAll"
|
||||
style="margin-right: 8px;"
|
||||
/>全选
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!selectedIds.length"
|
||||
@click="handleBatchDownload"
|
||||
style="margin-left: 8px;"
|
||||
>批量打包下载</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-space class="file-space">
|
||||
<input
|
||||
v-if="showDownloadIcon"
|
||||
type="checkbox"
|
||||
:checked="selectedIds.includes(file.id)"
|
||||
@change="e => toggleSelectOne(file.id, e)"
|
||||
style="margin-right: 8px;"
|
||||
/>
|
||||
<PaperClipOutlined/>
|
||||
<span class="file-name-span" @click="actions.preview">{{ file.name }}</span>
|
||||
<a-tooltip v-if="showDownloadIcon" title="下载"><span @click="actions.download" class="file-outlined-span"><DownloadOutlined /></span></a-tooltip>
|
||||
<a-tooltip v-if="!disabled && showRemoveIcon" title="删除"><span @click="actions.remove" class="file-outlined-span"><DeleteOutlined /></span></a-tooltip>
|
||||
<a-tooltip v-if="showDownloadIcon" title="下载">
|
||||
<span @click="actions.download" class="file-outlined-span"><DownloadOutlined /></span>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-if="!disabled && showRemoveIcon" title="删除">
|
||||
<span @click="actions.remove" class="file-outlined-span"><DeleteOutlined /></span>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-if="'.doc,.docx,.xls,.xlsx,.pdf'.includes(file.fileType)" title="编辑文档">
|
||||
<span @click="editFile(file)" class="file-outlined-span"><EditOutlined /></span>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</a-upload>
|
||||
<a-modal
|
||||
@ -110,11 +140,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import { nextTick, ref, watch, computed } from 'vue';
|
||||
import { Upload } from 'ant-design-vue';
|
||||
import { UploadOutlined, PlusOutlined, DownloadOutlined, DeleteOutlined, EditOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import {deleteSingleFile, getAppToken, getFileList, getOnlineEditUrl} from '/@/api/system/file';
|
||||
import {deleteSingleFile, getAppToken, getFileList, getOnlineEditUrl, getZipFiles} from '/@/api/system/file';
|
||||
import { downloadByUrl } from '/@/utils/file/download';
|
||||
import { uploadMultiApi } from '/@/api/sys/upload';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
@ -122,8 +152,12 @@
|
||||
import { getAppEnvConfig } from '/@/utils/env';
|
||||
import WebOfficeSDK from "/@/assets/libs/open-jssdk-v0.1.3.es.js";
|
||||
import {getToken} from "/@/utils/auth";
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const { VITE_GLOB_UPLOAD_ALERT_TIP } = getAppEnvConfig();
|
||||
|
||||
const { createSuccessModal, } = useMessage();
|
||||
|
||||
const props = defineProps({
|
||||
value: String,
|
||||
maxNumber: Number,
|
||||
@ -346,6 +380,52 @@
|
||||
previewVisible.value = false;
|
||||
previewTitle.value = '';
|
||||
};
|
||||
|
||||
const selectedIds = ref<string[]>([]);
|
||||
const isAllSelected = computed(() => fileList.value.length > 0 && selectedIds.value.length === fileList.value.length);
|
||||
const fileListWithHeader = computed(() => {
|
||||
// 只在有文件时插入头部
|
||||
if (fileList.value.length) {
|
||||
return [{ __header: true, uid: '__header__' }, ...fileList.value];
|
||||
}
|
||||
return fileList.value;
|
||||
});
|
||||
function toggleSelectAll(e: Event) {
|
||||
const checked = (e.target as HTMLInputElement).checked;
|
||||
selectedIds.value = checked ? fileList.value.map(f => f.id) : [];
|
||||
}
|
||||
function toggleSelectOne(id: string, e: Event) {
|
||||
const checked = (e.target as HTMLInputElement).checked;
|
||||
if (checked) {
|
||||
selectedIds.value = [...selectedIds.value, id];
|
||||
} else {
|
||||
selectedIds.value = selectedIds.value.filter(item => item !== id);
|
||||
}
|
||||
}
|
||||
async function handleBatchDownload() {
|
||||
if (!selectedIds.value.length) return;
|
||||
// getZipFiles 返回下载url
|
||||
let formName = '';
|
||||
try {
|
||||
formName = route.query.formName as string || '';
|
||||
// 获取当前页面得form name
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
const res = await getZipFiles({ fileIds: selectedIds.value.join(',') , insertionFileName: formName});
|
||||
if (!res) {
|
||||
notification.error({
|
||||
message: 'Tip',
|
||||
description: '批量下载失败,请稍后重试!',
|
||||
});
|
||||
return;
|
||||
} else if (res.type === 'async') {
|
||||
createSuccessModal({ title: 'Tip', content: res.msg });
|
||||
return;
|
||||
} else if (res.type === 'synced') {
|
||||
downloadByUrl({ url: res.url, fileName: res.name || 'files.zip' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.list-upload {
|
||||
|
||||
@ -127,6 +127,7 @@ export type ComponentType =
|
||||
| 'DeptTree'
|
||||
| 'Dept'
|
||||
| 'User'
|
||||
| 'UserTree'
|
||||
| 'Info'
|
||||
| 'Area'
|
||||
| 'SubForm'
|
||||
@ -158,6 +159,7 @@ export type ComponentType =
|
||||
| 'ErpCheck'
|
||||
| 'FormView'
|
||||
| 'XjrIframe'
|
||||
| 'CustomComponent'
|
||||
| 'TableLayout';
|
||||
|
||||
/**
|
||||
|
||||
@ -1,15 +1,34 @@
|
||||
<template>
|
||||
<a-modal
|
||||
ref="modalRef"
|
||||
:visible="visible"
|
||||
:title="title"
|
||||
:maskClosable="false"
|
||||
:width="hasLeftSlot ? 1200 : width || 600"
|
||||
:okText="t('确定')"
|
||||
:cancelText="t('取消')"
|
||||
@ok="$emit('submit')"
|
||||
@cancel="$emit('close')"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:getContainer="draggable ? undefined : 'body'"
|
||||
:wrap-style="draggable ? { overflow: 'hidden' } : {}"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
|
||||
<template #title>
|
||||
<div
|
||||
ref="modalTitleRef"
|
||||
:style="{ width: '100%', cursor: draggable ? 'move' : 'default' }"
|
||||
>
|
||||
{{ title }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #modalRender="{ originVNode }">
|
||||
<div :style="draggable ? transformStyle : {}">
|
||||
<component :is="originVNode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="content">
|
||||
<div :class="['left', isDeptSelect ? 'left-box' : '']" v-if="hasLeftSlot">
|
||||
<slot name="left"></slot>
|
||||
@ -21,19 +40,99 @@
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { computed, useSlots, ref, watch } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDraggable } from '@vueuse/core';
|
||||
const { t } = useI18n();
|
||||
defineEmits(['submit', 'close']);
|
||||
defineProps({
|
||||
|
||||
const emit = defineEmits(['submit', 'close']);
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
width: Number,
|
||||
visible: { type: Boolean, default: false },
|
||||
isDeptSelect: { type: Boolean, default: false },
|
||||
confirmLoading: { type: Boolean, default: false },
|
||||
draggable: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
const modalRef = ref(null);
|
||||
const modalTitleRef = ref(null);
|
||||
const hasLeftSlot = computed(() => {
|
||||
return !!useSlots().left;
|
||||
});
|
||||
|
||||
const startX = ref(0);
|
||||
const startY = ref(0);
|
||||
const startedDrag = ref(false);
|
||||
const transformX = ref(0);
|
||||
const transformY = ref(0);
|
||||
const preTransformX = ref(0);
|
||||
const preTransformY = ref(0);
|
||||
const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });
|
||||
|
||||
const { x, y, isDragging } = useDraggable(modalTitleRef, {
|
||||
enabled: computed(() => props.draggable)
|
||||
});
|
||||
|
||||
const handleOk = () => {
|
||||
emit('submit');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
watch([x, y], () => {
|
||||
if (!props.draggable || !startedDrag.value) {
|
||||
startX.value = x.value;
|
||||
startY.value = y.value;
|
||||
const bodyRect = document.body.getBoundingClientRect();
|
||||
if (modalTitleRef.value) {
|
||||
const titleRect = modalTitleRef.value.getBoundingClientRect();
|
||||
dragRect.value.right = bodyRect.width - titleRect.width;
|
||||
dragRect.value.bottom = bodyRect.height - titleRect.height;
|
||||
}
|
||||
preTransformX.value = transformX.value;
|
||||
preTransformY.value = transformY.value;
|
||||
}
|
||||
startedDrag.value = true;
|
||||
});
|
||||
|
||||
// 监听拖拽状态变化
|
||||
watch(isDragging, (newVal) => {
|
||||
if (!newVal) {
|
||||
startedDrag.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVisible) => {
|
||||
if (!newVisible) {
|
||||
transformX.value = 0;
|
||||
transformY.value = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const transformStyle = computed(() => {
|
||||
if (!props.draggable) return {};
|
||||
|
||||
if (startedDrag.value) {
|
||||
transformX.value =
|
||||
preTransformX.value +
|
||||
Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -
|
||||
startX.value;
|
||||
transformY.value =
|
||||
preTransformY.value +
|
||||
Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -
|
||||
startY.value;
|
||||
}
|
||||
|
||||
return {
|
||||
transform: `translate(${transformX.value}px, ${transformY.value}px)`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.content {
|
||||
@ -49,6 +148,7 @@
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div :class="{ disabled }" class="multiple-popup">
|
||||
<div v-if="disabled && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="popupValue">
|
||||
{{popupValue}}
|
||||
</div>
|
||||
<a-input
|
||||
v-model:value="popupValue"
|
||||
v-if="disabledShowBorder || !disabled"
|
||||
:addonAfter="addonAfter"
|
||||
:addonBefore="addonBefore"
|
||||
:bordered="bordered"
|
||||
@ -35,6 +39,7 @@
|
||||
:mainKey="mainKey"
|
||||
:params="params"
|
||||
:popupType="popupType"
|
||||
:popupTitle="popupTitle"
|
||||
:subTableIndex="index"
|
||||
:valueField="valueField"
|
||||
:backPagination="backPagination"
|
||||
@ -68,6 +73,7 @@
|
||||
|
||||
const props = defineProps({
|
||||
popupType: { type: String },
|
||||
popupTitle: { type: String },
|
||||
value: { type: String },
|
||||
labelField: { type: String, default: 'label' },
|
||||
valueField: { type: String, default: 'value' },
|
||||
@ -247,3 +253,11 @@
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-modal centered :width="1250" :visible="props.multipleDialog" :title="title" :destroyOnClose="true"
|
||||
@ok="submitDialog" @cancel="closeDialog" :okText="t('确认')" :cancelText="t('取消')" :bodyStyle="{ padding: '20px' }">
|
||||
@ok="submitDialog" @cancel="closeDialog" :okText="t('确认')" :cancelText="t('取消')" :bodyStyle="{ padding: '20px' }" v-loading="loading">
|
||||
<a-row :gutter="12" style="margin-bottom: 10px">
|
||||
<a-col :span="8">
|
||||
<a-input v-model:value="state.searchText" :placeholder="t('请输入要查询的关键字')" />
|
||||
@ -28,10 +28,10 @@
|
||||
<a-table :dataSource="state.dataSourceList" :columns="state.sourceColumns" :row-selection="{
|
||||
selectedRowKeys: state.selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
}" :pagination="paginationProps" :scroll="{ y: '420px' }" />
|
||||
}" :pagination="paginationProps" :scroll="{ y: '420px' }" :loading="loading"/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="t('已选记录')" force-render>
|
||||
<a-table :dataSource="selectedList" :columns="state.selectedColumns" :scroll="{ y: '400px' }">
|
||||
<a-table :dataSource="selectedList" :columns="state.selectedColumns" :scroll="{ y: '400px' }" :loading="loading">
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'delete'">
|
||||
<Icon icon="ant-design:delete-outlined" color="#f56c6c" @click="deleteSelected(record, index)"
|
||||
@ -41,7 +41,7 @@
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-table v-else-if="popupType === 'associate'" :dataSource="state.dataSourceList" :columns="state.sourceColumns"
|
||||
<a-table v-else-if="popupType === 'associate'" :dataSource="state.dataSourceList" :columns="state.sourceColumns" :loading="loading"
|
||||
:pagination="paginationProps" :row-selection="{
|
||||
selectedRowKeys: state.selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
@ -64,6 +64,7 @@ const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
multipleDialog: { type: Boolean },
|
||||
popupType: { type: String },
|
||||
popupTitle: { type: String },
|
||||
dataSourceOptions: { type: Array },
|
||||
params: {
|
||||
type: [Array, Object, String, Number],
|
||||
@ -105,6 +106,7 @@ onMounted(async () => {
|
||||
await getDatasourceList(1);
|
||||
}
|
||||
});
|
||||
const loading = ref(false)
|
||||
|
||||
const state = reactive({
|
||||
selectedRowKeys: [] as any[],
|
||||
@ -127,6 +129,9 @@ const paginationProps = reactive({
|
||||
const formModel = inject<any>('formModel', null);
|
||||
const isCamelCase = inject<boolean>('isCamelCase', false);
|
||||
const title = computed(() => {
|
||||
if(props.popupTitle){
|
||||
return t(props.popupTitle);
|
||||
};
|
||||
switch (props.popupType) {
|
||||
case 'multiple':
|
||||
return t('多选弹层-选择记录');
|
||||
@ -214,6 +219,9 @@ const resetSearch = () => {
|
||||
};
|
||||
const closeDialog = () => {
|
||||
emit('update:multipleDialog', false);
|
||||
selectedList.value = [];
|
||||
state.selectedRowKeys = [];
|
||||
state.dataSourceList = [];
|
||||
};
|
||||
|
||||
const submitDialog = () => {
|
||||
@ -291,12 +299,18 @@ const setFormModel = (isNull?) => {
|
||||
let bindField = !isCamelCase ? item.bindField : camelCaseString(item.bindField);
|
||||
dataObj[bindField as string] = item.prestrainField ? x[item.prestrainField] : null;
|
||||
});
|
||||
if (formModel[table]) formModel[table].push(dataObj);
|
||||
if (formModel[table]) {
|
||||
formModel[table].push(dataObj)
|
||||
} else {
|
||||
formModel[table] = []
|
||||
formModel[table].push(dataObj)
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getDatasourceList = async (limit = 1) => {
|
||||
loading.value = true;
|
||||
paginationProps.current = limit;
|
||||
let api;
|
||||
if (props.datasourceType) {
|
||||
@ -342,8 +356,8 @@ const getDatasourceList = async (limit = 1) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
loading.value = false;
|
||||
if (!api || !isFunction(api)) return;
|
||||
state.dataSourceList = [];
|
||||
try {
|
||||
let dataParams = {};
|
||||
const pageParams = { order: 'desc', size: 10, limit, keyword: state.searchText };
|
||||
@ -359,9 +373,11 @@ const getDatasourceList = async (limit = 1) => {
|
||||
pageParams,
|
||||
);
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await api(dataParams);
|
||||
state.dataSourceList = res.list;
|
||||
paginationProps.total = Number(res.total);
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<span class="node-name">{{node.activityName}}</span>
|
||||
<a-switch :checked="node.chooseNode" v-if="!node.hiddenNode" style="margin-left: 10px;" @change="agreeNodeChange(node)"></a-switch>
|
||||
</div>
|
||||
<a-form-item :required="(flowNextNodes.length === 1 || node.chooseNode)" v-if="_action === 'agree' && !isEnd" :label="'审批人'">
|
||||
<a-form-item :required="(flowNextNodes.length === 1 || node.chooseNode)" v-if="(_action === 'agree' || _action == 'disagree') && !isEnd" :label="'审批人'">
|
||||
<a-select v-show="node.chooseAssign" v-model:value="node.assignees" :options="node.nextAssignees" :disabled="loading"
|
||||
:placeholder="'审批人'" max-tag-count="responsive"
|
||||
:mode="node.isChooseMulti? 'multiple' : ''"
|
||||
@ -30,10 +30,12 @@
|
||||
<a-select-option v-for="(item, index) in rejectNodeList" :key="index" :value="item.activityId">{{ item.activityName }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<template v-for="node in rejectNodeList">
|
||||
<a-form-item v-if="_action === 'reject'&&rejectNodeId===node.activityId" label="审批人">
|
||||
<a-select v-show="node.chooseAssign" v-model:value="node.assignees" :options="node.nextAssignees" :disabled="loading"
|
||||
:placeholder="'请选择' + node.activityName + '的审批人'" max-tag-count="responsive" mode="multiple"
|
||||
<template v-for="node in rejectNodeList" :key="node.activityId">
|
||||
<a-form-item required v-if="_action === 'reject'&&rejectNodeId===node.activityId" label="审批人">
|
||||
<a-select v-show="node.chooseAssign" v-model:value="node.assignees" :options="node.nextAssignees"
|
||||
:placeholder="'请选择' + node.activityName + '的审批人'" max-tag-count="responsive"
|
||||
:disabled="loading"
|
||||
:mode="node.rejectIsChooseMulti? 'multiple' : ''"
|
||||
:filterOption="search"
|
||||
></a-select>
|
||||
<span v-show="!node.chooseAssign">{{ getAssigneeText(node) }}</span>
|
||||
@ -81,7 +83,7 @@
|
||||
}
|
||||
|
||||
function getNextNodesName() {
|
||||
return flowNextNodes.value.length > 1 ? '多个节点,请选择流向节点' : flowNextNodes?.value[0]?.activityName;
|
||||
return flowNextNodes.value.length > 1 ? '多个节点,请确认流向节点' : flowNextNodes?.value[0]?.activityName;
|
||||
}
|
||||
|
||||
function toggleDialog({ isClose, action, callback, rejectCancel, processId, taskId, nextNodes } = {}) {
|
||||
@ -251,7 +253,7 @@
|
||||
}
|
||||
rejectNodeList.value.forEach((nNode) => {
|
||||
if(nNode.activityId==rejectNodeId.value){
|
||||
nextTaskUser[nNode.activityId] = isEnd.value ? '' : nNode.assignees.join(',');
|
||||
nextTaskUser[nNode.activityId] = isEnd.value ? '' : (typeof(nNode.assignees) == 'string' ? nNode.assignees : nNode.assignees.join(','));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-modal :mask-closable="false" :title="dialogTitle" :visible="isOpen" :width="500" class="geg" centered>
|
||||
<a-modal :mask-closable="false" :title="dialogTitle" :visible="isOpen" :width="500" class="geg" centered @cancel="onClickCancel">
|
||||
<template #footer>
|
||||
<a-button :disabled="loading" @click="onClickCancel">取消</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="onClickOK">确定</a-button>
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<a-select v-model:value="selectedValue" :filter-option="handleFilterOption" :mode="mode" :options="getOptions" :placeholder="placeholder" allowClear v-bind="$attrs" @change="handleChange" @dropdown-visible-change="handleFetch">
|
||||
<div>
|
||||
<div v-if="disabled && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="departNames">
|
||||
{{departNames}}
|
||||
</div>
|
||||
<a-select v-model:value="selectedValue" :filter-option="handleFilterOption" :mode="mode" :options="getOptions" :placeholder="placeholder" allowClear v-bind="$attrs" @change="handleChange" @dropdown-visible-change="handleFetch" v-else>
|
||||
<template v-for="item in Object.keys($slots)" #[item]="data">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
@ -13,6 +17,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</a-select>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, computed, unref, watch, inject, onMounted, watchEffect } from 'vue';
|
||||
@ -63,7 +68,15 @@
|
||||
mainKey: String,
|
||||
index: Number,
|
||||
sepTextField: String, // 独立存储文本部分
|
||||
row: Object // 行数据,在明细表里生效
|
||||
row: Object, // 行数据,在明细表里生效
|
||||
wordWrap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledShowBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['options-change', 'change', 'update:value'],
|
||||
setup(props, { emit }) {
|
||||
@ -80,6 +93,7 @@
|
||||
const valChanged = ref(false);
|
||||
// label分开存储时,第一次懒加载会同时处罚两次fetch,所以这里延迟执行
|
||||
const fetch = debounce(_fetch, 200, { leading: false, trailing: true });
|
||||
const selectName = ref('')
|
||||
|
||||
const getOptions = computed(() => {
|
||||
const { labelField, valueField, numberToString } = props;
|
||||
@ -156,6 +170,7 @@
|
||||
() => props.value,
|
||||
() => {
|
||||
selectedValue.value = ((typeof props.value === 'string' && !!props.value ? props.value?.split(',') : props.value) || undefined) as any;
|
||||
updateSepTextField(Array.isArray(selectedValue.value) ? selectedValue.value : [selectedValue.value]);
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
@ -163,15 +178,16 @@
|
||||
);
|
||||
|
||||
function updateSepTextField(arr) {
|
||||
if (!props.sepTextField || !props.row) {
|
||||
return;
|
||||
}
|
||||
const options = unref(getOptions);
|
||||
const txtArr = options
|
||||
.filter((opt) => {
|
||||
return arr.includes(opt.value);
|
||||
})
|
||||
.map((item) => item.label);
|
||||
selectName.value = txtArr.join(',')
|
||||
if (!props.sepTextField || !props.row) {
|
||||
return;
|
||||
}
|
||||
props.row[camelCaseString(props.sepTextField)] = txtArr.join(',');
|
||||
}
|
||||
|
||||
@ -260,7 +276,6 @@
|
||||
emit('update:value', val);
|
||||
emit('change', val, args);
|
||||
selectedValue.value = props.value === undefined ? val : (((typeof props.value === 'string' && !!props.value ? props.value?.split(',') : props.value) || undefined) as any);
|
||||
updateSepTextField(Array.isArray(value) ? value : [value]);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -276,3 +291,11 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -52,12 +52,13 @@
|
||||
import { PostInfo } from '/@/api/system/post/model';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const emits = defineEmits(['change','selectedNodes']);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedIds: Array<string>;
|
||||
multiple?: Boolean;
|
||||
needNode?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
selectedIds: () => {
|
||||
@ -66,6 +67,9 @@
|
||||
disabledIds: () => {
|
||||
return [];
|
||||
},
|
||||
needNode: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
);
|
||||
let data: {
|
||||
@ -106,7 +110,10 @@
|
||||
data.visible = true;
|
||||
}
|
||||
function submit() {
|
||||
emits('change', data.selectedIds);
|
||||
emits('change', data.selectedIds, data.selectedList);
|
||||
if(props.needNode && props.needNode === true) {
|
||||
emits('selectedNodes', data.selectedList);
|
||||
}
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
@ -136,6 +143,7 @@
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
data.selectedList = [item]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,9 @@
|
||||
});
|
||||
|
||||
const getSchemas = computed<FormSchema[]>(() => {
|
||||
return (unref(getProps).schemas as any) || unref(schemaRef);
|
||||
let schemas = (unref(getProps).schemas as any) || unref(schemaRef)
|
||||
getComponent(schemas);
|
||||
return schemas;
|
||||
});
|
||||
|
||||
// Get the basic configuration of the form
|
||||
@ -110,7 +112,7 @@
|
||||
}
|
||||
|
||||
function getColWidth(schema: any) {
|
||||
const compProps = schema.componentProps;
|
||||
const compProps = schema?.componentProps;
|
||||
if (compProps?.responsive) {
|
||||
if (compProps.respNewRow) {
|
||||
return 24; // 响应式布局下独立成行
|
||||
@ -125,7 +127,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return schema.colProps?.span;
|
||||
return schema?.colProps?.span;
|
||||
}
|
||||
|
||||
const debGetWrapSize = debounce(getWrapSize, 300);
|
||||
@ -148,7 +150,7 @@
|
||||
});
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema?.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema, itemValue: any): boolean {
|
||||
@ -175,22 +177,24 @@
|
||||
}
|
||||
|
||||
function getIfShow(schema: FormSchema, itemValue: any): boolean {
|
||||
const { ifShow } = schema;
|
||||
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(ifShow)) {
|
||||
isIfShow = ifShow;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
if (isFunction(ifShow)) {
|
||||
isIfShow = ifShow({
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
|
||||
if (isFunction(componentProps?.isShow)) {
|
||||
isShow = componentProps.isShow({
|
||||
values: itemValue,
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
return isIfShow;
|
||||
return isShow;
|
||||
}
|
||||
|
||||
const formModel = reactive<Recordable>(props.formModel);
|
||||
@ -218,8 +222,6 @@
|
||||
|
||||
const refreshFieldObj = ref<object>({});
|
||||
|
||||
getComponent(getSchemas.value);
|
||||
|
||||
function getComponent(component) {
|
||||
const layoutComponents = ['tab', 'grid', 'card'];
|
||||
component?.map((info) => {
|
||||
@ -253,8 +255,10 @@
|
||||
|
||||
function setComponentDefault(item) {
|
||||
if ((staticDataComponents.includes(item.component) && (item.componentProps as any)?.datasourceType === 'staticData') || (needDicDefaultValue.includes(item.type) && (item.componentProps as any)?.datasourceType === 'dic')) {
|
||||
if(formModel[item.field]==undefined) {
|
||||
let { defaultSelect } = item.componentProps as any;
|
||||
formModel[item.field] = defaultSelect;
|
||||
}
|
||||
return;
|
||||
}
|
||||
let { defaultValue } = item;
|
||||
@ -277,7 +281,9 @@
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if(formModel[item.field]==undefined){
|
||||
formModel[item.field] = item.component === 'SubForm' ? [] : defaultValue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -413,7 +419,8 @@
|
||||
* 修改bug #5090
|
||||
* 为了保证表单赋值触发所有组件的change事件
|
||||
*/
|
||||
const executeEvent = (allSchemas: FormSchema[]) => {
|
||||
const executeEvent = (allSchemas: FormSchema[] = []) => {
|
||||
if(!allSchemas) return;
|
||||
for (const schema of allSchemas) {
|
||||
//如果是这几个组件 需要查询子级
|
||||
if (['Card', 'Tab', 'Grid'].includes(schema.component)) {
|
||||
@ -442,9 +449,9 @@
|
||||
try {
|
||||
if (typeof handler === 'string') {
|
||||
const event = new Function('schema', 'formModel', 'formActionType', 'extParams', handler);
|
||||
event(schema, formModel, formApi, { formData, allSchemas });
|
||||
event(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
} else if (typeof handler === 'function') {
|
||||
handler(schema, formModel, formApi, { formData, allSchemas });
|
||||
handler(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
@ -675,7 +682,7 @@
|
||||
schema.componentProps.style = { ...schema.componentProps.style, ...style };
|
||||
};
|
||||
|
||||
const formApi: FormActionType = {
|
||||
const formApi = {
|
||||
submit,
|
||||
validate,
|
||||
clearValidate,
|
||||
@ -694,9 +701,10 @@
|
||||
httpRequest,
|
||||
refreshAPI,
|
||||
changeStyle,
|
||||
setDefaultValue
|
||||
setDefaultValue,
|
||||
formModel
|
||||
};
|
||||
|
||||
//将表单方法 导出 给父组件使用。
|
||||
defineExpose<FormActionType>(formApi);
|
||||
defineExpose(formApi);
|
||||
</script>
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
}
|
||||
|
||||
function getColWidth(schema: any) {
|
||||
const compProps = schema.componentProps;
|
||||
const compProps = schema?.componentProps;
|
||||
if (compProps?.responsive) {
|
||||
if (compProps.respNewRow) {
|
||||
return 24; // 响应式布局下独立成行
|
||||
@ -125,7 +125,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return schema.colProps?.span;
|
||||
return schema?.colProps?.span;
|
||||
}
|
||||
|
||||
const debGetWrapSize = debounce(getWrapSize, 300);
|
||||
@ -148,7 +148,7 @@
|
||||
});
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema, itemValue: any): boolean {
|
||||
@ -175,22 +175,15 @@
|
||||
}
|
||||
|
||||
function getIfShow(schema: FormSchema, itemValue: any): boolean {
|
||||
const { ifShow } = schema;
|
||||
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(ifShow)) {
|
||||
isIfShow = ifShow;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
if (isFunction(ifShow)) {
|
||||
isIfShow = ifShow({
|
||||
values: itemValue,
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
return isIfShow;
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
return isShow;
|
||||
}
|
||||
|
||||
const formModel = reactive<Recordable>(props.formModel);
|
||||
@ -413,7 +406,8 @@
|
||||
* 修改bug #5090
|
||||
* 为了保证表单赋值触发所有组件的change事件
|
||||
*/
|
||||
const executeEvent = (allSchemas: FormSchema[]) => {
|
||||
const executeEvent = (allSchemas: FormSchema[] = []) => {
|
||||
if(!allSchemas) return;
|
||||
for (const schema of allSchemas) {
|
||||
//如果是这几个组件 需要查询子级
|
||||
if (['Card', 'Tab', 'Grid'].includes(schema.component)) {
|
||||
@ -442,9 +436,9 @@
|
||||
try {
|
||||
if (typeof handler === 'string') {
|
||||
const event = new Function('schema', 'formModel', 'formActionType', 'extParams', handler);
|
||||
event(schema, formModel, formApi, { formData, allSchemas });
|
||||
event(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
} else if (typeof handler === 'function') {
|
||||
handler(schema, formModel, formApi, { formData, allSchemas });
|
||||
handler(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
@ -672,12 +666,15 @@
|
||||
httpRequest,
|
||||
refreshAPI,
|
||||
changeStyle,
|
||||
setDefaultValue
|
||||
setDefaultValue,
|
||||
formModel
|
||||
};
|
||||
|
||||
//将表单方法 导出 给父组件使用。
|
||||
expose({
|
||||
...formApi
|
||||
...formApi,
|
||||
formModel,
|
||||
getSchemas
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -31,6 +31,9 @@
|
||||
:labelAlign="formProps?.labelAlign"
|
||||
:name="schema.field"
|
||||
:wrapperCol="itemLabelWidthProp.wrapperCol"
|
||||
:style="{
|
||||
overflow:'hidden'
|
||||
}"
|
||||
>
|
||||
<component :is="formComponent(schema)" v-model:value="formModel![schema.field]" :disabled="getDisable" :size="formProps?.size" v-bind="schema.componentProps" />
|
||||
</FormItem>
|
||||
@ -84,7 +87,7 @@
|
||||
:wrapperCol="itemLabelWidthProp.wrapperCol"
|
||||
>
|
||||
<template v-if="getDisable && readonlySupport(schema.component)">
|
||||
<readonly :model="formModel" :schema="schema" />
|
||||
<readonly :schema="schema" :model="formModel"/>
|
||||
<component
|
||||
:is="componentMap.get(schema.component)"
|
||||
v-show="false"
|
||||
@ -164,11 +167,11 @@
|
||||
:validateTrigger="['blur', 'change']"
|
||||
:wrapperCol="itemLabelWidthProp.wrapperCol"
|
||||
>
|
||||
<template v-if="getDisable && readonlySupport(schema.component)">
|
||||
<template v-if="getDisable && readonlySupport(schema.component) && !getDisabledShowBorder">
|
||||
<readonly :schema="schema" :value="formModel![schema.field]" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<component :is="defaultComponent(schema)" :key="refreshFieldObj[schema.field]" v-model:value="formModel![schema.field]" :disabled="getDisable" :formData="formModel" :size="formProps?.size" v-bind="getComponentsProps" />
|
||||
<component :is="defaultComponent(schema)" :key="refreshFieldObj[schema.field]" v-model:value="formModel![schema.field]" :disabled="getDisable" :formData="formModel" :size="formProps?.size" v-bind="{...getComponentsProps, placeholder: getDisable ? '' : getComponentsProps.placeholder, disabledShowBorder: getDisabledShowBorder}" :title="readonlySupport(schema.component) ? formModel![schema.field] : ''"/>
|
||||
</template>
|
||||
</FormItem>
|
||||
</template>
|
||||
@ -218,7 +221,8 @@
|
||||
const tabActiveKey = inject<Ref<number>>('tabActiveKey', ref(0));
|
||||
const activeKey = ref<number>(0);
|
||||
const isCamelCase = inject<boolean>('isCamelCase', false);
|
||||
|
||||
// 注入整个表单的配置,formProps是个计算属性,不能修改,formData则来自每个业务的表单页面。
|
||||
const formData = inject('formData', { noInject: true });
|
||||
watch(
|
||||
() => tabActiveKey?.value,
|
||||
(val) => {
|
||||
@ -262,6 +266,17 @@
|
||||
return disabled;
|
||||
});
|
||||
|
||||
const getDisabledShowBorder = computed(() => {
|
||||
let disabledShowBorder = false
|
||||
if (getComponentsProps.value?.disabledShowBorder) {
|
||||
disabledShowBorder = true;
|
||||
}
|
||||
if(import.meta.env?.VITE_GLOB_READ_ONLY_BORDER_DISABLED) {
|
||||
disabledShowBorder = import.meta.env?.VITE_GLOB_READ_ONLY_BORDER_DISABLED == 'true'
|
||||
}
|
||||
return disabledShowBorder
|
||||
})
|
||||
|
||||
const getComponentsProps = computed(() => {
|
||||
let { componentProps = {} } = props.schema;
|
||||
|
||||
@ -356,9 +371,9 @@
|
||||
// console.log('formitem watch!!!!!!!!');
|
||||
//填值以后需要手动校验的组件
|
||||
const validateComponents = ['User', 'RichTextEditor', 'Upload', 'SelectMap'];
|
||||
if (validateComponents.includes(props.schema.component) && formModel![props.schema.field]) {
|
||||
if (validateComponents.includes(props.schema?.component) && formModel![props.schema?.field]) {
|
||||
setTimeout(() => {
|
||||
props.formApi?.validateFields([props.schema.field]);
|
||||
props.formApi?.validateFields([props.schema?.field]);
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
@ -394,32 +409,53 @@
|
||||
};
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema?.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function readonlySupport(name) {
|
||||
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker|InputTextArea)$/.test(name);
|
||||
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker)$/.test(name);
|
||||
}
|
||||
|
||||
function getShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isIfShow = show;
|
||||
}
|
||||
return isIfShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { componentProps, show } = schema as any;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
|
||||
if (isFunction(componentProps?.isShow)) {
|
||||
isShow = componentProps.isShow({
|
||||
values:formModel![schema.field],
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
|
||||
return isShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
|
||||
let isShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isShow = show;
|
||||
}
|
||||
if (isFunction(show)) {
|
||||
isShow = show({
|
||||
values:formModel![schema.field],
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
|
||||
isShow = isShow;
|
||||
return isShow;
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -53,6 +53,8 @@
|
||||
const tabActiveKey = inject<Ref<number>>('tabActiveKey', ref(0));
|
||||
const activeKey = ref<number>(0);
|
||||
const isCamelCase = inject<boolean>('isCamelCase', false);
|
||||
// 注入整个表单的配置,formProps是个计算属性,不能修改,formData则来自每个业务的表单页面。
|
||||
const formData = inject('formData', { noInject: true });
|
||||
watch(
|
||||
() => tabActiveKey?.value,
|
||||
(val) => {
|
||||
@ -122,7 +124,7 @@
|
||||
let field = camelCaseString(item);
|
||||
if (field) cloneFormModel[field] = cloneFormModel[item];
|
||||
}
|
||||
event(props.schema, isCamelCase ? cloneFormModel : formModel, props.formApi, {});
|
||||
event(props.schema, isCamelCase ? cloneFormModel : formModel, props.formApi, { formData });
|
||||
|
||||
if (isCamelCase) {
|
||||
for (let item in formModel) {
|
||||
@ -188,7 +190,7 @@
|
||||
// console.log('formitem watch!!!!!!!!');
|
||||
//填值以后需要手动校验的组件
|
||||
const validateComponents = ['User', 'RichTextEditor', 'Upload', 'SelectMap'];
|
||||
if (validateComponents.includes(props.schema.component) && formModel![props.schema.field]) {
|
||||
if (validateComponents.includes(props.schema?.component) && formModel![props.schema.field]) {
|
||||
setTimeout(() => {
|
||||
props.formApi?.validateFields([props.schema.field]);
|
||||
}, 100);
|
||||
@ -209,7 +211,7 @@
|
||||
};
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema?.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function readonlySupport(name) {
|
||||
@ -217,24 +219,35 @@
|
||||
}
|
||||
|
||||
function getShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isIfShow = show;
|
||||
}
|
||||
return isIfShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { componentProps, show } = schema as any;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
return isShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
|
||||
let isShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isShow = show;
|
||||
}
|
||||
if (isFunction(show)) {
|
||||
isShow = show({
|
||||
values: itemValue,
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
|
||||
isShow = isShow;
|
||||
return isShow;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="visible">
|
||||
<div v-if="visible" class="system-form">
|
||||
<component
|
||||
:is="componentName"
|
||||
v-if="visible"
|
||||
@ -28,7 +28,10 @@
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { createFormEvent, loadFormEvent } from '/@/hooks/web/useFormEvent';
|
||||
import { changeFormJson } from '/@/hooks/web/useWorkFlowForm';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import {message} from "ant-design-vue";
|
||||
const userStore = useUserStore();
|
||||
const userInfo = userStore.getUserInfo;
|
||||
|
||||
const props = defineProps({
|
||||
systemComponent: {
|
||||
@ -176,6 +179,13 @@
|
||||
async function getFieldsValue(){
|
||||
return SystemFormRef.value.getFieldsValue();
|
||||
}
|
||||
async function getFormModels() {
|
||||
try {
|
||||
return (SystemFormRef.value?.getFormModel && SystemFormRef.value?.getFormModel()) || await validate()
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
async function getValue(){
|
||||
let values = null;
|
||||
if(approvalData.value?.approvedResult === ApproveCode.FINISH){
|
||||
@ -198,21 +208,31 @@
|
||||
// 提交表单
|
||||
if (visible.value) {
|
||||
let id = await submit(saveRowKey);
|
||||
if(!id) {
|
||||
throw new Error(`提交表单失败`);
|
||||
|
||||
}
|
||||
let rowKey = getRowKey();
|
||||
values[rowKey] = id;
|
||||
values['_id'] = id;
|
||||
//重新查一遍
|
||||
let newValues=await SystemFormRef.value.setFormDataFromId(id,true);
|
||||
let newValues=await SystemFormRef.value.setFormDataFromId(id);
|
||||
if(newValues){
|
||||
values=newValues;
|
||||
} else {
|
||||
throw new Error(`获取表单失败`);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function submit(saveRowKey) {
|
||||
try {
|
||||
let saveValId = '';
|
||||
let values = await SystemFormRef.value.validate();
|
||||
let rowKey = getRowKey();
|
||||
@ -221,7 +241,10 @@
|
||||
}
|
||||
if (values[rowKey]) {
|
||||
// 编辑
|
||||
await SystemFormRef.value.update({ values, rowId: values[rowKey] });
|
||||
let res = await SystemFormRef.value.update({ values, rowId: values[rowKey] });
|
||||
if(!res) {
|
||||
throw new Error(`提交表单失败`);
|
||||
}
|
||||
saveValId = values[rowKey];
|
||||
} else {
|
||||
// 新增
|
||||
@ -232,6 +255,9 @@
|
||||
}
|
||||
}
|
||||
return saveValId;
|
||||
} catch(e) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function setDisabledForm(isDisabled) {
|
||||
@ -245,13 +271,22 @@
|
||||
async function handleDelete(id) {
|
||||
let ret;
|
||||
try {
|
||||
if(!SystemFormRef.value?.handleDelete) {
|
||||
throw new Error(`表单未配置删除`);
|
||||
}
|
||||
ret = await SystemFormRef.value.handleDelete(id);
|
||||
} catch (e) {
|
||||
message.error('表单未配置删除');
|
||||
return null;
|
||||
throw new Error(e);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function handleInnerFun(funcName) {
|
||||
if(!SystemFormRef.value?.[funcName]) {
|
||||
message.error(`表单未配置${funcName}方法`);
|
||||
return
|
||||
}
|
||||
SystemFormRef.value[funcName]()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
workflowSubmit,
|
||||
@ -263,7 +298,9 @@
|
||||
getFieldsValue,
|
||||
getIsOldSystem,
|
||||
setDisabledForm,
|
||||
handleDelete
|
||||
handleDelete,
|
||||
getFormModels,
|
||||
handleInnerFun
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
background-color: @component-background;
|
||||
|
||||
.ant-tree-treenode {
|
||||
align-items: center;
|
||||
align-items: center!important;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ import { ref, reactive, onMounted, } from 'vue';
|
||||
import Preview from '/@/views/workflow/design/Preview.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
import { h } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@ -89,6 +90,25 @@ const configColumns = [
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
width: 180,
|
||||
customRender: ({ text }) => {
|
||||
return h(
|
||||
'a-tooltip',
|
||||
{ title: text || '' },
|
||||
[
|
||||
h('div', {
|
||||
style: {
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
wordBreak: 'break-all',
|
||||
cursor: 'pointer',
|
||||
}
|
||||
}, text || '')
|
||||
]
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('创建时间'),
|
||||
@ -147,6 +167,15 @@ const getHistoryList = async () => {
|
||||
if (item.chooseCount == 0) {
|
||||
item.chooseCount = '';//置空
|
||||
}
|
||||
// 渲染备注 由json返回
|
||||
if (item.jsonContent) {
|
||||
try {
|
||||
const remarkObj = JSON.parse(item.jsonContent || '{}');
|
||||
item.remark = remarkObj?.processConfig?.remark || '';
|
||||
} catch (e) {
|
||||
item.remark = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {}
|
||||
|
||||
32
src/directives/allPermission.ts
Normal file
32
src/directives/allPermission.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Global authority directive
|
||||
* Used for fine-grained control of component permissions
|
||||
* @Example v-auth="RoleEnum.TEST"
|
||||
*/
|
||||
import type { App, Directive, DirectiveBinding } from 'vue';
|
||||
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
|
||||
function isAllAuth(el: Element, binding: any) {
|
||||
const { hasAllPermission } = usePermission();
|
||||
|
||||
const value = binding.value;
|
||||
if (!value) return;
|
||||
if (!hasAllPermission(value)) {
|
||||
el.parentNode?.removeChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
const mounted = (el: Element, binding: DirectiveBinding<any>) => {
|
||||
isAllAuth(el, binding);
|
||||
};
|
||||
|
||||
const allAuthDirective: Directive = {
|
||||
mounted,
|
||||
}
|
||||
|
||||
export function setupAllPermissionDirective(app: App) {
|
||||
app.directive('allAuth', allAuthDirective);
|
||||
}
|
||||
|
||||
export default allAuthDirective;
|
||||
@ -3,9 +3,11 @@
|
||||
*/
|
||||
import type { App } from 'vue';
|
||||
import { setupPermissionDirective } from './permission';
|
||||
import { setupAllPermissionDirective } from './allPermission';
|
||||
import { setupLoadingDirective } from './loading';
|
||||
|
||||
export function setupGlobDirectives(app: App) {
|
||||
setupPermissionDirective(app);
|
||||
setupAllPermissionDirective(app);
|
||||
setupLoadingDirective(app);
|
||||
}
|
||||
|
||||
@ -52,7 +52,13 @@ export enum NoHandler {
|
||||
// 指定审批人
|
||||
export enum DesignatedApprover {
|
||||
NOT_SPECIFIED = 0, //不指定审批人
|
||||
PREVIOUS_NODE, //由上一节点审批人指定
|
||||
PREVIOUS_NODE = 1, //由上一节点审批人指定
|
||||
}
|
||||
// 退回审批人
|
||||
export enum RejectApprover {
|
||||
APPROVER = 0, //候选人
|
||||
PREVIOUS = 1, //审批人
|
||||
PREVIOUS_LAST = 2, //最终审批人
|
||||
}
|
||||
|
||||
//权限类型
|
||||
@ -195,13 +201,25 @@ export enum ApproveType {
|
||||
REJECT, //驳回
|
||||
FINISH, //结束
|
||||
OTHER, //其他(用户自定义按钮)
|
||||
TRANSFER, //转办
|
||||
ADDSTEP, // 会签
|
||||
DRAWBACK, // 撤回
|
||||
FLOWBPMN, // 流程BPMN
|
||||
DRAFT, // 草稿
|
||||
FLOWRECORD, // 流程记录
|
||||
}
|
||||
export enum ApproveCode {
|
||||
AGREE = 'agree', //同意
|
||||
DISAGREE = 'disagree', //拒绝
|
||||
DISAGREE = 'disagree', //不同意
|
||||
REJECT = 'reject', //驳回
|
||||
TRANSFER = 'transfer', // 转办
|
||||
ADDSTEP = 'addStep', // 会签
|
||||
DRAWBACK = 'drawBack', // 撤回
|
||||
FINISH = 'finish', //结束
|
||||
OTHER = 'other', //其他(用户自定义按钮)
|
||||
FLOWBPMN = 'flowBpmn', // 流程BPMN
|
||||
FLOWRECORD='flowRecord', // 流程记录
|
||||
DRAFT = 'draft'
|
||||
}
|
||||
// 流程监控状态
|
||||
export enum ProcessMonitorStatus {
|
||||
|
||||
@ -2,10 +2,11 @@ import type { RouteLocationRaw, Router } from 'vue-router';
|
||||
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { isString } from '/@/utils/is';
|
||||
import { unref } from 'vue';
|
||||
import { unref, nextTick } from 'vue';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
import { REDIRECT_NAME } from '/@/router/constant';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
|
||||
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
|
||||
|
||||
@ -13,6 +14,8 @@ function handleError(e: Error) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
const specialRouterName = ['CreateFlow', 'ApproveFlow', 'viewForm', 'ProcessMonitoring', 'updateForm', 'createForm']
|
||||
|
||||
// page switch
|
||||
export function useGo(_router?: Router) {
|
||||
let router;
|
||||
@ -40,10 +43,11 @@ export function useGo(_router?: Router) {
|
||||
* @description: redo current page
|
||||
*/
|
||||
export const useRedo = (_router?: Router) => {
|
||||
const tabStore = useMultipleTabStore();
|
||||
const { push, currentRoute } = _router || useRouter();
|
||||
const { query, params = {}, name, fullPath } = unref(currentRoute.value);
|
||||
const { query, params = {}, name, fullPath, path } = unref(currentRoute.value);
|
||||
function redo(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(async(resolve) => {
|
||||
if (name === REDIRECT_NAME) {
|
||||
resolve(false);
|
||||
return;
|
||||
@ -55,7 +59,14 @@ export const useRedo = (_router?: Router) => {
|
||||
params['_redirect_type'] = 'path';
|
||||
params['path'] = fullPath;
|
||||
}
|
||||
if(specialRouterName.includes(name)){
|
||||
await tabStore.closeTab(currentRoute.value, _router || useRouter())
|
||||
nextTick(() => {
|
||||
push({ path: path, params, query }).then(() => resolve(true));
|
||||
})
|
||||
} else {
|
||||
push({ name: REDIRECT_NAME, params, query }).then(() => resolve(true));
|
||||
}
|
||||
});
|
||||
}
|
||||
return redo;
|
||||
|
||||
@ -60,6 +60,14 @@ export function usePermission() {
|
||||
closeAll();
|
||||
}
|
||||
|
||||
function hasAllPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
|
||||
if (!value) {
|
||||
return def;
|
||||
}
|
||||
const allPerm = permissionStore.getAllButtonPermCodeList
|
||||
return allPerm.includes(value as string)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether there is permission
|
||||
*/
|
||||
@ -269,6 +277,7 @@ export function usePermission() {
|
||||
return {
|
||||
changeRole,
|
||||
hasPermission,
|
||||
hasAllPermission,
|
||||
togglePermissionMode,
|
||||
refreshMenu,
|
||||
changeMenu,
|
||||
|
||||
@ -245,6 +245,8 @@ function getSchemePermissionItem(
|
||||
if (isViewProcess) {
|
||||
schema.dynamicDisabled = true;
|
||||
}
|
||||
} else if(schema.componentProps?.alwaysShow===true){
|
||||
schema.show=true;
|
||||
} else {
|
||||
schema.show = false;
|
||||
schema.dynamicDisabled = true;
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<BasicForm @register="registerForm" />
|
||||
<!-- <BasicForm @register="registerForm" /> -->
|
||||
|
||||
<div :class="`${prefixCls}__footer`">
|
||||
<a-button type="primary" block class="mt-2" @click="handleLock">
|
||||
@ -44,7 +44,7 @@
|
||||
const userStore = useUserStore();
|
||||
const lockStore = useLockStore();
|
||||
|
||||
const getRealName = computed(() => userStore.getUserInfo?.realName);
|
||||
const getRealName = computed(() => userStore.getUserInfo?.name);
|
||||
const [register, { closeModal }] = useModalInner();
|
||||
|
||||
const [registerForm, { validateFields, resetFields }] = useForm({
|
||||
@ -63,15 +63,14 @@
|
||||
});
|
||||
|
||||
async function handleLock() {
|
||||
const values = (await validateFields()) as any;
|
||||
const password: string | undefined = values.password;
|
||||
// const values = (await validateFields()) as any;
|
||||
// const password: string | undefined = values.password;
|
||||
closeModal();
|
||||
|
||||
lockStore.setLockInfo({
|
||||
isLock: true,
|
||||
pwd: password,
|
||||
});
|
||||
await resetFields();
|
||||
// await resetFields();
|
||||
}
|
||||
|
||||
const avatar = computed(() => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { is } from '/@/utils/is';
|
||||
const { t } = useI18n();
|
||||
export interface ListItem {
|
||||
id: string;
|
||||
@ -18,6 +19,7 @@ export interface ListItem {
|
||||
processId?: string;
|
||||
schemaId?: string;
|
||||
timeFormat?: string;
|
||||
isRead?: number;
|
||||
}
|
||||
|
||||
export interface TabItem {
|
||||
@ -55,4 +57,10 @@ export const tabListData: TabItem[] = [
|
||||
read: [],
|
||||
unreadNum: 0,
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
name: t('系统通知'),
|
||||
list: [],
|
||||
unreadNum: 0,
|
||||
},
|
||||
];
|
||||
|
||||
@ -72,6 +72,33 @@
|
||||
<span @click="setReadAll(item.key)">{{ t('全部设置已读') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!--系统通知消息-->
|
||||
<div v-else-if="item.key === '5'" class="h-88">
|
||||
<div v-if="item.list.length > 0" class="h-82">
|
||||
<div
|
||||
class="list-item readed-mark"
|
||||
v-for="it in item.list"
|
||||
:key="it.id"
|
||||
:class="it.isRead === 1 ? 'readed' : ''"
|
||||
@click="
|
||||
() => {
|
||||
it.isRead = 1;
|
||||
setReadSingle(it.id, item.key);
|
||||
goToRouter(it);
|
||||
}
|
||||
"
|
||||
>
|
||||
<span class="list-item-title" style="width: 220px;">
|
||||
<a-tooltip>
|
||||
<template #title>{{ it.title }}</template>
|
||||
{{ it.title }}
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<span class="list-item-time">{{ it.createDate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<a-empty :image="simpleImage" v-else/>
|
||||
</div>
|
||||
<div v-else class="h-88">
|
||||
<div v-if="item.list.length > 0" class="h-82">
|
||||
<div
|
||||
@ -138,6 +165,7 @@
|
||||
import { tabListData } from './data';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { downloadByUrl } from '/@/utils/file/download';
|
||||
|
||||
import {
|
||||
getOaMessage,
|
||||
@ -149,11 +177,14 @@
|
||||
setScheduleRead,
|
||||
setScheduleReadAll,
|
||||
} from '/@/api/system/login';
|
||||
import {queryLoginUserNotices, setSystemNoticeRead} from '/@/api/system/systemNotice/index'
|
||||
import { getInfoByDownloadUrl } from '/@/api/system/file';
|
||||
import { Empty } from 'ant-design-vue';
|
||||
|
||||
import ApprovalProcess from '/@/views/workflow/task/components/ApprovalProcess.vue';
|
||||
import LookProcess from '/@/views/workflow/task/components/LookProcess.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import {useRouter } from 'vue-router';
|
||||
const { t } = useI18n();
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -166,6 +197,7 @@
|
||||
LookProcess,
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const Approval = ref<{
|
||||
taskId?: string;
|
||||
processId?: string;
|
||||
@ -196,7 +228,8 @@
|
||||
if (o.read) o.read = [];
|
||||
});
|
||||
try {
|
||||
let res = import.meta.env.VITE_GLOB_DISABLE_NEWS ? [] : await getOaNews(1);
|
||||
if(import.meta.env.VITE_GLOB_DISABLE_NEWS !== 'true') {
|
||||
let res = await getOaNews(1);
|
||||
res.list.forEach((o) => {
|
||||
if (!o.readId) listData.value[0].unreadNum += 1;
|
||||
listData.value[0].list.push({
|
||||
@ -210,7 +243,7 @@
|
||||
read: o.isRead,
|
||||
});
|
||||
});
|
||||
let res1 = import.meta.env.VITE_GLOB_DISABLE_NEWS ? [] : await getOaNews(2);
|
||||
let res1 = await getOaNews(2);
|
||||
res1.list.forEach((o) => {
|
||||
if (!o.readId) listData.value[1].unreadNum += 1;
|
||||
listData.value[1].list.push({
|
||||
@ -224,7 +257,7 @@
|
||||
read: o.isRead,
|
||||
});
|
||||
});
|
||||
let res2 = import.meta.env.VITE_GLOB_DISABLE_NEWS ? [] : await getOaMessage();
|
||||
let res2 = await getOaMessage();
|
||||
res2.forEach((o) => {
|
||||
if (o.messageType === 0) {
|
||||
if (!o.isRead) listData.value[2].unreadNum += 1;
|
||||
@ -272,11 +305,20 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//系统消息
|
||||
let res4 = import.meta.env.VITE_DISABLE_NOTE === 'true' ? {list:[]} : await queryLoginUserNotices({limit: 1, size: 10});
|
||||
listData.value[4].list = res4.list;
|
||||
listData.value[4].unreadNum = res4.list.filter((item) => item.isRead == 0).length;
|
||||
}
|
||||
|
||||
let res3 = await getScheduleMsg();
|
||||
res3.list.forEach((item) => (item.read = item.isRead));
|
||||
listData.value[2].unreadNum = res3.list.filter((x) => !x.isRead).length;
|
||||
listData.value[2].list.push(...res3.list);
|
||||
console.log('message', listData.value);
|
||||
} catch (error) {
|
||||
console.error('message error', error)
|
||||
clearInterval(times.value);
|
||||
}
|
||||
}
|
||||
@ -317,11 +359,26 @@
|
||||
await setScheduleRead([ids]);
|
||||
} else if (num == 4) {
|
||||
await setSingleRead(ids);
|
||||
} else if (num == 5) {
|
||||
await setSystemNoticeRead([ids]);
|
||||
} else {
|
||||
await setOaRead([ids]);
|
||||
}
|
||||
if (listData.value[num - 1].unreadNum > 0) listData.value[num - 1].unreadNum -= 1;
|
||||
}
|
||||
function goToRouter(record){
|
||||
if(record.type === '99') {
|
||||
//异步打包下载
|
||||
record.paramsJson = record.paramsJson ? JSON.parse(record.paramsJson) : {};
|
||||
let fileId = record.paramsJson.id || [];
|
||||
getInfoByDownloadUrl({id: fileId}).then((res)=>{
|
||||
let fileUrl = res.fileUrlFixed || res.fileUrl;
|
||||
downloadByUrl({ url: fileUrl, fileName: (res.fileName+res.fileType) || 'files.zip' });
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push(record.path)
|
||||
}
|
||||
onUnmounted(() => {
|
||||
clearInterval(times.value);
|
||||
});
|
||||
@ -358,6 +415,7 @@
|
||||
Approval,
|
||||
LookData,
|
||||
t,
|
||||
goToRouter,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
:width="24"
|
||||
:height="24"
|
||||
:src="getUserInfo.avatar"
|
||||
fallback="src/assets/images/header.jpg"
|
||||
:fallback="headerImg"
|
||||
/>
|
||||
</div>
|
||||
<span :class="`${prefixCls}__info hidden md:block`">
|
||||
|
||||
@ -63,6 +63,8 @@ async function bootstrap() {
|
||||
|
||||
//全局挂载axios
|
||||
app.config.globalProperties.$axios = axios;
|
||||
//挂载到全局变量(浏览器环境)
|
||||
window.$appContext = app._context;
|
||||
//取消点击esc关闭弹窗
|
||||
Modal.props.keyboard.default = false;
|
||||
|
||||
|
||||
@ -25,7 +25,9 @@ import {
|
||||
ExecutionType,
|
||||
MemberType,
|
||||
ApproveType,
|
||||
NodeEventExType, SubProcessType,
|
||||
NodeEventExType,
|
||||
SubProcessType,
|
||||
RejectApprover,
|
||||
} from '/@/enums/workflowEnum';
|
||||
import { BpmnNodeKey } from '/@/enums/workflowEnum';
|
||||
|
||||
@ -98,8 +100,12 @@ export interface ProcessConfig {
|
||||
nameRule?: string; //命名规则
|
||||
nameRuleConfigs?: NameRule; // 命名规则列表
|
||||
autoAgreeRule?: Array<AutoAgreeRule>; //自动同意规则
|
||||
autoAgreeMsg?: string; //自动审批意见
|
||||
isPrevChooseNext: DesignatedApprover; //是否上一节点审批人指定下一节点审批人
|
||||
noHandler: NoHandler; //无对应处理人
|
||||
isChooseMulti: Boolean;
|
||||
isChooseAll: Boolean;
|
||||
isReadOnly: Boolean;
|
||||
defaultFormList: Array<FormSettingItem>; //默认表单
|
||||
appShow: boolean; //移动端是否显示
|
||||
content?: string; //bpmn 设计 xml
|
||||
@ -119,6 +125,7 @@ export interface ProcessConfig {
|
||||
globalSuspendedBeforeEventConfigs: NodeEventConfig[];//全局 挂起/暂停事件
|
||||
globalRestoreAfterEventConfigs: NodeEventConfig[];//全局 恢复事件
|
||||
globalSetSignAfterEventConfigs: NodeEventConfig[];//全局 会签事件
|
||||
globalWorkflowDataUpdate: string; //回写流程状态脚本
|
||||
xmlContent: String; //xml
|
||||
}
|
||||
|
||||
@ -174,6 +181,7 @@ export interface UserTaskConfig extends BasicNodeConfig {
|
||||
parentId: string; //父节点(流程id)
|
||||
currentProgress: undefined | number; //进度
|
||||
autoAgreeRule: Array<AutoAgreeRule>; //自动同意规则
|
||||
autoAgreeMsg: string; //自动审批意见
|
||||
isPrevChooseNext: DesignatedApprover; //是否上一节点审批人指定下一节点审批人
|
||||
noHandler: NoHandler; //无对应处理人
|
||||
provisionalApprover: Boolean; //临时审批人
|
||||
@ -188,6 +196,20 @@ export interface UserTaskConfig extends BasicNodeConfig {
|
||||
assignmentConfig: AssignmentConfig; //参数操作
|
||||
timeOutHandle: TimeOutConfig; //超时处理
|
||||
isChooseParallel: Boolean; //是否选择并行节点
|
||||
isChooseMulti: Boolean;
|
||||
isChooseAll: Boolean;
|
||||
isReadOnly: Boolean;
|
||||
rejectToPerson: RejectApprover, //退回到候选人/审批人
|
||||
rejectIsChooseAll: Boolean, //退回是否全选
|
||||
rejectIsChooseMulti: Boolean, //退回是否多选
|
||||
rejectIsReadOnly: Boolean, //退回是否只读
|
||||
forbidRejectToNodes: string[], //禁止退回到节点
|
||||
rejectToLastNodeOnly: Boolean,//只能退回到上个节点
|
||||
forbidRejectByOtherNodes: Boolean, //不能被其他节点退回到
|
||||
commitToRejectNode:Boolean, //允许直接提交到退回前节点
|
||||
// rejectToMainProcess:true, //退回到主流程
|
||||
rejectNotToStorey: string[], //禁止退回到子流程
|
||||
rejectAllSubProcess:Boolean, //退回所有子流程
|
||||
}
|
||||
/**
|
||||
* 脚本节点配置
|
||||
@ -227,12 +249,12 @@ export interface EndEventConfig extends BasicNodeConfig {
|
||||
*/
|
||||
export interface SubProcessConfig extends BasicNodeConfig {
|
||||
parentId: string; //父节点(流程id)
|
||||
subProcessType: SubProcessType.MULTIPLE, // 调用类型
|
||||
finishType: FinishType.ALL, //完成条件
|
||||
subProcessType: SubProcessType, // 调用类型
|
||||
finishType: FinishType, //完成条件
|
||||
percentOf: undefined, //百分比数值
|
||||
executionType: ExecutionType.PARALLEL, //执行类型
|
||||
executionType: ExecutionType, //执行类型
|
||||
originatorNode: '', //如果调用类型为单实例 子流程发起人
|
||||
originatorType: MemberType.FORM_FIELD, //发起人类型
|
||||
originatorType: MemberType, //发起人类型
|
||||
originatorConfig: '', //表单数据
|
||||
approverConfigs: MemberConfig[], //审批人
|
||||
inParams: [], //输入参数
|
||||
|
||||
@ -9,12 +9,19 @@ import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
|
||||
|
||||
import { RootRoute } from '/@/router/routes';
|
||||
|
||||
import { getAppEnvConfig } from '/@/utils/env';
|
||||
import {useTenantManager} from "/@/utils/tenantManager";
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||
|
||||
const ROOT_PATH = RootRoute.path;
|
||||
|
||||
const whitePathList: PageEnum[] = [LOGIN_PATH];
|
||||
|
||||
const tenantEnabled=getAppEnvConfig().VITE_GLOB_TENANT_ENABLED;
|
||||
|
||||
export function createPermissionGuard(router: Router) {
|
||||
const userStore = useUserStoreWithOut();
|
||||
const permissionStore = usePermissionStoreWithOut();
|
||||
@ -22,9 +29,27 @@ export function createPermissionGuard(router: Router) {
|
||||
let isOnlyShowContent=to.query?.isOnlyShowContent;
|
||||
if(isOnlyShowContent=='Y'){
|
||||
window.isOnlyShowContent='Y';
|
||||
}else{
|
||||
}else if(isOnlyShowContent=='N'){
|
||||
window.isOnlyShowContent='N';
|
||||
}
|
||||
let autoToggleTenant=to.query?.autoToggleTenant;
|
||||
if(autoToggleTenant=='Y'){
|
||||
window.autoToggleTenant='Y';
|
||||
}else if(autoToggleTenant=='N'){
|
||||
window.autoToggleTenant='N';
|
||||
}
|
||||
if (tenantEnabled==='true') {
|
||||
let switchTenant = to.query?.switchTenant;
|
||||
let tenantCode = to.query?.tenantCode;
|
||||
if (switchTenant==='Y'&&tenantCode) {
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = storeToRefs(userStore);
|
||||
if(tenantCode!==userInfo?.value?.tenantCode){
|
||||
const {toggleLocal} = useTenantManager();
|
||||
await toggleLocal({tenantCode: tenantCode, goHome: false, tabCloseAction: "none"});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
from.path === ROOT_PATH &&
|
||||
to.path === PageEnum.BASE_HOME &&
|
||||
|
||||
@ -226,3 +226,21 @@ export const FLOW_ROUTE: AppRouteRecordRaw[] = [{
|
||||
}
|
||||
]
|
||||
}];
|
||||
export const PROCESS_MONITORING: AppRouteRecordRaw[] = [{
|
||||
path: '/processMonitoring/:arg1/:arg2',
|
||||
name: 'ProcessMonitoring',
|
||||
meta: {
|
||||
title: '流程监控'
|
||||
},
|
||||
component: LAYOUT,
|
||||
children: [
|
||||
{
|
||||
path: 'processMonitoringFlow',
|
||||
name: 'ProcessMonitoringFlow',
|
||||
component: () => import('/@/views/secondDev/processMonitoringPage.vue'),
|
||||
meta: {
|
||||
title: (route) => '流程监控'
|
||||
}
|
||||
},
|
||||
]
|
||||
}]
|
||||
|
||||
@ -5,7 +5,8 @@ import {
|
||||
REDIRECT_ROUTE,
|
||||
SYSTEM_ROUTE,
|
||||
USERCENTER_ROUTE,
|
||||
FLOW_ROUTE
|
||||
FLOW_ROUTE,
|
||||
PROCESS_MONITORING
|
||||
// CUSTOMFORM_ROUTE,
|
||||
} from '/@/router/routes/basic';
|
||||
|
||||
@ -64,6 +65,7 @@ export const basicRoutes = [
|
||||
PAGE_NOT_FOUND_ROUTE,
|
||||
SYSTEM_ROUTE,
|
||||
USERCENTER_ROUTE,
|
||||
...FLOW_ROUTE
|
||||
...FLOW_ROUTE,
|
||||
...PROCESS_MONITORING
|
||||
// CUSTOMFORM_ROUTE,
|
||||
];
|
||||
|
||||
@ -5,7 +5,8 @@ import { defineStore } from 'pinia';
|
||||
import { LOCK_INFO_KEY } from '/@/enums/cacheEnum';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
import { useUserStore } from './user';
|
||||
|
||||
import { usePermissionStore } from '/@/store/modules/permission';
|
||||
import { sendMobileLoginCode } from '/@/api/system/login';
|
||||
interface LockState {
|
||||
lockInfo: Nullable<LockInfo>;
|
||||
}
|
||||
@ -32,21 +33,23 @@ export const useLockStore = defineStore({
|
||||
// Unlock
|
||||
async unLock(password?: string) {
|
||||
const userStore = useUserStore();
|
||||
if (this.lockInfo?.pwd === password) {
|
||||
if (userStore.getToken && this.lockInfo?.pwd === password) {
|
||||
this.resetLockInfo();
|
||||
return true;
|
||||
}
|
||||
const tryLogin = async () => {
|
||||
try {
|
||||
const userName = userStore.getUserInfo?.userName;
|
||||
const tenantCode = userStore.getUserInfo?.tenantCode || '';
|
||||
const res = await userStore.login({
|
||||
userName,
|
||||
password: password!,
|
||||
tenantCode: tenantCode, // 补充
|
||||
goHome: false,
|
||||
mode: 'none',
|
||||
});
|
||||
if (res) {
|
||||
this.resetLockInfo();
|
||||
this.updatePermissionAndResetLock();
|
||||
}
|
||||
return res;
|
||||
} catch (error) {
|
||||
@ -55,5 +58,34 @@ export const useLockStore = defineStore({
|
||||
};
|
||||
return await tryLogin();
|
||||
},
|
||||
// Unlock by phone
|
||||
async unLockByPhone(code?: string) {
|
||||
const tryLogin = async () => {
|
||||
try {
|
||||
const userStore = useUserStore();
|
||||
const mobile = userStore.getUserInfo?.mobile;
|
||||
let params = {
|
||||
mobile: mobile,
|
||||
code: code
|
||||
}
|
||||
let res = await sendMobileLoginCode(params)
|
||||
if (res) {
|
||||
let params = {mode: 'none', goHome: false };
|
||||
userStore.tokenLogin(res.token, params);
|
||||
this.updatePermissionAndResetLock();
|
||||
}
|
||||
return res;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return await tryLogin();
|
||||
},
|
||||
// 更新权限 关闭 锁屏页
|
||||
async updatePermissionAndResetLock() {
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.changePermissionCode();
|
||||
this.resetLockInfo();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -25,6 +25,7 @@ import { MenuAuthModel } from '/@/api/system/login/model';
|
||||
import { getSubSystemList } from '/@/api/system/subSystem';
|
||||
|
||||
interface PermissionState {
|
||||
allButtonPermCodeList: string[];
|
||||
// Permission code list
|
||||
permCodeList: MenuAuthModel[];
|
||||
// Whether the route has been dynamically added
|
||||
@ -40,6 +41,7 @@ interface PermissionState {
|
||||
export const usePermissionStore = defineStore({
|
||||
id: 'app-permission',
|
||||
state: (): PermissionState => ({
|
||||
allButtonPermCodeList: [],
|
||||
permCodeList: [],
|
||||
// Whether the route has been dynamically added
|
||||
isDynamicAddedRoute: false,
|
||||
@ -54,6 +56,9 @@ export const usePermissionStore = defineStore({
|
||||
subSystemList: [],
|
||||
}),
|
||||
getters: {
|
||||
getAllButtonPermCodeList(): string[] {
|
||||
return this.allButtonPermCodeList || [];
|
||||
},
|
||||
getPermCodeList(): MenuAuthModel[] {
|
||||
return this.permCodeList || [];
|
||||
},
|
||||
@ -77,6 +82,12 @@ export const usePermissionStore = defineStore({
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setAllButtonPermCodeList(permList: string[]) {
|
||||
this.allButtonPermCodeList = permList;
|
||||
},
|
||||
getHasPermission(code) {
|
||||
return this.allButtonPermCodeList.includes(code);
|
||||
},
|
||||
setPermCodeList(permList: MenuAuthModel[]) {
|
||||
this.permCodeList = permList;
|
||||
},
|
||||
@ -138,6 +149,13 @@ export const usePermissionStore = defineStore({
|
||||
userInfo.desktopSchema = permResult.desktopSchema;
|
||||
|
||||
userStore.setUserInfo(userInfo);
|
||||
let allButtonPermCode = [];
|
||||
permResult.menuAuthList.forEach((item) => {
|
||||
if (item.buttonAuthCode) {
|
||||
allButtonPermCode = allButtonPermCode.concat(item.buttonAuthCode);
|
||||
}
|
||||
})
|
||||
this.setAllButtonPermCodeList(allButtonPermCode)
|
||||
this.setPermCodeList(permResult.menuAuthList);
|
||||
},
|
||||
async buildRoutesAction(isRefrashPermisson = true): Promise<AppRouteRecordRaw[]> {
|
||||
|
||||
@ -92,7 +92,7 @@ export const useUserStore = defineStore({
|
||||
): Promise<GetUserInfoModel | null> {
|
||||
try {
|
||||
const { token } = params;
|
||||
params.goHome=true;
|
||||
params.goHome = true;
|
||||
return await this.tokenLogin(token,params);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
@ -111,7 +111,7 @@ export const useUserStore = defineStore({
|
||||
const { goHome = true, mode, ...loginParams } = params;
|
||||
const data = await loginApi(loginParams, mode);
|
||||
const { token } = data;
|
||||
params.goHome=true;
|
||||
params.goHome = true;
|
||||
return await this.tokenLogin(token,params);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
|
||||
@ -22,7 +22,7 @@ export function getAppEnvConfig() {
|
||||
(import.meta.env as unknown as GlobEnvConfig)
|
||||
: window[ENV_NAME as any]) as unknown as GlobEnvConfig;
|
||||
|
||||
const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_APP_SHORT_NAME, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_REQUEST_TIMEOUT, VITE_GLOB_UPLOAD_URL, VITE_GLOB_UPLOAD_PREVIEW, VITE_GLOB_OUT_LINK_URL, VITE_GLOB_REPORT_URL, VITE_GLOB_PRINT_BASE_URL, VITE_GLOB_TENANT_ENABLED,VITE_GLOB_TENANT_INPUT_REQUIRED,VITE_GLOB_UPLOAD_ALERT_TIP } = ENV;
|
||||
const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL, VITE_GLOB_APP_SHORT_NAME, VITE_GLOB_API_URL_PREFIX, VITE_GLOB_REQUEST_TIMEOUT, VITE_GLOB_UPLOAD_URL, VITE_GLOB_UPLOAD_PREVIEW, VITE_GLOB_OUT_LINK_URL, VITE_GLOB_REPORT_URL, VITE_GLOB_PRINT_BASE_URL, VITE_GLOB_TENANT_ENABLED,VITE_GLOB_TENANT_INPUT_REQUIRED,VITE_GLOB_UPLOAD_ALERT_TIP, VITE_GLOB_CLOSE_ALERT_DISABLED } = ENV;
|
||||
|
||||
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||
warn(`VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`);
|
||||
@ -41,7 +41,8 @@ export function getAppEnvConfig() {
|
||||
VITE_GLOB_PRINT_BASE_URL,
|
||||
VITE_GLOB_TENANT_ENABLED,
|
||||
VITE_GLOB_TENANT_INPUT_REQUIRED,
|
||||
VITE_GLOB_UPLOAD_ALERT_TIP
|
||||
VITE_GLOB_UPLOAD_ALERT_TIP,
|
||||
VITE_GLOB_CLOSE_ALERT_DISABLED
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
25
src/utils/flow/index.js
Normal file
25
src/utils/flow/index.js
Normal file
@ -0,0 +1,25 @@
|
||||
const statusMap = {
|
||||
ACTIVE: '审批中',
|
||||
SUSPENDED: '挂起',
|
||||
COMPLETED: '已完成',
|
||||
'INTERNALLY_TERMINATED': '已终止'
|
||||
}
|
||||
|
||||
const statusColorMap = {
|
||||
ACTIVE: '#0000FF',
|
||||
SUSPENDED: '#EFBD47',
|
||||
COMPLETED: '#009900',
|
||||
'INTERNALLY_TERMINATED': '#FF0000'
|
||||
}
|
||||
|
||||
export function setIndexFlowStatus(workflowData) {
|
||||
const status = {};
|
||||
if (workflowData.taskIds) {
|
||||
status.label = '待审批';
|
||||
status.style = { color: '#CC9900' };
|
||||
} else {
|
||||
status.label = statusMap[workflowData.status]
|
||||
status.style = { color: statusColorMap[workflowData.status] }
|
||||
}
|
||||
return status
|
||||
}
|
||||
@ -645,6 +645,7 @@ ${hasTemplatePrint ? ' reactive ' : ''}
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { useFormConfig } from '/@/hooks/web/useFormConfig';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { setIndexFlowStatus } from '/@/utils/flow/index'
|
||||
import { get${pascalMainTableName} } from '/@/api/${
|
||||
model.outputConfig.outputValue
|
||||
}/${lowerClassName}';
|
||||
@ -1493,7 +1494,7 @@ ${hasTemplatePrint ? ' reactive ' : ''}
|
||||
if (record.workflowData?.enabled) {
|
||||
//与工作流有关联的表单
|
||||
if (record.workflowData.status) {
|
||||
// 查看按钮现在同时有流程和表单的功能
|
||||
actionsList.unshift(setIndexFlowStatus(record.workflowData))
|
||||
} else {
|
||||
actionsList = actionsList.concat(editAndDelBtn);
|
||||
}
|
||||
@ -1674,7 +1675,7 @@ export function buildSimpleFormCode(model: GeneratorConfig, _tableInfo: TableInf
|
||||
import { reactive, ref,onBeforeMount,onMounted } from 'vue';
|
||||
import { formProps, formEventConfigs ,formConfig} from './config';
|
||||
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
|
||||
import { add${pascalMainTableName}, get${pascalMainTableName}, update${pascalMainTableName} } from '/@/api/${
|
||||
import { add${pascalMainTableName}, get${pascalMainTableName}, update${pascalMainTableName}, delete${pascalMainTableName} } from '/@/api/${
|
||||
model.outputConfig.outputValue
|
||||
}/${lowerClassName}';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
@ -1867,6 +1868,12 @@ export function buildSimpleFormCode(model: GeneratorConfig, _tableInfo: TableInf
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
}
|
||||
function getFormModel() {
|
||||
return systemFormRef.value.formModel
|
||||
}
|
||||
async function handleDelete(id) {
|
||||
return await delete${pascalMainTableName}([id]);
|
||||
}
|
||||
defineExpose({
|
||||
setFieldsValue,
|
||||
resetFields,
|
||||
@ -1878,6 +1885,8 @@ export function buildSimpleFormCode(model: GeneratorConfig, _tableInfo: TableInf
|
||||
setMenuPermission,
|
||||
setWorkFlowForm,
|
||||
getRowKey,
|
||||
getFormModel,
|
||||
handleDelete
|
||||
});
|
||||
</script>\n
|
||||
`;
|
||||
|
||||
@ -24,6 +24,9 @@ import { notification } from 'ant-design-vue';
|
||||
import { throttle } from 'lodash-es';
|
||||
import useGlobalFlag from '/@/hooks/core/useGlobalFlag';
|
||||
|
||||
import { useLockStore } from '/@/store/modules/lock';
|
||||
|
||||
|
||||
const globSetting = useGlobSetting();
|
||||
const urlPrefix = globSetting.urlPrefix;
|
||||
const { createMessage, createErrorModal } = useMessage();
|
||||
@ -66,7 +69,7 @@ const transform: AxiosTransform = {
|
||||
throw new Error(t('请求出错,请稍候重试'));
|
||||
}
|
||||
// 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
|
||||
const { code, data, msg } = result;
|
||||
const { code, data, msg, tid } = result;
|
||||
|
||||
// 这里逻辑可以根据项目进行修改
|
||||
const hasSuccess = code === ResultEnum.SUCCESS;
|
||||
@ -83,10 +86,23 @@ const transform: AxiosTransform = {
|
||||
|
||||
const userStore = useUserStoreWithOut();
|
||||
userStore.setToken(undefined);
|
||||
console.log('登录超时,请重新登录!');
|
||||
|
||||
if (!window.location.hash.includes('login')&&!window.location.pathname.includes('login')
|
||||
&&!window.location.hash.includes('tokenLogin')&&!window.location.pathname.includes('tokenLogin')) {
|
||||
if (userStore.getUserInfo.loginConfig?.lockPageReLogin) {
|
||||
console.error('not login page goto lockpage window.location.hash={}, window.location.pathname={}', window.location.hash, window.location.pathname, timeoutMsg);
|
||||
// 触发全局弹层锁屏
|
||||
const lockStore = useLockStore();
|
||||
lockStore.setLockInfo({
|
||||
isLock: true,
|
||||
msg: timeoutMsg
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
userStore.logout(true);
|
||||
}
|
||||
}
|
||||
if (data) {
|
||||
if(data.urlToRedirectTo){
|
||||
//登录页面不跳转
|
||||
@ -98,12 +114,29 @@ const transform: AxiosTransform = {
|
||||
}else{
|
||||
// data 有logout 信息 ,存sessionStorage 给login page 弹框显示
|
||||
if (data.logoutMessage) {
|
||||
console.error('logoutMessage', data.logoutMessage);
|
||||
timeoutMsg = data.logoutMessage;
|
||||
sessionStorage.setItem('logoutInfoData', JSON.stringify({
|
||||
logoutMessage: data.logoutMessage,
|
||||
}));
|
||||
}
|
||||
if (userStore.getUserInfo.loginConfig?.lockPageReLogin) {
|
||||
console.error('go-login', timeoutMsg);
|
||||
// 触发全局弹层锁屏
|
||||
const lockStore = useLockStore();
|
||||
lockStore.setLockInfo({
|
||||
isLock: true,
|
||||
msg: timeoutMsg
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
const go = useGo();
|
||||
if(go) {
|
||||
go('/login');
|
||||
} else {
|
||||
throw new Error(timeoutMsg || t('请求出错,请稍候重试'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -118,6 +151,10 @@ const transform: AxiosTransform = {
|
||||
if (options.ignoreErrorInEditor && isEditorOpen.value) {
|
||||
ajaxError();
|
||||
} else {
|
||||
if (tid) {
|
||||
timeoutMsg = timeoutMsg + '(异常操作号:' + tid + ')';
|
||||
}
|
||||
console.error('异常操作:', timeoutMsg, new Date().toLocaleString());
|
||||
if (options.errorMessageMode === 'modal') {
|
||||
createErrorModal({ title: t('错误提示'), content: timeoutMsg });
|
||||
} else if (options.errorMessageMode === 'message') {
|
||||
@ -252,7 +289,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||
// authentication schemes,e.g: Bearer
|
||||
// authenticationScheme: 'Bearer',
|
||||
authenticationScheme: 'Bearer',
|
||||
timeout: 600000,
|
||||
timeout: globSetting.requestTimeout||60 * 1000,
|
||||
// 基础接口地址
|
||||
// baseURL: globSetting.apiUrl,
|
||||
|
||||
|
||||
@ -234,7 +234,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-col ant-form-item-control ant-col-12">
|
||||
<a-button v-auth="'monitor:appointedAuditor'" @click="flowChange">{{
|
||||
<a-button v-if="canClick" v-allAuth="'adminOperation:toDesignatedNode'" @click="flowChange">{{
|
||||
t('将任务流转到')
|
||||
}}</a-button>
|
||||
</div>
|
||||
@ -244,7 +244,12 @@
|
||||
<div class="ant-col ant-col-24">
|
||||
<div class="ant-row ant-form-item" style="row-gap: 0px;">
|
||||
<div class="ant-col ant-form-item-label" style="width: 120px;">
|
||||
<label>审批人</label>
|
||||
<label>
|
||||
审批人
|
||||
<a-tooltip title="当前节点为会签节点时是加减签,为普通节点时是加减审批人抢令牌">
|
||||
<QuestionCircleOutlined style="margin-left: 4px; cursor: pointer; color: #1890ff;" />
|
||||
</a-tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<div class="ant-col ant-form-item-control">
|
||||
<div class="ant-form-item-control-input">
|
||||
@ -256,16 +261,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-col ant-form-item-control ant-col-12">
|
||||
<!-- <a-button style="width: 20%;" v-auth="'monitor:appointedAuditor'" @click="addOrSubtractUser">{{
|
||||
t('加减签')
|
||||
}}</a-button> -->
|
||||
<a-button v-if="!showAdd" v-auth="'monitor:appointedAuditor'" style="margin-right: 10px;"
|
||||
@click="approveUser">{{
|
||||
t('修改审批人')
|
||||
}}</a-button>
|
||||
<AddOrSubtract v-else :schemaId="schemaId" :taskId="assignee.value.taskId"
|
||||
:selectedUser="assignee.value.assigneeVoList">
|
||||
</AddOrSubtract>
|
||||
<AddOrSubtractV2 :schemaId="schemaId" :taskId="assignee.value.taskId" v-if="canClick" v-allAuth="'adminOperation:setAssignee'"
|
||||
:lastAddStepUser="assignee.value.assigneeVoList" @change="changedUser">
|
||||
</AddOrSubtractV2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -316,7 +314,6 @@
|
||||
<!-- 指派审核人 -->
|
||||
<!-- 流程流转 -->
|
||||
<opinionDialog ref="opinionDlg" />
|
||||
<!-- <AddOrSubtract v-if="showAdd"></AddOrSubtract> -->
|
||||
|
||||
</template>
|
||||
|
||||
@ -326,12 +323,13 @@ import SimpleFormSetup from '/@/components/SimpleForm/src/SimpleFormSetup.vue';
|
||||
import { Col, Form, message, Row } from 'ant-design-vue';
|
||||
import SimpleFormItem from '/@/components/SimpleForm/src/components/SimpleFormItem.vue';
|
||||
import { ref, reactive, inject } from 'vue';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { CheckCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue';
|
||||
import ApproveProcessMonitor from '../../../views/workflow/task/components/flow/ApproveProcessMonitorUser.vue';
|
||||
import { data } from '../../demo/excel/data';
|
||||
import opinionDialog from '/@/components/SecondDev/OpinionDialogSelected.vue';
|
||||
import { getProcessUserNodes, SetChangeProcessNode } from '/@/api/workflow/adminOperation'
|
||||
import AddOrSubtract from '../../workflow/task/components/flow/AddOrSubtractWork.vue';
|
||||
import AddOrSubtractV2 from '../../workflow/task/components/flow/AddOrSubtractV2.vue';
|
||||
|
||||
|
||||
const FormItem = Form.Item;
|
||||
@ -339,6 +337,7 @@ const FormItem = Form.Item;
|
||||
export default {
|
||||
components: {
|
||||
CheckCircleOutlined,
|
||||
QuestionCircleOutlined,
|
||||
Form,
|
||||
Col,
|
||||
SimpleFormItem,
|
||||
@ -347,6 +346,7 @@ export default {
|
||||
ApproveProcessMonitor,
|
||||
opinionDialog,
|
||||
AddOrSubtract,
|
||||
AddOrSubtractV2
|
||||
},
|
||||
mixins: [SimpleFormSetup],
|
||||
setup(props, ctx) {
|
||||
@ -357,26 +357,22 @@ export default {
|
||||
const isCustom = ref(Boolean)
|
||||
const schemaId = ctx.attrs.schemaId;
|
||||
const processId = ctx.attrs.processId;
|
||||
const canClick = ctx.attrs.canClick
|
||||
isCustom.value = ctx.attrs.isCustom;
|
||||
|
||||
const showApproveUser = ref(Boolean);
|
||||
showApproveUser.value = false;
|
||||
const showAdd = ref(Boolean);
|
||||
showAdd.value = true;
|
||||
|
||||
const opinionDlg = ref();
|
||||
const selectedInfo = ref();
|
||||
const allTaskNodes = inject('taskNode');;
|
||||
const allTaskNodes = inject('taskNode');
|
||||
const refreshInfo = inject('refreshApproveInfo', () => {})
|
||||
|
||||
const users = ref('');
|
||||
users.value = assignee.value.assigneeVoList.map((ele) => {
|
||||
return ele.name + '(' + ele.code + ')';
|
||||
}).join(',')
|
||||
|
||||
if (assignee.value.assigneeVoList.length > 1) {
|
||||
showAdd.value = false;
|
||||
}
|
||||
|
||||
function approveUser() {
|
||||
showApproveUser.value = true;
|
||||
}
|
||||
@ -395,6 +391,10 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
function changedUser() {
|
||||
refreshInfo()
|
||||
}
|
||||
|
||||
function submit(info) {
|
||||
const key = info.taskId;
|
||||
const userIds = info.selectedList.map((ele) => {
|
||||
@ -426,7 +426,6 @@ export default {
|
||||
return {
|
||||
approveUser,
|
||||
flowChange,
|
||||
showAdd,
|
||||
processId,
|
||||
schemaId,
|
||||
showApproveUser,
|
||||
@ -435,6 +434,8 @@ export default {
|
||||
currentTaskAssigneeNames,
|
||||
opinionDlg,
|
||||
users,
|
||||
canClick,
|
||||
changedUser,
|
||||
...ret
|
||||
};
|
||||
},
|
||||
@ -455,7 +456,7 @@ export default {
|
||||
}
|
||||
});
|
||||
return schemaMap;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getIfShow2: function (key) {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<SimpleForm ref="systemFormRef" :formProps="data.formDataProps" :formModel="{}"
|
||||
:isWorkFlow="props.fromPage != FromPageType.MENU" :clickedTaskAssignees="props.clickedTaskAssignees"
|
||||
:processId="props.processId" :isCustom="props.customFlg" :schemaId="props.schemaId"
|
||||
:currentTaskAssigneeNames="taskAssigneeNames" />
|
||||
:currentTaskAssigneeNames="taskAssigneeNames" :canClick="canClick"/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, onMounted, computed } from 'vue';
|
||||
@ -27,6 +27,10 @@ const props = defineProps({
|
||||
customFlg: false,
|
||||
clickedTaskAssignees: {},
|
||||
currentTaskAssigneeNames: {},
|
||||
canClick: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fromPage: {
|
||||
type: Number,
|
||||
default: FromPageType.MENU,
|
||||
|
||||
@ -755,7 +755,6 @@
|
||||
}
|
||||
}
|
||||
async function handleLaunchProcess(record: Recordable) {
|
||||
debugger
|
||||
if (record.workflowData) {
|
||||
if (record.workflowData.draftId) {
|
||||
let res = await getDraftInfo(record.workflowData.draftId);
|
||||
|
||||
@ -23,9 +23,22 @@
|
||||
<a-button type="primary" @click="handleStepNext" v-show="current < 5">
|
||||
{{ t('下一步') }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleCodeGenerator" v-show="current === 5">
|
||||
<a-dropdown placement="bottom" :arrow="{ pointAtCenter: true }">
|
||||
<a-button type="primary" v-show="current === 5">
|
||||
{{ t('完成') }}
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;" @click="handleCodeGenerator('packAndDownload')">打包下载</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a href="javascript:;" @click="handleCodeGenerator('genCodeToProject')">生成到项目中</a>
|
||||
</a-menu-item>
|
||||
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -59,6 +72,11 @@
|
||||
import { AppFormProps } from '/@/model/generator/appFormConfig';
|
||||
import { addMobileFunc, editMobileFuncData, getMobileFuncInfo } from '/@/api/mobileDesign';
|
||||
import DesignLogo from '/@/components/ModalPanel/src/DesignLogo.vue';
|
||||
import {
|
||||
downloadCodes,
|
||||
} from '/@/api/system/generator';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
|
||||
const { t } = useI18n();
|
||||
const TableConfigStep = defineAsyncComponent({
|
||||
@ -229,16 +247,16 @@
|
||||
handleClose();
|
||||
emit('success');
|
||||
}
|
||||
async function handleCodeGenerator() {
|
||||
async function handleCodeGenerator(actionType:String) {
|
||||
const isOk = await stepValidate[5]();
|
||||
if (!isOk) {
|
||||
return;
|
||||
}
|
||||
await setParams(enabledMark.value);
|
||||
await setParams(enabledMark.value, actionType);
|
||||
handleClose();
|
||||
emit('success');
|
||||
}
|
||||
async function setParams(enabledMark) {
|
||||
async function setParams(enabledMark, actionType?:String) {
|
||||
if (
|
||||
generatorConfig.formJson?.hiddenComponent &&
|
||||
generatorConfig.formJson?.hiddenComponent.length
|
||||
@ -273,12 +291,22 @@
|
||||
if (data.formType === 1) {
|
||||
data.isGeneratorCode = generatorConfig!.outputConfig!.createCode ? 1 : 0;
|
||||
}
|
||||
data.actionType=actionType;
|
||||
let result;
|
||||
if (templateId.value) {
|
||||
data.id = templateId.value;
|
||||
data.appMenuId = appMenuId.value;
|
||||
await editMobileFuncData(data);
|
||||
result = await editMobileFuncData(data);
|
||||
} else {
|
||||
await addMobileFunc(data);
|
||||
result = await addMobileFunc(data);
|
||||
}
|
||||
if(data.actionType=="packAndDownload"&&result){
|
||||
const fileName=generatorConfig!.outputConfig!.className+'_'+dateUtil(new Date()).format('YYYY-MM-DD_HH_mm_ss');
|
||||
const res = await downloadCodes({uuid:result});
|
||||
downloadByData(
|
||||
res.data,
|
||||
fileName+".zip"
|
||||
);
|
||||
}
|
||||
}
|
||||
const handleClose = () => {
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('绑定字段')" v-if="!noHaveField.includes(data.type)">
|
||||
<a-select :value="data.bindField" size="mini" :placeholder="t('请选择表字段')" disabled>
|
||||
<a-select :value="data.bindField" size="mini" showSearch :placeholder="t('请选择表字段')" disabled>
|
||||
<a-select-option v-for="(field, idx) in fieldsInfo" :value="field.name" :key="idx">
|
||||
{{ field.name }}
|
||||
<span>
|
||||
|
||||
@ -225,27 +225,36 @@
|
||||
}
|
||||
|
||||
async function saveDraftData() {
|
||||
try {
|
||||
let formModes = {};
|
||||
|
||||
for (let index = 0; index < forms.configs.length; index++) {
|
||||
const ele = forms.configs[index];
|
||||
if (ele.formType == FormType.SYSTEM) {
|
||||
let values = await itemRefs.value[index].validate();
|
||||
let values = await itemRefs.value[index].getFormModels();
|
||||
formModes[ele.formKey] = values;
|
||||
} else {
|
||||
formModes[ele.formKey] = ele.formModel;
|
||||
}
|
||||
}
|
||||
return formModes;
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function getFormModels(saveRowKey) {
|
||||
try {
|
||||
let formModes = {};
|
||||
|
||||
for (let index = 0; index < forms.configs.length; index++) {
|
||||
const ele = forms.configs[index];
|
||||
if (ele.formType == FormType.SYSTEM) {
|
||||
let values = await itemRefs.value[index].workflowSubmit(saveRowKey);
|
||||
if(!values) {
|
||||
return
|
||||
}
|
||||
formModes[ele.formKey] = values;
|
||||
} else {
|
||||
formModes[ele.formKey] = ele.formModel;
|
||||
@ -260,6 +269,10 @@
|
||||
await submitFormEvent(ele, forms.configs[i]?.formModel);
|
||||
});
|
||||
return formModes;
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function getSystemType() {
|
||||
@ -273,13 +286,23 @@
|
||||
return system;
|
||||
}
|
||||
|
||||
function handleInnerFun(funcName) {
|
||||
for (let index = 0; index < forms.configs.length; index++) {
|
||||
const ele = forms.configs[index];
|
||||
if (ele.formType == FormType.SYSTEM) {
|
||||
itemRefs.value[index].handleInnerFun(funcName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
validateForm,
|
||||
getFormModels,
|
||||
saveDraftData,
|
||||
setFormData,
|
||||
getUploadComponentIds,
|
||||
getSystemType
|
||||
getSystemType,
|
||||
handleInnerFun
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -48,30 +48,7 @@
|
||||
redirect = list[1];
|
||||
}
|
||||
|
||||
|
||||
let tenantCode='';
|
||||
if(getAppEnvConfig().VITE_GLOB_TENANT_ENABLED=='true'){
|
||||
let url='';
|
||||
if(targetURL){
|
||||
url=targetURL;
|
||||
}else{
|
||||
url=redirect;
|
||||
}
|
||||
if(url.includes('tenantCode')){
|
||||
const fullString = decodeURIComponent(url);
|
||||
const queryString = fullString.split('?')[1];
|
||||
tenantCode = (queryString.match(/tenantCode=([^&]+)/) || [])[1]
|
||||
}
|
||||
|
||||
if(!tenantCode){
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('租户码不能为空!'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
let params = {...currentRoute.value.query, targetURL: targetURL,redirect: redirect, mode: 'none',tenantCode:tenantCode }; //不要默认的错误提示
|
||||
let params = {...currentRoute.value.query, targetURL: targetURL,redirect: redirect, mode: 'none'}; //不要默认的错误提示
|
||||
await userStore.singleLogin(params);
|
||||
}
|
||||
|
||||
|
||||
@ -10,38 +10,32 @@
|
||||
</slot>
|
||||
关闭
|
||||
</a-button>
|
||||
<a-button @click="saveDraft" v-if="!readonly">
|
||||
暂存
|
||||
</a-button>
|
||||
<a-button @click="setDraft" v-if="rDraftsId && !readonly">
|
||||
从草稿导入
|
||||
</a-button>
|
||||
<a-button v-if="!readonly && hasBtnApprove" type="primary" @click="onApproveClick()">
|
||||
<slot name="icon">
|
||||
<template v-for="(btn, index) in buttonMap.normal">
|
||||
<a-button @click="onClickBtn(btn)" :type="btn.buttonCode === ApproveCode.AGREE ? 'primary' : ''">
|
||||
<slot name="icon" v-if="btn.buttonCode === ApproveCode.AGREE">
|
||||
<check-circle-outlined />
|
||||
</slot>
|
||||
同意
|
||||
</a-button>
|
||||
<a-button v-if="!readonly && hasBtnReject" @click="onDenyClick">
|
||||
<slot name="icon">
|
||||
<slot name="icon" v-if="btn.buttonCode === ApproveCode.REJECT">
|
||||
<stop-outlined />
|
||||
</slot>
|
||||
拒绝
|
||||
{{btn.buttonName}}
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
</template>
|
||||
<template v-for="(btnGroup, btnGroupKey) in buttonMap">
|
||||
<a-dropdown v-if="btnGroupKey!=='normal' && btnGroup.length">
|
||||
<template #overlay>
|
||||
<a-menu @click="onMoreClick">
|
||||
<a-menu-item v-if="!readonly && hasBtnFinish" key="finish">终止</a-menu-item>
|
||||
<a-menu-item v-if="!readonly" key="transfer">转办</a-menu-item>
|
||||
<a-menu-item v-if="readonly && drawNode" key="drawBack">撤回</a-menu-item>
|
||||
<a-menu-item key="flowchart">查看流程图</a-menu-item>
|
||||
<a-menu>
|
||||
<a-menu-item v-for="(btn, index) in btnGroup" :key="btn.buttonCode" @click="onClickBtn(btn)">
|
||||
{{btn.buttonName}}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
更多
|
||||
{{btnGroupKey}}
|
||||
<down-outlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-space>
|
||||
</div>
|
||||
<FormInformation
|
||||
@ -67,6 +61,15 @@
|
||||
<a-button type="primary" @click="closeFlowChart">关闭</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
<a-modal :closable="false" v-if="showRecord" visible="true" centered class="geg" title="流程记录" width="1200px" @cancel="closeFlowRecord">
|
||||
<div class="flow-record-box">
|
||||
<FlowRecord :list="data.taskRecords" :processId="processId"/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button type="primary" @click="closeFlowRecord">关闭</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
<SelectUserV2 ref="selectUser" v-model:value="addStepUser" @change="changeAddStepUser" just-dialog title="加签减签"/>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
@ -74,14 +77,15 @@
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { onMounted, reactive, ref, unref, createVNode } from 'vue';
|
||||
import { onMounted, reactive, ref, unref, createVNode, provide } from 'vue';
|
||||
import FormInformation from '/@/views/secondDev/FormInformation.vue';
|
||||
import userTaskItem from '/@/views/workflow/task/hooks/userTaskItem';
|
||||
import { getApprovalProcess, postApproval, postGetNextTaskMaybeArrival, postTransfer, getDrawNode, withdraw } from '/@/api/workflow/task';
|
||||
import { ApproveCode, ApproveType } from '/@/enums/workflowEnum';
|
||||
import { ApproveCode, ApproveType, ButtonType } from '/@/enums/workflowEnum';
|
||||
import { CheckCircleOutlined, StopOutlined, CloseOutlined, DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import OpinionDialog from '/@/components/SecondDev/OpinionDialog.vue';
|
||||
import TransferDialog from '/@/components/SecondDev/TransferDialog.vue';
|
||||
import SelectUserV2 from '/@/components/Form/src/components/SelectUserV2.vue';
|
||||
import { separator } from '/@bpmn/config/info';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import Title from '/@/components/Title/src/Title.vue';
|
||||
@ -94,6 +98,8 @@
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { deleteDraft, postDraft, putDraft, getDraftInfo } from '/@/api/workflow/process';
|
||||
import { TaskTypeUrl } from '/@/enums/workflowEnum';
|
||||
import { postSetSign, postSetSignV2 } from '/@/api/workflow/task';
|
||||
import FlowRecord from '/@/views/workflow/task/components/flow/FlowRecord.vue';
|
||||
|
||||
|
||||
const spinning = ref(false);
|
||||
@ -121,10 +127,29 @@
|
||||
const transferDlg = ref();
|
||||
const validateSuccess = ref(false);
|
||||
const formInformation = ref();
|
||||
const selectUser = ref()
|
||||
const addStepUser = ref('')
|
||||
const lastAddStepUser = ref('')
|
||||
const hasBtnAddStep = ref(false)
|
||||
const hasBtnTransfer = ref(false)
|
||||
const showFlowChart = ref(false);
|
||||
const hasBtnApprove = ref(true);
|
||||
const showRecord = ref(false)
|
||||
const hasBtnApprove = ref(false);
|
||||
const hasBtnDisagree = ref(false)
|
||||
const hasBtnReject = ref(false);
|
||||
const hasBtnFinish = ref(false);
|
||||
const hasBtnDraft = ref(false)
|
||||
const approveBtnName = ref('同意')
|
||||
const transferBtnName = ref('转办')
|
||||
const addStepBtnName = ref('会签')
|
||||
const finishBtnName = ref('终止')
|
||||
const rejectBtnName = ref('拒绝')
|
||||
const drawBackBtnName = ref('撤回')
|
||||
const draftBtnName = ref('暂存')
|
||||
const disagreeBtnName = ref('不同意')
|
||||
const buttonMap = ref({
|
||||
normal: []
|
||||
})
|
||||
let draftData = {}
|
||||
const drawNode = ref('');
|
||||
const props = defineProps({
|
||||
@ -132,6 +157,8 @@
|
||||
type: String
|
||||
}
|
||||
});
|
||||
const processInfo = ref();
|
||||
provide("processInfo", processInfo);
|
||||
|
||||
let approvalData = reactive({
|
||||
isCountersign: false,
|
||||
@ -151,21 +178,51 @@
|
||||
nextTaskUser: {} // 格式为taskKey: 用户id(逗号分隔)
|
||||
});
|
||||
let approvedType = ref(ApproveType.AGREE);
|
||||
|
||||
function showButton(btn) {
|
||||
// 撤回有drawNode才显示,流程图任何情况下都显示
|
||||
let show = (btn.checked && ((!readonly.value && btn.buttonCode !== ApproveCode.DRAWBACK) || (readonly.value && btn.buttonCode === ApproveCode.DRAWBACK && drawNode.value))) || (btn.buttonCode === ApproveCode.FLOWBPMN || btn.buttonCode === ApproveCode.FLOWRECORD)
|
||||
return show
|
||||
}
|
||||
|
||||
|
||||
function onClickBtn(btn) {
|
||||
const key = btn.buttonCode;
|
||||
if(btn.buttonType === ButtonType.DEFAULT) {
|
||||
let funName = `handle${key}`
|
||||
methods[funName](btn)
|
||||
} else if(btn.buttonType === ButtonType.SCRIPT) {
|
||||
handleFunction(btn)
|
||||
}
|
||||
}
|
||||
function onMoreClick(e) {
|
||||
const key = e.key;
|
||||
if (key === 'flowchart') {
|
||||
openFlowChart();
|
||||
} else if (key === 'finish') {
|
||||
Modal.confirm({
|
||||
title: () => '提示',
|
||||
content: () => '确定终止吗?',
|
||||
onOk: () => {
|
||||
onFinishClick();
|
||||
if(btn.buttonType === ButtonType.DEFAULT) {
|
||||
let funName = `handle${key}`
|
||||
methods[funName](btn)
|
||||
} else if(btn.buttonType === ButtonType.SCRIPT) {
|
||||
handleFunction(btn)
|
||||
}
|
||||
});
|
||||
} else if (key === 'transfer') {
|
||||
onTransferClick();
|
||||
} else if (key === 'drawBack') {
|
||||
}
|
||||
|
||||
function handleFunction(btn) {
|
||||
// eval(script)
|
||||
if (btn.handleFuncName) {
|
||||
formInformation.value?.handleInnerFun(btn.handleFuncName)
|
||||
}
|
||||
}
|
||||
|
||||
const methods = {
|
||||
handlesetDraft() {
|
||||
setDraft()
|
||||
},
|
||||
handledraft() {
|
||||
saveDraft();
|
||||
},
|
||||
handleaddStep() {
|
||||
selectUser.value.show()
|
||||
},
|
||||
handledrawBack() {
|
||||
Modal.confirm({
|
||||
title: t('提示'),
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
@ -195,19 +252,49 @@
|
||||
},
|
||||
onCancel() {}
|
||||
});
|
||||
},
|
||||
handlefinish(btn) {
|
||||
Modal.confirm({
|
||||
title: () => '提示',
|
||||
content: () => `确定${btn.buttonName}吗?`,
|
||||
onOk: () => {
|
||||
onFinishClick();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleflowBpmn() {
|
||||
showFlowChart.value = true;
|
||||
},
|
||||
handletransfer() {
|
||||
onTransferClick();
|
||||
},
|
||||
handleagree() {
|
||||
onApproveClick()
|
||||
},
|
||||
handledisagree() {
|
||||
onDisagreeClick()
|
||||
},
|
||||
handlereject() {
|
||||
onDenyClick()
|
||||
},
|
||||
handleflowRecord() {
|
||||
showRecord.value = true;
|
||||
},
|
||||
}
|
||||
|
||||
function closeFlowChart() {
|
||||
showFlowChart.value = false;
|
||||
}
|
||||
|
||||
function openFlowChart() {
|
||||
showFlowChart.value = true;
|
||||
function closeFlowRecord() {
|
||||
showRecord.value = false
|
||||
}
|
||||
|
||||
function close() {
|
||||
tabStore.closeTab(currentRoute, router);
|
||||
if(window?.isOnlyShowContent=='Y') {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
async function setDraft(needModal = true) {
|
||||
let formData = [];
|
||||
@ -259,8 +346,40 @@
|
||||
notificationError(title);
|
||||
}
|
||||
}
|
||||
async function changeAddStepUser (ids, memberList) {
|
||||
try {
|
||||
spinning.value = true;
|
||||
let idList = memberList.map(item => {
|
||||
return item.id
|
||||
})
|
||||
let lastIdList = lastAddStepUser.value.split(',')
|
||||
let addUserIds = idList.filter(item => {
|
||||
return lastIdList.indexOf(item) == -1
|
||||
})
|
||||
let subUserIds = lastIdList.filter(item => {
|
||||
return idList.indexOf(item) == -1
|
||||
})
|
||||
let data = {
|
||||
addUserIds,
|
||||
subUserIds,
|
||||
schemaId: schemaId.value,
|
||||
taskId: taskId.value
|
||||
}
|
||||
await postSetSignV2(data);
|
||||
let res = await getApprovalProcess(unref(taskId), unref(processId));
|
||||
initProcessData(res);
|
||||
spinning.value = false;
|
||||
message.success('操作成功');
|
||||
} catch (e) {
|
||||
message.error(e)
|
||||
spinning.value = false;
|
||||
message.error('操作失败,请稍后再试');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function onApproveClick(isAutoAgreeBreak = false) {
|
||||
try {
|
||||
openSpinning();
|
||||
if (!isAutoAgreeBreak) {
|
||||
await submit();
|
||||
@ -288,6 +407,42 @@
|
||||
onFinish('approve');
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
closeSpinning();
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function onDisagreeClick() {
|
||||
|
||||
try {
|
||||
openSpinning();
|
||||
await submit();
|
||||
if (!validateSuccess.value) {
|
||||
closeSpinning();
|
||||
return;
|
||||
}
|
||||
const params = await getApproveParams();
|
||||
const nextNodes = await postGetNextTaskMaybeArrival(params);
|
||||
approvalData.approvedType = ApproveType.DISAGREE;
|
||||
approvalData.approvedResult = ApproveCode.DISAGREE;
|
||||
closeSpinning();
|
||||
opinionDlg.value.toggleDialog({
|
||||
action: 'disagree',
|
||||
nextNodes,
|
||||
callback: (args) => {
|
||||
approvalData.approvedContent = args.opinion;
|
||||
onFinish('disagree');
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
closeSpinning();
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function onDenyClick() {
|
||||
@ -379,12 +534,37 @@
|
||||
|
||||
function setBtnStatus() {
|
||||
const btnConfigs = approvalData.buttonConfigs;
|
||||
let draftBtn = btnConfigs.find((item) => item.buttonCode === ApproveCode.DRAFT)
|
||||
if(draftBtn && rDraftsId.value) {
|
||||
btnConfigs.push({
|
||||
...draftBtn,
|
||||
buttonName: t('从草稿导入'),
|
||||
buttonCode: 'setDraft',
|
||||
approveType: ApproveType.DRAFT,
|
||||
index: 1
|
||||
})
|
||||
}
|
||||
btnConfigs.forEach((btn) => {
|
||||
const code = btn.buttonCode;
|
||||
if (code === 'reject') {
|
||||
hasBtnReject.value = true;
|
||||
} else if (code === 'finish') {
|
||||
hasBtnFinish.value = true;
|
||||
const index = btn.index
|
||||
const buttonGroup = btn?.buttonGroup
|
||||
if(buttonGroup) {
|
||||
if(!buttonMap.value[buttonGroup]) {
|
||||
buttonMap.value[buttonGroup] = []
|
||||
}
|
||||
if(showButton(btn)) {
|
||||
buttonMap.value[buttonGroup].push(btn)
|
||||
}
|
||||
} else {
|
||||
if(showButton(btn)) {
|
||||
buttonMap.value['normal'].push(btn)
|
||||
}
|
||||
}
|
||||
for(let key in buttonMap.value) {
|
||||
buttonMap.value[key].sort((a, b) => {
|
||||
let aIndex = a?.index || 0
|
||||
let bIndex = b?.index || 0
|
||||
return aIndex - bIndex
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -399,16 +579,26 @@
|
||||
try {
|
||||
let res = await getApprovalProcess(unref(taskId), unref(processId));
|
||||
initProcessData(res);
|
||||
await getBackNode();
|
||||
processInfo.value = res;
|
||||
const title = res?.schemaInfo?.name;
|
||||
if (title) {
|
||||
const tabPrefix = readonly.value ? '查看' : '审批';
|
||||
tabStore.changeTitle(fullPath, `${tabPrefix}:${title}`);
|
||||
}
|
||||
if (!readonly.value) {
|
||||
if(taskId.value) {
|
||||
let ids = ''
|
||||
res.currentTaskAssignees[res.taskInfo.taskDefinitionKey].forEach((item, index) => {
|
||||
ids = `${ids}${index == 0 ? '' : ','}${item.assigneeIdStr}`
|
||||
})
|
||||
addStepUser.value = ids
|
||||
lastAddStepUser.value = ids
|
||||
}
|
||||
if (res.buttonConfigs) {
|
||||
approvalData.buttonConfigs = res.buttonConfigs;
|
||||
setBtnStatus();
|
||||
}
|
||||
if (!readonly.value) {
|
||||
if (res.relationTasks) {
|
||||
data.predecessorTasks = res.relationTasks;
|
||||
}
|
||||
@ -424,13 +614,12 @@
|
||||
approvalData.circulateConfigs = [];
|
||||
}
|
||||
renderKey.value = Math.random() + '';
|
||||
getBackNode();
|
||||
setDraft()
|
||||
} catch (error) {}
|
||||
});
|
||||
|
||||
function getBackNode() {
|
||||
getDrawNode(processId.value).then((res) => {
|
||||
async function getBackNode() {
|
||||
await getDrawNode(processId.value).then((res) => {
|
||||
if (res.length) {
|
||||
drawNode.value = res[0].activityId;
|
||||
} else {
|
||||
@ -481,6 +670,7 @@
|
||||
}
|
||||
|
||||
async function getApproveParams() {
|
||||
try {
|
||||
let formModels = await formInformation.value.getFormModels();
|
||||
let system = formInformation.value.getSystemType();
|
||||
let fileFolderIds = getUploadFileFolderIds(formModels);
|
||||
@ -499,11 +689,14 @@
|
||||
isEnd:approvalData.isEnd,
|
||||
nextTaskUser: approvalData.nextTaskUser
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function onFinish(values) {
|
||||
try {
|
||||
if (validateSuccess.value || values === 'reject' || values === 'finish') {
|
||||
if (validateSuccess.value || values === 'reject' || values === 'finish' || values === 'disagree') {
|
||||
let params = await getApproveParams();
|
||||
let response = await postApproval(params);
|
||||
// 判断返回值是否带有isAutoAgree 来判断中间是否有自动审批的业务,如有再执行判断待审人员是否包含自己,不包含就直接flowSuccess
|
||||
@ -513,6 +706,7 @@
|
||||
}
|
||||
} catch (error) {
|
||||
flowFail();
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,7 +719,6 @@
|
||||
&& response.length != 0
|
||||
&& response[0].isAutoAgree == true //
|
||||
&& response[0].approveUserIds.includes(userStore.getUserInfo.id)) {
|
||||
console.error('will reSelect user=', response[0].taskId);
|
||||
// 注入新得taskId
|
||||
taskId.value = response[0].taskId;
|
||||
data.submitLoading = false;
|
||||
@ -544,3 +737,10 @@
|
||||
spinning.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.flow-record-box {
|
||||
height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page-bg-wrap">
|
||||
<a-spin :spinning="loading" tip="加载中...">
|
||||
<div class="page-bg-wrap">
|
||||
<div class="geg-flow-page">
|
||||
<div class="top-toolbar">
|
||||
<a-space :size="10" wrap>
|
||||
@ -22,11 +22,8 @@
|
||||
</slot>
|
||||
暂存
|
||||
</a-button>
|
||||
<a-button>
|
||||
<slot name="icon">
|
||||
<printer-outlined />
|
||||
</slot>
|
||||
打印
|
||||
<a-button @click="handleDelete" danger v-if="rDraftsId!='0'">
|
||||
删除
|
||||
</a-button>
|
||||
<a-button @click="openFlowChart">
|
||||
<slot name="icon">
|
||||
@ -47,8 +44,8 @@
|
||||
</template>
|
||||
</a-modal>
|
||||
<opinion-dialog ref="opinionDlg" />
|
||||
</a-spin>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -63,7 +60,7 @@
|
||||
import { deleteDraft, postDraft, putDraft } from '/@/api/workflow/process';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { separator } from '/@bpmn/config/info';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import OpinionDialog from '/@/components/SecondDev/OpinionDialog.vue';
|
||||
import { ApproveCode, ApproveType } from '/@/enums/workflowEnum';
|
||||
import useEventBus from '/@/hooks/event/useEventBus';
|
||||
@ -80,7 +77,7 @@
|
||||
const fullPath = currentRoute.fullPath;
|
||||
const rQuery = currentRoute.query;
|
||||
const rSchemaId = rParams.arg1;
|
||||
const rDraftsId = rParams.arg2;
|
||||
let rDraftsId = ref(rParams.arg2);
|
||||
const taskId = ref();
|
||||
const loading = ref(false);
|
||||
const draftsJsonStr = localStorage.getItem('draftsJsonStr');
|
||||
@ -183,11 +180,12 @@
|
||||
try {
|
||||
disableSubmit.value = true;
|
||||
let formModels = await formInformation.value.saveDraftData();
|
||||
if (rDraftsId !== '0') {
|
||||
let res = await putDraft(rSchemaId, formModels, rDraftsId, props.rowKeyData);
|
||||
if (rDraftsId.value !== '0') {
|
||||
let res = await putDraft(rSchemaId, formModels, rDraftsId.value, props.rowKeyData);
|
||||
showResult(res, '保存草稿');
|
||||
} else {
|
||||
let res = await postDraft(rSchemaId, formModels, props.rowKeyData);
|
||||
rDraftsId.value = res
|
||||
showResult(res, '保存草稿');
|
||||
}
|
||||
} catch (error) {
|
||||
@ -291,11 +289,27 @@
|
||||
stampPassword: values.password,*/
|
||||
isOldSystem: system,
|
||||
nextTaskUser: approvalData.nextTaskUser,
|
||||
draftId: rDraftsId,
|
||||
draftId: rDraftsId.value,
|
||||
};
|
||||
}
|
||||
|
||||
async function saveLaunch() {
|
||||
if (!taskId.value && rDraftsId.value!='0') {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '请确认是否提交流程,提交后流程不能删除',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk: () => resolve(),
|
||||
onCancel: () => reject()
|
||||
});
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
data.submitLoading = true;
|
||||
loading.value = true;
|
||||
try {
|
||||
@ -306,6 +320,7 @@
|
||||
let successValidate = validateForms.filter((ele) => {
|
||||
return ele.validate;
|
||||
});
|
||||
console.info("validateForms:"+JSON.stringify(validateForms));
|
||||
if (successValidate.length == validateForms.length) {
|
||||
mainFormModels.value = await formInformation.value.getFormModels(true);
|
||||
/*for (let i in mainFormModels.value) {
|
||||
@ -388,6 +403,29 @@
|
||||
});
|
||||
return fileFolderIds;
|
||||
}
|
||||
async function handleDelete() {
|
||||
Modal.confirm({
|
||||
title: '提示信息',
|
||||
content: '是否确认删除?删除后无法恢复数据!',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
try {
|
||||
let res = await deleteDraft([rDraftsId.value]);
|
||||
if (res) {
|
||||
message.success('删除成功');
|
||||
setTimeout(() => {
|
||||
bus.emit(CREATE_FLOW, {});
|
||||
close();
|
||||
}, 500);
|
||||
} else {
|
||||
message.error('删除失败');
|
||||
}
|
||||
} catch (error) {}
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
|
||||
493
src/views/secondDev/flowFormInformation.vue
Normal file
493
src/views/secondDev/flowFormInformation.vue
Normal file
@ -0,0 +1,493 @@
|
||||
<template>
|
||||
<!-- 表单信息 -->
|
||||
<div class="form-container">
|
||||
<div class="box">
|
||||
<div class="form-right">
|
||||
<div v-for="(item, index) in forms.configs" :key="index" :tab="item.formName">
|
||||
<div v-show="activeIndex == index">
|
||||
<div class="page-bg-wrap">
|
||||
<div class="top-toolbar" style="display: flex;margin-bottom: 10px">
|
||||
<div id="adminButtons" v-show="activeIndex == index" style="margin-right:10px">
|
||||
<a-button @click="handleCancel" v-if="forms.modes[index] == 'edit'">取消</a-button>
|
||||
<a-button @click="handleSave" v-if="forms.modes[index] == 'edit'" type="primary" style="margin-left: 12px">保存</a-button>
|
||||
<a-button @click="handleEdit" v-if="forms.modes[index] == 'view'">编辑</a-button>
|
||||
<a-button @click="handleDelete" type="danger" style="margin-left: 12px">删除</a-button>
|
||||
</div>
|
||||
<div id="approveExtendButton"></div>
|
||||
<div id="approveRightButton"></div>
|
||||
<div id="approveExtendButtonLeft" v-show="false"></div>
|
||||
</div>
|
||||
<SystemForm class="form-box" v-if="item.formType == FormType.SYSTEM"
|
||||
:systemComponent="item.systemComponent" :isViewProcess="props.disabled" :formModel="item.formModel"
|
||||
:workflowConfig="item" :ref="setItemRef" />
|
||||
<SimpleForm v-else-if="item.formType == FormType.CUSTOM" class="form-box testClass" :ref="setItemRef"
|
||||
:formProps="item.formProps" :formModel="item.formModel" :isWorkFlow="true" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
|
||||
import { FewerLeft, FewerRight } from '/@/components/ModalPanel';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { onBeforeUpdate, nextTick, onMounted, reactive, computed, ref, inject, createVNode } from 'vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { TaskApproveOpinion, ValidateForms } from '/@/model/workflow/bpmnConfig';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { FormEventColumnConfig } from '/@/model/generator/formEventConfig';
|
||||
import { changeFormJson } from '/@/hooks/web/useWorkFlowForm';
|
||||
import { SystemForm } from '/@/components/SystemForm/index';
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { createFormEvent, loadFormEvent, submitFormEvent } from '/@/hooks/web/useFormEvent';
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { updateWorkflow } from '/@/api/workflow/adminOperation';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import { useRouter } from 'vue-router';
|
||||
import useEventBus from '/@/hooks/event/useEventBus';
|
||||
|
||||
const tabStore = useMultipleTabStore();
|
||||
const { bus, FLOW_PROCESSED } = useEventBus();
|
||||
const router = useRouter();
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled: boolean | undefined;
|
||||
formInfos: Array<any>;
|
||||
opinions?: Array<TaskApproveOpinion> | undefined;
|
||||
opinionsComponents?: Array<string> | undefined;
|
||||
formAssignmentData?: null | Recordable;
|
||||
processId: string;
|
||||
}>(),
|
||||
{
|
||||
disabled: false,
|
||||
formInfos: () => {
|
||||
return [];
|
||||
},
|
||||
processId: ''
|
||||
},
|
||||
);
|
||||
const flowInfo = inject('flowInfo')
|
||||
|
||||
const emits = defineEmits(['getFormConfigs']);
|
||||
let uploadComponent: { ids: Array<string> } = reactive({ ids: [] });
|
||||
|
||||
let activeIndex = ref(0);
|
||||
let itemRefs = ref([]) as any;
|
||||
const setItemRef = (el: never) => {
|
||||
itemRefs.value.push(el);
|
||||
};
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
itemRefs.value = [];
|
||||
});
|
||||
let forms: {
|
||||
formModels: Array<Recordable>;
|
||||
configs: Array<{
|
||||
formName: string;
|
||||
formProps: {};
|
||||
formModel: Recordable;
|
||||
formKey: string;
|
||||
validate: boolean;
|
||||
formType: FormType;
|
||||
workflowPermissions?: Array<any>;
|
||||
opinions?: Array<any>;
|
||||
opinionsComponents?: Array<any>;
|
||||
systemComponent?: {
|
||||
functionalModule: string;
|
||||
functionName: string;
|
||||
functionFormName: string;
|
||||
};
|
||||
formJson?: Array<any>;
|
||||
isOldSystem?: boolean;
|
||||
}>;
|
||||
formEventConfigs: FormEventColumnConfig[];
|
||||
modes: string[];
|
||||
} = reactive({
|
||||
formModels: [],
|
||||
configs: [],
|
||||
formEventConfigs: [],
|
||||
modes: []
|
||||
});
|
||||
onMounted(async () => {
|
||||
for await (let element of props.formInfos) {
|
||||
let formModels = {};
|
||||
if (element.formData) {
|
||||
formModels = cloneDeep(element.formData);
|
||||
}
|
||||
// 参数赋值[赋值权限最大]
|
||||
if (props.formAssignmentData) {
|
||||
if (props.formAssignmentData[element.formConfig.formId]) {
|
||||
formModels = { ...formModels, ...props.formAssignmentData[element.formConfig.formId] };
|
||||
}
|
||||
}
|
||||
forms.formModels.push(formModels);
|
||||
// 默认赋值view
|
||||
forms.modes.push('view');
|
||||
// 系统表单
|
||||
if (element.formType == FormType.SYSTEM) {
|
||||
forms.configs.push({
|
||||
formName: element.formConfig.formName,
|
||||
formProps: {},
|
||||
formModel: formModels,
|
||||
formKey: element.formConfig.key,
|
||||
validate: true,
|
||||
formType: element.formType,
|
||||
workflowPermissions: element.formConfig.children,
|
||||
opinions: props.opinions,
|
||||
opinionsComponents: props.opinionsComponents,
|
||||
systemComponent: {
|
||||
functionalModule: element.functionalModule,
|
||||
functionName: element.functionName,
|
||||
functionFormName: 'Form',
|
||||
},
|
||||
formJson: element.formJson,
|
||||
isOldSystem: false,
|
||||
});
|
||||
// 上传组件Id集合
|
||||
setTimeout(() => {
|
||||
getSystemUploadComponentIds();
|
||||
}, 0);
|
||||
} else {
|
||||
const model = JSON.parse(element.formJson) as GeneratorConfig;
|
||||
const { formJson, formEventConfig } = model;
|
||||
if (formEventConfig) {
|
||||
forms.formEventConfigs.push(formEventConfig);
|
||||
|
||||
//初始化表单
|
||||
await createFormEvent(formEventConfig, formModels, true);
|
||||
//加载表单
|
||||
await loadFormEvent(formEventConfig, formModels, true);
|
||||
|
||||
//TODO 暂不放开 工作流没有获取表单数据这个步骤 获取表单数据
|
||||
// getFormDataEvent(formEventConfig, formModels,true);
|
||||
}
|
||||
let formKey = element.formConfig.key;
|
||||
|
||||
let config = {
|
||||
formName: element.formConfig.formName,
|
||||
formProps: {},
|
||||
formModel: {},
|
||||
formKey,
|
||||
validate: true,
|
||||
formType: element.formType,
|
||||
};
|
||||
let isViewProcess = props.disabled;
|
||||
let { buildOptionJson, uploadComponentIds } = changeFormJson(
|
||||
{
|
||||
formJson,
|
||||
formConfigChildren: element.formConfig.children,
|
||||
formConfigKey: element.formConfig.key,
|
||||
opinions: props.opinions,
|
||||
opinionsComponents: props.opinionsComponents,
|
||||
},
|
||||
isViewProcess,
|
||||
uploadComponent.ids,
|
||||
);
|
||||
uploadComponent.ids = uploadComponentIds;
|
||||
if (buildOptionJson.schemas) {
|
||||
config.formProps = buildOptionJson;
|
||||
forms.configs.push(config);
|
||||
}
|
||||
}
|
||||
// });
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
setTimeout(() => {
|
||||
setFormModel();
|
||||
}, 0);
|
||||
emits('getFormConfigs', forms.configs.length ? forms.configs[activeIndex.value] : null);
|
||||
});
|
||||
|
||||
function setFormModel() {
|
||||
for (let index = 0; index < itemRefs.value.length; index++) {
|
||||
if (itemRefs.value[index]) {
|
||||
itemRefs.value[index].setFieldsValue(forms.formModels[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setFormData(formData) {
|
||||
await nextTick();
|
||||
forms.formModels = formData;
|
||||
setFormModel();
|
||||
}
|
||||
|
||||
function changeActiveIndex(index: number) {
|
||||
activeIndex.value = index;
|
||||
emits('getFormConfigs', forms.configs[activeIndex.value]);
|
||||
}
|
||||
function getSystemUploadComponentIds() {
|
||||
for (let index = 0; index < itemRefs.value.length; index++) {
|
||||
if (itemRefs.value[index] && itemRefs.value[index].getUploadComponentIds) {
|
||||
let ids = itemRefs.value[index].getUploadComponentIds();
|
||||
uploadComponent.ids = [...uploadComponent.ids, ...ids];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取上传组件的字段值集合
|
||||
function getUploadComponentIds() {
|
||||
return uploadComponent.ids;
|
||||
}
|
||||
async function validateForm() {
|
||||
let validateForms: ValidateForms = [];
|
||||
for (let index = 0; index < itemRefs.value.length; index++) {
|
||||
if (itemRefs.value[index]) {
|
||||
try {
|
||||
await itemRefs.value[index]?.validate();
|
||||
validateForms.push({
|
||||
validate: true,
|
||||
msgs: [],
|
||||
isOldSystem: forms.configs[index].isOldSystem,
|
||||
});
|
||||
forms.configs[index].validate = true;
|
||||
} catch (error: any | Array<{ errors: Array<string>; name: Array<string> }>) {
|
||||
validateForms.push({
|
||||
validate: false,
|
||||
msgs: error?.errorFields,
|
||||
});
|
||||
forms.configs[index].validate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return validateForms;
|
||||
}
|
||||
|
||||
async function saveDraftData() {
|
||||
let formModes = {};
|
||||
|
||||
for (let index = 0; index < forms.configs.length; index++) {
|
||||
const ele = forms.configs[index];
|
||||
if (ele.formType == FormType.SYSTEM) {
|
||||
let values = await itemRefs.value[index].validate();
|
||||
formModes[ele.formKey] = values;
|
||||
} else {
|
||||
formModes[ele.formKey] = ele.formModel;
|
||||
}
|
||||
}
|
||||
return formModes;
|
||||
}
|
||||
async function getFormModels(saveRowKey,isOnlyActive) {
|
||||
let formModes = {};
|
||||
for (let index = 0; index < forms.configs.length; index++) {
|
||||
if (isOnlyActive && index != activeIndex.value) {
|
||||
continue;
|
||||
}
|
||||
const ele = forms.configs[index];
|
||||
if (ele.formType == FormType.SYSTEM) {
|
||||
let values = await itemRefs.value[index].workflowSubmit(saveRowKey);
|
||||
formModes[ele.formKey] = values;
|
||||
} else {
|
||||
formModes[ele.formKey] = ele.formModel;
|
||||
}
|
||||
}
|
||||
|
||||
// forms.configs.forEach((ele) => {
|
||||
// formModes[ele.formKey] = ele.formModel;
|
||||
// });
|
||||
forms.formEventConfigs.forEach(async (ele, i) => {
|
||||
if (isOnlyActive && i != activeIndex.value) {
|
||||
return true;
|
||||
}
|
||||
//此组件 获取数据 就是为了提交表单 所以 表单提交数据 事件 就此处执行
|
||||
await submitFormEvent(ele, forms.configs[i]?.formModel);
|
||||
});
|
||||
return formModes;
|
||||
}
|
||||
function getSystemType() {
|
||||
let system = {};
|
||||
for (let index = 0; index < forms.configs.length; index++) {
|
||||
const ele = forms.configs[index];
|
||||
if (ele.formType == FormType.SYSTEM) {
|
||||
system[ele.formKey] = itemRefs.value[index].getIsOldSystem();
|
||||
}
|
||||
}
|
||||
return system;
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
itemRefs.value[activeIndex.value].setDisabledForm(true);
|
||||
forms.modes[activeIndex.value] = 'view';
|
||||
itemRefs.value[activeIndex.value].setFieldsValue(forms.formModels[activeIndex.value]);
|
||||
}
|
||||
async function handleDelete() {
|
||||
Modal.confirm({
|
||||
title: '提示信息',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: '是否确认删除?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
async onOk() {
|
||||
try{
|
||||
let formVal = await itemRefs.value[activeIndex.value].getFormModels();
|
||||
await itemRefs.value[activeIndex.value].handleDelete(formVal.id)
|
||||
message.success('删除成功')
|
||||
setTimeout(() => {
|
||||
tabStore.closeTab(currentRoute.value, router);
|
||||
bus.emit(FLOW_PROCESSED);
|
||||
}, 1000)
|
||||
} catch (err){
|
||||
message.error('删除失败,请稍后再试');
|
||||
}
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
const params = await getFormModels(true, true);
|
||||
const code = await updateWorkflow({ 'variables': params, 'processInstanceId': flowInfo.value.processId })
|
||||
|
||||
if (code) {
|
||||
message.success(t('保存成功'));
|
||||
} else {
|
||||
message.success(t('保存失败,请稍后再试'));
|
||||
}
|
||||
itemRefs.value[activeIndex.value].setDisabledForm(true);
|
||||
forms.modes[activeIndex.value] = 'view';
|
||||
}
|
||||
|
||||
function handleEdit() {
|
||||
itemRefs.value[activeIndex.value].setDisabledForm(false);
|
||||
forms.modes[activeIndex.value] = 'edit';
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
validateForm,
|
||||
getFormModels,
|
||||
saveDraftData,
|
||||
setFormData,
|
||||
getUploadComponentIds,
|
||||
getSystemType,
|
||||
handleEdit,
|
||||
handleSave,
|
||||
handleCancel,
|
||||
handleDelete
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.form-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 100%;
|
||||
|
||||
.form-left {
|
||||
float: left;
|
||||
height: 100vh;
|
||||
box-shadow: 5px 5px 5px rgb(0 0 0 / 10%);
|
||||
z-index: 9998;
|
||||
|
||||
.resize-shrink-sidebar {
|
||||
cursor: col-resize;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 9999;
|
||||
|
||||
.shrink-sidebar-text {
|
||||
padding: 0 2px;
|
||||
background: #f2f2f2;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.left-box {
|
||||
margin-right: 10px;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.form-name {
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
color: rgb(102 102 102 / 99.6%);
|
||||
margin-right: -2px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.actived {
|
||||
border-right: 1px solid #5e95ff;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.validate {
|
||||
background-color: @clear-color;
|
||||
}
|
||||
|
||||
.icon-box {
|
||||
font-size: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.left-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 10px 4px 10px 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
.in-or-out {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-box {
|
||||
overflow: auto;
|
||||
height: calc(100vh - 280px);
|
||||
}
|
||||
|
||||
.form-right {
|
||||
width: 100%;
|
||||
padding-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-bg-wrap {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.top-toolbar {
|
||||
min-height: 44px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<a-spin :spinning="spinning" tip="请稍后...">
|
||||
<div class="page-bg-wrap">
|
||||
<div class="top-toolbar">
|
||||
<div class="top-toolbar" id="formViewPage">
|
||||
<a-space :size="10" wrap style="gap:0">
|
||||
<a-button style="margin-right: 10px" @click="close">
|
||||
<slot name="icon">
|
||||
@ -18,6 +19,7 @@
|
||||
</div>
|
||||
<component :is="dynamicComponent" ref="formRef" :fromPage="FromPageType.MENU" @form-mounted="onFormMounted" />
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -46,6 +48,8 @@ const tabStore = useMultipleTabStore();
|
||||
const formProps = ref(null);
|
||||
const formId = ref(currentRoute.value?.params?.id);
|
||||
|
||||
const spinning = ref(false)
|
||||
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const hash = location.hash||location.pathname;
|
||||
@ -84,6 +88,7 @@ function close() {
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
spinning.value = true
|
||||
try {
|
||||
const saveSuccess = await saveModal();
|
||||
if (saveSuccess) {
|
||||
@ -98,6 +103,7 @@ async function handleSubmit() {
|
||||
}, 1000);
|
||||
}
|
||||
} finally {
|
||||
spinning.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,7 +111,8 @@ async function saveModal() {
|
||||
let saveSuccess = false;
|
||||
const _mode = mode.value;
|
||||
try {
|
||||
const values = await formRef.value?.validate();
|
||||
await formRef.value?.validate();
|
||||
const values = (formRef.value?.getFormModal && formRef.value.getFormModal()) || await formRef.value?.validate();
|
||||
//添加隐藏组件
|
||||
if (formProps.hiddenComponent?.length) {
|
||||
formProps.hiddenComponent.forEach((component) => {
|
||||
|
||||
82
src/views/secondDev/processMonitoringFlowPage.vue
Normal file
82
src/views/secondDev/processMonitoringFlowPage.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="flow-page">
|
||||
<FormInformation
|
||||
v-if="!isSelfForm"
|
||||
:key="renderKey"
|
||||
ref="formInformation"
|
||||
:disabled="readonly"
|
||||
:formAssignmentData="data.formAssignmentData"
|
||||
:formInfos="data.formInfos"
|
||||
:opinions="data.opinions"
|
||||
:opinionsComponents="data.opinionsComponents"
|
||||
@get-form-configs="(config) => (formConfigs = config)"
|
||||
/>
|
||||
<component v-else-if="data" :is="componentName" :formData="data" :flowData="flowData"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { onMounted, reactive, ref, unref, createVNode, provide, defineExpose, computed, defineAsyncComponent } from 'vue';
|
||||
import FormInformation from './flowFormInformation.vue';
|
||||
import { getApprovalProcess } from '/@/api/workflow/task';
|
||||
import Title from '/@/components/Title/src/Title.vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
const selfFormList = [
|
||||
// 流程编码
|
||||
// 'exchange_opinion',
|
||||
// 'audit_programme'
|
||||
]
|
||||
const selfFormMap = {
|
||||
// 流程编码对应文件路径
|
||||
// 'exchange_opinion': 'auditOperationsCenter/exchangeViews/index.vue',
|
||||
// 'audit_programme': 'auditOperationsCenter/preAuditProgramme/index.vue'
|
||||
|
||||
}
|
||||
const isSelfForm = computed(() => {
|
||||
return selfFormList.includes(props.data.item.code)
|
||||
})
|
||||
|
||||
const componentName = computed(() => {
|
||||
return defineAsyncComponent({
|
||||
loader: () =>
|
||||
import(
|
||||
`../${selfFormMap[props.data.item.code]}`
|
||||
|
||||
),
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
const readonly = ref(true); // 查看流程会触发只读模式
|
||||
const renderKey = ref('');
|
||||
const formConfigs = ref();
|
||||
const formInformation = ref();
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
},
|
||||
flowData: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.flow-page {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
144
src/views/secondDev/processMonitoringPage.vue
Normal file
144
src/views/secondDev/processMonitoringPage.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div class="process-monitoring-page">
|
||||
<a-spin :spinning="spinning" tip="请稍后...">
|
||||
<a-tabs tab-position="top" v-model:activeKey="activeKey" type="card">
|
||||
<a-tab-pane :key="1" :tab="'表单信息'" force-render>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :key="2" :tab="'流程信息'">
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :key="3" :tab="'流转记录'" style="overflow: auto">
|
||||
</a-tab-pane>
|
||||
<!-- <a-tab-pane :key="4" :tab="'附件汇总'">
|
||||
<SummaryOfAttachments :processId="processId"/>
|
||||
</a-tab-pane> -->
|
||||
<a-tab-pane :key="5" :tab="'流程变更记录'">
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :key="6" :tab="'审批记录'">
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :key="7" :tab="'流程变量'">
|
||||
</a-tab-pane>
|
||||
<!-- <a-tab-pane :key="8 + index" v-for="(item, index) in predecessorTasks" :tab="item.schemaName">
|
||||
<LookRelationTask
|
||||
v-show="activeKey === 8 + index"
|
||||
:taskId="item.taskId"
|
||||
:processId="item.processId"
|
||||
position="left"
|
||||
/>
|
||||
</a-tab-pane> -->
|
||||
</a-tabs>
|
||||
<div class="tab-content" v-if="dataComplete">
|
||||
<processMonitoringFlowPage :data="data" :flowData="flowData" v-show="activeKey == '1'"></processMonitoringFlowPage>
|
||||
<ProcessInformationWithInfo :process-id="processId" :data="data" :flowData="flowData" v-if="activeKey == '2'" :canClick="true" :key="ProcessInformationKey"/>
|
||||
<FlowRecord :list="data.taskRecords" :processId="processId" v-show="activeKey == '3'"/>
|
||||
<ChangeRecord v-if="activeKey == 5" :processId="processId" :formDataId="data.formInfos[0].formData.id" :formInfos="data.formInfos" />
|
||||
<AuditRecord :processId="processId" :schemaId="schemaId" :xml="xml" v-show="activeKey == '6'"/>
|
||||
<ProcVarPage :processId="processId" :schemaId="schemaId" :xml="xml" v-show="activeKey == '7'"/>
|
||||
</div>
|
||||
|
||||
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, unref, provide } from 'vue';
|
||||
|
||||
import FlowRecord from '/@/views/workflow/task/components/flow/FlowRecord.vue';
|
||||
import ProcessInformationWithInfo from '/@/views/workflow/task/components/flow/ProcessInformationWithInfo.vue';
|
||||
import SummaryOfAttachments from '/@/views/workflow/task/components/flow/SummaryOfAttachments.vue';
|
||||
import LookRelationTask from '/@/views/workflow/task/components/flow/LookRelationTask.vue';
|
||||
import { SchemaTaskItem } from '/@/model/workflow/bpmnConfig';
|
||||
import ChangeRecord from '/@/views/formChange/formChangeLog/index.vue';
|
||||
import AuditRecord from '/@/views/auditOpt/auditRecord/index.vue';
|
||||
import processMonitoringFlowPage from './processMonitoringFlowPage.vue'
|
||||
// import ProcVarPage from '/@/views/editProVar/procVarManage/index.vue';
|
||||
import { getApprovalProcess } from '/@/api/workflow/task';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
|
||||
import userTaskItem from '/@/views/workflow/task/hooks/userTaskItem';
|
||||
const { data, initProcessData } = userTaskItem();
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const currentRoute = router.currentRoute.value;
|
||||
const fullPath = currentRoute.fullPath;
|
||||
const rQuery = currentRoute.query;
|
||||
const rParams = currentRoute.params;
|
||||
const schemaId = ref(rParams.arg1);
|
||||
const processId = ref(rParams.arg2);
|
||||
const ProcessInformationKey = ref(0)
|
||||
|
||||
const spinning = ref(false);
|
||||
const dataComplete = ref(false);
|
||||
|
||||
const flowInfo = ref({
|
||||
schemaId: schemaId.value,
|
||||
processId: processId.value
|
||||
})
|
||||
const flowData = ref({})
|
||||
provide('flowInfo', flowInfo)
|
||||
provide('refreshApproveInfo', getApproveInfo)
|
||||
|
||||
|
||||
const activeKey = ref(1);
|
||||
|
||||
onMounted(async () => {
|
||||
await getApproveInfo()
|
||||
});
|
||||
async function getApproveInfo() {
|
||||
try {
|
||||
spinning.value = true
|
||||
let res = await getApprovalProcess('', unref(processId.value));
|
||||
initProcessData(res);
|
||||
flowData.value = res
|
||||
spinning.value = false
|
||||
dataComplete.value = true
|
||||
ProcessInformationKey.value++
|
||||
} catch (error) {
|
||||
console.error('processMonitoringPage Error', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.process-monitoring-page {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
.ant-spin-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
.ant-tabs-nav {
|
||||
margin: 0;
|
||||
}
|
||||
.top-toolbar {
|
||||
display: flex;
|
||||
border-bottom: none;
|
||||
padding: 16px 0;
|
||||
}
|
||||
.tab-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
.process-information-with-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.flow-record-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -10,47 +10,107 @@
|
||||
v-show="showDate"
|
||||
>
|
||||
<LockOutlined />
|
||||
<span>{{ t('sys.lock.unlock') }}</span>
|
||||
<span>{{ t('解锁') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex w-screen h-screen justify-center items-center">
|
||||
<div :class="`${prefixCls}__hour`" class="relative mr-5 md:mr-20 w-2/5 h-2/5 md:h-4/5">
|
||||
<div class="flex flex-col w-screen h-screen justify-center items-center">
|
||||
<!-- 第一行:logo和系统名 -->
|
||||
<div :class="`${prefixCls}__logo-row`" class="flex items-center justify-center mb-8">
|
||||
<img :src="logoConfig.menuLogoUrl || logo" width="144" style="height: 144px" />
|
||||
<div :class="`${prefixCls}__sysname-box`" class="ml-4">
|
||||
{{ sysName }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第二行:hour和minute并排 -->
|
||||
<div class="flex items-center justify-center">
|
||||
<div :class="`${prefixCls}__hour`" class="relative mr-5 md:mr-20">
|
||||
<span>{{ hour }}</span>
|
||||
<span class="meridiem absolute left-5 top-5 text-md xl:text-xl" v-show="showDate">
|
||||
{{ meridiem }}
|
||||
</span>
|
||||
</div>
|
||||
<div :class="`${prefixCls}__minute w-2/5 h-2/5 md:h-4/5 `">
|
||||
<div :class="`${prefixCls}__minute`">
|
||||
<span> {{ minute }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 新增:lockErrorMsg 显示 -->
|
||||
<div v-if="lockErrorMsg" style="margin-top: 16px; color: #ff4d4f; font-size: 1.2rem;">
|
||||
{{ lockErrorMsg }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<transition name="fade-slide">
|
||||
<div :class="`${prefixCls}-entry`" v-show="!showDate">
|
||||
<div :class="`${prefixCls}-entry-content`">
|
||||
<div :class="`${prefixCls}-entry__header enter-x`">
|
||||
<img :src="userinfo.avatar || headerImg" :class="`${prefixCls}-entry__header-img`" />
|
||||
<p :class="`${prefixCls}-entry__header-name`">
|
||||
{{ userinfo.realName }}
|
||||
{{ userinfo.name }}
|
||||
</p>
|
||||
<p :class="`${prefixCls}-entry__header-name`">
|
||||
{{ userinfo.mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') }}
|
||||
</p>
|
||||
</div>
|
||||
<template v-if="loginType === 'password'">
|
||||
<InputPassword
|
||||
:placeholder="t('sys.lock.placeholder')"
|
||||
:placeholder="t('登录密码')"
|
||||
class="enter-x"
|
||||
v-model:value="password"
|
||||
@pressEnter="handleLogin"
|
||||
/>
|
||||
<span :class="`${prefixCls}-entry__err-msg enter-x`" v-if="errMsg">
|
||||
{{ t('sys.lock.alert') }}
|
||||
{{ t('请重新登录') }}
|
||||
</span>
|
||||
</template>
|
||||
<div v-else>
|
||||
<Input v-model:value="formData.code" :placeholder="t('验证码')" class="enter-x" size="small" style="height: 58px;">
|
||||
<template #suffix>
|
||||
<Button type="link" class="f-16" @click="getLoginCode" size="small" :disabled="codeButtonDisabled">
|
||||
{{ getCodeButtonName }}
|
||||
</Button>
|
||||
</template>
|
||||
</Input>
|
||||
</div>
|
||||
<div style="margin-top: 10px">
|
||||
<a-button :style="{ 'border-radius': '8px' }" block class="sub-button" type="primary" @click="handleLogin">
|
||||
解锁
|
||||
</a-button>
|
||||
</div>
|
||||
<div :class="`${prefixCls}-entry__footer enter-x`">
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
class="mt-2 mr-2 enter-x"
|
||||
:disabled="loading"
|
||||
style="font-size: 1.2rem;"
|
||||
@click="handleShowForm(true)"
|
||||
>
|
||||
{{ t('common.back') }}
|
||||
<!-- 返回锁屏界面 -->
|
||||
{{ t('取消') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
class="mt-2 mr-2 enter-x"
|
||||
:disabled="loading"
|
||||
style="font-size: 1.2rem;"
|
||||
@click="goLogin"
|
||||
>
|
||||
{{ t('返回登录界面') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
class="mt-2"
|
||||
type="link"
|
||||
size="small"
|
||||
style="font-size: 1.2rem;"
|
||||
@click="changeLoginType"
|
||||
:loading="loading">
|
||||
{{ loginType == 'sms' ? t('密码解锁') : t('短信解锁') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="back_login">
|
||||
<a-button
|
||||
type="link"
|
||||
size="small"
|
||||
@ -58,13 +118,9 @@
|
||||
:disabled="loading"
|
||||
@click="goLogin"
|
||||
>
|
||||
{{ t('sys.lock.backToLogin') }}
|
||||
{{ t('返回登录界面') }}
|
||||
</a-button>
|
||||
<a-button class="mt-2" type="link" size="small" @click="unLock()" :loading="loading">
|
||||
{{ t('sys.lock.entry') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
@ -74,6 +130,28 @@
|
||||
</div>
|
||||
<div class="text-2xl">{{ year }}/{{ month }}/{{ day }} {{ week }}</div>
|
||||
</div>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:maskClosable="false"
|
||||
centered
|
||||
title="请完成下列验证后继续"
|
||||
width="20%"
|
||||
cancelText=""
|
||||
>
|
||||
<div class="login-modal-content">
|
||||
<div class="refresh" @click="refreshTodo">
|
||||
刷新
|
||||
<a-icon :spin="refreshLoading" icon="ant-design:redo-outlined" class="redo-outlined" />
|
||||
</div>
|
||||
<a-image
|
||||
:width="200"
|
||||
:src="imgObj.imgBase64 || ''"
|
||||
/>
|
||||
<a-input v-model:value="imgCode" :placeholder="t('验证码')" size="large" />
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@ -87,16 +165,42 @@
|
||||
import { LockOutlined } from '@ant-design/icons-vue';
|
||||
import headerImg from '/@/assets/images/header.jpg';
|
||||
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
import logo from '/@/assets/images/logo_geg.png';
|
||||
import { getMobileLoginCode, getMobileLoginImg } from '/@/api/system/login';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const logoConfig = appStore.getLogoConfig;
|
||||
const sysName = ref(import.meta.env.VITE_SYSTEM_NAME);
|
||||
|
||||
const InputPassword = Input.Password;
|
||||
|
||||
const password = ref('');
|
||||
const captchaCode = ref('');
|
||||
const formData = ref({
|
||||
code: ''
|
||||
});
|
||||
const imgBase64 = ref('');
|
||||
const loginType = ref('password');
|
||||
const loading = ref(false);
|
||||
const errMsg = ref(false);
|
||||
const showDate = ref(true);
|
||||
|
||||
const lockErrorMsg = computed(() => lockStore.getLockInfo?.msg || '');
|
||||
|
||||
const { prefixCls } = useDesign('lock-page');
|
||||
const lockStore = useLockStore();
|
||||
const userStore = useUserStore();
|
||||
const getCodeButtonName = ref('获取验证码')
|
||||
const codeButtonDisabled = ref(false)
|
||||
const countdown = ref(60)
|
||||
const visible = ref(false);
|
||||
const refreshLoading = ref(false);
|
||||
const imgCode = ref('');
|
||||
const imgObj = ref({
|
||||
imgBase64: ''
|
||||
})
|
||||
let setCodeInterval = null
|
||||
|
||||
const { hour, month, minute, meridiem, year, day, week } = useNow(true);
|
||||
|
||||
@ -131,12 +235,87 @@
|
||||
function handleShowForm(show = false) {
|
||||
showDate.value = show;
|
||||
}
|
||||
|
||||
const getLoginCode = async () => {
|
||||
countdown.value = 60
|
||||
visible.value = true
|
||||
imgCode.value = ''
|
||||
await onMobileLoginImg()
|
||||
}
|
||||
|
||||
async function onMobileLoginImg() {
|
||||
imgObj.value = await getMobileLoginImg({account: userinfo.value.mobile})
|
||||
}
|
||||
|
||||
async function refreshTodo() {
|
||||
refreshLoading.value = true
|
||||
await onMobileLoginImg()
|
||||
refreshLoading.value = false
|
||||
}
|
||||
|
||||
async function handleOk() {
|
||||
await getMobileLoginCode({captchaCode: imgCode.value ,mobile: userinfo.value.mobile})
|
||||
setCodeInterval = setInterval(updateCountdown, 1000);
|
||||
onVisible()
|
||||
}
|
||||
|
||||
function updateCountdown() {
|
||||
if (countdown.value === 0) {
|
||||
getCodeButtonName.value = '获取验证码';
|
||||
codeButtonDisabled.value = false;
|
||||
clearInterval(setCodeInterval)
|
||||
} else {
|
||||
countdown.value--;
|
||||
getCodeButtonName.value = countdown.value + ' 秒后可重发';
|
||||
codeButtonDisabled.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onVisible () {
|
||||
visible.value = false
|
||||
imgObj.value.imgBase64 = ''
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
onVisible()
|
||||
}
|
||||
|
||||
function changeLoginType () {
|
||||
loginType.value = loginType.value == 'password' ? 'sms' : 'password'
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: unLockByPhone
|
||||
*/
|
||||
async function unLockByPhone() {
|
||||
if (!formData.value.code) {
|
||||
return;
|
||||
}
|
||||
let code = formData.value.code;
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await lockStore.unLockByPhone(code);
|
||||
errMsg.value = !res;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleLogin() {
|
||||
const obj = {
|
||||
password: unLock,
|
||||
sms: unLockByPhone,
|
||||
}
|
||||
obj[loginType.value]()
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-lock-page';
|
||||
|
||||
.@{prefix-cls} {
|
||||
z-index: @lock-page-z-index;
|
||||
z-index: 999;
|
||||
// 新增或修改背景色
|
||||
background: linear-gradient(180deg, #00356d 0%, rgb(0 53 109 / 0%) 100%), linear-gradient(180deg, #0074d3 2%, #011853 100%);
|
||||
|
||||
&__unlock {
|
||||
transform: translate(-50%, 0);
|
||||
@ -146,8 +325,8 @@
|
||||
&__minute {
|
||||
display: flex;
|
||||
font-weight: 700;
|
||||
color: #bababa;
|
||||
background-color: #141313;
|
||||
color: #ffffff;
|
||||
background-color: linear-gradient(180deg, #00356d 0%, rgb(0 53 109 / 0%) 100%), linear-gradient(180deg, #0074d3 2%, #011853 100%);
|
||||
border-radius: 30px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -187,6 +366,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__logo-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
img {
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
&__sysname-box {
|
||||
font-size: 6rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&-entry {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -200,7 +400,7 @@
|
||||
align-items: center;
|
||||
|
||||
&-content {
|
||||
width: 260px;
|
||||
width: 390px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
@ -231,4 +431,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.back_login {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
.login-modal-content {
|
||||
text-align: center;
|
||||
.refresh {
|
||||
text-align: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
216
src/views/system/Testfrom3/components/Form.vue
Normal file
216
src/views/system/Testfrom3/components/Form.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<SimpleForm
|
||||
ref="systemFormRef"
|
||||
:formProps="data.formDataProps"
|
||||
:formModel="{}"
|
||||
:isWorkFlow="props.fromPage!=FromPageType.MENU"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref,onBeforeMount,onMounted } from 'vue';
|
||||
import { formProps, formEventConfigs ,formConfig} from './config';
|
||||
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
|
||||
import { addTestfrom3, getTestfrom3, updateTestfrom3 } from '/@/api/system/Testfrom3';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { FormDataProps } from '/@/components/Designer/src/types';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { useFormConfig } from '/@/hooks/web/useFormConfig';
|
||||
import { FromPageType } from '/@/enums/workflowEnum';
|
||||
import { createFormEvent, getFormDataEvent, loadFormEvent, submitFormEvent,} from '/@/hooks/web/useFormEvent';
|
||||
import { changeWorkFlowForm, changeSchemaDisabled } from '/@/hooks/web/useWorkFlowForm';
|
||||
import { WorkFlowFormParams } from '/@/model/workflow/bpmnConfig';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const { filterFormSchemaAuth } = usePermission();
|
||||
const { mergeFormSchemas,mergeFormEventConfigs } = useFormConfig();
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
const RowKey = 'id';
|
||||
const emits = defineEmits(['changeUploadComponentIds','loadingCompleted', 'form-mounted']);
|
||||
const props = defineProps({
|
||||
fromPage: {
|
||||
type: Number,
|
||||
default: FromPageType.MENU,
|
||||
},
|
||||
});
|
||||
const systemFormRef = ref();
|
||||
const data: { formDataProps: FormDataProps } = reactive({
|
||||
formDataProps: {schemas:[]} as FormDataProps,
|
||||
});
|
||||
const state = reactive({
|
||||
formModel: {},
|
||||
});
|
||||
|
||||
let customFormEventConfigs=[];
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 合并渲染覆盖配置中的字段配置、表单事件配置
|
||||
await mergeCustomFormRenderConfig();
|
||||
|
||||
if (props.fromPage == FromPageType.MENU) {
|
||||
setMenuPermission();
|
||||
await createFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:初始化表单
|
||||
await loadFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
} else if (props.fromPage == FromPageType.FLOW) {
|
||||
emits('loadingCompleted'); //告诉系统表单已经加载完毕
|
||||
// loadingCompleted后 工作流页面直接利用Ref调用setWorkFlowForm方法
|
||||
} else if (props.fromPage == FromPageType.PREVIEW) {
|
||||
// 预览 无需权限,表单事件也无需执行
|
||||
} else if (props.fromPage == FromPageType.DESKTOP) {
|
||||
// 桌面设计 表单事件需要执行
|
||||
emits('loadingCompleted'); //告诉系统表单已经加载完毕
|
||||
await createFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:初始化表单
|
||||
await loadFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
}
|
||||
emits('form-mounted', formProps);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function mergeCustomFormRenderConfig() {
|
||||
let cloneProps=cloneDeep(formProps);
|
||||
let fEventConfigs=cloneDeep(formEventConfigs);
|
||||
if (formConfig.useCustomConfig) {
|
||||
if(props.fromPage !== FromPageType.FLOW){
|
||||
let formPath=currentRoute.value.query.formPath;
|
||||
//1.合并字段配置
|
||||
cloneProps.schemas=await mergeFormSchemas({formSchema:cloneProps.schemas!,formPath:formPath});
|
||||
//2.合并表单事件配置
|
||||
fEventConfigs=await mergeFormEventConfigs({formEventConfigs:fEventConfigs,formPath:formPath});
|
||||
}
|
||||
}
|
||||
data.formDataProps=cloneProps;
|
||||
customFormEventConfigs=fEventConfigs;
|
||||
}
|
||||
|
||||
// 根据菜单页面权限,设置表单属性(必填,禁用,显示)
|
||||
function setMenuPermission() {
|
||||
data.formDataProps.schemas = filterFormSchemaAuth(data.formDataProps.schemas!);
|
||||
}
|
||||
|
||||
// 校验form 通过返回表单数据
|
||||
async function validate() {
|
||||
let values = [];
|
||||
try {
|
||||
values = await systemFormRef.value?.validate();
|
||||
//添加隐藏组件
|
||||
if (data.formDataProps.hiddenComponent?.length) {
|
||||
data.formDataProps.hiddenComponent.forEach((component) => {
|
||||
values[component.bindField] = component.value;
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
return values;
|
||||
}
|
||||
// 根据行唯一ID查询行数据,并设置表单数据 【编辑】
|
||||
async function setFormDataFromId(rowId, skipUpdate) {
|
||||
try {
|
||||
const record = await getTestfrom3(rowId);
|
||||
if (skipUpdate) {
|
||||
return record;
|
||||
}
|
||||
setFieldsValue(record);
|
||||
state.formModel = record;
|
||||
await getFormDataEvent(customFormEventConfigs, state.formModel, systemFormRef.value, formProps.schemas); //表单事件:获取表单数据
|
||||
return record;
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
// 辅助设置表单数据
|
||||
function setFieldsValue(record) {
|
||||
systemFormRef.value.setFieldsValue(record);
|
||||
}
|
||||
// 重置表单数据
|
||||
async function resetFields() {
|
||||
await systemFormRef.value.resetFields();
|
||||
}
|
||||
// 设置表单数据全部为Disabled 【查看】
|
||||
async function setDisabledForm(isDisabled) {
|
||||
data.formDataProps.schemas = changeSchemaDisabled(cloneDeep(data.formDataProps.schemas),isDisabled);
|
||||
}
|
||||
// 获取行键值
|
||||
function getRowKey() {
|
||||
return RowKey;
|
||||
}
|
||||
// 更新api表单数据
|
||||
async function update({ values, rowId }) {
|
||||
try {
|
||||
values[RowKey] = rowId;
|
||||
state.formModel = values;
|
||||
let saveVal = await updateTestfrom3(values);
|
||||
await submitFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:提交表单
|
||||
return saveVal;
|
||||
} catch (error) {}
|
||||
}
|
||||
// 新增api表单数据
|
||||
async function add(values) {
|
||||
try {
|
||||
state.formModel = values;
|
||||
let saveVal = await addTestfrom3(values);
|
||||
await submitFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:提交表单
|
||||
return saveVal;
|
||||
} catch (error) {}
|
||||
}
|
||||
// 根据工作流页面权限,设置表单属性(必填,禁用,显示)
|
||||
async function setWorkFlowForm(obj: WorkFlowFormParams) {
|
||||
try {
|
||||
const cloneProps=cloneDeep(formProps);
|
||||
customFormEventConfigs=cloneDeep(formEventConfigs);
|
||||
if (formConfig.useCustomConfig) {
|
||||
const parts = obj.formConfigKey.split('_');
|
||||
const formId=parts[1];
|
||||
cloneProps.schemas=await mergeFormSchemas({formSchema:cloneProps.schemas!,formId:formId});
|
||||
customFormEventConfigs=await mergeFormEventConfigs({formEventConfigs:customFormEventConfigs,formId:formId});
|
||||
}
|
||||
|
||||
let flowData = changeWorkFlowForm(cloneProps, obj);
|
||||
let { buildOptionJson, uploadComponentIds, formModels, isViewProcess } = flowData;
|
||||
data.formDataProps = buildOptionJson;
|
||||
emits('changeUploadComponentIds', uploadComponentIds); //工作流中必须保存上传组件id【附件汇总需要】
|
||||
if (isViewProcess) {
|
||||
setDisabledForm(); //查看
|
||||
}
|
||||
state.formModel = formModels;
|
||||
if(formModels[RowKey]) {
|
||||
setFormDataFromId(formModels[RowKey], false)
|
||||
} else {
|
||||
setFieldsValue(formModels)
|
||||
}
|
||||
} catch (error) {}
|
||||
await createFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:初始化表单
|
||||
await loadFormEvent(customFormEventConfigs, state.formModel,
|
||||
systemFormRef.value,
|
||||
formProps.schemas); //表单事件:加载表单
|
||||
}
|
||||
defineExpose({
|
||||
setFieldsValue,
|
||||
resetFields,
|
||||
validate,
|
||||
add,
|
||||
update,
|
||||
setFormDataFromId,
|
||||
setDisabledForm,
|
||||
setMenuPermission,
|
||||
setWorkFlowForm,
|
||||
getRowKey,
|
||||
});
|
||||
</script>
|
||||
110
src/views/system/Testfrom3/components/Testfrom3Modal.vue
Normal file
110
src/views/system/Testfrom3/components/Testfrom3Modal.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" @cancel="handleClose" :paddingRight="15" :bodyStyle="{ minHeight: '400px !important' }">
|
||||
<ModalForm ref="formRef" :fromPage="FromPageType.MENU" />
|
||||
</BasicModal>
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { formProps } from './config';
|
||||
import ModalForm from './Form.vue';
|
||||
import { FromPageType } from '/@/enums/workflowEnum';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const { notification } = useMessage();
|
||||
const formRef = ref();
|
||||
const state = reactive({
|
||||
formModel: {},
|
||||
isUpdate: true,
|
||||
isView: false,
|
||||
isCopy: false,
|
||||
rowId: '',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
state.isUpdate = !!data?.isUpdate;
|
||||
state.isView = !!data?.isView;
|
||||
state.isCopy = !!data?.isCopy;
|
||||
|
||||
setModalProps({
|
||||
destroyOnClose: true,
|
||||
maskClosable: false,
|
||||
showCancelBtn: !state.isView,
|
||||
showOkBtn: !state.isView,
|
||||
canFullscreen: true,
|
||||
width: 900,
|
||||
});
|
||||
if (state.isUpdate || state.isView || state.isCopy) {
|
||||
state.rowId = data.id;
|
||||
if (state.isView) {
|
||||
await formRef.value.setDisabledForm();
|
||||
}
|
||||
await formRef.value.setFormDataFromId(state.rowId);
|
||||
} else {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (state.isView ? '查看' : !state.isUpdate ? '新增' : '编辑'));
|
||||
|
||||
async function saveModal() {
|
||||
let saveSuccess = false;
|
||||
try {
|
||||
const values = await formRef.value?.validate();
|
||||
//添加隐藏组件
|
||||
if (formProps.hiddenComponent?.length) {
|
||||
formProps.hiddenComponent.forEach((component) => {
|
||||
values[component.bindField] = component.value;
|
||||
});
|
||||
}
|
||||
if (values !== false) {
|
||||
try {
|
||||
if (!state.isUpdate || state.isCopy) {
|
||||
saveSuccess = await formRef.value.add(values);
|
||||
} else {
|
||||
saveSuccess = await formRef.value.update({ values, rowId: state.rowId });
|
||||
}
|
||||
return saveSuccess;
|
||||
} catch (error) {}
|
||||
}
|
||||
} catch (error) {
|
||||
return saveSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const saveSuccess = await saveModal();
|
||||
setModalProps({ confirmLoading: true });
|
||||
if (saveSuccess) {
|
||||
if (!state.isUpdate || state.isCopy) {
|
||||
//false 新增
|
||||
notification.success({
|
||||
message: 'Tip',
|
||||
description: t('新增成功!'),
|
||||
}); //提示消息
|
||||
} else {
|
||||
notification.success({
|
||||
message: 'Tip',
|
||||
description: t('修改成功!'),
|
||||
}); //提示消息
|
||||
}
|
||||
closeModal();
|
||||
formRef.value.resetFields();
|
||||
emit('success');
|
||||
}
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
</script>
|
||||
231
src/views/system/Testfrom3/components/config.ts
Normal file
231
src/views/system/Testfrom3/components/config.ts
Normal file
@ -0,0 +1,231 @@
|
||||
import { FormProps, FormSchema } from '/@/components/Form';
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
|
||||
export const formConfig = {
|
||||
useCustomConfig: false,
|
||||
};
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'danXingWenBen5518',
|
||||
label: '单行文本',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'danXingWenBen5841',
|
||||
label: '单行文本',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'jiShuZuJian6835',
|
||||
label: '计数组件',
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
dataIndex: 'danXingWenBen5518',
|
||||
title: '单行文本',
|
||||
componentType: 'input',
|
||||
align: 'left',
|
||||
|
||||
sorter: true,
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'danXingWenBen5841',
|
||||
title: '单行文本',
|
||||
componentType: 'input',
|
||||
align: 'left',
|
||||
|
||||
sorter: true,
|
||||
},
|
||||
|
||||
{
|
||||
dataIndex: 'jiShuZuJian6835',
|
||||
title: '计数组件',
|
||||
componentType: 'number',
|
||||
align: 'left',
|
||||
|
||||
sorter: true,
|
||||
},
|
||||
];
|
||||
//表单事件
|
||||
export const formEventConfigs = {
|
||||
0: [
|
||||
{
|
||||
type: 'circle',
|
||||
color: '#2774ff',
|
||||
text: '开始节点',
|
||||
icon: '#icon-kaishi',
|
||||
bgcColor: '#D8E5FF',
|
||||
isUserDefined: false,
|
||||
},
|
||||
{
|
||||
color: '#F6AB01',
|
||||
icon: '#icon-chushihua',
|
||||
text: '初始化表单',
|
||||
bgcColor: '#f9f5ea',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
1: [
|
||||
{
|
||||
color: '#B36EDB',
|
||||
icon: '#icon-shujufenxi',
|
||||
text: '获取表单数据',
|
||||
detail: '(新增无此操作)',
|
||||
bgcColor: '#F8F2FC',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
2: [
|
||||
{
|
||||
color: '#F8625C',
|
||||
icon: '#icon-jiazai',
|
||||
text: '加载表单',
|
||||
bgcColor: '#FFF1F1',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
3: [
|
||||
{
|
||||
color: '#6C6AE0',
|
||||
icon: '#icon-jsontijiao',
|
||||
text: '提交表单',
|
||||
bgcColor: '#F5F4FF',
|
||||
isUserDefined: false,
|
||||
nodeInfo: { processEvent: [] },
|
||||
},
|
||||
],
|
||||
4: [
|
||||
{
|
||||
type: 'circle',
|
||||
color: '#F8625C',
|
||||
text: '结束节点',
|
||||
icon: '#icon-jieshuzhiliao',
|
||||
bgcColor: '#FFD6D6',
|
||||
isLast: true,
|
||||
isUserDefined: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
export const formProps: FormProps = {
|
||||
labelCol: { span: 3, offset: 0 },
|
||||
labelAlign: 'right',
|
||||
layout: 'horizontal',
|
||||
size: 'default',
|
||||
schemas: [
|
||||
{
|
||||
key: '4251bdeb18574f2cb014ca33d650be52',
|
||||
field: 'danXingWenBen5518',
|
||||
label: '单行文本',
|
||||
type: 'input',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
width: '100%',
|
||||
span: '',
|
||||
defaultValue: '',
|
||||
labelWidthMode: 'fix',
|
||||
labelFixWidth: 120,
|
||||
responsive: false,
|
||||
respNewRow: false,
|
||||
placeholder: '请输入单行文本',
|
||||
maxlength: null,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
addonBefore: '',
|
||||
addonAfter: '',
|
||||
disabled: false,
|
||||
allowClear: false,
|
||||
showLabel: true,
|
||||
required: false,
|
||||
rules: [],
|
||||
events: {},
|
||||
isSave: false,
|
||||
isShow: true,
|
||||
scan: false,
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
{
|
||||
key: '3cea59512d464fa3b5a81814999718f3',
|
||||
field: 'danXingWenBen5841',
|
||||
label: '单行文本',
|
||||
type: 'input',
|
||||
component: 'Input',
|
||||
colProps: { span: 24 },
|
||||
defaultValue: '',
|
||||
componentProps: {
|
||||
width: '100%',
|
||||
span: '',
|
||||
defaultValue: '',
|
||||
labelWidthMode: 'fix',
|
||||
labelFixWidth: 120,
|
||||
responsive: false,
|
||||
respNewRow: false,
|
||||
placeholder: '请输入单行文本',
|
||||
maxlength: null,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
addonBefore: '',
|
||||
addonAfter: '',
|
||||
disabled: false,
|
||||
allowClear: false,
|
||||
showLabel: true,
|
||||
required: false,
|
||||
rules: [],
|
||||
events: {},
|
||||
isSave: false,
|
||||
isShow: true,
|
||||
scan: false,
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'c4aaaf58ffbc468bb3f6c4f013b0350b',
|
||||
field: 'jiShuZuJian6835',
|
||||
label: '计数组件',
|
||||
type: 'number',
|
||||
component: 'InputNumber',
|
||||
colProps: { span: 24 },
|
||||
defaultValue: 0,
|
||||
componentProps: {
|
||||
labelWidthMode: 'fix',
|
||||
labelFixWidth: 120,
|
||||
responsive: false,
|
||||
width: '100%',
|
||||
span: '',
|
||||
defaultValue: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
maxlength: null,
|
||||
disabled: false,
|
||||
showLabel: true,
|
||||
controls: true,
|
||||
required: false,
|
||||
subTotal: false,
|
||||
isShow: true,
|
||||
rules: [],
|
||||
events: {},
|
||||
style: { width: '100%' },
|
||||
},
|
||||
},
|
||||
],
|
||||
showActionButtonGroup: false,
|
||||
buttonLocation: 'center',
|
||||
actionColOptions: { span: 24 },
|
||||
showResetButton: false,
|
||||
showSubmitButton: false,
|
||||
hiddenComponent: [],
|
||||
};
|
||||
47
src/views/system/Testfrom3/components/workflowPermission.ts
Normal file
47
src/views/system/Testfrom3/components/workflowPermission.ts
Normal file
@ -0,0 +1,47 @@
|
||||
export const permissionList = [
|
||||
{
|
||||
required: true,
|
||||
view: true,
|
||||
edit: true,
|
||||
disabled: false,
|
||||
isSaveTable: false,
|
||||
tableName: '',
|
||||
fieldName: '单行文本',
|
||||
fieldId: 'danXingWenBen5518',
|
||||
isSubTable: false,
|
||||
showChildren: true,
|
||||
type: 'input',
|
||||
key: '4251bdeb18574f2cb014ca33d650be52',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
view: true,
|
||||
edit: true,
|
||||
disabled: false,
|
||||
isSaveTable: false,
|
||||
tableName: '',
|
||||
fieldName: '单行文本',
|
||||
fieldId: 'danXingWenBen5841',
|
||||
isSubTable: false,
|
||||
showChildren: true,
|
||||
type: 'input',
|
||||
key: '3cea59512d464fa3b5a81814999718f3',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
view: true,
|
||||
edit: true,
|
||||
disabled: false,
|
||||
isSaveTable: false,
|
||||
tableName: '',
|
||||
fieldName: '计数组件',
|
||||
fieldId: 'jiShuZuJian6835',
|
||||
isSubTable: false,
|
||||
showChildren: true,
|
||||
type: 'number',
|
||||
key: 'c4aaaf58ffbc468bb3f6c4f013b0350b',
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
469
src/views/system/Testfrom3/index.vue
Normal file
469
src/views/system/Testfrom3/index.vue
Normal file
@ -0,0 +1,469 @@
|
||||
<template>
|
||||
<PageWrapper dense fixedHeight contentFullHeight contentClass="flex">
|
||||
<BasicTable @register="registerTable" ref="tableRef" :row-selection="{ selectedRowKeys: selectedKeys, onChange: onSelectChange }" @row-dbClick="dbClickRow">
|
||||
|
||||
<template #toolbar>
|
||||
<template v-for="button in tableButtonConfig" :key="button.code">
|
||||
<a-button v-if="button.isDefault" :type="button.type" @click="buttonClick(button.code)">
|
||||
<template #icon><Icon :icon="button.icon" /></template>
|
||||
{{ button.name }}
|
||||
</a-button>
|
||||
<a-button v-else :type="button.type">
|
||||
<template #icon><Icon :icon="button.icon" /></template>
|
||||
{{ button.name }}
|
||||
</a-button>
|
||||
</template>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<TableAction :actions="getActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<PrintPreview
|
||||
v-if="printData.visible"
|
||||
:id="printData.id"
|
||||
:request-params-configs="printData.requestParamsConfigs"
|
||||
:request-body-configs="printData.requestBodyConfigs"
|
||||
:request-header-configs="printData.requestHeaderConfigs"
|
||||
@close="printData.visible = false"
|
||||
/>
|
||||
<Testfrom3Modal @register="registerModal" @success="handleSuccess" />
|
||||
<ImportModal @register="registerImportModal" importUrl="/system/testfrom3/import" @success="handleImportSuccess"/>
|
||||
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onUnmounted, createVNode,
|
||||
reactive
|
||||
} from 'vue';
|
||||
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { BasicTable, useTable, TableAction, ActionItem } from '/@/components/Table';
|
||||
import { getTestfrom3Page, deleteTestfrom3, exportTestfrom3} from '/@/api/system/Testfrom3';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
import { useFormConfig } from '/@/hooks/web/useFormConfig';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { getTestfrom3 } from '/@/api/system/Testfrom3';
|
||||
import {generateTableJson } from '/@/utils/event/design';
|
||||
import printJS from 'print-js';
|
||||
|
||||
import { getPrintConfigInfo } from '/@/api/system/generator/print';
|
||||
import PrintPreview from '/@/views/generator/print/Preview.vue';
|
||||
import { PrintButton } from '/@/enums/printEnum';
|
||||
import { InputParamItem } from '/@/components/ApiConfig/src/interface';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import Testfrom3Modal from './components/Testfrom3Modal.vue';
|
||||
import { ImportModal } from '/@/components/Import';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import {formConfig, searchFormSchema, columns } from './components/config';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import useEventBus from '/@/hooks/event/useEventBus';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const { bus, CREATE_FLOW, FLOW_PROCESSED, FORM_LIST_MODIFIED } = useEventBus();
|
||||
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
defineEmits(['register']);
|
||||
const { filterColumnAuth, filterButtonAuth } = usePermission();
|
||||
const { mergeColumns,mergeSearchFormSchema,mergeButtons } = useFormConfig();
|
||||
|
||||
const filterColumns = cloneDeep(filterColumnAuth(columns));
|
||||
const customConfigColums =ref(filterColumns);
|
||||
const customSearchFormSchema =ref(searchFormSchema);
|
||||
|
||||
const tableRef = ref();
|
||||
//所有按钮
|
||||
const buttons = ref([{"isUse":true,"name":"新增","code":"add","icon":"ant-design:plus-outlined","isDefault":true,"type":"primary"},{"isUse":true,"name":"编辑","code":"edit","icon":"ant-design:form-outlined","isDefault":true},{"isUse":true,"name":"刷新","code":"refresh","icon":"ant-design:reload-outlined","isDefault":true},{"isUse":true,"name":"查看","code":"view","icon":"ant-design:eye-outlined","isDefault":true},{"isUse":true,"name":"快速导入","code":"import","icon":"ant-design:import-outlined","isDefault":true},{"isUse":true,"name":"快速导出","code":"export","icon":"ant-design:export-outlined","isDefault":true},{"isUse":true,"name":"打印","code":"print","icon":"ant-design:printer-outlined","isDefault":true},{"isUse":true,"name":"模板打印","code":"templateprint","icon":"ant-design:printer-outlined","isDefault":true},{"isUse":true,"name":"删除","code":"delete","icon":"ant-design:delete-outlined","isDefault":true}]);
|
||||
//展示在列表内的按钮
|
||||
const actionButtons = ref<string[]>(['view', 'edit', 'copyData', 'delete', 'startwork','flowRecord']);
|
||||
const buttonConfigs = computed(()=>{
|
||||
return filterButtonAuth(buttons.value);
|
||||
})
|
||||
|
||||
const tableButtonConfig = computed(() => {
|
||||
return buttonConfigs.value?.filter((x) => !actionButtons.value.includes(x.code));
|
||||
});
|
||||
|
||||
const actionButtonConfig = computed(() => {
|
||||
return buttonConfigs.value?.filter((x) => actionButtons.value.includes(x.code));
|
||||
});
|
||||
|
||||
const btnEvent = {add : handleAdd,edit : handleEdit,refresh : handleRefresh,view : handleView,import : handleImport,export : handleExport,print : handlePrint,templateprint : handleTemplateprint,delete : handleDelete,}
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
const router = useRouter();
|
||||
const printMenuId = computed(() => currentRoute.value.meta.menuId as string);
|
||||
|
||||
const formIdComputedRef = ref();
|
||||
formIdComputedRef.value = currentRoute.value.meta.formId
|
||||
const schemaIdComputedRef = ref();
|
||||
schemaIdComputedRef.value = currentRoute.value.meta.schemaId
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const selectedRowsData = ref<any[]>([]);
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerImportModal, { openModal: openImportModal }] = useModal();
|
||||
|
||||
// 模板打印 入参参数
|
||||
let printData: {
|
||||
visible:boolean;
|
||||
id: string;
|
||||
code:string;
|
||||
requestParamsConfigs: Array<InputParamItem>;
|
||||
requestHeaderConfigs: Array<InputParamItem>;
|
||||
requestBodyConfigs: Array<InputParamItem>;
|
||||
|
||||
} = reactive({
|
||||
visible:false,
|
||||
id: '',
|
||||
code:'',
|
||||
requestParamsConfigs: [],
|
||||
requestHeaderConfigs: [],
|
||||
requestBodyConfigs: [],
|
||||
});
|
||||
|
||||
const formName='测试_极简模式';
|
||||
const [registerTable, { reload, getRawDataSource, }] = useTable({
|
||||
title: '' || (formName + '列表'),
|
||||
api: getTestfrom3Page,
|
||||
rowKey: 'id',
|
||||
columns: customConfigColums,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: customSearchFormSchema,
|
||||
fieldMapToTime: [],
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, FormId: formIdComputedRef.value, PK: 'id' };
|
||||
},
|
||||
afterFetch: (res) => {
|
||||
tableRef.value.setToolBarWidth();
|
||||
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 160,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
customRow,
|
||||
});
|
||||
|
||||
function dbClickRow(record) {
|
||||
if (!actionButtonConfig?.value.some(element => element.code == 'view')) {
|
||||
return;
|
||||
}
|
||||
const { processId, taskIds, schemaId } = record.workflowData || {};
|
||||
if (taskIds && taskIds.length) {
|
||||
router.push({
|
||||
path: '/flow/' + schemaId + '/' + (processId || '') + '/approveFlow',
|
||||
query: {
|
||||
taskId: taskIds[0],
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
} else if (schemaId && !taskIds && processId) {
|
||||
router.push({
|
||||
path: '/flow/' + schemaId + '/' + processId + '/approveFlow',
|
||||
query: {
|
||||
readonly: 1,
|
||||
taskId: '',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
router.push({
|
||||
path: '/form/Testfrom3/' + record.id + '/viewForm',
|
||||
query: {
|
||||
formPath: 'system/Testfrom3',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buttonClick(code) {
|
||||
if (code.includes(PrintButton.CODE)) {
|
||||
printData.code = code;
|
||||
}
|
||||
btnEvent[code]();
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
if (schemaIdComputedRef.value) {
|
||||
router.push({
|
||||
path: '/flow/' + schemaIdComputedRef.value + '/0/createFlow'
|
||||
});
|
||||
} else {
|
||||
router.push({
|
||||
path: '/form/Testfrom3/0/createForm',
|
||||
query: {
|
||||
formPath: 'system/Testfrom3',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleEdit(record: Recordable) {
|
||||
|
||||
router.push({
|
||||
path: '/form/Testfrom3/' + record.id + '/updateForm',
|
||||
query: {
|
||||
formPath: 'system/Testfrom3',
|
||||
formName: formName,
|
||||
formId:currentRoute.value.meta.formId
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleDelete(record: Recordable) {
|
||||
deleteList([record.id]);
|
||||
}
|
||||
|
||||
function deleteList(ids) {
|
||||
Modal.confirm({
|
||||
title: '提示信息',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: '是否确认删除?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
deleteTestfrom3(ids).then((_) => {
|
||||
handleSuccess();
|
||||
notification.success({
|
||||
message: 'Tip',
|
||||
description: t('删除成功!'),
|
||||
});
|
||||
});
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
}
|
||||
|
||||
async function handlePrint() {
|
||||
const dataSource = Array.isArray(getRawDataSource())
|
||||
? getRawDataSource()
|
||||
: getRawDataSource().list;
|
||||
const json = generateTableJson(filterColumns, dataSource);
|
||||
const properties = filterColumns.map((item) => item.title);
|
||||
printJS({
|
||||
printable: json,
|
||||
properties: properties,
|
||||
type: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
// 模板打印
|
||||
async function handleTemplateprint() {
|
||||
if (!selectedKeys.value.length) {
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('请选择数据'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (selectedKeys.value.length > 1) {
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('只能选择一条数据进行操作'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
let id = selectedKeys.value[0];
|
||||
try {
|
||||
const record = await getTestfrom3(id);
|
||||
let res = await getPrintConfigInfo(printData.code, printMenuId.value);
|
||||
if(res.enabledMark==null){
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('当前功能未绑定打印模板,请绑定后再进行模板打印。'),
|
||||
});
|
||||
return ;
|
||||
}
|
||||
if(res.enabledMark==0){
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('找不到打印模板,请联系管理员。'),
|
||||
});
|
||||
return ;
|
||||
}
|
||||
printData.id = res.schemaId;
|
||||
if (res.apiConfig) {
|
||||
let json = JSON.parse(res.apiConfig);
|
||||
if (json.requestParamsConfigs && json.requestParamsConfigs.length > 0) {
|
||||
printData.requestParamsConfigs = json.requestParamsConfigs.map((ele) => {
|
||||
if (ele.config && record[ele.config] != undefined) {
|
||||
ele.value = record[ele.config];
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
if (json.requestHeaderConfigs && json.requestHeaderConfigs.length > 0) {
|
||||
printData.requestHeaderConfigs = json.requestHeaderConfigs.map((ele) => {
|
||||
if (ele.config && record[ele.config] != undefined) {
|
||||
ele.value = record[ele.config];
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
if (json.requestBodyConfigs && json.requestBodyConfigs.length > 0) {
|
||||
printData.requestBodyConfigs = json.requestBodyConfigs.map((ele) => {
|
||||
if (ele.config && record[ele.config] != undefined) {
|
||||
ele.value = record[ele.config];
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
printData.visible = true;
|
||||
}else{
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('当前功能未绑定打印模板,请绑定后再进行模板打印。'),
|
||||
});
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
function onSelectChange(selectedRowKeys: [], selectedRows) {
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
selectedRowsData.value = selectedRows;
|
||||
}
|
||||
|
||||
function customRow(record: Recordable) {
|
||||
return {
|
||||
onClick: () => {
|
||||
let selectedRowKeys = [...selectedKeys.value];
|
||||
if (selectedRowKeys.indexOf(record.id) >= 0) {
|
||||
let index = selectedRowKeys.indexOf(record.id);
|
||||
selectedRowKeys.splice(index, 1);
|
||||
} else {
|
||||
selectedRowKeys.push(record.id);
|
||||
}
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
},
|
||||
};
|
||||
}
|
||||
function handleRefresh() {
|
||||
reload();
|
||||
}
|
||||
function handleSuccess() {
|
||||
|
||||
selectedKeys.value = [];
|
||||
selectedRowsData.value = [];
|
||||
reload();
|
||||
}
|
||||
|
||||
function handleView(record: Recordable) {
|
||||
|
||||
dbClickRow(record);
|
||||
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
const res = await exportTestfrom3({ isTemplate: false });
|
||||
downloadByData(
|
||||
res.data,
|
||||
'Testfrom3.xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
);
|
||||
}
|
||||
|
||||
function handleImport() {
|
||||
openImportModal(true, {
|
||||
title: '快速导入',
|
||||
downLoadUrl:'/system/testfrom3/export',
|
||||
});
|
||||
}
|
||||
function handleImportSuccess(){
|
||||
reload()
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
if (schemaIdComputedRef.value) {
|
||||
bus.on(FLOW_PROCESSED, handleRefresh);
|
||||
bus.on(CREATE_FLOW, handleRefresh);
|
||||
} else {
|
||||
bus.on(FORM_LIST_MODIFIED, handleRefresh);
|
||||
}
|
||||
|
||||
// 合并渲染覆盖配置中的列表配置,包括展示字段配置、搜索字段配置、按钮配置
|
||||
mergeCustomListRenderConfig();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (schemaIdComputedRef.value) {
|
||||
bus.off(FLOW_PROCESSED, handleRefresh);
|
||||
bus.off(CREATE_FLOW, handleRefresh);
|
||||
} else {
|
||||
bus.off(FORM_LIST_MODIFIED, handleRefresh);
|
||||
}
|
||||
});
|
||||
function getActions(record: Recordable):ActionItem[] {
|
||||
|
||||
const actionsList: ActionItem[] = actionButtonConfig.value?.map((button) => {
|
||||
if (!record.workflowData?.processId) {
|
||||
return {
|
||||
icon: button?.icon,
|
||||
tooltip: button?.name,
|
||||
color: button.code === 'delete' ? 'error' : undefined,
|
||||
onClick: btnEvent[button.code].bind(null, record),
|
||||
};
|
||||
} else {
|
||||
if (button.code === 'view') {
|
||||
return {
|
||||
icon: button?.icon,
|
||||
tooltip: button?.name,
|
||||
onClick: btnEvent[button.code].bind(null, record),
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
});
|
||||
return actionsList;
|
||||
}
|
||||
async function mergeCustomListRenderConfig(){
|
||||
if (formConfig.useCustomConfig) {
|
||||
let formId=currentRoute.value.meta.formId;
|
||||
//1.合并展示字段配置
|
||||
let cols= await mergeColumns(customConfigColums.value,formId);
|
||||
customConfigColums.value=cols;
|
||||
//2.合并搜索字段配置
|
||||
let sFormSchema= await mergeSearchFormSchema(customSearchFormSchema.value,formId);
|
||||
customSearchFormSchema.value=sFormSchema;
|
||||
//3.合并按钮配置
|
||||
let btns= await mergeButtons(buttons.value,formId);
|
||||
buttons.value=btns;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-table-selection-col) {
|
||||
width: 50px;
|
||||
}
|
||||
.show{
|
||||
display: flex;
|
||||
}
|
||||
.hide{
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
77
src/views/system/dataMigration/components/config.ts
Normal file
77
src/views/system/dataMigration/components/config.ts
Normal file
@ -0,0 +1,77 @@
|
||||
export const items=[
|
||||
/* {
|
||||
code:"租户",
|
||||
name:'租户',
|
||||
ref:"tenantRef",
|
||||
}, */
|
||||
{
|
||||
code:'角色',
|
||||
name:'角色',
|
||||
ref:"roleRef",
|
||||
},
|
||||
{
|
||||
code:'岗位',
|
||||
name:'岗位' ,
|
||||
ref:"postRef",
|
||||
},
|
||||
|
||||
{
|
||||
code:'用户组',
|
||||
name:'用户组',
|
||||
ref:"userGroupRef",
|
||||
},
|
||||
{
|
||||
code:'菜单',
|
||||
name:'菜单',
|
||||
ref:"menuRef",
|
||||
},
|
||||
|
||||
{
|
||||
code:'表单',
|
||||
name:'表单',
|
||||
ref:"formRef",
|
||||
},
|
||||
{
|
||||
code:'流程定义',
|
||||
name:'流程定义',
|
||||
ref:"workflowRef",
|
||||
},
|
||||
{
|
||||
code:'系统配置',
|
||||
name:'系统配置',
|
||||
ref:"systemConfigRef",
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'数据字典',
|
||||
name:'数据字典',
|
||||
ref:"dictionaryRef",
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'自动编码',
|
||||
name:'自动编码',
|
||||
ref:"codeRuleRef",
|
||||
},
|
||||
{
|
||||
code:'桌面设计',
|
||||
name:'桌面设计',
|
||||
ref:"desktopRef",
|
||||
},
|
||||
{
|
||||
checked:false,
|
||||
code:'角色-菜单授权',
|
||||
name:'权限:角色-菜单授权(菜单、按钮、列表、表单)',
|
||||
ref:"roleMenuAuthRef",
|
||||
},
|
||||
{
|
||||
code:'角色-自定义接口授权',
|
||||
name:'权限:角色-自定义接口授权',
|
||||
ref:"roleInterfaceAuthRef",
|
||||
},
|
||||
{
|
||||
code:'租户-菜单授权',
|
||||
name:'权限:租户-菜单授权',
|
||||
ref:"tenantMenuAuthRef",
|
||||
}
|
||||
]
|
||||
239
src/views/system/dataMigration/components/export/ExportModal.vue
Normal file
239
src/views/system/dataMigration/components/export/ExportModal.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="data-migration-modal" >
|
||||
<template #title>
|
||||
<a-steps :current="current">
|
||||
<a-step
|
||||
v-for="step in steps"
|
||||
:title="t(step.name)"
|
||||
/>
|
||||
</a-steps>
|
||||
<div class="btn-box">
|
||||
<a-button type="primary" @click="handleStepPrev" :disabled="current == 0">{{t('上一步')}}</a-button>
|
||||
<a-button type="primary" @click="handleStepNext" :disabled="current>=steps.length-1">{{t('下一步')}}</a-button>
|
||||
<a-button type="primary" @click="handleExport" :disabled="current<steps.length-1">{{t('导出')}}</a-button>
|
||||
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="step-container">
|
||||
<Tenant ref="tenantRef" v-show="showIf('租户')"/>
|
||||
<Role ref="roleRef" v-show="showIf('角色')"/>
|
||||
<Post ref="postRef" v-show="showIf('岗位')"/>
|
||||
<UserGroup ref="userGroupRef" v-show="showIf('用户组')"/>
|
||||
<Menu ref="menuRef" v-show="showIf('菜单')"/>
|
||||
<Form ref="formRef" v-show="showIf('表单')"/>
|
||||
<Workflow ref="workflowRef" v-show="showIf('流程定义')"/>
|
||||
<SystemConfig ref="systemConfigRef" v-show="showIf('系统配置')"/>
|
||||
<Dictionary ref="dictionaryRef" v-show="showIf('数据字典')"/>
|
||||
<CodeRule ref="codeRuleRef" v-show="showIf('自动编码')"/>
|
||||
<Desktop ref="desktopRef" v-show="showIf('桌面设计')"/>
|
||||
<!-- <span ref="roleMenuAuthRef" v-show="showIf('角色-菜单授权')" >角色-菜单授权 选择列表</span> -->
|
||||
<RoleMenuAuth ref="roleMenuAuthRef" v-show="showIf('角色-菜单授权')"/>
|
||||
<InterfaceAuth ref="roleInterfaceAuthRef" v-show="showIf('角色-自定义接口授权')"/>
|
||||
<TenantAuthorize ref="tenantMenuAuthRef" v-show="showIf('租户-菜单授权')"/>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref,unref,provide,Ref,onMounted} from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import {isFunction } from 'lodash-es';
|
||||
import { isNumber } from '/@/utils/is';
|
||||
import { downloadByData } from '/@/utils/file/download';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { exportDatas,downloadDatas} from '/@/api/system/dataMigration';
|
||||
import Tenant from './components/Tenant.vue';
|
||||
import Role from './components/Role.vue';
|
||||
import Post from './components/Post.vue';
|
||||
import Menu from './components/Menu.vue';
|
||||
import Form from './components/Form.vue';
|
||||
import Workflow from './components/Workflow.vue';
|
||||
import SystemConfig from './components/SystemConfig.vue';
|
||||
import Dictionary from './components/Dictionary.vue';
|
||||
import CodeRule from './components/CodeRule.vue';
|
||||
import Desktop from './components/Desktop.vue';
|
||||
import UserGroup from './components/UserGroup.vue';
|
||||
import RoleMenuAuth from './components/RoleMenuAuth.vue';
|
||||
import InterfaceAuth from './components/InterfaceAuth.vue';
|
||||
import TenantAuthorize from './components/TenantAuthorize.vue';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
const current = ref(0);
|
||||
const steps=ref([]);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
steps.value=data.items;
|
||||
current.value=0;
|
||||
selectedModel.value={};
|
||||
initStep(0);
|
||||
setModalProps({
|
||||
confirmLoading: false,
|
||||
canFullscreen: false,
|
||||
defaultFullscreen: true,
|
||||
destroyOnClose: true,
|
||||
draggable: false,
|
||||
showOkBtn: false,
|
||||
showCancelBtn: false,
|
||||
footer: null,
|
||||
closable: false,
|
||||
});
|
||||
});
|
||||
|
||||
const tenantRef=ref();
|
||||
const roleRef=ref();
|
||||
const postRef= ref();
|
||||
const userGroupRef= ref();
|
||||
const menuRef = ref();
|
||||
const formRef=ref();
|
||||
const workflowRef=ref();
|
||||
const systemConfigRef= ref();
|
||||
const dictionaryRef= ref();
|
||||
const codeRuleRef= ref();
|
||||
const desktopRef= ref();
|
||||
const roleMenuAuthRef= ref();
|
||||
const roleInterfaceAuthRef= ref();
|
||||
const tenantMenuAuthRef= ref();
|
||||
|
||||
const refs = {
|
||||
tenantRef,
|
||||
roleRef,
|
||||
postRef,
|
||||
userGroupRef,
|
||||
menuRef,
|
||||
formRef,
|
||||
workflowRef,
|
||||
systemConfigRef,
|
||||
dictionaryRef,
|
||||
codeRuleRef,
|
||||
desktopRef,
|
||||
roleMenuAuthRef,
|
||||
roleInterfaceAuthRef,
|
||||
tenantMenuAuthRef
|
||||
};
|
||||
|
||||
const selectedModel: Ref<{
|
||||
menu?:{
|
||||
menuIds: number[],
|
||||
buttonIds: number[],
|
||||
columnIds:number[],
|
||||
formIds:number[]
|
||||
},
|
||||
tenant?:{
|
||||
tenantIds:number[]
|
||||
}
|
||||
}> = ref({});
|
||||
|
||||
provide('selectedModel', selectedModel);
|
||||
|
||||
function handleClose() {
|
||||
closeModal();
|
||||
}
|
||||
|
||||
function showIf(itemType:string){
|
||||
let currentStep=getStep('current');
|
||||
if(currentStep==null){
|
||||
return false;
|
||||
}
|
||||
return currentStep.code==itemType;
|
||||
}
|
||||
|
||||
//上一步
|
||||
function handleStepPrev() {
|
||||
completeStep(current.value);
|
||||
current.value--;
|
||||
initStep(current.value);
|
||||
|
||||
}
|
||||
//下一步
|
||||
async function handleStepNext() {
|
||||
completeStep(current.value);
|
||||
current.value++;
|
||||
initStep(current.value);
|
||||
}
|
||||
|
||||
//导出
|
||||
async function handleExport(){
|
||||
completeStep(current.value);
|
||||
let result= await exportDatas({
|
||||
exportType:'exportSelected',
|
||||
...unref(selectedModel)
|
||||
});
|
||||
|
||||
if(result){
|
||||
const fileName='data-mg-'+dateUtil(new Date()).format('YYYY-MM-DD_HH_mm_ss');
|
||||
const res = await downloadDatas({uuid:result});
|
||||
downloadByData(
|
||||
res.data,
|
||||
fileName+".zip"
|
||||
);
|
||||
}
|
||||
handleClose();
|
||||
}
|
||||
|
||||
function getStep(key:string|number){
|
||||
let stepList=unref(steps);
|
||||
if(!stepList||stepList.length==0){
|
||||
return null;
|
||||
}
|
||||
let step=null;
|
||||
if(key=='current'){
|
||||
step=unref(stepList[current.value]);
|
||||
}else if(key=='prev'){
|
||||
step=unref(stepList[current.value-1]);
|
||||
}else if(key=='next'){
|
||||
step=unref(stepList[current.value+1]);
|
||||
}else if(isNumber(key)&&key>=0&&key<stepList.length){
|
||||
step=unref(stepList[key]);
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
function initStep(key:string|number){
|
||||
let stepRef = refs[getStep(key).ref].value;
|
||||
stepRef.initStep&& stepRef.initStep();
|
||||
|
||||
};
|
||||
|
||||
function completeStep(key:string|number){
|
||||
let stepRef = refs[getStep(key).ref].value;
|
||||
stepRef.completeStep&& stepRef.completeStep();
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.data-migration-modal{
|
||||
.step-container {
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.ant-modal-header{
|
||||
height: 100px;
|
||||
}
|
||||
.ant-steps{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ant-steps-horizontal:not(.ant-steps-label-vertical) .ant-steps-item {
|
||||
padding-left:16px!important;
|
||||
margin-bottom: 10px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.btn-box {
|
||||
height: 100px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top:60px;
|
||||
:deep(.ant-btn) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection"/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getCodeList } from '/@/api/system/code';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: t('编码编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编码名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('当前流水号'),
|
||||
dataIndex: 'currentNumber',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('编码说明'),
|
||||
dataIndex: 'description',
|
||||
width: 180,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getCodeList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
currentNumber: item.currentNumber,
|
||||
description: item.description
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let codeRule = selectedModel.value.codeRule || (selectedModel.value.codeRule = {});
|
||||
codeRule.codeRuleIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.codeRule=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getPageList } from '/@/api/desktop';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getPageList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let deskTop = selectedModel.value.deskTop || (selectedModel.value.deskTop = {});
|
||||
deskTop.desktopIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.deskTop=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<BasicTable
|
||||
@register="registerTableItem"
|
||||
@row-click="handleRowClick"
|
||||
class="w-2/5 xl:w-2/5 mr-2"
|
||||
>
|
||||
<!-- <template #action="{ record }">
|
||||
<a-checkbox v-model:checked="checkedItems[record.id]"/>
|
||||
</template> -->
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-checkbox v-model:checked="checkedItems[record.id]"/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<BasicTable
|
||||
@register="registerTableDetail"
|
||||
class="w-3/5 xl:w-3/5"
|
||||
:rowSelection="rowSelectionDetail"
|
||||
>
|
||||
</BasicTable>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref,inject,reactive, } from 'vue';
|
||||
import { BasicTable, useTable, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {
|
||||
getDicItemPageList,
|
||||
getDicDetailPageList
|
||||
} from '/@/api/system/dic';
|
||||
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const checkedItems=ref({});
|
||||
const checkedDetails=ref({});
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const searchFormSchemaItem: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('项目名'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const itemColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('项目名'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchemaDetail: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('字典名'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const detailColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('字典名'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('编号'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('值'),
|
||||
dataIndex: 'value',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
align: 'left',
|
||||
dataIndex: 'remark',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
// Row selection for detail table
|
||||
const selectedRowKeysDetail = ref<string[]>([]);
|
||||
const rowSelectionDetail = reactive<any> ({
|
||||
selectedRowKeys: selectedRowKeysDetail,
|
||||
onChange: (selectedRowKeys: string[]) => {
|
||||
selectedRowKeysDetail.value = selectedRowKeys;
|
||||
syncCheckedDetails();
|
||||
},
|
||||
onSelectAll: (selected: boolean, selectedRows: any[], changeRows: any[]) => {
|
||||
const ids = (changeRows || selectedRows || []).map((r: any) => String(r.id));
|
||||
if (selected) {
|
||||
selectedRowKeysDetail.value = Array.from(new Set([...selectedRowKeysDetail.value, ...ids]));
|
||||
} else {
|
||||
selectedRowKeysDetail.value = selectedRowKeysDetail.value.filter((k) => !ids.includes(k));
|
||||
}
|
||||
syncCheckedDetails();
|
||||
},
|
||||
});
|
||||
|
||||
const { notification } = useMessage();
|
||||
|
||||
const selectItemId = ref('');
|
||||
|
||||
const [registerTableItem, { reload: itemReload }] = useTable({
|
||||
title: t('数据字典项'),
|
||||
api: getDicItemPageList,
|
||||
rowKey: 'id',
|
||||
columns: itemColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaItem,
|
||||
showResetButton: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
/* actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
}, */
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTableDetail, { reload: detailReload }] = useTable({
|
||||
title: t('数据字典详情'),
|
||||
api: getDicDetailPageList,
|
||||
rowKey: 'id',
|
||||
columns: detailColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaDetail,
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, itemId: selectItemId.value };
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
selectItemId.value = record.id;
|
||||
detailReload();
|
||||
}
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
selectItemId.value = rowKey[0];
|
||||
if(selectItemId.value){
|
||||
detailReload();
|
||||
}
|
||||
}
|
||||
|
||||
function syncCheckedDetails() {
|
||||
const map: Record<string, boolean> = {};
|
||||
selectedRowKeysDetail.value.forEach((key) => {
|
||||
map[key] = true;
|
||||
});
|
||||
checkedDetails.value = map;
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
inited.value=true;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let items=checkedItems.value;
|
||||
let details=checkedDetails.value;
|
||||
let checkItemIds = Object.keys(items).filter(key => items[key] === true);
|
||||
let checkDetailIds = Object.keys(details).filter(key => details[key] === true);
|
||||
|
||||
if((checkItemIds&&checkItemIds.length>0)||(checkDetailIds&&checkDetailIds.length>0)){
|
||||
let dictionary = selectedModel.value.dictionary || (selectedModel.value.dictionary = {});
|
||||
dictionary.itemIds=checkItemIds;
|
||||
dictionary.detailIds=checkDetailIds;
|
||||
}else{
|
||||
selectedModel.value.dictionary=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getFormTemplatePage} from '/@/api/form/design';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '表单id',
|
||||
key:'id',
|
||||
dataIndex:'id',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '表单名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '模板类型',
|
||||
dataIndex: '',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.formDesignType===0) {
|
||||
return '数据优先模板';
|
||||
}else if(record.formDesignType===1){
|
||||
return '界面优先模板';
|
||||
}else if(record.formDesignType===2){
|
||||
return '简易模板';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getFormTemplatePage({limit:1,size:99,type:0,category:''});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
formDesignType: item.formDesignType
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let formTemplate = selectedModel.value.formTemplate || (selectedModel.value.formTemplate = {});
|
||||
formTemplate.formTemplateIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.formTemplate=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<BasicTable
|
||||
@register="registerTableRole"
|
||||
@row-click="handleRowClick"
|
||||
class="w-2/5 xl:w-2/5 mr-2"
|
||||
>
|
||||
<!-- <template #action="{ record }">
|
||||
<a-checkbox v-model:checked="checkedRoles[record.id]"/>
|
||||
</template> -->
|
||||
</BasicTable>
|
||||
<BasicTable @register="registerInterface" class="w-3/5 xl:w-3/5" ref="interfaceRef">
|
||||
<!-- <template #action="{ record }">
|
||||
<a-checkbox v-model:checked="checkedInterfaces[record.roleId+'_'+record.id]"/>
|
||||
</template> -->
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'operation'">
|
||||
<a-checkbox v-model:checked="checkedInterfaces[record.roleId+'_'+record.id]"
|
||||
@change="onParentCheckChange(record, $event)"/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject,nextTick,unref } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {getRolePageList,getRoleInterfaceList} from '/@/api/system/role';
|
||||
import {getAllInterface } from '/@/api/system/interface';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const checkedRoles=ref({});
|
||||
const checkedInterfaces=ref({});
|
||||
const allInterfacesTree=ref([]);
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const searchFormSchemaRole: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('角色名称'),
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('角色编码'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const roleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('角色名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('角色编码'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const searchFormSchemaInterface: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('名称'),
|
||||
component: 'Input',
|
||||
}
|
||||
];
|
||||
|
||||
const interfaceColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('选择'),
|
||||
dataIndex: 'operation',
|
||||
width: 50,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('接口名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
|
||||
const selectRoleId = ref('');
|
||||
|
||||
const [registerTableRole, { reload: roleReload }] = useTable({
|
||||
title: t('角色'),
|
||||
api: getRolePageList,
|
||||
rowKey: 'id',
|
||||
columns: roleColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaRole,
|
||||
showResetButton: false,
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onChange: onSelectChange,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
/* actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
}, */
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerInterface, {reload: interfaceReload, expandAll, collapseAll }] = useTable({
|
||||
title: '自定义接口',
|
||||
isTreeTable: true,
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, roleId: selectRoleId.value };
|
||||
},
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchemaInterface,
|
||||
showResetButton: false,
|
||||
},
|
||||
useSearchForm: true,
|
||||
columns:interfaceColumns,
|
||||
api:getAuthInterface,
|
||||
rowKey: 'id',
|
||||
/* actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
}, */
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
if(selectRoleId.value===record.id){
|
||||
return;
|
||||
}
|
||||
selectRoleId.value = record.id;
|
||||
interfaceReload();
|
||||
}
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
if(selectRoleId.value== rowKey[0]){
|
||||
return;
|
||||
}
|
||||
|
||||
if(rowKey[0]){
|
||||
selectRoleId.value =rowKey[0] ;
|
||||
interfaceReload();
|
||||
}
|
||||
}
|
||||
|
||||
function setChildrenChecked(record, checked) {
|
||||
if (record.children && record.children.length > 0) {
|
||||
record.children.forEach(child => {
|
||||
checkedInterfaces.value[child.roleId + '_' + child.id] = checked;
|
||||
setChildrenChecked(child, checked);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onParentCheckChange(record, e) {
|
||||
const checked = e.target.checked;
|
||||
setChildrenChecked(record, checked);
|
||||
}
|
||||
|
||||
function getAuthTree(authTree,allTree,authList,roleId) {
|
||||
let result=false;
|
||||
for (let i = 0; i < allTree.length; i++) {
|
||||
let o = allTree[i];
|
||||
let include=false;
|
||||
let childrenInclude=false;
|
||||
if (authList.includes(o.id)) {
|
||||
include=true;
|
||||
}
|
||||
|
||||
let children=[];
|
||||
if (o.children?.length > 0) {
|
||||
childrenInclude=getAuthTree(children, o.children, authList,roleId);
|
||||
}
|
||||
if(include===true||childrenInclude===true){
|
||||
let node=cloneDeep(o);
|
||||
node.children=children;
|
||||
node.roleId=roleId;
|
||||
authTree.push(node);
|
||||
result=true;
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function getAuthInterface(params, mode){
|
||||
if(!params.roleId){
|
||||
return [];
|
||||
}
|
||||
let allTree=allInterfacesTree.value;
|
||||
if(!allTree||allTree.length==0){
|
||||
allTree=allInterfacesTree.value=await getAllInterface();
|
||||
}
|
||||
|
||||
let authList=await getRoleInterfaceList(params,mode);
|
||||
|
||||
let authTree=[];
|
||||
getAuthTree(authTree,allTree,authList,params.roleId);
|
||||
|
||||
expandAll();
|
||||
|
||||
return authTree;
|
||||
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
inited.value=true;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let interfaces=checkedInterfaces.value;
|
||||
let checkIds = Object.keys(interfaces).filter(key => interfaces[key] === true);
|
||||
if(checkIds&&checkIds.length>0){
|
||||
let map={};
|
||||
for(let i=0;i<checkIds.length;i++){
|
||||
let splits=checkIds[i].split('_');
|
||||
let roleId=splits[0];
|
||||
let interfaceId=splits[1];
|
||||
|
||||
let interfaceList=map[roleId]||(map[roleId]=[]);
|
||||
interfaceList.push(interfaceId);
|
||||
}
|
||||
let interfaceAuth = selectedModel.value.interfaceAuth || (selectedModel.value.interfaceAuth = {});
|
||||
interfaceAuth.interfaceAuthIdsMap=map;
|
||||
}else{
|
||||
selectedModel.value.interfaceAuth=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<a-row :gutter="[16, 16]" class="h-full">
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('菜单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="menuData"
|
||||
checkable
|
||||
ref="menuRef"
|
||||
@check="handleTreeSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('按钮')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="buttonSelectData"
|
||||
checkable
|
||||
ref="ButtonRef"
|
||||
@check="handleButtonSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('字段')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="columnSelectData"
|
||||
checkable
|
||||
ref="ColumnRef"
|
||||
@check="handleColumnSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('表单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="fieldSelectData"
|
||||
checkable
|
||||
ref="FieldRef"
|
||||
@check="handleFieldSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,unref,inject} from 'vue';
|
||||
import { TreeItem,BasicTree,TreeActionType} from '/@/components/Tree';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { getMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { getMenuButtonList, getMenuColumnList, getMenuFieldList } from '/@/api/system/menuButton';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const menuRef = ref<Nullable<TreeActionType>>(null);
|
||||
const ButtonRef = ref<Nullable<TreeActionType>>(null);
|
||||
const ColumnRef = ref<Nullable<TreeActionType>>(null);
|
||||
const FieldRef = ref<Nullable<TreeActionType>>(null);
|
||||
|
||||
const menuData = ref<TreeItem[]>([]);
|
||||
const buttonData = ref<TreeItem[]>([]);
|
||||
const columnData = ref<TreeItem[]>([]);
|
||||
const fieldData = ref<TreeItem[]>([]);
|
||||
|
||||
const buttonSelectData = ref<TreeItem[]>([]);
|
||||
const columnSelectData = ref<TreeItem[]>([]);
|
||||
const fieldSelectData = ref<TreeItem[]>([]);
|
||||
|
||||
const menuKeys = ref<string[]>([]);
|
||||
const btnKeys = ref<string[]>([]);
|
||||
const colKeys = ref<string[]>([]);
|
||||
const fieldKeys = ref<string[]>([]);
|
||||
|
||||
const btnFilterKeys = ref<string[]>([]);
|
||||
const colFilterKeys = ref<string[]>([]);
|
||||
const fieldFilterKeys = ref<string[]>([]);
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
menuData.value = await getMenuSimpleTree();
|
||||
buttonData.value = await getMenuButtonList();
|
||||
columnData.value = await getMenuColumnList();
|
||||
fieldData.value = await getMenuFieldList();
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTreeSelect(keys, e) {
|
||||
//keys 所有全选中的key
|
||||
//e.halfCheckedKeys 所有半选中的
|
||||
|
||||
//如果没有半选 就证明全部全选 直接使用keys
|
||||
//如果有半选需要 使用keys + e.halfCheckedKeys
|
||||
|
||||
const menuSelect: string[] = [...e.halfCheckedKeys, ...keys] as String[];
|
||||
//选择菜单的时候。对应后面的button column field 只进行增量添加
|
||||
incrementTree(menuSelect);
|
||||
|
||||
menuKeys.value = menuSelect;
|
||||
}
|
||||
|
||||
|
||||
function incrementTree(addMenuSelect) {
|
||||
/* btnFilterKeys.value = [];
|
||||
colFilterKeys.value = [];
|
||||
fieldFilterKeys.value = []; */
|
||||
|
||||
// 确保未选中菜单的子节点不会丢失
|
||||
findMenuTree(buttonSelectData.value, menuData.value, Array.from(addMenuSelect), 'button');
|
||||
findMenuTree(columnSelectData.value, menuData.value, Array.from(addMenuSelect), 'column');
|
||||
findMenuTree(fieldSelectData.value, menuData.value, Array.from(addMenuSelect), 'field');
|
||||
|
||||
// 更新按钮、字段、表单的选中状态
|
||||
btnKeys.value = Array.from(new Set([...btnKeys.value, ...addMenuSelect]));
|
||||
colKeys.value = Array.from(new Set([...colKeys.value, ...addMenuSelect]));
|
||||
fieldKeys.value = Array.from(new Set([...fieldKeys.value, ...addMenuSelect]));
|
||||
|
||||
// 扩展树的展开状态
|
||||
unref(ButtonRef).setExpandedKeys(Array.from(addMenuSelect));
|
||||
unref(ColumnRef).setExpandedKeys(Array.from(addMenuSelect));
|
||||
unref(FieldRef).setExpandedKeys(Array.from(addMenuSelect));
|
||||
}
|
||||
|
||||
function findMenuTree(menuSelectTree, treeData, keys, type) {
|
||||
for (let i = 0; i < treeData.length; i++) {
|
||||
let o = cloneDeep(treeData[i]);
|
||||
if (keys.includes(o.id)) {
|
||||
let hadData = false;
|
||||
let index = 0;
|
||||
for (let j = 0; j < menuSelectTree.length; j++) {
|
||||
if (menuSelectTree[j].id == o.id) {
|
||||
hadData = true;
|
||||
index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hadData) {
|
||||
o.children = [];
|
||||
menuSelectTree.push(o);
|
||||
} else {
|
||||
o = menuSelectTree[index];
|
||||
}
|
||||
if (treeData[i].children?.length > 0) {
|
||||
findMenuTree(o.children, treeData[i].children, keys, type);
|
||||
} else {
|
||||
let list =
|
||||
type == 'button'
|
||||
? buttonData.value
|
||||
: type == 'column'
|
||||
? columnData.value
|
||||
: fieldData.value;
|
||||
o.children = getAuthData(list, o.id, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthData(list, id, type) {
|
||||
let arr: TreeItem[] =list.filter((o) => {
|
||||
return o.menuId == id;
|
||||
});
|
||||
arr.forEach((o) => {
|
||||
o.parentId = o.menuId;
|
||||
if (type == 'button') {
|
||||
btnFilterKeys.value.push(o.id);
|
||||
} else if (type == 'column') {
|
||||
colFilterKeys.value.push(o.id);
|
||||
} else {
|
||||
if (o.children && o.children.length > 0) {
|
||||
o.children?.forEach((k) => {
|
||||
fieldFilterKeys.value.push(k.id);
|
||||
});
|
||||
}
|
||||
fieldFilterKeys.value.push(o.id);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function handleButtonSelect(keys, e) {
|
||||
btnKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function handleColumnSelect(keys, e) {
|
||||
colKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function handleFieldSelect(keys, e) {
|
||||
fieldKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let menuIds=menuKeys.value;
|
||||
let buttonIds=btnKeys.value.filter((o) =>{ return btnFilterKeys.value.includes(o)});
|
||||
let columnIds=colKeys.value.filter((o) => {return colFilterKeys.value.includes(o)});
|
||||
let formIds=fieldKeys.value.filter((o) => {return fieldFilterKeys.value.includes(o)});
|
||||
if((menuIds&&menuIds.length>0)||(buttonIds&&buttonIds.length>0)
|
||||
||(columnIds&&columnIds.length>0)||(formIds&&formIds.length>0)){
|
||||
let menu = selectedModel.value.menu || (selectedModel.value.menu = {});
|
||||
menu.menuIds=menuIds;
|
||||
menu.buttonIds=buttonIds;
|
||||
menu.columnIds=columnIds;
|
||||
menu.formIds=formIds;
|
||||
}else{
|
||||
selectedModel.value.menu=null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less">
|
||||
.autherizeDialog {
|
||||
.ant-modal {
|
||||
top: 20px;
|
||||
height: calc(100% - 40px) !important;
|
||||
|
||||
.ant-modal-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-modal-body {
|
||||
height: calc(100% - 120px) !important;
|
||||
|
||||
.scrollbar__view {
|
||||
height: 100%;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
max-height: none !important;
|
||||
|
||||
.ant-col {
|
||||
height: 100%;
|
||||
|
||||
.treebox {
|
||||
height: calc(100% - 42px);
|
||||
overflow: auto;
|
||||
padding-right: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-right: solid 1px #ccc;
|
||||
|
||||
.vben-tree {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<PageWrapper v-if="inited" dense contentFullHeight fixedHeight contentClass="flex">
|
||||
<div class="w-1/3 xl:w-1/4">
|
||||
<DeptTree @select="handleSelect" />
|
||||
</div>
|
||||
<div class="w-2/3 xl:w-3/4">
|
||||
<BasicTable @register="registerTable">
|
||||
</BasicTable>
|
||||
</div>
|
||||
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, h, ref, inject, reactive, watch } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import {getTreePostList} from '/@/api/system/post';
|
||||
import { SelectDepartment } from '/@/components/SelectOrganizational/index';
|
||||
import { Tag } from 'ant-design-vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
|
||||
import DeptTree from '/@/views/system/user/components/DeptTree.vue';
|
||||
import { UserInfo } from '/#/store';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const inited=ref(false);
|
||||
const checkedPosts=ref<Record<string, boolean>>({});
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: t('岗位名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('岗位编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('状态'),
|
||||
dataIndex: 'enabledMark',
|
||||
width: 80,
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
customRender: ({ record }) => {
|
||||
const status = record.enabledMark;
|
||||
const enable = ~~status === 1;
|
||||
const color = enable ? 'green' : 'red';
|
||||
const text = enable ? t('启用') : t('停用');
|
||||
return h(Tag, { color: color }, () => text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'name',
|
||||
label: t('岗位名称'),
|
||||
component: 'Input',
|
||||
colProps: { lg: 8, md: 12, sm: 12 },
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('岗位编码'),
|
||||
component: 'Input',
|
||||
colProps: { lg: 8, md: 12, sm: 12 },
|
||||
},
|
||||
{
|
||||
field: 'enabledMark',
|
||||
label: t('状态'),
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: t('启用'), value: 1 },
|
||||
{ label: t('停用'), value: 0 },
|
||||
],
|
||||
},
|
||||
colProps: { lg: 8, md: 12, sm: 12 },
|
||||
},
|
||||
];
|
||||
|
||||
const { notification } = useMessage();
|
||||
const selectDeptId = ref('');
|
||||
const postId = ref('');
|
||||
const selectedUserIds = ref<string[]>([]);
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
// --- 新增:用于树形表格多选(父选中 -> 子也选中) ---
|
||||
const selectedRowKeys = ref<string[]>([]);
|
||||
const rowSelection = reactive<any>({
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: selectedRowKeys.value,
|
||||
// onChange 来自 ant table,会在任何选择变化时触发
|
||||
onChange: (keys: (string|number)[]) => {
|
||||
selectedRowKeys.value = (keys || []).map(k => String(k));
|
||||
syncCheckedPosts();
|
||||
},
|
||||
// onSelect 单条选择(可以用于父级级联选中子级)
|
||||
onSelect: (record: any, selected: boolean) => {
|
||||
const data = getDataSource ? getDataSource() : [];
|
||||
const node = findNode(data, String(record.id));
|
||||
const ids = node ? collectIds(node) : [String(record.id)];
|
||||
if (selected) {
|
||||
selectedRowKeys.value = Array.from(new Set([...selectedRowKeys.value, ...ids]));
|
||||
} else {
|
||||
selectedRowKeys.value = selectedRowKeys.value.filter(k => !ids.includes(k));
|
||||
}
|
||||
// 把 reactive 对象的 selectedRowKeys 同步(ant table 读取该字段)
|
||||
rowSelection.selectedRowKeys = selectedRowKeys.value;
|
||||
syncCheckedPosts();
|
||||
},
|
||||
// 全选/取消全选
|
||||
onSelectAll: (selected: boolean, selectedRows: any[], changeRows: any[]) => {
|
||||
// changeRows 通常是当前页被切换选中状态的行,使用它来只影响当前页
|
||||
const pageIds = (changeRows || selectedRows || []).map((r: any) => String(r.id));
|
||||
if (selected) {
|
||||
// 仅将当前页的 id 加入已选集合(保留跨页已选)
|
||||
selectedRowKeys.value = Array.from(new Set([...selectedRowKeys.value, ...pageIds]));
|
||||
} else {
|
||||
// 仅移除当前页的 id,不清空其它页的已选
|
||||
selectedRowKeys.value = selectedRowKeys.value.filter((k) => !pageIds.includes(k));
|
||||
}
|
||||
rowSelection.selectedRowKeys = selectedRowKeys.value;
|
||||
syncCheckedPosts();
|
||||
},
|
||||
});
|
||||
|
||||
// helper: 在 tree data 中查找节点
|
||||
function findNode(list: any[], id: string): any | null {
|
||||
for (const n of list || []) {
|
||||
if (String(n.id) === id) return n;
|
||||
const found = findNode(n.children || [], id);
|
||||
if (found) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// helper: 收集节点以及所有子孙 id(字符串形式)
|
||||
function collectIds(node: any, out: string[] = []) {
|
||||
out.push(String(node.id));
|
||||
(node.children || []).forEach((c: any) => collectIds(c, out));
|
||||
return out;
|
||||
}
|
||||
// helper: 返回所有节点 id(平展)
|
||||
function flattenAllIds(list: any[], out: string[] = []) {
|
||||
for (const n of list || []) {
|
||||
out.push(String(n.id));
|
||||
if (n.children && n.children.length) flattenAllIds(n.children, out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function syncCheckedPosts() {
|
||||
const map: Record<string, boolean> = {};
|
||||
(selectedRowKeys.value || []).forEach(k => (map[String(k)] = true));
|
||||
checkedPosts.value = map;
|
||||
}
|
||||
|
||||
// --- 使用 useTable 时将 rowSelection 传入(支持父选中级联子级) ---
|
||||
const [registerTable, { reload, getDataSource }] = useTable({
|
||||
title: t('岗位列表'),
|
||||
api: getTreePostList,
|
||||
columns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchema,
|
||||
actionColOptions: { span: 16 },
|
||||
showResetButton: false,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
//发送请求默认新增 左边树结构所选机构id
|
||||
return { ...params, deptId: selectDeptId.value };
|
||||
},
|
||||
rowKey: 'id',
|
||||
pagination: {
|
||||
pageSize: 20,
|
||||
},
|
||||
striped: false,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
showIndexColumn: false,
|
||||
defaultExpandAllRows: true,
|
||||
// 把我们准备好的 rowSelection 对象传入
|
||||
rowSelection,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleSelect(deptId = '') {
|
||||
selectDeptId.value = deptId;
|
||||
reload({ searchInfo: { deptId: deptId } });
|
||||
}
|
||||
|
||||
async function initStep() {
|
||||
inited.value=true;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let posts=checkedPosts.value;
|
||||
let checkPostIds = Object.keys(posts).filter(key => posts[key] === true);
|
||||
|
||||
if(checkPostIds&&checkPostIds.length>0){
|
||||
let post = selectedModel.value.post || (selectedModel.value.post = {});
|
||||
post.postIds=checkPostIds;
|
||||
}else{
|
||||
selectedModel.value.post=null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getRolePageList} from '/@/api/system/role';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '角色名',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '角色编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getRolePageList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark: item.enabledMark
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let role = selectedModel.value.role || (selectedModel.value.role = {});
|
||||
role.roleIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.role=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,423 @@
|
||||
<template>
|
||||
<div class="role-menu-auth">
|
||||
<a-row :gutter="[16, 16]" class="h-full">
|
||||
<a-col :span="8" >
|
||||
<BasicTable
|
||||
@register="registerTableRole"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-col>
|
||||
<a-col :span="4" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('菜单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="menuAuthData"
|
||||
checkable
|
||||
ref="menuRef"
|
||||
@check="handleMenuSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="4" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('按钮')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="buttonAuthData"
|
||||
checkable
|
||||
ref="buttonRef"
|
||||
@check="handleButtonSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="4" >
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('字段')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="columnAuthData"
|
||||
checkable
|
||||
ref="columnRef"
|
||||
@check="handleColumnSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">{{
|
||||
t('表单')
|
||||
}}</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="fieldAuthData"
|
||||
checkable
|
||||
ref="fieldRef"
|
||||
@check="handleFieldSelect"
|
||||
:fieldNames="{ title: 'title', key: 'roleObjectId' }"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,unref,inject,nextTick} from 'vue';
|
||||
import { BasicTable, useTable, TableAction, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import { TreeItem,BasicTree,TreeActionType} from '/@/components/Tree';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import {getRolePageList,getRoleAuth} from '/@/api/system/role';
|
||||
import { getMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { getMenuButtonList, getMenuColumnList, getMenuFieldList } from '/@/api/system/menuButton';
|
||||
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const menuRef = ref<Nullable<TreeActionType>>(null);
|
||||
const buttonRef = ref<Nullable<TreeActionType>>(null);
|
||||
const columnRef = ref<Nullable<TreeActionType>>(null);
|
||||
const fieldRef = ref<Nullable<TreeActionType>>(null);
|
||||
|
||||
const selectRoleId = ref('');
|
||||
|
||||
let menuData =[];
|
||||
let buttonData =[];
|
||||
let columnData = [];
|
||||
let fieldData =[];
|
||||
|
||||
const menuAuthData = ref<TreeItem[]>([]);
|
||||
const buttonAuthData = ref<TreeItem[]>([]);
|
||||
const columnAuthData = ref<TreeItem[]>([]);
|
||||
const fieldAuthData = ref<TreeItem[]>([]);
|
||||
|
||||
let selectedMenuData={};
|
||||
let selectedButtonData={};
|
||||
let selectedColumnData={};
|
||||
let selectedFieldData={};
|
||||
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const inited=ref(false);
|
||||
|
||||
const roleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('角色名称'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('角色编码'),
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
align: 'left',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
menuData = await getMenuSimpleTree();
|
||||
buttonData = await getMenuButtonList();
|
||||
columnData = await getMenuColumnList();
|
||||
fieldData = await getMenuFieldList();
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const [registerTableRole, { reload: roleReload }] = useTable({
|
||||
title: t('角色'),
|
||||
api: getRolePageList,
|
||||
rowKey: 'id',
|
||||
columns: roleColumns,
|
||||
formConfig: {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: [],
|
||||
showResetButton: false,
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'radio',
|
||||
onChange: onSelectChange,
|
||||
},
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
striped: false,
|
||||
pagination: { pageSize: 100 },
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
function handleRowClick(record: any) {
|
||||
if(selectRoleId.value===record.id){
|
||||
return;
|
||||
}
|
||||
selectRoleId.value = record.id;
|
||||
reloadRoleAuth();
|
||||
}
|
||||
|
||||
function onSelectChange(rowKey: string[]) {
|
||||
if(selectRoleId.value== rowKey[0]){
|
||||
return;
|
||||
}
|
||||
|
||||
if(rowKey[0]){
|
||||
selectRoleId.value =rowKey[0] ;
|
||||
reloadRoleAuth();
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthMenuTree(authTree,allTree,authList) {
|
||||
let result=false;
|
||||
for (let i = 0; i < allTree.length; i++) {
|
||||
let o = allTree[i];
|
||||
let include=false;
|
||||
let childrenInclude=false;
|
||||
if (authList.includes(o.id)) {
|
||||
include=true;
|
||||
}
|
||||
|
||||
let children=[];
|
||||
if (o.children?.length > 0) {
|
||||
childrenInclude=getAuthMenuTree(children, o.children, authList);
|
||||
}
|
||||
if(include===true||childrenInclude===true){
|
||||
let node=cloneDeep(o);
|
||||
node.children=children;
|
||||
node.roleId=selectRoleId.value;
|
||||
authTree.push(node);
|
||||
result=true;
|
||||
}
|
||||
}
|
||||
|
||||
setRoleObjectId(authTree);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAuthObjectTree(menuTree,authList,type) {
|
||||
let cloneMenuTree=cloneDeep(menuTree);
|
||||
for(let i = 0; i < authList.length; i++){
|
||||
let object=authList[i];
|
||||
object.type=type;
|
||||
let result=hangToMenuTree(object,cloneMenuTree,type);
|
||||
if(result!==true){
|
||||
cloneMenuTree.push(object);
|
||||
}
|
||||
}
|
||||
|
||||
let objectTree=[];
|
||||
fillToObjectTree(objectTree,cloneMenuTree,type);
|
||||
setRoleObjectId(objectTree);
|
||||
return objectTree;
|
||||
}
|
||||
|
||||
function hangToMenuTree(object,menuTree,type) {
|
||||
for(let i = 0; i< menuTree.length; i++){
|
||||
let menu=menuTree[i];
|
||||
if(menu.type&&menu.type===type){
|
||||
continue;
|
||||
}
|
||||
let flag=false;
|
||||
if(menu.id==object.menuId){
|
||||
let children = menu.children || (menu.children = []);
|
||||
children.push(object);
|
||||
flag=true;
|
||||
}else{
|
||||
if (menu.children?.length > 0) {
|
||||
flag=hangToMenuTree(object,menu.children,type);
|
||||
}
|
||||
}
|
||||
if(flag===true){
|
||||
menu.hasObjects=true;
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function fillToObjectTree(objectTree,menuTree,type){
|
||||
for(let i = 0; i< menuTree.length; i++){
|
||||
let menu=menuTree[i];
|
||||
if(menu.type===type){
|
||||
objectTree.push(menu);
|
||||
}else if(menu.hasObjects===true){
|
||||
let children=[]
|
||||
if (menu.children?.length > 0) {
|
||||
fillToObjectTree(children,menu.children,type);
|
||||
}
|
||||
menu.children=children;
|
||||
objectTree.push(menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setRoleObjectId(objectTree){
|
||||
for(let i = 0; i< objectTree.length; i++){
|
||||
let object=objectTree[i];
|
||||
object.roleObjectId=selectRoleId.value+"_"+object.id;
|
||||
if (object.children?.length > 0) {
|
||||
setRoleObjectId(object.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadRoleAuth(){
|
||||
let roleId=selectRoleId.value;
|
||||
if(!roleId){
|
||||
return [];
|
||||
}
|
||||
const authList = await getRoleAuth(roleId);
|
||||
let authMenuIds= authList.menuIds || [];
|
||||
let authButtonIds= authList.buttonIds || [];
|
||||
let authColumnIds= authList.columnIds || [];
|
||||
let authFieldIds= authList.formIds || [];
|
||||
|
||||
let menuAuthTree=[];
|
||||
getAuthMenuTree(menuAuthTree,menuData,authMenuIds);
|
||||
menuAuthData.value=menuAuthTree;
|
||||
|
||||
let authButtonData=buttonData.filter((item)=>authButtonIds.includes(item.id));
|
||||
let buttonAuthTree=getAuthObjectTree(menuData,authButtonData,'button');
|
||||
buttonAuthData.value=buttonAuthTree;
|
||||
|
||||
let authColumnData=columnData.filter((item)=>authColumnIds.includes(item.id));
|
||||
let columnAuthTree=getAuthObjectTree(menuData,authColumnData,'column');
|
||||
columnAuthData.value=columnAuthTree;
|
||||
|
||||
let authFieldData=fieldData.filter((item)=>authFieldIds.includes(item.id));
|
||||
let fieldAuthTree=getAuthObjectTree(menuData,authFieldData,'field');
|
||||
fieldAuthData.value=fieldAuthTree;
|
||||
|
||||
nextTick(() => {
|
||||
unref(menuRef).setCheckedKeys(selectedMenuData[selectRoleId.value]);
|
||||
unref(buttonRef).setCheckedKeys(selectedButtonData[selectRoleId.value]);
|
||||
unref(columnRef).setCheckedKeys(selectedColumnData[selectRoleId.value]);
|
||||
unref(fieldRef).setCheckedKeys(selectedFieldData[selectRoleId.value]);
|
||||
unref(menuRef)?.expandAll(true);
|
||||
unref(buttonRef)?.expandAll(true);
|
||||
unref(columnRef)?.expandAll(true);
|
||||
unref(fieldRef)?.expandAll(true);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMenuSelect(keys, e){
|
||||
selectedMenuData[selectRoleId.value]=keys;
|
||||
}
|
||||
|
||||
function handleButtonSelect(keys, e){
|
||||
selectedButtonData[selectRoleId.value]=keys;
|
||||
}
|
||||
|
||||
function handleColumnSelect(keys, e){
|
||||
selectedColumnData[selectRoleId.value]=keys;
|
||||
}
|
||||
function handleFieldSelect(keys, e){
|
||||
selectedFieldData[selectRoleId.value]=keys;
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let map={};
|
||||
for(let key of Object.keys(selectedMenuData)){
|
||||
let list=selectedMenuData[key];
|
||||
if(list&&list.length>0){
|
||||
let menuIdList=list.map((item)=>{ return item.split("_")[1];});
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...menuIdList);
|
||||
}
|
||||
}
|
||||
|
||||
for(let key of Object.keys(selectedButtonData)){
|
||||
let list=selectedButtonData[key];
|
||||
if(list&&list.length>0){
|
||||
let allbuttonList=buttonData.map((item)=>{return item.id});
|
||||
let buttonList=list.map((item)=>{ return item.split("_")[1];}).filter((item)=>{return allbuttonList.includes(item);});
|
||||
if(buttonList&&buttonList.length>0){
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...buttonList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(let key of Object.keys(selectedColumnData)){
|
||||
let list=selectedColumnData[key];
|
||||
if(list&&list.length>0){
|
||||
let allColumnList=columnData.map((item)=>{return item.id});
|
||||
let columnList=list.map((item)=>{ return item.split("_")[1];}).filter((item)=>{return allColumnList.includes(item);});
|
||||
if(columnList&&columnList.length>0){
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...columnList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for(let key of Object.keys(selectedFieldData)){
|
||||
let list=selectedFieldData[key];
|
||||
if(list&&list.length>0){
|
||||
let allFieldList=fieldData.map((item)=>{return item.id});
|
||||
let fieldList=list.map((item)=>{ return item.split("_")[1];}).filter((item)=>{return allFieldList.includes(item);});
|
||||
if(fieldList&&fieldList.length>0){
|
||||
let objectList=map[key]||(map[key]=[]);
|
||||
objectList.push(...fieldList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Object.keys(map).length>0){
|
||||
let authorize = selectedModel.value.authorize || (selectedModel.value.authorize = {});
|
||||
authorize.authorizeMap=map;
|
||||
}else{
|
||||
selectedModel.value.authorize=null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-table-title){
|
||||
display:none!important;
|
||||
}
|
||||
|
||||
.treebox {
|
||||
height: calc(100% - 42px);
|
||||
overflow: auto;
|
||||
padding-right: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-right: solid 1px #ccc;
|
||||
|
||||
.vben-tree {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getXjrSystemConfigPage} from '/@/api/system/systemConfig';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '配置值',
|
||||
key:'value',
|
||||
dataIndex:'value',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getXjrSystemConfigPage({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
value: item.value
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let systemConfig = selectedModel.value.systemConfig || (selectedModel.value.systemConfig = {});
|
||||
systemConfig.systemConfigIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.systemConfig=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import {getTenantPageList} from '/@/api/system/tenant';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '租户名',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '租户编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getTenantPageList({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let tenant = selectedModel.value.tenant || (selectedModel.value.tenant = {});
|
||||
tenant.tenantIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.tenant=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div style="margin-left: 30%">
|
||||
<div class="text-base border-l-6 border-[#5e95ff] pl-3 h-5 leading-5 mb-3 ml-2">
|
||||
{{ t('已授权菜单') }}
|
||||
</div>
|
||||
<div class="treebox">
|
||||
<BasicTree
|
||||
:treeData="treeData"
|
||||
checkable
|
||||
ref="treeRef"
|
||||
@check="handleTreeSelect"
|
||||
:fieldNames="{ title: 'title', key: 'id' }"
|
||||
>
|
||||
<template #title="{ title, systemName, parentId }">
|
||||
<div v-if="parentId == 0">{{ title }} 【{{ systemName }}】</div>
|
||||
<div v-else>{{ title }}</div>
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref,inject,onMounted,nextTick } from 'vue';
|
||||
import { getTenantMenuSimpleTree } from '/@/api/system/menu';
|
||||
import { TreeItem, BasicTree, TreeActionType } from '/@/components/Tree';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getAuthorize } from '/@/api/system/tenant';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
|
||||
|
||||
const emits = defineEmits(['success', 'register']);
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const treeRef = ref<Nullable<TreeActionType>>(null);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
function getTree(tree) {
|
||||
return tree || null;
|
||||
}
|
||||
|
||||
function handleTreeSelect(keys, e) {
|
||||
selectedKeys.value = [...e.halfCheckedKeys, ...keys];
|
||||
}
|
||||
|
||||
function getAuthTree(authTree,allTree,authList) {
|
||||
let result=false;
|
||||
for (let i = 0; i < allTree.length; i++) {
|
||||
let o = allTree[i];
|
||||
let include=false;
|
||||
let childrenInclude=false;
|
||||
if (authList.includes(o.id)) {
|
||||
include=true;
|
||||
}
|
||||
|
||||
let children=[];
|
||||
if (o.children?.length > 0) {
|
||||
childrenInclude=getAuthTree(children, o.children, authList);
|
||||
}
|
||||
if(include===true||childrenInclude===true){
|
||||
let node=cloneDeep(o);
|
||||
node.children=children;
|
||||
authTree.push(node);
|
||||
result=true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = storeToRefs(userStore);
|
||||
|
||||
let allTree = await getTenantMenuSimpleTree();
|
||||
const authList = await getAuthorize(userInfo.value.tenantId);
|
||||
|
||||
let authTree=[];
|
||||
getAuthTree(authTree,allTree,authList);
|
||||
|
||||
treeData.value=authTree;
|
||||
|
||||
nextTick(() => {
|
||||
getTree(unref(treeRef)).expandAll(true);
|
||||
});
|
||||
|
||||
inited.value=true;
|
||||
}catch(e){
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function builldSelectedTree(tree,selecteds,arr){
|
||||
if((!tree||tree.length==0)||(!selecteds||selecteds.length==0)){
|
||||
return;
|
||||
}
|
||||
|
||||
for(let i=0;i<tree.length;i++){
|
||||
let treeNode=tree[i];
|
||||
if(selecteds.includes(treeNode.id)){
|
||||
arr.push(treeNode);
|
||||
|
||||
let children=treeNode.children;
|
||||
let childArr=[];
|
||||
builldSelectedTree(children,selecteds,childArr);
|
||||
treeNode.children=childArr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
let cloneTreeData= cloneDeep(treeData.value);
|
||||
let arr=[];
|
||||
builldSelectedTree(cloneTreeData,selectedKeys.value,arr);
|
||||
if(arr.length>0){
|
||||
let tenantAuthorize = selectedModel.value.tenantAuthorize || (selectedModel.value.tenantAuthorize = {});
|
||||
tenantAuthorize.menuTree=arr;
|
||||
tenantAuthorize.menuIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.tenantAuthorize=null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<a-table :columns="columns" :data-source="datas" :row-selection="rowSelection" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {ref,inject} from 'vue';
|
||||
import { getXjrGroupPage} from '/@/api/system/group';
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '用户组名称',
|
||||
key:'name',
|
||||
dataIndex:'name',
|
||||
align: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '用户组编码',
|
||||
key:'code',
|
||||
dataIndex:'code',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabledMark',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
customRender: ({ record }) => {
|
||||
if (record.enabledMark===1) {
|
||||
return '启用';
|
||||
}else{
|
||||
return '禁用';
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const datas=ref([]);
|
||||
const inited=ref(false);
|
||||
const selectedKeys = ref([]);
|
||||
const selectedModel = inject('selectedModel');
|
||||
|
||||
const rowSelection = ref({
|
||||
onChange: (selectedRowKeys, selectedRows) => {
|
||||
selectedKeys.value=selectedRowKeys;
|
||||
},
|
||||
});
|
||||
|
||||
async function initStep() {
|
||||
try {
|
||||
if(inited.value){
|
||||
return;
|
||||
}
|
||||
const rtObject=await getXjrGroupPage({limit:1,size:99});
|
||||
let resList=rtObject.list;
|
||||
let arr=[];
|
||||
if(resList) {
|
||||
let index = 0;
|
||||
resList.forEach(item => {
|
||||
arr.push(
|
||||
{
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
code: item.code,
|
||||
enabledMark: item.enabledMark
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
datas.value=arr;
|
||||
inited.value=true;
|
||||
}catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function completeStep(){
|
||||
if(selectedKeys.value.length>0){
|
||||
let userGroup = selectedModel.value.userGroup || (selectedModel.value.userGroup = {});
|
||||
userGroup.userGroupIds=selectedKeys.value;
|
||||
}else{
|
||||
selectedModel.value.userGroup=null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
initStep,
|
||||
completeStep
|
||||
})
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user