<template>
|
<div class="batch-publish-container">
|
<!-- 批量发稿表单 -->
|
<div class="form-container">
|
<!-- 合同信息 -->
|
<div class="form-item">
|
<span class="required-mark">*</span>
|
<span class="form-label">合同信息</span>
|
<a-select
|
v-model:value="formState.contractId"
|
placeholder="请选择合同"
|
style="width: 360px;"
|
show-search
|
:filter-option="filterOption"
|
@change="handleContractChange"
|
>
|
<a-select-option v-for="contract in contractList" :key="contract.value" :value="contract.value">
|
{{ contract.label }}
|
</a-select-option>
|
</a-select>
|
</div>
|
|
<!-- 媒体组 -->
|
<div class="form-item">
|
<span class="required-mark">*</span>
|
<span class="form-label">媒体渠道</span>
|
<a-input
|
v-model:value="formState.mediaGroups"
|
placeholder="请输入媒体组,以逗号分隔"
|
style="width: 360px;"
|
/>
|
</div>
|
|
<!-- 操作按钮 -->
|
<div class="form-actions">
|
<a-button type="primary" @click="handleBatchPublish" :loading="submitLoading" style="width: 120px; height: 40px; font-size: 16px;">
|
批量发稿
|
</a-button>
|
</div>
|
</div>
|
|
<!-- 发稿结果 -->
|
<div class="result-container" v-show="showResult">
|
<a-divider orientation="left">发稿结果</a-divider>
|
|
<div v-if="publishResults.length > 0">
|
<a-table :columns="resultColumns" :data-source="publishResults" :pagination="false">
|
<template #action="{ record }">
|
<a-button size="small" style="margin-right: 8px">查看</a-button>
|
<a-button size="small" type="primary">详情</a-button>
|
</template>
|
</a-table>
|
</div>
|
<a-empty v-else description="暂无发稿结果" />
|
</div>
|
</div>
|
</template>
|
|
<script lang="ts" setup>
|
// 导入必要的依赖
|
import { ref, reactive, onMounted } from 'vue';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useRouter } from 'vue-router';
|
import { defHttp } from '/@/utils/http/axios';
|
import { getToken } from '/@/utils/auth';
|
|
// 表单状态
|
const formRef = ref(); // 表单引用,用于验证
|
const submitLoading = ref(false); // 提交加载状态
|
const showResult = ref(false); // 默认隐藏发稿结果
|
|
// 合同列表
|
const contractList = ref([]); // 合同下拉列表
|
const contractListCache = ref([]); // 合同原始数据缓存
|
|
// 表单数据
|
const formState = reactive({
|
contractId: '', // 合同ID(必填)
|
mediaGroups: '', // 媒体组(必填,以逗号分隔)
|
});
|
|
// 表单验证规则
|
const rules = {
|
contractId: [{ required: true, message: '请选择合同', trigger: 'change' }],
|
mediaGroups: [{ required: true, message: '请输入媒体组', trigger: 'blur' }],
|
};
|
|
// 发稿结果
|
const publishResults = ref([]); // 发稿结果列表
|
|
// 结果表格列定义
|
const resultColumns = [
|
{
|
title: '序号',
|
dataIndex: 'index',
|
key: 'index',
|
width: 80
|
},
|
{
|
title: '标题',
|
dataIndex: 'title',
|
key: 'title'
|
},
|
{
|
title: '状态',
|
dataIndex: 'status',
|
key: 'status',
|
width: 100,
|
customRender: (text) => {
|
const statusMap = {
|
pending: '待处理',
|
success: '成功',
|
failed: '失败'
|
};
|
return statusMap[text] || text;
|
}
|
},
|
{
|
title: '操作',
|
key: 'action',
|
width: 150,
|
scopedSlots: {
|
customRender: 'action'
|
}
|
}
|
];
|
|
const { createMessage } = useMessage(); // 消息提示
|
const router = useRouter();
|
|
// 加载合同列表
|
const loadContractList = async () => {
|
try {
|
console.log('开始加载合同列表...');
|
|
// 真实数据获取代码
|
const response = await defHttp.get({
|
url: '/contract/contract/list',
|
params: {
|
column: 'createTime',
|
order: 'desc',
|
pageNo: 1,
|
pageSize: 200,
|
isImportStatus: '1', // 后端要求的条件
|
},
|
});
|
console.log('合同列表接口响应:', response);
|
|
const records = normalizeContractRecords(response);
|
console.log('标准化后的合同记录:', records);
|
contractListCache.value = records;
|
|
const options = records.map((item: any) => ({
|
label: resolveContractLabel(item),
|
value: resolveContractValue(item),
|
}));
|
console.log('生成的合同选项:', options);
|
|
contractList.value = options;
|
console.log('合同列表加载完成');
|
|
} catch (error) {
|
console.error('加载合同列表失败:', error);
|
createMessage.error('加载合同列表失败');
|
}
|
};
|
|
// 标准化合同记录
|
const normalizeContractRecords = (response: any): any[] => {
|
if (Array.isArray(response)) return response;
|
if (Array.isArray(response?.records)) {
|
return response.records;
|
}
|
if (Array.isArray(response?.result?.records)) {
|
return response.result.records;
|
}
|
if (Array.isArray(response?.data?.records)) {
|
return response.data.records;
|
}
|
return [];
|
};
|
|
// 解析合同标签
|
const resolveContractLabel = (item: any) => {
|
if (item.contractName) return item.contractName;
|
if (item.contractNo) return `合同编号:${item.contractNo}`;
|
return '未命名合同';
|
};
|
|
// 解析合同值
|
const resolveContractValue = (item: any) => {
|
return item.id || item.contractId || item.contractName || '';
|
};
|
|
// 生成请求签名
|
const generateSign = (token, timestamp, contractId) => {
|
// 简单的签名生成逻辑,实际项目中可能需要更复杂的算法
|
const data = `${token}${timestamp}${contractId}`;
|
let hash = 0;
|
for (let i = 0; i < data.length; i++) {
|
const char = data.charCodeAt(i);
|
hash = ((hash << 5) - hash) + char;
|
hash = hash & hash;
|
}
|
return Math.abs(hash).toString(16).toUpperCase().padStart(32, '0');
|
};
|
|
// 批量发稿
|
const handleBatchPublish = async () => {
|
try {
|
// 1. 表单验证
|
if (!formState.contractId) {
|
createMessage.error('请选择合同');
|
return;
|
}
|
|
submitLoading.value = true;
|
|
// 2. 获取登录 Token
|
const token = getToken();
|
if (!token) {
|
createMessage.error('未登录,请先登录系统');
|
submitLoading.value = false;
|
return;
|
}
|
|
// 3. 构建 FormData
|
const formData = new FormData();
|
formData.append('contractId', formState.contractId);
|
if (formState.mediaGroups) {
|
// 将媒体列表字符串按逗号分割并添加到FormData中
|
const mediaArray = formState.mediaGroups.split(',').map(item => item.trim());
|
console.log('媒体数组:', mediaArray);
|
// 对于multipart/form-data格式,重复添加同名参数来传递数组
|
mediaArray.forEach((media, index) => {
|
formData.append('mediaList', media);
|
console.log(`添加参数: mediaList = ${media}`);
|
});
|
}
|
|
// 4. 发送请求 - 使用 XMLHttpRequest 发送 multipart/form-data 请求
|
const domainUrl = 'http://192.168.31.222:8080/jeecg-boot';
|
const url = `${domainUrl}/api/excel/batchPublish`;
|
|
console.log('===== 发送 multipart/form-data 请求 =====');
|
console.log('请求 URL:', url);
|
console.log('合同ID:', formState.contractId);
|
console.log('媒体列表:', formState.mediaGroups);
|
|
// 发送请求
|
const responseData = await new Promise((resolve, reject) => {
|
const xhr = new XMLHttpRequest();
|
|
// 响应处理
|
xhr.onload = function() {
|
if (xhr.status >= 200 && xhr.status < 300) {
|
try {
|
const data = JSON.parse(xhr.responseText);
|
console.log('响应数据:', data);
|
resolve(data);
|
} catch (error) {
|
console.error('解析响应失败:', error);
|
reject(new Error('解析响应失败'));
|
}
|
} else {
|
console.error('请求失败:', xhr.status, xhr.statusText);
|
reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`));
|
}
|
};
|
|
// 网络错误处理
|
xhr.onerror = function() {
|
console.error('网络错误');
|
reject(new Error('网络错误'));
|
};
|
|
// 打开连接
|
xhr.open('POST', url, true);
|
|
// 设置请求头
|
xhr.setRequestHeader('X-Access-Token', token);
|
xhr.setRequestHeader('X-Tenant-Id', '0');
|
xhr.setRequestHeader('X-Version', 'v3');
|
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
|
// 注意:使用FormData时,浏览器会自动设置Content-Type头,包含正确的边界
|
|
// 发送请求
|
xhr.send(formData);
|
});
|
|
console.log('后端响应数据:', responseData);
|
|
// 4. 处理响应
|
const data = responseData.data;
|
if (data && (data.success || data.code === 200 || data.code === 201)) {
|
// 处理成功响应
|
publishResults.value = [
|
{
|
key: 1,
|
index: 1,
|
title: `合同 ${formState.contractId} 发稿`,
|
status: 'success'
|
}
|
];
|
showResult.value = true; // 成功时显示结果
|
createMessage.success('批量发稿完成');
|
// 成功后返回上一级页面
|
setTimeout(() => {
|
router.back();
|
}, 1000);
|
} else {
|
// 处理失败响应
|
showResult.value = false; // 失败时不显示结果
|
createMessage.error(data?.message || data?.msg || '批量发稿失败');
|
}
|
|
} catch (error: any) {
|
// 错误处理
|
console.error('批量发稿失败:', error);
|
if (error.response) {
|
if (error.response.status === 401) {
|
createMessage.error('Token失效,请重新登录');
|
} else {
|
createMessage.error(error.response.data?.message || '批量发稿失败');
|
}
|
} else if (error.message) {
|
createMessage.error(error.message);
|
} else {
|
createMessage.error('批量发稿失败,请重试');
|
}
|
showResult.value = false; // 错误时不显示结果
|
} finally {
|
submitLoading.value = false;
|
}
|
};
|
|
// 合同选择变化处理
|
const handleContractChange = async (value) => {
|
console.log('选择的合同ID:', value);
|
};
|
|
// 过滤选项
|
const filterOption = (input: string, option: any) => {
|
return (option?.label || '').toLowerCase().includes(input.toLowerCase());
|
};
|
|
// 页面加载时初始化
|
onMounted(async () => {
|
// 加载合同列表
|
await loadContractList();
|
});
|
</script>
|
|
<style lang="less" scoped>
|
// 页面容器样式
|
.batch-publish-container {
|
padding: 20px;
|
}
|
|
// 表单容器样式
|
.form-container {
|
max-width: 800px;
|
margin: 0 auto;
|
padding: 20px;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
// 表单项样式
|
.form-item {
|
margin-bottom: 24px;
|
display: flex;
|
align-items: center;
|
width: 100%;
|
max-width: 600px;
|
}
|
|
// 表单标签样式
|
.form-label {
|
width: 100px;
|
font-size: 14px;
|
margin-right: 16px;
|
}
|
|
// 必填标记样式
|
.required-mark {
|
color: #ff4d4f;
|
margin-right: 4px;
|
}
|
|
// 操作按钮样式
|
.form-actions {
|
margin-top: 24px;
|
text-align: center;
|
width: 100%;
|
display: flex;
|
justify-content: center;
|
}
|
|
// 结果容器样式
|
.result-container {
|
max-width: 800px;
|
margin: 20px auto;
|
padding: 20px;
|
}
|
|
// 确保所有输入框和选择框宽度一致
|
:deep(.ant-select),
|
:deep(.ant-input),
|
:deep(.ant-input-textarea) {
|
width: 360px !important;
|
}
|
</style>
|