3333
This commit is contained in:
@@ -126,6 +126,7 @@ export interface OnboardingTaskContractVariableItem {
|
||||
variableName: string;
|
||||
variableType: string;
|
||||
dataType: string;
|
||||
sortCode: number;
|
||||
variableValue?: string | number;
|
||||
required: boolean;
|
||||
hrEditable: boolean;
|
||||
|
||||
@@ -173,6 +173,21 @@ const processTableData = computed(() => {
|
||||
// 监听页面 initParam 改化,重新获取表格数据
|
||||
watch(() => props.initParam, getTableList, { deep: true });
|
||||
|
||||
// 监听 props.data 变化,动态更新分页 total,并在超出页码时自动修正
|
||||
watch(
|
||||
() => props.data,
|
||||
newData => {
|
||||
if (newData) {
|
||||
pageable.value.total = newData.length;
|
||||
const maxPage = Math.ceil(newData.length / pageable.value.pageSize) || 1;
|
||||
if (pageable.value.pageNum > maxPage) {
|
||||
pageable.value.pageNum = 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 接收 columns 并设置为响应式
|
||||
const tableColumns = reactive<ColumnProps[]>(props.columns);
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@ import { ElNotification } from "element-plus";
|
||||
* @description 全局代码错误捕捉
|
||||
* */
|
||||
const errorHandler = (error: any) => {
|
||||
// 过滤 HTTP 请求错误
|
||||
if (error.status || error.status == 0) return false;
|
||||
// 过滤 HTTP 请求错误与接口业务返回错误(这类错误已在 Axios 拦截器中统一通过 ElMessage 弹出提示,无需重复告警)
|
||||
if (!error || error.status !== undefined || error.code !== undefined || error.isAxiosError || error.config) {
|
||||
return false;
|
||||
}
|
||||
let errorMap: { [key: string]: string } = {
|
||||
InternalError: "Javascript引擎内部错误",
|
||||
ReferenceError: "未找到对象",
|
||||
@@ -29,10 +31,10 @@ const errorHandler = (error: any) => {
|
||||
EvalError: "错误的使用了Eval",
|
||||
URIError: "URI错误"
|
||||
};
|
||||
let errorName = errorMap[error.name] || "未知错误";
|
||||
let errorName = errorMap[error?.name] || "未知错误";
|
||||
ElNotification({
|
||||
title: errorName,
|
||||
message: error,
|
||||
message: error?.message || String(error),
|
||||
type: "error",
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
plain
|
||||
size="small"
|
||||
icon="Download"
|
||||
@click="download(contract.generatedDocxFileId)"
|
||||
@click="download(contract.generatedDocxFileId, `${detail?.employee?.employeeName || '员工'}_${contract.contractName || '入职合同'}.docx`)"
|
||||
>
|
||||
下载合同 DOCX
|
||||
</el-button>
|
||||
@@ -159,6 +159,7 @@
|
||||
<script setup lang="ts" name="onboardingEmployee">
|
||||
import { ref } from "vue";
|
||||
import { UserFilled, Download } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import {
|
||||
onboardingTaskApi,
|
||||
type OnboardingEmployee
|
||||
@@ -243,8 +244,42 @@ async function showDetail(row: OnboardingEmployee) {
|
||||
detailVisible.value = true;
|
||||
}
|
||||
|
||||
function download(fileId: number) {
|
||||
window.open(`/api/sys/dev/file/download?id=${fileId}`, "_blank");
|
||||
function resolveDownloadFileName(response: any, fallbackFileName?: string) {
|
||||
const disposition = String(response?.headers?.["content-disposition"] || "");
|
||||
const encodedName =
|
||||
disposition.match(/filename\*=UTF-8\'\'([^;]+)/i)?.[1] ||
|
||||
disposition.match(/filename="?([^";]+)"?/i)?.[1];
|
||||
|
||||
if (encodedName) {
|
||||
try {
|
||||
return decodeURIComponent(encodedName.replace(/\+/g, "%20"));
|
||||
} catch {
|
||||
return encodedName;
|
||||
}
|
||||
}
|
||||
|
||||
return fallbackFileName || "入职合同.docx";
|
||||
}
|
||||
|
||||
async function download(fileId: number, fallbackFileName?: string) {
|
||||
try {
|
||||
const response: any = await onboardingTaskApi.downloadFile(fileId);
|
||||
const blob = response?.data;
|
||||
if (!(blob instanceof Blob)) {
|
||||
throw new Error("服务器未返回有效的文件流");
|
||||
}
|
||||
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = objectUrl;
|
||||
link.download = resolveDownloadFileName(response, fallbackFileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.msg || error?.message || "合同文件下载失败");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
|
||||
<el-table-column prop="variableName" label="中文显示名称" min-width="160" />
|
||||
|
||||
<el-table-column prop="sortCode" label="排序" width="80" align="center" sortable />
|
||||
|
||||
<el-table-column prop="dataType" label="数据类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" type="info">{{ row.dataType || 'string' }}</el-tag>
|
||||
|
||||
@@ -1320,6 +1320,10 @@ async function onOnboardingTemplateChange(templateId: number) {
|
||||
const contractName = contractTemplates.value.find(item => item.id === contract.contractTemplateId)?.templateName || "合同";
|
||||
return (variableResults[index].data?.list || [])
|
||||
.filter(variable => variable.hrEditable && ["contract", "company"].includes(variable.variableType))
|
||||
.sort((left, right) => {
|
||||
const sortResult = (left.sortCode ?? 100) - (right.sortCode ?? 100);
|
||||
return sortResult || left.variableCode.localeCompare(right.variableCode);
|
||||
})
|
||||
.map(variable => ({
|
||||
contractTemplateVersionId: contract.contractTemplateVersionId,
|
||||
variableCode: variable.variableCode,
|
||||
@@ -1423,10 +1427,15 @@ async function copyOrShareTaskLink(row: OnboardingTask) {
|
||||
}
|
||||
}
|
||||
|
||||
async function showDetail(row: OnboardingTask) {
|
||||
async function showDetail(row: OnboardingTask, preserveTab: boolean = false) {
|
||||
const currentTab = activeDetailTab.value;
|
||||
const { data }: any = await onboardingTaskApi.detail({ id: row.id });
|
||||
detail.value = data;
|
||||
activeDetailTab.value = "profile";
|
||||
if (!preserveTab) {
|
||||
activeDetailTab.value = "profile";
|
||||
} else {
|
||||
activeDetailTab.value = currentTab || "profile";
|
||||
}
|
||||
detailVisible.value = true;
|
||||
void loadMaterialPreviews(data?.materials || []);
|
||||
}
|
||||
@@ -1539,7 +1548,7 @@ async function generate(taskContractId: number) {
|
||||
await onboardingTaskApi.generateContract(taskContractId);
|
||||
ElMessage.success("DOCX合同文件已生成");
|
||||
if (detail.value?.task?.id) {
|
||||
await showDetail(detail.value.task);
|
||||
await showDetail(detail.value.task, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1580,7 +1589,7 @@ async function reviewMaterial(taskMaterialId: number, reviewStatus: string) {
|
||||
ElMessage.success(reviewStatus === "passed" ? "该材料已标记为合格" : "已标记需补充");
|
||||
|
||||
if (detail.value?.task) {
|
||||
await showDetail(detail.value.task);
|
||||
await showDetail(detail.value.task, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,31 +5,40 @@
|
||||
!-->
|
||||
<template>
|
||||
<form-container v-model="visible" :title="`授权权限->${grantProps.name}`" form-size="1200px" @close="onClose">
|
||||
<el-alert
|
||||
title="注:此功能界面需要与代码查询条件配合使用,并非所有接口都需设置数据范围,多用于业务模块!"
|
||||
type="warning"
|
||||
show-icon
|
||||
style="margin-bottom: 10px"
|
||||
/>
|
||||
<ProTable ref="proTable" :columns="columns" :tool-button="false" :data="grantProps.tableData" row-key="api">
|
||||
<!-- 自定义api表头 -->
|
||||
<template #apiHeader>
|
||||
<el-row class="row-bg" justify="space-between">
|
||||
<el-col :span="12"><el-checkbox @change="onCheckAllChange"> 接口 </el-checkbox></el-col>
|
||||
<el-col :span="12"> <el-input v-model="searchValue" @input="searchApi" placeholder="请输入接口名称" /></el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<!-- 接口 -->
|
||||
<template #api="scope">
|
||||
<el-checkbox v-model="scope.row.check" @change="val => changeApi(scope.row, val)">
|
||||
{{ scope.row.api }}
|
||||
</el-checkbox>
|
||||
</template>
|
||||
<!-- 数据范围 -->
|
||||
<template #dataScope="scope">
|
||||
<DataScopeSelector v-if="scope.row.check" v-model="scope.row.dataScope"></DataScopeSelector>
|
||||
</template>
|
||||
</ProTable>
|
||||
<div class="grant-permission-container">
|
||||
<el-alert
|
||||
title="注:此功能界面需要与代码查询条件配合使用,并非所有接口都需设置数据范围,多用于业务模块!"
|
||||
type="warning"
|
||||
show-icon
|
||||
style="margin-bottom: 10px"
|
||||
/>
|
||||
<ProTable
|
||||
ref="proTable"
|
||||
:columns="columns"
|
||||
:tool-button="false"
|
||||
:data="grantProps.tableData"
|
||||
row-key="api"
|
||||
max-height="560px"
|
||||
>
|
||||
<!-- 自定义api表头 -->
|
||||
<template #apiHeader>
|
||||
<el-row class="row-bg" justify="space-between">
|
||||
<el-col :span="12"><el-checkbox @change="onCheckAllChange"> 接口 </el-checkbox></el-col>
|
||||
<el-col :span="12"> <el-input v-model="searchValue" clearable @input="searchApi" placeholder="请输入接口名称" /></el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<!-- 接口 -->
|
||||
<template #api="scope">
|
||||
<el-checkbox v-model="scope.row.check" @change="val => changeApi(scope.row, val)">
|
||||
{{ scope.row.api }}
|
||||
</el-checkbox>
|
||||
</template>
|
||||
<!-- 数据范围 -->
|
||||
<template #dataScope="scope">
|
||||
<DataScopeSelector v-if="scope.row.check" v-model="scope.row.dataScope"></DataScopeSelector>
|
||||
</template>
|
||||
</ProTable>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
@@ -89,8 +98,11 @@ const columns: ColumnProps<SysRole.ResTreeSelector>[] = [
|
||||
async function onOpen(props: FormProps.Base<SysRole.SysRoleInfo | SysUser.SysUserInfo>) {
|
||||
grantProps.id = props.record.id || 0;
|
||||
grantProps.name = props.record.name || "";
|
||||
await loadData();
|
||||
grantProps.tableData = [];
|
||||
grantProps.permissionData = [];
|
||||
searchValue.value = "";
|
||||
visible.value = true;
|
||||
await loadData();
|
||||
}
|
||||
|
||||
/** 加载数据 */
|
||||
@@ -110,6 +122,7 @@ async function loadData() {
|
||||
* @param ownPermission 已拥有的权限
|
||||
*/
|
||||
function echoModuleData(apiList: string[], ownPermission: SysRole.RoleOwnPermission) {
|
||||
grantProps.tableData = [];
|
||||
//遍历api列表
|
||||
apiList.forEach(item => {
|
||||
let data: SysRole.RoleGrantPermission = {
|
||||
@@ -132,23 +145,37 @@ function echoModuleData(apiList: string[], ownPermission: SysRole.RoleOwnPermiss
|
||||
}
|
||||
grantProps.tableData.push(data);
|
||||
});
|
||||
grantProps.permissionData = grantProps.tableData; //保存权限数据
|
||||
grantProps.permissionData = [...grantProps.tableData]; //保存权限数据
|
||||
}
|
||||
|
||||
/** 搜索api
|
||||
* @param value 搜索值
|
||||
*/
|
||||
function searchApi(value: string) {
|
||||
console.log("[ value ] >", value);
|
||||
//如果搜索值为空,就显示全部
|
||||
if (!value) {
|
||||
grantProps.tableData = grantProps.permissionData;
|
||||
return;
|
||||
}
|
||||
//过滤api
|
||||
grantProps.tableData = grantProps.permissionData.filter(item => {
|
||||
return item.api.indexOf(value) > -1;
|
||||
// 先把当前当前页面的修改同步到 permissionData 中
|
||||
grantProps.permissionData.forEach(item => {
|
||||
let data = grantProps.tableData.find(data => data.api === item.api);
|
||||
if (data) {
|
||||
item.check = data.check;
|
||||
item.dataScope = data.dataScope;
|
||||
}
|
||||
});
|
||||
|
||||
const query = value ? value.trim().toLowerCase() : "";
|
||||
//如果搜索值为空,就显示全部
|
||||
if (!query) {
|
||||
grantProps.tableData = [...grantProps.permissionData];
|
||||
} else {
|
||||
//过滤api (支持不区分大小写)
|
||||
grantProps.tableData = grantProps.permissionData.filter(item => {
|
||||
return item.api.toLowerCase().includes(query);
|
||||
});
|
||||
}
|
||||
|
||||
// 筛选后重置页码至第1页
|
||||
if (proTable.value?.pageable) {
|
||||
proTable.value.pageable.pageNum = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交之前转换数据 */
|
||||
@@ -236,4 +263,15 @@ defineExpose({
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.grant-permission-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: calc(85vh - 120px);
|
||||
overflow-y: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
:deep(.el-table__body-wrapper) {
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user