This commit is contained in:
fuenmao
2026-08-28 11:09:20 +08:00
parent d0d7ca8e69
commit 14515ccdd6
8 changed files with 1073 additions and 254 deletions
+138 -7
View File
@@ -12,13 +12,26 @@
</el-button>
</template>
<template #companyId="scope">
<el-tag effect="plain" type="primary">
{{ scope.row.companyName || '-' }}
</el-tag>
</template>
<template #departmentId="scope">
<el-tag v-if="scope.row.departmentName" effect="light" type="warning">
{{ scope.row.departmentName }}
</el-tag>
<span v-else class="text-gray-400">-</span>
</template>
<template #contractType="scope">
<el-tag effect="plain" type="primary">
{{ scope.row.contractType }}
</el-tag>
</template>
<template #positionNames="scope">
<template #positionId="scope">
<el-space wrap>
<el-tag v-for="name in scope.row.positionNames || []" :key="name" size="small">
{{ name }}
@@ -498,6 +511,31 @@ const templateEditing = ref(false);
const companies = ref<OnboardingCompanyOption[]>([]);
const departments = ref<OnboardingDepartmentOption[]>([]);
const positions = ref<OnboardingPositionOption[]>([]);
const searchCompanies = ref<OnboardingCompanyOption[]>([]);
const searchDepartments = ref<OnboardingDepartmentOption[]>([]);
const searchPositions = ref<OnboardingPositionOption[]>([]);
const companySearchOptions = computed(() =>
searchCompanies.value.map(c => ({
label: c.companyName,
value: c.id
}))
);
const departmentSearchOptions = computed(() =>
searchDepartments.value.map(d => ({
label: d.departmentName,
value: d.id
}))
);
const positionSearchOptions = computed(() =>
searchPositions.value.map(p => ({
label: p.positionName,
value: p.id
}))
);
const versions = ref<OnboardingContractVersion[]>([]);
const versionVisible = ref(false);
const variablesVisible = ref(false);
@@ -560,11 +598,38 @@ const versionForm = reactive<Partial<OnboardingContractVersion>>(emptyVersion())
const variableForm = reactive<Partial<OnboardingTemplateVariable>>(emptyVariable());
const templateColumns: ColumnProps<OnboardingContractTemplate>[] = [
{ prop: "companyName", label: "所属公司", minWidth: 160 },
{ prop: "departmentName", label: "所属部门", minWidth: 140 },
{
prop: "companyId",
label: "所属公司",
minWidth: 160,
enum: companySearchOptions,
search: {
el: "select",
props: { filterable: true, clearable: true, placeholder: "请选择所属公司" }
}
},
{
prop: "departmentId",
label: "所属部门",
minWidth: 140,
enum: departmentSearchOptions,
search: {
el: "select",
props: { filterable: true, clearable: true, placeholder: "请选择所属部门" }
}
},
{
prop: "positionId",
label: "适用岗位",
minWidth: 200,
enum: positionSearchOptions,
search: {
el: "select",
props: { filterable: true, clearable: true, placeholder: "请选择适用岗位" }
}
},
{ prop: "templateCode", label: "模板编码", minWidth: 160, search: { el: "input" } },
{ prop: "templateName", label: "模板名称", minWidth: 200, search: { el: "input" } },
{ prop: "positionNames", label: "适用岗位", minWidth: 200 },
{ prop: "contractType", label: "合同类型", width: 140, search: { el: "input" } },
{
prop: "status",
@@ -737,9 +802,47 @@ async function removeVersion(row: OnboardingContractVersion) {
ElMessage.success("版本删除成功");
}
function downloadDocx(fileId?: number) {
/**
* 使用后台登录令牌读取文件流,避免直接打开受保护接口时出现 401。
* 移动端使用独立的入职 Token 公共接口,不经过这里。
*/
async function downloadDocx(fileId?: number) {
if (!fileId) return ElMessage.warning("暂无 DOCX 源文件");
window.open(`/api/sys/dev/file/download?id=${fileId}`, "_blank");
try {
const response: any = await onboardingTaskApi.downloadFile(fileId);
const blob = response?.data;
if (!(blob instanceof Blob)) {
throw new Error("服务器未返回有效的文件流");
}
const disposition = String(response?.headers?.["content-disposition"] || "");
const encodedName =
disposition.match(/filename\*=UTF-8\'\'([^;]+)/i)?.[1] ||
disposition.match(/filename="?([^";]+)"?/i)?.[1];
let fileName = "合同模板.docx";
if (encodedName) {
try {
fileName = decodeURIComponent(encodedName.replace(/\+/g, "%20"));
} catch {
fileName = encodedName;
}
}
const objectUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = objectUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
link.remove();
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 0);
} catch (error: any) {
ElMessage.error(
error?.msg ||
error?.message ||
"DOCX 源文件下载失败"
);
}
}
type EpTagType = "success" | "warning" | "info" | "primary" | "danger";
@@ -806,7 +909,35 @@ async function saveVariable() {
ElMessage.success("变量已确认并保存");
}
onMounted(loadCompanies);
async function loadSearchOptions() {
await loadCompanies();
searchCompanies.value = companies.value;
const deptList: OnboardingDepartmentOption[] = [];
const posList: OnboardingPositionOption[] = [];
if (companies.value && companies.value.length > 0) {
const promises = companies.value.map(async comp => {
const [depRes, posRes] = await Promise.all([
onboardingTaskApi.listDepartments(comp.id),
onboardingTaskApi.listPositions(comp.id)
]);
if (depRes.data) deptList.push(...depRes.data);
if (posRes.data) posList.push(...posRes.data);
});
await Promise.all(promises);
}
const uniqueDeptMap = new Map<number, OnboardingDepartmentOption>();
deptList.forEach(d => uniqueDeptMap.set(d.id, d));
searchDepartments.value = Array.from(uniqueDeptMap.values());
const uniquePosMap = new Map<number, OnboardingPositionOption>();
posList.forEach(p => uniquePosMap.set(p.id, p));
searchPositions.value = Array.from(uniquePosMap.values());
}
onMounted(loadSearchOptions);
</script>
<style scoped>