| | |
| | | style="width: 360px;" |
| | | @change="handleContractChange" |
| | | > |
| | | <a-select-option v-for="contract in contractList" :key="contract.value" :value="contract.value"> |
| | | <a-select-option v-for="contract in contractOptions" :key="contract.value" :value="contract.value"> |
| | | {{ contract.label }} |
| | | </a-select-option> |
| | | </a-select> |
| | |
| | | <a-select v-model:value="formState.fileName" placeholder="请选择原始文件" style="width: 360px;"> |
| | | <a-select-option v-for="attachment in attachmentList" :key="attachment.value" :value="attachment.value"> |
| | | {{ attachment.label }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | |
| | | <!-- 文案策略 --> |
| | | <!-- 说明:新增字段,用户可选择文案策略 --> |
| | | <!-- <a-form-item |
| | | label="选择策略" |
| | | name="strategy" |
| | | > |
| | | <a-select v-model:value="formState.strategy" placeholder="请选择文案策略" style="width: 360px;"> |
| | | <a-select-option v-for="scheme in schemeList" :key="scheme.value" :value="scheme.value"> |
| | | {{ scheme.label }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </a-form-item> --> |
| | | |
| | | <!-- 方案选择 --> |
| | | <!-- 说明:必填字段,选择文案生成方案 --> |
| | | <a-form-item |
| | | label="方案选择" |
| | | name="csId" |
| | | :required="true" |
| | | > |
| | | <a-select v-model:value="formState.csId" placeholder="请选择方案" style="width: 360px;"> |
| | | <a-select-option v-for="scheme in schemeList" :key="scheme.value" :value="scheme.value"> |
| | | {{ scheme.label }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | |
| | | import { router } from '/@/router'; |
| | | import { defHttp } from '/@/utils/http/axios'; |
| | | import { getToken } from '/@/utils/auth'; |
| | | import { BasicTable } from '/@/components/Table'; |
| | | import { useListPage } from '/@/hooks/system/useListPage'; |
| | | import { columns, searchFormSchema } from './Contract.data'; |
| | | import { list, listByRole, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Contract.api'; |
| | | // 移除未使用的导入 |
| | | // import { BasicTable } from '/@/components/Table'; |
| | | // import { useListPage } from '/@/hooks/system/useListPage'; |
| | | // import { columns, searchFormSchema } from './Contract.data'; |
| | | import { list as getContractList, listByRole, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Contract.api'; |
| | | import { useUserStore } from '/@/store/modules/user'; |
| | | import { list as schemeListApi } from '../copywritingScheme/CopywritingScheme.api'; |
| | | |
| | | // 列表页相关 |
| | | // 说明:虽然本页面主要是表单,但保留了列表相关的引用 |
| | | const { registerTable, selectedRowKeys } = useListPage({ |
| | | tableProps: { |
| | | columns: columns, |
| | | bordered: true, |
| | | size: 'default', |
| | | }, |
| | | searchParams: {}, |
| | | getListApi: list, |
| | | }); |
| | | // 列表页相关 - 移除不必要的配置,因为本页面主要是表单 |
| | | // const { registerTable, selectedRowKeys } = useListPage({ |
| | | // tableProps: { |
| | | // columns: columns, |
| | | // bordered: true, |
| | | // size: 'default', |
| | | // }, |
| | | // searchParams: {}, |
| | | // getListApi: contractList, |
| | | // }); |
| | | |
| | | // 表单状态 |
| | | const formRef = ref(); // 表单引用,用于验证 |
| | |
| | | const showResult = ref(false); // 默认隐藏生成结果 |
| | | |
| | | // 合同列表 |
| | | const contractList = ref([]); // 合同下拉列表 |
| | | const contractOptions = ref([]); // 合同下拉列表 |
| | | const contractListCache = ref([]); // 合同原始数据缓存 |
| | | |
| | | // 用户信息 |
| | |
| | | // 附件列表 |
| | | const attachmentList = ref([]); // 合同附件下拉列表 |
| | | |
| | | // 文案生成方案列表 |
| | | const schemeList = ref([]); // 文案生成方案下拉列表 |
| | | |
| | | // 表单数据 |
| | | // 说明:根据后端最新接口文档定义的数据结构 |
| | | const formState = reactive({ |
| | | contractId: undefined, // 合同ID(必填) |
| | | fileName: undefined, // 原始文件名(必填) |
| | | youshang: '', // 对标友商(必填) |
| | | wenti: '', // 问题描述(后端仍需,传递空值) |
| | | benchmarkUrl: '', // 对标链接(新增) |
| | | auditor: '', // 审核人ID(不显示,自动设置) |
| | | auditorName: '' // 审核人姓名(不显示,自动设置) |
| | | fileName: undefined, // 原始文件名(根据方案决定) |
| | | youshang: '', // 优势描述/合同背景(必填) |
| | | wenti: '', // 问题描述(弃用,传递空值) |
| | | benchmarkUrl: '', // 对标链接(根据方案决定) |
| | | // strategy: undefined, // 文案策略(新增) |
| | | csId: undefined, // 方案编号(必填) |
| | | auditor: '', // 审核人ID(不显示,自动设置,必填) |
| | | auditorName: '' // 审核人姓名(不显示,自动设置,必填) |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | // 说明:只验证必填字段 |
| | | const rules = { |
| | | contractId: [{ required: true, message: '请选择合同', trigger: 'change' }], |
| | | youshang: [{ required: true, message: '请输入对标友商信息', trigger: 'blur' }] |
| | | youshang: [{ required: true, message: '请输入对标友商信息', trigger: 'blur' }], |
| | | csId: [{ required: true, message: '请选择方案', trigger: 'change' }] |
| | | }; |
| | | |
| | | // 创作结果 |
| | |
| | | })); |
| | | console.log('生成的合同选项:', options); |
| | | |
| | | contractList.value = options; |
| | | contractOptions.value = options; |
| | | console.log('合同列表加载完成'); |
| | | } catch (error) { |
| | | console.error('加载合同列表失败:', error); |
| | |
| | | submitLoading.value = true; |
| | | |
| | | // 2. 构建请求体 |
| | | // 注意:auditor和auditorName字段虽然在页面上不显示,但会通过以下代码传递给后端接口 |
| | | // 按照后端最新接口文档构建请求数据 |
| | | // 注意:后端仍然需要wenti字段,虽然在页面上不显示 |
| | | const requestData = { |
| | | contractId: formState.contractId, |
| | | fileName: formState.fileName, |
| | | youshang: formState.youshang || '', // 后端需要youshang字段,使用对标友商的值,确保传递空字符串而不是null |
| | | youshang: formState.youshang || '', // 优势描述/合同背景 |
| | | wenti: formState.wenti || '', // 问题描述(后端仍然需要,传递空字符串) |
| | | benchmarkUrl: formState.benchmarkUrl, // 对标链接 |
| | | csId: formState.csId, // 方案编号(必填) |
| | | auditor: formState.auditor, // 审核人ID:从setAuditorInfo函数自动设置,页面不显示 |
| | | auditorName: formState.auditorName // 审核人姓名:从setAuditorInfo函数自动设置,页面不显示 |
| | | }; |
| | |
| | | auditor: formState.auditor, |
| | | auditorName: formState.auditorName |
| | | }); |
| | | console.log('对标友商(前端显示):', formState.youshang); |
| | | console.log('对标友商:', formState.youshang); |
| | | console.log('对标链接:', formState.benchmarkUrl); |
| | | console.log('方案编号:', formState.csId); |
| | | console.log('问题描述(传递给后端):', formState.wenti || ''); // 确保显示传递的值 |
| | | |
| | | // 4. 获取登录 Token |
| | |
| | | } |
| | | |
| | | // 5. 构建接口地址 |
| | | // 使用环境变量配置接口基础地址,避免硬编码 |
| | | const domainUrl = import.meta.env.VITE_GLOB_DOMAIN_URL || 'http://192.168.31.222:8080/jeecg-boot'; |
| | | const url = `${domainUrl}/api/excel/batchGenerateCopy`; |
| | | // 使用接口文档中指定的地址 |
| | | const url = 'http://192.168.31.137:3100/jeecgboot/api/excel/batchGenerateCopy'; |
| | | |
| | | console.log('批量生成请求 URL:', url); |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | // 加载文案生成方案列表 |
| | | const loadSchemeList = async () => { |
| | | try { |
| | | console.log('开始加载文案生成方案列表...'); |
| | | // 使用defHttp调用API,与文案生成页面保持一致 |
| | | const response = await defHttp.get({ |
| | | url: '/copywritingScheme/copywritingScheme/list' |
| | | }); |
| | | console.log('获取文案生成方案列表响应:', response); |
| | | |
| | | // 处理响应数据,支持多种数据结构(与文案生成页面一致) |
| | | let records = []; |
| | | if (Array.isArray(response)) { |
| | | records = response; |
| | | } else if (response && response.result && Array.isArray(response.result)) { |
| | | records = response.result; |
| | | } else if (response && response.data && Array.isArray(response.data)) { |
| | | records = response.data; |
| | | } else if (response && response.records && Array.isArray(response.records)) { |
| | | records = response.records; |
| | | } else if (response && response.success && response.result && response.result.records && Array.isArray(response.result.records)) { |
| | | records = response.result.records; |
| | | } |
| | | |
| | | console.log('提取的方案记录:', records); |
| | | |
| | | // 将方案列表转换为下拉框选项格式 |
| | | schemeList.value = records.map(item => ({ |
| | | label: item.name || item.schemeName || '未命名方案', |
| | | value: item.id || item.schemeId || Math.random().toString(36).substr(2, 9) |
| | | })); |
| | | console.log('加载文案生成方案列表成功:', schemeList.value); |
| | | } catch (error) { |
| | | console.error('加载文案生成方案列表失败:', error); |
| | | // 如果API调用失败,使用模拟数据(与文案生成页面一致) |
| | | schemeList.value = [ |
| | | { label: '文件+url', value: '1' }, |
| | | { label: 'url', value: '2' }, |
| | | { label: '文件', value: '3' } |
| | | ]; |
| | | console.log('使用模拟数据加载文案生成方案列表:', schemeList.value); |
| | | } |
| | | }; |
| | | |
| | | // 合同选择变化处理 |
| | | // 说明:选择合同后,自动获取该合同的附件列表 |
| | | const handleContractChange = async (value) => { |
| | |
| | | }; |
| | | |
| | | // 页面加载时初始化 |
| | | // 说明:加载合同列表并设置审核人信息 |
| | | // 说明:加载合同列表、方案列表并设置审核人信息 |
| | | onMounted(async () => { |
| | | // 先加载合同列表 |
| | | await loadContractList(); |
| | | |
| | | // 加载文案生成方案列表 |
| | | await loadSchemeList(); |
| | | |
| | | // 设置审核人信息 |
| | | await setAuditorInfo(); |
| | | }); |
| | |
| | | import {defHttp} from '/@/utils/http/axios'; |
| | | import { useMessage } from "/@/hooks/web/useMessage"; |
| | | import { useUserStore } from '/@/store/modules/user'; |
| | | import { getToken } from '/@/utils/auth'; |
| | | |
| | | |
| | | const { createConfirm } = useMessage(); |
| | |
| | | |
| | | /** |
| | | * AI创建文案 - 使用form-data格式传参 |
| | | * @param params FormData对象 |
| | | * @param params 包含所需字段的对象 |
| | | * @returns Promise<any> |
| | | * @description 由于后端要求使用multipart/form-data格式接收参数, |
| | | * 且之前的defHttp.post方法在处理这种格式时存在问题, |
| | | * 因此使用原生XMLHttpRequest手动构建和发送请求, |
| | | * 确保参数被正确传递到后端。 |
| | | */ |
| | | export const aiCreateCopyWriting = (params) => { |
| | | return defHttp.post({ |
| | | url: Api.aiCreateCopyWriting, |
| | | data: params, |
| | | timeout:300000, |
| | | headers: { |
| | | 'Content-Type': 'multipart/form-data' |
| | | // 使用XMLHttpRequest手动构建multipart/form-data请求 |
| | | return new Promise((resolve, reject) => { |
| | | // 生成随机边界,用于分隔multipart/form-data中的各个部分 |
| | | const boundary = '----WebKitFormBoundary' + Math.random().toString(36).substr(2, 16); |
| | | |
| | | // 构建请求体的各个部分 |
| | | const parts = []; |
| | | |
| | | // 添加wenanyaoqiu参数(对标链接) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="wenanyaoqiu"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.wenanyaoqiu || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加strategy参数(文案策略) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="strategy"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.strategy || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加louchu参数(露出信息) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="louchu"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.louchu || params.outWord || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加youshang参数(其他友商) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="youshang"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.youshang || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加wenti参数(主要问题) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="wenti"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.wenti || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加user参数(用户ID) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="user"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.user || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加jianli参数(简历/附件) |
| | | if (params.jianli) { |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="file"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.jianli, |
| | | '\r\n' |
| | | ); |
| | | } |
| | | |
| | | // 添加url参数(参考文章链接) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="url"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.url || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加csId参数(文章生成方案ID) |
| | | parts.push( |
| | | `--${boundary}\r\n`, |
| | | 'Content-Disposition: form-data; name="csId"\r\n', |
| | | 'Content-Type: text/plain\r\n', |
| | | '\r\n', |
| | | params.csId || '', |
| | | '\r\n' |
| | | ); |
| | | |
| | | // 添加结束边界,表示multipart/form-data的结束 |
| | | parts.push(`--${boundary}--\r\n`); |
| | | |
| | | // 构建完整的请求体 |
| | | const requestBody = parts.join(''); |
| | | |
| | | // 创建XMLHttpRequest对象 |
| | | const xhr = new XMLHttpRequest(); |
| | | |
| | | // 设置请求超时时间为5分钟 |
| | | xhr.timeout = 300000; |
| | | |
| | | // 处理响应 |
| | | xhr.onload = function() { |
| | | if (xhr.status >= 200 && xhr.status < 300) { |
| | | try { |
| | | // 尝试将响应解析为JSON |
| | | const data = JSON.parse(xhr.responseText); |
| | | resolve(data); |
| | | } catch (error) { |
| | | // 如果解析失败,直接返回响应文本 |
| | | resolve(xhr.responseText); |
| | | } |
| | | } else { |
| | | // 请求失败,返回错误信息 |
| | | reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`)); |
| | | } |
| | | }; |
| | | |
| | | // 处理网络错误 |
| | | xhr.onerror = function() { |
| | | reject(new Error('网络错误')); |
| | | }; |
| | | |
| | | // 处理超时 |
| | | xhr.ontimeout = function() { |
| | | reject(new Error('请求超时')); |
| | | }; |
| | | |
| | | // 构建请求URL,使用环境配置中的域名或默认值 |
| | | const domainUrl = import.meta.env.VITE_GLOB_DOMAIN_URL || 'http://localhost:8080/jeecg-boot'; |
| | | const url = `${domainUrl}${Api.aiCreateCopyWriting}`; |
| | | |
| | | // 打开连接 |
| | | xhr.open('POST', url, true); |
| | | |
| | | // 设置请求头 |
| | | xhr.setRequestHeader('X-Access-Token', getToken() || ''); // 认证令牌 |
| | | xhr.setRequestHeader('X-Tenant-Id', '0'); // 租户ID |
| | | xhr.setRequestHeader('X-Version', 'v3'); // 接口版本 |
| | | xhr.setRequestHeader('Content-Type', `multipart/form-data; boundary=${boundary}`); // 内容类型,包含边界 |
| | | |
| | | // 发送请求 |
| | | xhr.send(requestBody); |
| | | }); |
| | | } |
| | | |
| | |
| | | /** |
| | | * 文案管理相关数据配置 |
| | | * @description 包含列表列配置、查询表单配置、编辑表单配置和高级查询配置 |
| | | */ |
| | | import {BasicColumn} from '/@/components/Table'; |
| | | import {FormSchema} from '/@/components/Table'; |
| | | import { rules} from '/@/utils/helper/validator'; |
| | | import { render } from '/@/utils/common/renderUtils'; |
| | | import { getWeekMonthQuarterYear } from '/@/utils'; |
| | | //列表数据 |
| | | |
| | | /** |
| | | * 列表数据列配置 |
| | | * @description 定义文案管理列表页面的显示列 |
| | | */ |
| | | export const columns: BasicColumn[] = [ |
| | | { |
| | | title: '语义词', |
| | | align:"center", |
| | | dataIndex: 'semanticWord.word' |
| | | dataIndex: 'semanticWord.word', |
| | | width: 200, |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '标题', |
| | | align:"center", |
| | | dataIndex: 'title', |
| | | width: 500, |
| | | ellipsis: false, |
| | | customRender: ({ text }) => { |
| | | return { |
| | | children: text, |
| | | attrs: { |
| | | style: 'text-align: left; white-space: nowrap; overflow: visible;' |
| | | } |
| | | }; |
| | | } |
| | | ellipsis: true |
| | | }, |
| | | // { |
| | | // title: '开始时间', |
| | |
| | | // dataIndex: 'semanticWord.changer' |
| | | // }, |
| | | ]; |
| | | //查询数据 |
| | | /** |
| | | * 查询表单配置 |
| | | * @description 定义文案管理列表页面的查询条件表单 |
| | | */ |
| | | export const searchFormSchema: FormSchema[] = [ |
| | | { |
| | | label: "标题", |
| | |
| | | } |
| | | }, |
| | | ]; |
| | | //表单数据 |
| | | /** |
| | | * 表单数据配置 |
| | | * @description 定义文案管理编辑页面的表单字段 |
| | | */ |
| | | export const formSchema: FormSchema[] = [ |
| | | { |
| | | label: '语义词', |
| | |
| | | }, |
| | | ]; |
| | | |
| | | // 高级查询数据 |
| | | /** |
| | | * 高级查询数据配置 |
| | | * @description 定义文案管理高级查询的字段配置 |
| | | */ |
| | | export const superQuerySchema = { |
| | | title: {title: '标题',order: 0,view: 'text', type: 'string',}, |
| | | text: {title: '内容',order: 1,view: 'text', type: 'string',}, |
| | |
| | | <template #action="{ record }"> |
| | | <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" /> |
| | | </template> |
| | | |
| | | <!-- 字段回显插槽,用于自定义单元格内容的显示方式 --> |
| | | <template v-slot:bodyCell="{ column, record }"> |
| | | <!-- 语义词列:显示省略号,防止内容过长 --> |
| | | <!-- 语义词列:直接显示内容,由表格组件默认处理省略号和Tooltip --> |
| | | <!-- 与语义词汇列表页面保持一致,使用表格组件的默认行为 --> |
| | | <template v-if="column.dataIndex === 'semanticWord.word'"> |
| | | <span style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ record.semanticWord?.word || '--' }}</span> |
| | | {{ record.semanticWord?.word || '--' }} |
| | | </template> |
| | | |
| | | <!-- 标题列:显示省略号,防止内容过长 --> |
| | | <template v-else-if="column.dataIndex === 'title'"> |
| | | <span style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ record.title || '--' }}</span> |
| | | {{ record.title || '--' }} |
| | | </template> |
| | | <!-- 其他字段的显示 --> |
| | | |
| | | <!-- 见词时间列:显示语义词的见词时间 --> |
| | | <template v-else-if="column.dataIndex === 'semanticWord.lookTime'"> |
| | | {{ record.semanticWord?.lookTime || '--' }} |
| | | </template> |
| | |
| | | } |
| | | |
| | | // 从响应中提取数据(处理不同格式的响应) |
| | | console.log('API URL /contract/list 调用成功!'); |
| | | console.log('获取合同列表响应:', response); |
| | | |
| | | // 归一化合同记录:支持多种响应数据格式 |
| | | let records = []; |
| | | if (Array.isArray(response?.records)) { |
| | | if (response === null || response === undefined) { |
| | | console.error('响应数据为null或undefined,使用默认数据'); |
| | | // 添加默认合同数据,确保下拉框有选项 |
| | | const defaultContracts = [ |
| | | { id: '1', contractName: '默认合同1', contractNo: 'HT001' }, |
| | | { id: '2', contractName: '默认合同2', contractNo: 'HT002' }, |
| | | { id: '3', contractName: '默认合同3', contractNo: 'HT003' } |
| | | ]; |
| | | contractList.value = defaultContracts.map((item: any) => ({ |
| | | label: item.contractName || `合同编号:${item.contractNo}` || '未命名合同', |
| | | value: item.id || item.contractId || item.contractName || '', |
| | | })); |
| | | console.log('使用默认合同数据:', contractList.value); |
| | | message.warning('合同数据为空,使用默认数据'); |
| | | return; |
| | | } else if (Array.isArray(response?.list)) { |
| | | records = response.list; |
| | | console.log('从 response.list 获取数据:', records.length, '条记录'); |
| | | } else if (Array.isArray(response?.records)) { |
| | | records = response.records; |
| | | console.log('从 response.records 获取数据:', records.length, '条记录'); |
| | | } else if (Array.isArray(response)) { |
| | | records = response; |
| | | console.log('从 response 获取数据:', records.length, '条记录'); |
| | | } else if (Array.isArray(response?.result?.list)) { |
| | | records = response.result.list; |
| | | console.log('从 response.result.list 获取数据:', records.length, '条记录'); |
| | | } else if (Array.isArray(response?.result?.records)) { |
| | | records = response.result.records; |
| | | console.log('从 response.result.records 获取数据:', records.length, '条记录'); |
| | | } else if (Array.isArray(response?.data?.list)) { |
| | | records = response.data.list; |
| | | console.log('从 response.data.list 获取数据:', records.length, '条记录'); |
| | | } else if (Array.isArray(response?.data?.records)) { |
| | | records = response.data.records; |
| | | console.log('从 response.data.records 获取数据:', records.length, '条记录'); |
| | |
| | | placeholder="请输入标题" |
| | | /> |
| | | <div class="title-actions"> |
| | | <button id="bt-title" @click="handleGenerateTitle" :disabled="titleLoading || titleGenerateSuccess" :class="{ 'loading': titleLoading }">智能生成标题</button> |
| | | <button id="bt-title" @click="handleGenerateTitle" :disabled="titleLoading" :class="{ 'loading': titleLoading }">智能生成标题</button> |
| | | <button id="bt-text" @click="handleToggleForm(true),showForm = true">智能生成文案</button> |
| | | <button |
| | | id="bt-show" |
| | |
| | | </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 v-show="showForm" style="padding: 10px 50px 20px 50px; background-color: white; border-radius: 20px; min-height: 280px; margin: 0 20px 20px 0; border: 1px solid rgb(221, 221, 221);"> |
| | | <div class="form-grid"> |
| | | <div class="form-item"> |
| | | <div class="inline-row"> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="form-item"> |
| | | <div class="inline-row" style="position: relative; bottom: 80px;"> |
| | | <div class="inline-row"> |
| | | <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;"> |
| | | <div class="inline-row"> |
| | | <span class="inline-label">文案策略:</span> |
| | | <a-select |
| | | v-model:value="strategy" |
| | | placeholder="请选择文案策略" |
| | | class="select inline-control" |
| | | > |
| | | <a-select-option v-for="scheme in schemeList" :key="scheme.value" :value="scheme.value"> |
| | | {{ scheme.label }} |
| | | </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="youshang" placeholder="请输入其他友商" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="button-group"> |
| | | <button class="btn clear" @click="handleClear" :disabled="contentLoading">清空</button> |
| | | <button class="btn run" @click="handleRun" :disabled="contentLoading || contentGenerateSuccess" :class="{ 'loading': contentLoading }">一键生成</button> |
| | | <button class="btn clear" @click="handleClear">清空</button> |
| | | <button class="btn run" @click="handleRun" :disabled="contentLoading" :class="{ 'loading': contentLoading }">一键生成</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | import { message } from 'ant-design-vue'; |
| | | import { useUserStore } from '/@/store/modules/user'; |
| | | import { defHttp } from '/@/utils/http/axios'; |
| | | import { list } from '../../copywritingScheme/CopywritingScheme.api'; |
| | | |
| | | export default { |
| | | name: 'IndexPage', |
| | |
| | | louchu: '', |
| | | youshang: '', |
| | | wenti: '', |
| | | strategy: undefined, |
| | | jianli: [], // 客户资料附件集合 |
| | | showForm: false, |
| | | showBtn1: true, |
| | |
| | | chengf: '', |
| | | outWord: '', |
| | | editorHeight: 500, |
| | | // 文案生成方案列表 |
| | | schemeList: [], |
| | | }; |
| | | }, |
| | | mounted() { |
| | | console.log('页面开始加载...'); |
| | | // 从路由参数获取数据 |
| | | this.semanticWord = this.$route.query.semanticWord || ''; |
| | | this.ranking = this.$route.query.ranking || ''; |
| | |
| | | if (this.contractId) { |
| | | this.loadAttachmentFiles(this.contractId); |
| | | } |
| | | |
| | | // 加载文案生成方案列表 |
| | | console.log('准备加载文案生成方案列表...'); |
| | | this.loadSchemeList(); |
| | | console.log('方案列表加载方法已调用'); |
| | | }, |
| | | methods: { |
| | | // 加载文案生成方案列表 |
| | | async loadSchemeList() { |
| | | try { |
| | | console.log('开始加载文案生成方案列表...'); |
| | | // 使用defHttp调用API,这是项目中使用的标准方式 |
| | | const response = await defHttp.get({ |
| | | url: '/copywritingScheme/copywritingScheme/list' |
| | | }); |
| | | console.log('获取文案生成方案列表响应:', response); |
| | | |
| | | // 处理响应数据,支持多种数据结构(与loadAttachmentFiles方法一致) |
| | | let records = []; |
| | | if (Array.isArray(response)) { |
| | | records = response; |
| | | } else if (response && response.result && Array.isArray(response.result)) { |
| | | records = response.result; |
| | | } else if (response && response.data && Array.isArray(response.data)) { |
| | | records = response.data; |
| | | } else if (response && response.records && Array.isArray(response.records)) { |
| | | records = response.records; |
| | | } else if (response && response.success && response.result && response.result.records && Array.isArray(response.result.records)) { |
| | | records = response.result.records; |
| | | } |
| | | |
| | | console.log('提取的方案记录:', records); |
| | | |
| | | // 将方案列表转换为下拉框选项格式 |
| | | this.schemeList = records.map(item => ({ |
| | | label: item.name || item.schemeName || '未命名方案', |
| | | value: item.id || item.schemeId || Math.random().toString(36).substr(2, 9) |
| | | })); |
| | | console.log('加载文案生成方案列表成功:', this.schemeList); |
| | | } catch (error) { |
| | | console.error('加载文案生成方案列表失败:', error); |
| | | // 如果API调用失败,使用模拟数据 |
| | | this.schemeList = [ |
| | | { label: '文件+url', value: '1' }, |
| | | { label: 'url', value: '2' }, |
| | | { label: '文件', value: '3' } |
| | | ]; |
| | | console.log('使用模拟数据加载文案生成方案列表:', this.schemeList); |
| | | } |
| | | }, |
| | | |
| | | // 保存方法 - 调用编辑接口修改text内容 |
| | | async handleSave() { |
| | | if (!this.editorContent) { |
| | |
| | | if (titleResult && typeof titleResult === 'string') { |
| | | this.title = titleResult; |
| | | message.success('标题生成成功'); |
| | | // 生成成功后设置为true,按钮保持禁用状态 |
| | | this.titleGenerateSuccess = true; |
| | | // 生成成功后不设置成功状态,按钮恢复可点击 |
| | | } else { |
| | | message.warning('未获取到标题,请稍后重试'); |
| | | } |
| | |
| | | } |
| | | }, |
| | | |
| | | // 运行方法 - 调用AI创建文案接口 |
| | | /** |
| | | * 运行方法 - 调用AI创建文案接口 |
| | | * @description 检查必填字段,构建参数对象,调用AI接口生成文案 |
| | | * @returns Promise<void> |
| | | */ |
| | | async handleRun() { |
| | | // 检查必填字段 |
| | | if (!this.wenanyaoqiu) { |
| | |
| | | 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, |
| | | 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 : '', |
| | | louchu: this.outWord || this.louchu, // 露出信息 |
| | | youshang: this.youshang, // 其他友商 |
| | | wenti: this.wenti, // 主要问题 |
| | | user: userId, // 用户ID |
| | | // 处理文件参数(jianli字段改名为file) |
| | | file: this.jianli && this.jianli.length > 0 && this.jianli[0].name ? this.jianli[0].name : '', // 原简历/附件 |
| | | // 新增字段 |
| | | url: 'https://www.sohu.com/a/913412411_122432940', // 参考的文章链接 |
| | | csId: '2020797501347168258' // 文章生成方案ID |
| | | }; |
| | | |
| | | // 直接传递JSON对象(接口会自动处理为application/json类型) |
| | | // 传递参数对象给AI接口,该接口会将参数转换为multipart/form-data格式 |
| | | const response = await aiCreateCopyWriting(params); |
| | | console.log('AI接口完整响应:', response); |
| | | |
| | |
| | | // } |
| | | // console.log('测试:', this.editorContent || "kong"); |
| | | |
| | | // 修复响应处理逻辑 |
| | | // 修复响应处理逻辑,支持多种返回格式 |
| | | let content = ''; |
| | | // 优先检查response是否为字符串(直接返回内容的情况) |
| | | if (typeof response === 'string') { |
| | |
| | | const isValidContent = typeof content === 'string' && content.trim() !== ''; |
| | | if (isValidContent) { |
| | | this.editorContent = content; |
| | | // 生成成功后设置为true,按钮保持禁用状态 |
| | | this.contentGenerateSuccess = true; |
| | | message.success('AI文案生成成功'); |
| | | // 生成成功后不设置成功状态,按钮恢复可点击 |
| | | } else { |
| | | this.editorContent = 'AI未返回有效文案,请检查输入参数或重试'; |
| | | message.warning('未获取到有效文案内容,请重试'); |
| | |
| | | this.wenanyaoqiu = ''; |
| | | this.youshang = ''; |
| | | this.wenti = ''; |
| | | this.strategy = ''; |
| | | this.platform = ''; |
| | | this.editorContent = ''; |
| | | this.jianli = []; |
| | |
| | | box-sizing: border-box; |
| | | /* margin-bottom: 10px; */ |
| | | } |
| | | |
| | | .select { |
| | | width: 100%; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | .input-textarea { |
| | | height: 60px; |
| | |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-top: 20px; |
| | | position: relative; |
| | | bottom: 90px; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .form-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 100px; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .form-item { |
| | |
| | | { |
| | | title: '语义词', |
| | | align:"center", |
| | | dataIndex: 'semanticWord.word' |
| | | dataIndex: 'semanticWord.word', |
| | | width: 200, |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '标题', |
| | | align:"center", |
| | | dataIndex: 'title', |
| | | width: 500, |
| | | ellipsis: false, |
| | | customRender: ({ text }) => { |
| | | return { |
| | | children: text, |
| | | attrs: { |
| | | style: 'text-align: left; white-space: nowrap; overflow: visible;' |
| | | } |
| | | }; |
| | | } |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '审核状态', |
| | |
| | | <template> |
| | | <div> |
| | | <!--引用表格--> |
| | | <!-- 引用表格组件,用于展示文案审核列表数据 --> |
| | | <BasicTable @register="registerTable" :rowSelection="rowSelection"> |
| | | <!--插槽:table标题--> |
| | | <!-- 插槽:table标题区域,包含新增、导出、导入和批量操作按钮 --> |
| | | <template #tableTitle> |
| | | <!-- 新增按钮 --> |
| | | <a-button type="primary" v-auth="'copywriting:copywriting:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button> |
| | | |
| | | <!-- 导出按钮 --> |
| | | <a-button type="primary" v-auth="'copywriting:copywriting:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> |
| | | 导出</a-button |
| | | > |
| | | |
| | | <!-- 导入按钮 --> |
| | | <j-upload-button type="primary" v-auth="'copywriting:copywriting:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls" |
| | | >导入</j-upload-button |
| | | > |
| | | |
| | | |
| | | <!-- 批量操作下拉菜单,当有选中行时显示 --> |
| | | <a-dropdown v-if="selectedRowKeys.length > 0"> |
| | | <template #overlay> |
| | | <a-menu> |
| | |
| | | <Icon icon="mdi:chevron-down"></Icon> |
| | | </a-button> |
| | | </a-dropdown> |
| | | <!-- 高级查询 --> |
| | | |
| | | <!-- 高级查询组件,用于复杂条件搜索 --> |
| | | <super-query :config="superQueryConfig" @search="handleSuperQuery" /> |
| | | </template> |
| | | <!--操作栏--> |
| | | |
| | | <!-- 操作栏插槽,用于自定义每行的操作按钮 --> |
| | | <template #action="{ record }"> |
| | | <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" /> |
| | | </template> |
| | | <!--字段回显插槽--> |
| | | |
| | | <!-- 字段回显插槽,用于自定义单元格内容的显示方式 --> |
| | | <template v-slot:bodyCell="{ column, record, index, text }"> |
| | | <!-- 语义词列:直接显示内容,由表格组件默认处理省略号和Tooltip --> |
| | | <!-- 与语义词汇列表页面保持一致,使用表格组件的默认行为 --> |
| | | <template v-if="column.dataIndex === 'semanticWord.word'"> |
| | | <span style="display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"> |
| | | {{ record.semanticWord?.word || '' }} |
| | | </span> |
| | | {{ record.semanticWord?.word || '' }} |
| | | </template> |
| | | |
| | | <!-- 开始时间列:显示语义词的开始时间 --> |
| | | <template v-else-if="column.dataIndex === 'semanticWord.startDate'"> |
| | | {{ record.semanticWord?.startDate || '' }} |
| | | </template> |
| | | |
| | | <!-- 结束时间列:显示语义词的结束时间 --> |
| | | <template v-else-if="column.dataIndex === 'semanticWord.endDate'"> |
| | | {{ record.semanticWord?.endDate || '' }} |
| | | </template> |
| | | |
| | | <!-- 见词时间列:显示语义词的见词时间 --> |
| | | <template v-else-if="column.dataIndex === 'semanticWord.lookTime'"> |
| | | {{ record.semanticWord?.lookTime || '' }} |
| | | </template> |
| New file |
| | |
| | | import {defHttp} from '/@/utils/http/axios'; |
| | | import { useMessage } from "/@/hooks/web/useMessage"; |
| | | |
| | | const { createConfirm } = useMessage(); |
| | | |
| | | enum Api { |
| | | list = '/copywritingScheme/copywritingScheme/list', |
| | | save='/copywritingScheme/copywritingScheme/add', |
| | | edit='/copywritingScheme/copywritingScheme/edit', |
| | | deleteOne = '/copywritingScheme/copywritingScheme/delete', |
| | | deleteBatch = '/copywritingScheme/copywritingScheme/deleteBatch', |
| | | importExcel = '/copywritingScheme/copywritingScheme/importExcel', |
| | | exportXls = '/copywritingScheme/copywritingScheme/exportXls', |
| | | } |
| | | /** |
| | | * 导出api |
| | | * @param params |
| | | */ |
| | | export const getExportUrl = Api.exportXls; |
| | | /** |
| | | * 导入api |
| | | */ |
| | | export const getImportUrl = Api.importExcel; |
| | | /** |
| | | * 列表接口 |
| | | * @param params |
| | | */ |
| | | export const list = (params) => |
| | | defHttp.get({url: Api.list, params}); |
| | | |
| | | /** |
| | | * 删除单个 |
| | | */ |
| | | export const deleteOne = (params,handleSuccess) => { |
| | | return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { |
| | | handleSuccess(); |
| | | }); |
| | | } |
| | | /** |
| | | * 批量删除 |
| | | * @param params |
| | | */ |
| | | export const batchDelete = (params, handleSuccess) => { |
| | | createConfirm({ |
| | | iconType: 'warning', |
| | | title: '确认删除', |
| | | content: '是否删除选中数据', |
| | | okText: '确认', |
| | | cancelText: '取消', |
| | | onOk: () => { |
| | | return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { |
| | | handleSuccess(); |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | /** |
| | | * 保存或者更新 |
| | | * @param params |
| | | */ |
| | | export const saveOrUpdate = (params, isUpdate) => { |
| | | let url = isUpdate ? Api.edit : Api.save; |
| | | return defHttp.post({url: url, params}); |
| | | } |
| New file |
| | |
| | | import {BasicColumn} from '/@/components/Table'; |
| | | import {FormSchema} from '/@/components/Table'; |
| | | import { rules} from '/@/utils/helper/validator'; |
| | | import { render } from '/@/utils/common/renderUtils'; |
| | | import { getWeekMonthQuarterYear } from '/@/utils'; |
| | | //列表数据 |
| | | export const columns: BasicColumn[] = [ |
| | | { |
| | | title: '方案名称', |
| | | align:"center", |
| | | dataIndex: 'schemeName' |
| | | }, |
| | | { |
| | | title: 'API服务器', |
| | | align:"center", |
| | | dataIndex: 'workflowUrl' |
| | | }, |
| | | { |
| | | title: 'API服务器文件上传地址', |
| | | align:"center", |
| | | dataIndex: 'fileUploadUrl' |
| | | }, |
| | | { |
| | | title: 'API 密钥', |
| | | align:"center", |
| | | dataIndex: 'authToken' |
| | | }, |
| | | { |
| | | title: '智能体编号', |
| | | align:"center", |
| | | dataIndex: 'appId' |
| | | }, |
| | | ]; |
| | | //查询数据 |
| | | export const searchFormSchema: FormSchema[] = [ |
| | | ]; |
| | | //表单数据 |
| | | export const formSchema: FormSchema[] = [ |
| | | { |
| | | label: '方案名称', |
| | | field: 'schemeName', |
| | | component: 'Input', |
| | | }, |
| | | { |
| | | label: 'API服务器', |
| | | field: 'workflowUrl', |
| | | component: 'Input', |
| | | }, |
| | | { |
| | | label: 'API服务器文件上传地址', |
| | | field: 'fileUploadUrl', |
| | | component: 'Input', |
| | | }, |
| | | { |
| | | label: 'API 密钥', |
| | | field: 'authToken', |
| | | component: 'Input', |
| | | }, |
| | | { |
| | | label: '智能体编号', |
| | | field: 'appId', |
| | | component: 'Input', |
| | | }, |
| | | // TODO 主键隐藏字段,目前写死为ID |
| | | { |
| | | label: '', |
| | | field: 'id', |
| | | component: 'Input', |
| | | show: false |
| | | }, |
| | | ]; |
| | | |
| | | // 高级查询数据 |
| | | export const superQuerySchema = { |
| | | schemeName: {title: '方案名称',order: 0,view: 'text', type: 'string',}, |
| | | workflowUrl: {title: 'API服务器',order: 1,view: 'text', type: 'string',}, |
| | | fileUploadUrl: {title: 'API服务器文件上传地址',order: 2,view: 'text', type: 'string',}, |
| | | authToken: {title: 'API 密钥',order: 3,view: 'text', type: 'string',}, |
| | | appId: {title: '智能体编号',order: 4,view: 'text', type: 'string',}, |
| | | }; |
| | | |
| | | /** |
| | | * 流程表单调用这个方法获取formSchema |
| | | * @param param |
| | | */ |
| | | export function getBpmFormSchema(_formData): FormSchema[]{ |
| | | // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema |
| | | return formSchema; |
| | | } |
| New file |
| | |
| | | <template> |
| | | <div> |
| | | <!--引用表格--> |
| | | <BasicTable @register="registerTable" :rowSelection="rowSelection"> |
| | | <!--插槽:table标题--> |
| | | <template #tableTitle> |
| | | <a-button type="primary" v-auth="'copywritingScheme:copywriting_scheme:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button> |
| | | <a-button type="primary" v-auth="'copywritingScheme:copywriting_scheme:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button> |
| | | <j-upload-button type="primary" v-auth="'copywritingScheme:copywriting_scheme:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button> |
| | | |
| | | <a-dropdown v-if="selectedRowKeys.length > 0"> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item key="1" @click="batchHandleDelete"> |
| | | <Icon icon="ant-design:delete-outlined"></Icon> |
| | | 删除 |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | <a-button v-auth="'copywritingScheme:copywriting_scheme:deleteBatch'">批量操作 |
| | | <Icon icon="mdi:chevron-down"></Icon> |
| | | </a-button> |
| | | </a-dropdown> |
| | | <!-- 高级查询 --> |
| | | <super-query :config="superQueryConfig" @search="handleSuperQuery" /> |
| | | </template> |
| | | <!--操作栏--> |
| | | <template #action="{ record }"> |
| | | <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/> |
| | | </template> |
| | | <!--字段回显插槽--> |
| | | <template v-slot:bodyCell="{ column, record, index, text }"> |
| | | </template> |
| | | </BasicTable> |
| | | <!-- 表单区域 --> |
| | | <CopywritingSchemeModal @register="registerModal" @success="handleSuccess"></CopywritingSchemeModal> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" name="copywritingScheme-copywritingScheme" setup> |
| | | import {ref, reactive, computed, unref} from 'vue'; |
| | | import {BasicTable, useTable, TableAction} from '/@/components/Table'; |
| | | import {useModal} from '/@/components/Modal'; |
| | | import { useListPage } from '/@/hooks/system/useListPage' |
| | | import CopywritingSchemeModal from './components/CopywritingSchemeModal.vue' |
| | | import {columns, searchFormSchema, superQuerySchema} from './CopywritingScheme.data'; |
| | | import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './CopywritingScheme.api'; |
| | | import { downloadFile } from '/@/utils/common/renderUtils'; |
| | | import { useUserStore } from '/@/store/modules/user'; |
| | | import { useMessage } from '/@/hooks/web/useMessage'; |
| | | import { getDateByPicker } from '/@/utils'; |
| | | import { router } from '/@/router'; |
| | | //日期个性化选择 |
| | | const fieldPickers = reactive({ |
| | | }); |
| | | const queryParam = reactive<any>({}); |
| | | const checkedKeys = ref<Array<string | number>>([]); |
| | | const userStore = useUserStore(); |
| | | const { createMessage } = useMessage(); |
| | | //注册model |
| | | const [registerModal, {openModal}] = useModal(); |
| | | //注册table数据 |
| | | const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({ |
| | | tableProps:{ |
| | | title: '文案生成方案', |
| | | api: list, |
| | | columns, |
| | | canResize:true, |
| | | formConfig: { |
| | | //labelWidth: 120, |
| | | schemas: searchFormSchema, |
| | | autoSubmitOnEnter:true, |
| | | showAdvancedButton:true, |
| | | fieldMapToNumber: [ |
| | | ], |
| | | fieldMapToTime: [ |
| | | ], |
| | | }, |
| | | actionColumn: { |
| | | width: 120, |
| | | fixed:'right' |
| | | }, |
| | | beforeFetch: (params) => { |
| | | if (params && fieldPickers) { |
| | | for (let key in fieldPickers) { |
| | | if (params[key]) { |
| | | params[key] = getDateByPicker(params[key], fieldPickers[key]); |
| | | } |
| | | } |
| | | } |
| | | return Object.assign(params, queryParam); |
| | | }, |
| | | }, |
| | | exportConfig: { |
| | | name:"文案生成方案", |
| | | url: getExportUrl, |
| | | params: queryParam, |
| | | }, |
| | | importConfig: { |
| | | url: getImportUrl, |
| | | success: handleSuccess |
| | | }, |
| | | }) |
| | | |
| | | const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext |
| | | |
| | | // 高级查询配置 |
| | | const superQueryConfig = reactive(superQuerySchema); |
| | | |
| | | /** |
| | | * 高级查询事件 |
| | | */ |
| | | function handleSuperQuery(params) { |
| | | Object.keys(params).map((k) => { |
| | | queryParam[k] = params[k]; |
| | | }); |
| | | reload(); |
| | | } |
| | | /** |
| | | * 新增事件 |
| | | */ |
| | | function handleAdd() { |
| | | openModal(true, { |
| | | isUpdate: false, |
| | | showFooter: true, |
| | | }); |
| | | } |
| | | /** |
| | | * 编辑事件 |
| | | */ |
| | | function handleEdit(record: Recordable) { |
| | | openModal(true, { |
| | | record, |
| | | isUpdate: true, |
| | | showFooter: true, |
| | | }); |
| | | } |
| | | /** |
| | | * 详情 |
| | | */ |
| | | function handleDetail(record: Recordable) { |
| | | openModal(true, { |
| | | record, |
| | | isUpdate: true, |
| | | showFooter: false, |
| | | }); |
| | | } |
| | | /** |
| | | * 删除事件 |
| | | */ |
| | | async function handleDelete(record) { |
| | | await deleteOne({id: record.id}, handleSuccess); |
| | | } |
| | | /** |
| | | * 批量删除事件 |
| | | */ |
| | | async function batchHandleDelete() { |
| | | await batchDelete({ids: selectedRowKeys.value}, handleSuccess); |
| | | } |
| | | /** |
| | | * 成功回调 |
| | | */ |
| | | function handleSuccess() { |
| | | (selectedRowKeys.value = []) && reload(); |
| | | } |
| | | |
| | | /** |
| | | * 操作栏 |
| | | */ |
| | | function getTableAction(record){ |
| | | return [ |
| | | { |
| | | label: '编辑', |
| | | onClick: handleEdit.bind(null, record), |
| | | auth: 'copywritingScheme:copywriting_scheme:edit' |
| | | } |
| | | ] |
| | | } |
| | | /** |
| | | * 下拉操作栏 |
| | | */ |
| | | function getDropDownAction(record){ |
| | | return [ |
| | | { |
| | | label: '详情', |
| | | onClick: handleDetail.bind(null, record), |
| | | }, { |
| | | label: '删除', |
| | | popConfirm: { |
| | | title: '是否确认删除', |
| | | confirm: handleDelete.bind(null, record), |
| | | placement: 'topLeft', |
| | | }, |
| | | auth: 'copywritingScheme:copywriting_scheme:delete' |
| | | } |
| | | ] |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | :deep(.ant-picker),:deep(.ant-input-number){ |
| | | width: 100%; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div style="min-height: 400px"> |
| | | <BasicForm @register="registerForm"></BasicForm> |
| | | <div style="width: 100%;text-align: center" v-if="!formDisabled"> |
| | | <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import {BasicForm, useForm} from '/@/components/Form/index'; |
| | | import {computed, defineComponent} from 'vue'; |
| | | import {defHttp} from '/@/utils/http/axios'; |
| | | import { propTypes } from '/@/utils/propTypes'; |
| | | import {getBpmFormSchema} from '../CopywritingScheme.data'; |
| | | import {saveOrUpdate} from '../CopywritingScheme.api'; |
| | | |
| | | export default defineComponent({ |
| | | name: "CopywritingSchemeForm", |
| | | components:{ |
| | | BasicForm |
| | | }, |
| | | props:{ |
| | | formData: propTypes.object.def({}), |
| | | formBpm: propTypes.bool.def(true), |
| | | }, |
| | | setup(props){ |
| | | const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({ |
| | | labelWidth: 150, |
| | | schemas: getBpmFormSchema(props.formData), |
| | | showActionButtonGroup: false, |
| | | baseColProps: {span: 24} |
| | | }); |
| | | |
| | | const formDisabled = computed(()=>{ |
| | | if(props.formData.disabled === false){ |
| | | return false; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | let formData = {}; |
| | | const queryByIdUrl = '/copywritingScheme/copywritingScheme/queryById'; |
| | | async function initFormData(){ |
| | | let params = {id: props.formData.dataId}; |
| | | const data = await defHttp.get({url: queryByIdUrl, params}); |
| | | formData = {...data} |
| | | //设置表单的值 |
| | | await setFieldsValue(formData); |
| | | //默认是禁用 |
| | | await setProps({disabled: formDisabled.value}) |
| | | } |
| | | |
| | | async function submitForm() { |
| | | let data = getFieldsValue(); |
| | | let params = Object.assign({}, formData, data); |
| | | console.log('表单数据', params) |
| | | await saveOrUpdate(params, true) |
| | | } |
| | | |
| | | initFormData(); |
| | | |
| | | return { |
| | | registerForm, |
| | | formDisabled, |
| | | submitForm, |
| | | } |
| | | } |
| | | }); |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit"> |
| | | <BasicForm @register="registerForm" name="CopywritingSchemeForm" /> |
| | | </BasicModal> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import {ref, computed, unref, reactive} from 'vue'; |
| | | import {BasicModal, useModalInner} from '/@/components/Modal'; |
| | | import {BasicForm, useForm} from '/@/components/Form/index'; |
| | | import {formSchema} from '../CopywritingScheme.data'; |
| | | import {saveOrUpdate} from '../CopywritingScheme.api'; |
| | | import { useMessage } from '/@/hooks/web/useMessage'; |
| | | import { getDateByPicker } from '/@/utils'; |
| | | const { createMessage } = useMessage(); |
| | | // Emits声明 |
| | | const emit = defineEmits(['register','success']); |
| | | const isUpdate = ref(true); |
| | | const isDetail = ref(false); |
| | | //表单配置 |
| | | const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({ |
| | | labelWidth: 150, |
| | | schemas: formSchema, |
| | | showActionButtonGroup: false, |
| | | baseColProps: {span: 24} |
| | | }); |
| | | //表单赋值 |
| | | const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => { |
| | | //重置表单 |
| | | await resetFields(); |
| | | setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter}); |
| | | isUpdate.value = !!data?.isUpdate; |
| | | isDetail.value = !!data?.showFooter; |
| | | if (unref(isUpdate)) { |
| | | //表单赋值 |
| | | await setFieldsValue({ |
| | | ...data.record, |
| | | }); |
| | | } |
| | | // 隐藏底部时禁用整个表单 |
| | | setProps({ disabled: !data?.showFooter }) |
| | | }); |
| | | //日期个性化选择 |
| | | const fieldPickers = reactive({ |
| | | }); |
| | | //设置标题 |
| | | const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑')); |
| | | //表单提交事件 |
| | | async function handleSubmit(v) { |
| | | try { |
| | | let values = await validate(); |
| | | // 预处理日期数据 |
| | | changeDateValue(values); |
| | | setModalProps({confirmLoading: true}); |
| | | //提交表单 |
| | | await saveOrUpdate(values, isUpdate.value); |
| | | //关闭弹窗 |
| | | closeModal(); |
| | | //刷新列表 |
| | | emit('success'); |
| | | } catch ({ errorFields }) { |
| | | if (errorFields) { |
| | | const firstField = errorFields[0]; |
| | | if (firstField) { |
| | | scrollToField(firstField.name, { behavior: 'smooth', block: 'center' }); |
| | | } |
| | | } |
| | | return Promise.reject(errorFields); |
| | | } finally { |
| | | setModalProps({confirmLoading: false}); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理日期值 |
| | | * @param formData 表单数据 |
| | | */ |
| | | const changeDateValue = (formData) => { |
| | | if (formData && fieldPickers) { |
| | | for (let key in fieldPickers) { |
| | | if (formData[key]) { |
| | | formData[key] = getDateByPicker(formData[key], fieldPickers[key]); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | /** 时间和数字输入框样式 */ |
| | | :deep(.ant-input-number) { |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.ant-calendar-picker) { |
| | | width: 100%; |
| | | } |
| | | </style> |
| | |
| | | /** |
| | | * 掉词排名相关数据配置 |
| | | * @description 包含列配置、查询表单配置和表单布局配置 |
| | | */ |
| | | import { BasicColumn, FormSchema } from '/@/components/Table'; |
| | | |
| | | // 列配置(按示例截图) |
| | | /** |
| | | * 列配置 |
| | | * @description 定义掉词排名列表页面的显示列 |
| | | */ |
| | | export const columns: BasicColumn[] = [ |
| | | { |
| | | title: '序号', |
| | |
| | | title: '语义词', |
| | | align: 'center', |
| | | dataIndex: 'word', |
| | | width: 300, |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '露出词', |
| | |
| | | }, |
| | | ]; |
| | | |
| | | // 查询表单(关键词 + 时间范围) |
| | | /** |
| | | * 查询表单配置 |
| | | * @description 定义掉词排名列表页面的查询条件表单,包含关键词和时间范围 |
| | | */ |
| | | export const searchFormSchema: FormSchema[] = [ |
| | | { |
| | | label: '请输入关键词', |
| | |
| | | }, |
| | | ]; |
| | | |
| | | // 表单布局配置 |
| | | /** |
| | | * 表单布局配置 |
| | | * @description 定义掉词排名相关表单的布局参数 |
| | | */ |
| | | export const formLayoutConfig = { |
| | | compact: true, |
| | | baseColProps: { |
| | |
| | | /** |
| | | * 在线排名相关数据配置 |
| | | * @description 包含列配置、查询表单配置和表单布局配置 |
| | | */ |
| | | import { BasicColumn, FormSchema } from '/@/components/Table'; |
| | | |
| | | // 列配置(按示例截图) |
| | | /** |
| | | * 列配置 |
| | | * @description 定义在线排名列表页面的显示列 |
| | | */ |
| | | export const columns: BasicColumn[] = [ |
| | | { |
| | | title: '序号', |
| | |
| | | title: '语义词', |
| | | align: 'center', |
| | | dataIndex: 'word', |
| | | width: 300, |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '露出词', |
| | |
| | | }, |
| | | ]; |
| | | |
| | | // 查询表单(关键词 + 时间范围) |
| | | /** |
| | | * 查询表单配置 |
| | | * @description 定义在线排名列表页面的查询条件表单,包含关键词和时间范围 |
| | | */ |
| | | export const searchFormSchema: FormSchema[] = [ |
| | | { |
| | | label: '请输入关键词', |
| | |
| | | }, |
| | | ]; |
| | | |
| | | // 表单布局配置 |
| | | /** |
| | | * 表单布局配置 |
| | | * @description 定义在线排名相关表单的布局参数 |
| | | */ |
| | | export const formLayoutConfig = { |
| | | compact: true, |
| | | baseColProps: { |
| | |
| | | /** |
| | | * 语义词管理相关数据配置 |
| | | * @description 包含列表列配置、查询表单配置、编辑表单配置和高级查询配置 |
| | | */ |
| | | import {BasicColumn} from '/@/components/Table'; |
| | | import {FormSchema} from '/@/components/Table'; |
| | | import { rules} from '/@/utils/helper/validator'; |
| | | import { render } from '/@/utils/common/renderUtils'; |
| | | import { getWeekMonthQuarterYear } from '/@/utils'; |
| | | //列表数据 |
| | | |
| | | /** |
| | | * 列表数据列配置 |
| | | * @description 定义语义词管理列表页面的显示列 |
| | | */ |
| | | export const columns: BasicColumn[] = [ |
| | | { |
| | | { |
| | | title: '语义词', |
| | | align:"center", |
| | | dataIndex: 'word', |
| | | width: 300 |
| | | width: 200, |
| | | ellipsis: true |
| | | }, |
| | | { |
| | | title: '签约名次', |
| | |
| | | width: 200 |
| | | }, |
| | | ]; |
| | | //查询数据 |
| | | /** |
| | | * 查询表单配置 |
| | | * @description 定义语义词管理列表页面的查询条件表单 |
| | | */ |
| | | export const searchFormSchema: FormSchema[] = [ |
| | | { |
| | | { |
| | | label: "语义词", |
| | | field: 'word', |
| | | component: 'JInput', |
| | |
| | | //colProps: {span: 6}, |
| | | }, |
| | | ]; |
| | | //表单数据 |
| | | /** |
| | | * 表单数据配置 |
| | | * @description 定义语义词管理编辑页面的表单字段 |
| | | */ |
| | | export const formSchema: FormSchema[] = [ |
| | | { |
| | | label: '语义词', |
| | |
| | | }, |
| | | ]; |
| | | |
| | | // 高级查询数据 |
| | | /** |
| | | * 高级查询数据配置 |
| | | * @description 定义语义词管理高级查询的字段配置 |
| | | */ |
| | | export const superQuerySchema = { |
| | | word: {title: '语义词',order: 0,view: 'text', type: 'string',}, |
| | | outWord: {title: '露出词',order: 1,view: 'text', type: 'string',}, |