<template>
|
|
<div class="page-wrapper">
|
<!-- 附件选择区域 -->
|
<div class="fujianSection">
|
<div class="info-row">
|
<span class="info-item">语义词:<span style="font-size: 15px; font-weight: 500;">{{ semanticWord || '暂无数据' }}</span></span>
|
<span class="info-item">签约排名: <span style="font-size: 15px; font-weight: 500;">{{ ranking || '暂无数据' }}</span></span>
|
<span class="info-item">露出信息: <span style="font-size: 15px; font-weight: 500;">{{ outWord || '暂无数据' }}</span></span>
|
<span class="info-item" v-if="ranking < 4">预警通知: <span style="font-size: 15px; font-weight: 500;">排名>{{ ranking || ' ' }}系统将发送掉词预警通知</span></span>
|
<span class="info-item" v-else>预警通知:<span style="font-size: 15px; font-weight: 500;">未在AI搜索答案里面展现,系统将发送掉词预警通知</span></span>
|
</div>
|
</div>
|
<!-- 智能文案区域 -->
|
<div class="panel">
|
<div class="title-row">
|
<h3 class="section-title">文案标题</h3>
|
<input
|
type="text"
|
class="input-field title-input"
|
v-model="title"
|
placeholder="请输入标题"
|
/>
|
<div class="title-actions">
|
<button id="bt-title" @click="handleGenerateTitle" :disabled="loading">智能生成标题</button>
|
<button id="bt-text" @click="handleToggleForm(true),showForm = true">智能生成文案</button>
|
<button
|
id="bt-show"
|
v-show="showBtn1"
|
@click="handleToggleForm(true)"
|
>
|
︾
|
</button>
|
<button
|
id="bt-hide"
|
v-show="showBtn2"
|
@click="handleToggleForm(false)"
|
>
|
︽
|
</button>
|
</div>
|
</div>
|
<div v-show="showForm" style="padding: 10px 50px 0 50px; background-color: white; border-radius: 20px; height: 175px; margin: 0 20px 20px 0; border: 1px solid rgb(221, 221, 221);">
|
<div class="form-grid">
|
<div class="form-item">
|
<div class="inline-row">
|
<span class="inline-label">选择附件:</span>
|
<a-select
|
v-model:value="selectedAttachment"
|
placeholder="请选择附件"
|
class="select inline-control"
|
:loading="attachmentLoading"
|
@change="handleAttachmentChange"
|
>
|
<a-select-option v-for="file in attachmentFiles" :key="file.id" :value="file.id" :title="file.name">
|
{{ file.name }}
|
</a-select-option>
|
</a-select>
|
</div>
|
</div>
|
<div class="form-item">
|
<div class="inline-row">
|
<span class="inline-label">对标链接:</span>
|
<input type="text" class="input-field inline-control" v-model="wenanyaoqiu" placeholder="请输入对标链接" />
|
</div>
|
</div>
|
<div class="form-item">
|
<div class="inline-row" style="position: relative; bottom: 80px;">
|
<span class="inline-label">主要问题:</span>
|
<input type="text" class="input-field inline-control" v-model="wenti" placeholder="请输入主要问题" />
|
</div>
|
</div>
|
<div class="form-item">
|
<div class="inline-row" style="position: relative; bottom: 80px;">
|
<span class="inline-label">其他友商:</span>
|
<input type="text" class="input-field inline-control" v-model="youshang" placeholder="请输入其他友商" />
|
</div>
|
</div>
|
</div>
|
<div class="button-group">
|
<button class="btn clear" @click="handleClear">清空</button>
|
<button class="btn run" @click="handleRun" :disabled="loading">一键生成</button>
|
</div>
|
</div>
|
</div>
|
<div class="page-container">
|
<!-- AI智能书写区域 -->
|
<div class="right-panel">
|
<div class="content-section">
|
<JEditor
|
v-model:value="editorContent"
|
:height="`${editorHeight}px`"
|
:key="editorHeight"
|
:disabled="fromReview"
|
/>
|
</div>
|
<div class="action-buttons">
|
<button class="action-btn cancel" @click="handleCancel" :disabled="loading">取消</button>
|
<button class="action-btn save" v-if="!chengf" @click="handleSave" :disabled="chengf">保存</button>
|
<button class="action-btn submit" v-if="!fromEdit" @click="handleSubmit" :disabled="fromEdit">提交审核</button>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import JEditor from '/@/components/Form/src/jeecg/components/JEditor.vue';
|
import { saveOrUpdate, aiCreateCopyWriting, aiCreateTitle } from '../Copywriting.api';
|
import { message } from 'ant-design-vue';
|
import { useUserStore } from '/@/store/modules/user';
|
import { defHttp } from '/@/utils/http/axios';
|
|
export default {
|
name: 'IndexPage',
|
components: {
|
JEditor,
|
},
|
data() {
|
return {
|
editorContent: '',
|
semanticWord: '',
|
ranking: '',
|
contractId: '',
|
id: '',
|
platform: '',
|
title: '',
|
wenanyaoqiu: '',
|
louchu: '',
|
youshang: '',
|
wenti: '',
|
jianli: [], // 客户资料附件集合
|
showForm: false,
|
showBtn1: true,
|
showBtn2: false,
|
loading: false,
|
// 附件选择相关状态
|
selectedAttachment: '',
|
attachmentFiles: [],
|
attachmentLoading: false,
|
// 是否来自编辑按钮跳转
|
fromEdit: '',
|
// 是否来自审核按钮跳转
|
fromReview: false,
|
chengf: '',
|
outWord: '',
|
editorHeight: 500,
|
};
|
},
|
mounted() {
|
// 从路由参数获取数据
|
this.semanticWord = this.$route.query.semanticWord || '';
|
this.ranking = this.$route.query.ranking || '';
|
this.contractId = this.$route.query.contractId || '';
|
this.id = this.$route.query.id || '';
|
this.title = this.$route.query.title || '';
|
this.editorContent = this.$route.query.text || '';
|
this.outWord = this.$route.query.outWord || '';
|
// 判断是否来自编辑按钮跳转
|
// this.fromEdit = this.$route.query.fromEdit || false;
|
this.fromEdit = this.$route.query.fromEdit !== undefined ? this.$route.query.fromEdit === 'true' : false;
|
// 判断是否来自审核按钮跳转
|
this.fromReview = this.$route.query.fromReview !== undefined ? this.$route.query.fromReview === 'true' : false;
|
this.chengf = this.$route.query.chengf !== undefined ? this.$route.query.chengf === 'true' : false;
|
console.log(
|
'word,ranking,contractId,id,title,text,fromEdit,fromReview,outWord',
|
this.semanticWord,
|
this.ranking,
|
this.contractId,
|
this.id,
|
this.title,
|
this.editorContent,
|
this.fromEdit,
|
this.fromReview,
|
this.outWord
|
);
|
|
// 从路由参数获取附件信息
|
const selectedAttachmentId = this.$route.query.selectedAttachmentId;
|
const selectedAttachmentName = this.$route.query.selectedAttachmentName;
|
const selectedAttachmentUrl = this.$route.query.selectedAttachmentUrl;
|
|
if (selectedAttachmentId && selectedAttachmentName) {
|
this.jianli = [
|
{
|
id: selectedAttachmentId,
|
name: selectedAttachmentName,
|
url: selectedAttachmentUrl || '',
|
},
|
];
|
console.log('已加载客户资料附件:', this.jianli);
|
}
|
|
// 如果有合同ID,加载附件列表
|
if (this.contractId) {
|
this.loadAttachmentFiles(this.contractId);
|
}
|
},
|
methods: {
|
// 保存方法 - 调用编辑接口修改text内容
|
async handleSave() {
|
if (!this.editorContent) {
|
message.warning('请先填写文案内容');
|
return;
|
}
|
|
this.loading = true;
|
try {
|
// 同步提交左侧表单里的信息,方便后端一并持久化
|
const params = {
|
id: this.id || '', // 文案ID
|
title: this.title || this.semanticWord || '', // 标题
|
text: this.editorContent, // 文案内容
|
platform: this.platform || '', // 推荐平台
|
wenanyaoqiu: this.wenanyaoqiu || '',
|
// louchu: this.louchu || '',
|
youshang: this.youshang || '',
|
wenti: this.wenti || '',
|
};
|
|
// 调用编辑接口
|
await saveOrUpdate(params, false);
|
// 保存成功后返回上一级页面
|
this.$router.back();
|
} catch (error) {
|
console.error('保存失败:', error);
|
message.error('保存失败');
|
} finally {
|
this.loading = false;
|
}
|
},
|
handleToggleForm(show) {
|
this.showForm = show;
|
this.showBtn1 = !show;
|
this.showBtn2 = show;
|
this.editorHeight = show ? 325 : 500;
|
},
|
|
// 智能生成标题
|
async handleGenerateTitle() {
|
this.loading = true;
|
try {
|
const userStore = useUserStore();
|
const userInfo = userStore.getUserInfo;
|
const userId = userInfo?.id || userInfo?.username || 'unknown';
|
|
const formatDate = (date) => {
|
const y = date.getFullYear();
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
const d = String(date.getDate()).padStart(2, '0');
|
return `${y}-${m}-${d}`;
|
};
|
|
const today = new Date();
|
const startTime = formatDate(today);
|
const endDate = new Date(today);
|
endDate.setDate(endDate.getDate() - 7);
|
const endTime = formatDate(endDate);
|
|
const params = {
|
louchu: this.outWord || this.louchu || '',
|
yuyici: this.semanticWord || '',
|
startTime,
|
endTime,
|
user: userId,
|
};
|
|
const res = await aiCreateTitle(params);
|
let titleResult = '';
|
if (typeof res === 'string') {
|
titleResult = res;
|
} else if (res && res.result) {
|
titleResult = res.result;
|
}
|
|
if (titleResult && typeof titleResult === 'string') {
|
this.title = titleResult;
|
message.success('标题生成成功');
|
} else {
|
message.warning('未获取到标题,请稍后重试');
|
}
|
} catch (error) {
|
console.error('AI生成标题失败:', error);
|
message.error('AI生成标题失败,请重试');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 提交审核方法
|
async handleSubmit() {
|
if (!this.editorContent) {
|
message.warning('请先填写文案内容');
|
return;
|
}
|
|
this.loading = true;
|
try {
|
const params = {
|
title: this.title || this.semanticWord || '智能文案', // 标题优先使用列表页的title,其次使用语义词
|
status: '2', // 2:已上传
|
platform: this.platform || '', // 推荐平台
|
wordId: this.id || '', // 关联语义词编号
|
text: this.editorContent, // 文案内容
|
outWord: this.outWord || '', // 露出信息
|
};
|
|
await saveOrUpdate(params, false);
|
message.success('提交审核成功');
|
// 提交审核成功后返回列表页
|
this.$router.push('/copywriting/copywritingList');
|
} catch (error) {
|
console.error('提交审核失败:', error);
|
message.error('提交审核失败');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 运行方法 - 调用AI创建文案接口
|
async handleRun() {
|
// 检查必填字段
|
if (!this.wenanyaoqiu) {
|
message.warning('请输入对标链接');
|
return;
|
}
|
// if (!this.louchu) {
|
// message.warning('请输入漏出信息');
|
// return;
|
// }
|
if (!this.youshang) {
|
message.warning('请输入其他友商');
|
return;
|
}
|
if (!this.wenti) {
|
message.warning('请输入主要问题');
|
return;
|
}
|
|
this.loading = true;
|
try {
|
const userStore = useUserStore();
|
const userInfo = userStore.getUserInfo;
|
const userId = userInfo?.id || userInfo?.username || 'unknown';
|
|
// // 构建FormData对象(form-data格式专用)
|
// const formData = new FormData();
|
// // 逐个添加参数(键名必须和接口要求一致,这里是"jianli"等)
|
// formData.append('wenanyaoqiu', this.wenanyaoqiu); // 添加文本参数
|
// formData.append('louchu', this.louchu);
|
// formData.append('youshang', this.youshang);
|
// formData.append('wenti', this.wenti);
|
// formData.append('user', userId);
|
|
// 构建JSON参数对象(替代FormData)
|
const params = {
|
wenanyaoqiu: this.wenanyaoqiu,
|
// 接口要求 louchu 传露出信息;优先用 outWord 回退到表单字段
|
louchu: this.outWord || this.louchu,
|
youshang: this.youshang,
|
wenti: this.wenti,
|
user: userId,
|
// 处理简历参数
|
jianli: this.jianli && this.jianli.length > 0 && this.jianli[0].name ? this.jianli[0].name : '',
|
};
|
|
// 直接传递JSON对象(接口会自动处理为application/json类型)
|
const response = await aiCreateCopyWriting(params);
|
console.log('AI接口完整响应:', response);
|
|
// // 调整响应处理逻辑,匹配新的返回格式
|
// if (response && response.success && response.result) {
|
// this.editorContent = response.result;
|
// console.log('AI接口完整响应:', response);
|
// message.success('AI文案生成成功');
|
// } else {
|
// message.warning('未获取到文案内容,请重试');
|
// }
|
// console.log('测试:', this.editorContent || "kong");
|
|
// 修复响应处理逻辑
|
let content = '';
|
// 优先检查response是否为字符串(直接返回内容的情况)
|
if (typeof response === 'string') {
|
content = response;
|
}
|
// 其次检查是否是标准接口格式(带result字段)
|
else if (response && response.success && response.result) {
|
content = response.result;
|
}
|
|
// 验证内容有效性
|
const isValidContent = typeof content === 'string' && content.trim() !== '';
|
if (isValidContent) {
|
this.editorContent = content;
|
message.success('AI文案生成成功');
|
} else {
|
this.editorContent = 'AI未返回有效文案,请检查输入参数或重试';
|
message.warning('未获取到有效文案内容,请重试');
|
}
|
console.log('测试:', this.editorContent || 'kong');
|
} catch (error) {
|
console.error('AI文案生成失败:', error);
|
message.error('AI文案生成失败,请重试');
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 清空方法
|
handleClear() {
|
this.title = '';
|
this.wenanyaoqiu = '';
|
this.youshang = '';
|
this.wenti = '';
|
this.platform = '';
|
this.editorContent = '';
|
this.jianli = [];
|
message.success('已清空所有内容');
|
},
|
|
// 取消方法
|
handleCancel() {
|
this.$router.back();
|
},
|
|
/**
|
* 加载附件数据
|
*/
|
async loadAttachmentFiles(contractId) {
|
this.attachmentLoading = true;
|
try {
|
const response = await defHttp.get({
|
url: '/contract/contract/queryContractFileByMainId',
|
params: { id: contractId },
|
});
|
|
console.log('附件文件数据:', response);
|
|
// 处理响应数据,支持多种数据结构
|
let fileList = [];
|
if (Array.isArray(response)) {
|
fileList = response;
|
} else if (response && response.result && Array.isArray(response.result)) {
|
fileList = response.result;
|
} else if (response && response.data && Array.isArray(response.data)) {
|
fileList = response.data;
|
}
|
|
if (fileList.length > 0) {
|
// 过滤出附件文件(同时支持'合同文件'和'合同附件'类型)
|
const attachmentFileList = [];
|
|
fileList.forEach((file) => {
|
// 同时支持两种文件类型
|
if ((file.fileType === '合同文件' || file.fileType === '合同附件') && file.appendixFile) {
|
const files = file.appendixFile.split(',');
|
files.forEach((fileName, index) => {
|
// 跳过空文件名
|
if (!fileName || fileName.trim() === '') return;
|
|
const uniqueId = `${file.id}_${index}`;
|
attachmentFileList.push({
|
id: uniqueId,
|
name: fileName,
|
url: fileName,
|
originalId: file.id,
|
fileType: file.fileType
|
});
|
});
|
}
|
});
|
|
this.attachmentFiles = attachmentFileList;
|
console.log('处理后的附件文件:', attachmentFileList);
|
|
// 如果有附件,默认选择第一个
|
if (attachmentFileList.length > 0) {
|
this.selectedAttachment = attachmentFileList[0].id;
|
// 更新客户资料附件
|
this.jianli = [
|
{
|
id: attachmentFileList[0].id,
|
name: attachmentFileList[0].name,
|
url: attachmentFileList[0].url || '',
|
},
|
];
|
console.log('默认选择的附件:', this.jianli);
|
}
|
}
|
} catch (error) {
|
console.error('加载附件文件失败:', error);
|
message.error('加载附件文件失败');
|
} finally {
|
this.attachmentLoading = false;
|
}
|
},
|
|
/**
|
* 附件选择变化事件
|
*/
|
handleAttachmentChange(value) {
|
this.selectedAttachment = value;
|
// 更新客户资料附件
|
const selectedFile = this.attachmentFiles.find((file) => file.id === value);
|
if (selectedFile) {
|
this.jianli = [
|
{
|
id: selectedFile.id,
|
name: selectedFile.name,
|
url: selectedFile.url || '',
|
},
|
];
|
console.log('已更新客户资料附件:', this.jianli);
|
}
|
},
|
},
|
};
|
</script>
|
|
<style scoped>
|
/* 整体布局 */
|
.page-container {
|
display: flex;
|
width: 100%;
|
/* height: 100vh; */
|
}
|
|
.info-row {
|
display: flex;
|
justify-content: space-between;
|
/* margin-bottom: 20px; */
|
}
|
|
.info-item {
|
flex: 1;
|
}
|
|
/* 附件选择区域样式 */
|
.fujianSection {
|
/* margin-bottom: 20px; */
|
padding: 16px;
|
background-color: #f8f9fa;
|
border-radius: 6px;
|
border: 1px solid #e9ecef;
|
}
|
|
.attachment-row {
|
display: flex;
|
align-items: center;
|
}
|
|
.attachment-row .label {
|
margin-right: 10px;
|
font-weight: 500;
|
color: #333;
|
min-width: 80px;
|
}
|
|
.attachment-row .select {
|
width: 300px;
|
}
|
|
/* 左侧面板 */
|
.panel {
|
width: 100%;
|
/* padding: 20px; */
|
padding-left: 20px;
|
border-right: 1px solid #eee;
|
box-sizing: border-box;
|
padding-bottom: -20px;
|
}
|
|
.panel-title {
|
font-size: 20px;
|
font-weight: bold;
|
margin-bottom: 20px;
|
}
|
|
.tab-group {
|
display: flex;
|
margin-bottom: 20px;
|
}
|
|
.tab {
|
padding: 5px 10px;
|
cursor: pointer;
|
border-bottom: 2px solid transparent;
|
}
|
|
.tab.active {
|
border-bottom-color: #007bff;
|
color: #007bff;
|
}
|
|
.section-title {
|
font-size: 16px;
|
margin: 15px 0 10px;
|
font-weight: bold;
|
}
|
|
.upload-buttons {
|
display: flex;
|
gap: 10px;
|
margin-bottom: 15px;
|
}
|
|
.upload-btn {
|
padding: 8px 15px;
|
background-color: #f0f2f5;
|
border: 1px solid #ddd;
|
border-radius: 4px;
|
cursor: pointer;
|
}
|
|
.file-item {
|
display: flex;
|
align-items: center;
|
padding: 10px;
|
background-color: #f9f9f9;
|
border-radius: 4px;
|
margin-bottom: 10px;
|
}
|
|
.doc-icon {
|
width: 24px;
|
height: 24px;
|
margin-right: 10px;
|
background-color: #e6f7ff;
|
/* 模拟文档图标背景 */
|
border-radius: 3px;
|
}
|
|
.file-size {
|
margin-left: auto;
|
font-size: 12px;
|
color: #666;
|
}
|
|
.delete-btn {
|
margin-left: 10px;
|
cursor: pointer;
|
color: #999;
|
}
|
|
.input-textarea,
|
.input-field {
|
width: 230px;
|
padding: 10px;
|
border: 1px solid #ddd;
|
border-radius: 4px;
|
box-sizing: border-box;
|
/* margin-bottom: 10px; */
|
}
|
|
.input-textarea {
|
height: 60px;
|
resize: none;
|
}
|
|
.button-group {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
margin-top: 20px;
|
position: relative;
|
bottom: 90px;
|
}
|
|
.form-grid {
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
gap: 100px;
|
}
|
|
.form-item {
|
width: 100%;
|
}
|
|
.inline-row {
|
display: flex;
|
align-items: center;
|
/* gap: 12px; */
|
}
|
|
.inline-label,.info-item {
|
min-width: 80px;
|
color: #333;
|
font-weight: 1000;
|
font-size: 15px;
|
}
|
|
.inline-control {
|
flex: 1;
|
width: 100%;
|
}
|
|
.title-row {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
margin-bottom: 20px;
|
margin-top: 20px;
|
}
|
|
.title-input {
|
flex: 0 0 500px;
|
margin-bottom: 0;
|
width: 400px;
|
}
|
|
.title-actions {
|
display: flex;
|
gap: 10px;
|
flex-shrink: 0;
|
}
|
|
.btn {
|
padding: 8px 15px;
|
border-radius: 4px;
|
cursor: pointer;
|
}
|
|
.btn.clear {
|
background-color: #fff;
|
border: 1px solid #ddd;
|
}
|
|
.btn.run {
|
background-color: #007bff;
|
color: #fff;
|
border: none;
|
}
|
|
#bt-show,#bt-hide{
|
color: #007bff;
|
font-size: 20px;
|
background: none;
|
}
|
|
#bt-show,#bt-hide:hover{
|
cursor: pointer;
|
}
|
|
/* 右侧面板 */
|
.right-panel {
|
flex: 1;
|
padding: 0px 20px 0 20px;
|
overflow: auto;
|
/* 内容超出时滚动 */
|
}
|
|
.workflow-tag {
|
display: inline-block;
|
padding: 5px 10px;
|
background-color: #e6f7ff;
|
color: #007bff;
|
border-radius: 4px;
|
margin-bottom: 15px;
|
cursor: pointer;
|
}
|
|
.content-section h3 {
|
font-size: 18px;
|
margin: 20px 0 15px;
|
}
|
|
.content-section h4 {
|
font-size: 16px;
|
margin: 15px 0 10px;
|
}
|
|
.content-section p,
|
.content-section li {
|
line-height: 1.6;
|
margin-bottom: 10px;
|
}
|
|
.content-section ul {
|
padding-left: 20px;
|
}
|
|
.content-section a {
|
color: #007bff;
|
text-decoration: none;
|
}
|
|
/* 操作按钮样式 */
|
.action-buttons {
|
display: flex;
|
justify-content: flex-end;
|
gap: 15px;
|
margin-top: 20px;
|
padding-top: 20px;
|
border-top: 1px solid #eee;
|
}
|
|
.action-btn {
|
padding: 10px 20px;
|
border-radius: 4px;
|
cursor: pointer;
|
font-size: 14px;
|
font-weight: 500;
|
border: 1px solid transparent;
|
transition: all 0.3s ease;
|
}
|
|
.action-btn.cancel {
|
background-color: #fff;
|
border-color: #ddd;
|
color: #666;
|
}
|
|
.action-btn.cancel:hover {
|
background-color: #f5f5f5;
|
border-color: #ccc;
|
}
|
|
.action-btn.save {
|
background-color: #007bff;
|
color: #fff;
|
border-color: #007bff;
|
}
|
|
.action-btn.save:hover {
|
background-color: #007bff;
|
border-color: #007bff;
|
}
|
|
.action-btn.submit {
|
background-color: #007bff;
|
color: #fff;
|
border-color: #007bff;
|
}
|
|
.action-btn.submit:hover {
|
background-color: #0069d9;
|
border-color: #0062cc;
|
}
|
|
#bt-title, #bt-text{
|
width: 100px;
|
height: 50px;
|
border-radius: 5px;
|
background-color: #007bff;
|
color: #fff;
|
border: none;
|
font-size: 14px;
|
margin: 0;
|
}
|
|
#bt-title,#bt-text:hover{
|
cursor: pointer;
|
}
|
</style>
|