| .env.docker | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Dockerfile | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Dockerfile.cloud | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/components/Tinymce/src/Editor.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/design/ant/index.less | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/design/ant/select.less | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/copywriting/CopywritingList.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/copywriting/generated/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/semanticwordPD/SemanticWordList.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.env.docker
@@ -16,7 +16,7 @@ VITE_GLOB_API_URL=/jeecgboot #后台接口全路径地址(必填) VITE_GLOB_DOMAIN_URL=http://jeecg-boot-system:8080/jeecg-boot VITE_GLOB_DOMAIN_URL=http://8.145.61.64:8080/jeecg-boot # 接口父路径前缀 VITE_GLOB_API_URL_PREFIX= Dockerfile
@@ -5,9 +5,9 @@ RUN echo "server { \ listen 80; \ location /jeecgboot/ { \ proxy_pass http://jeecg-boot-system:8080/jeecg-boot/; \ proxy_pass http://8.145.61.64:8080/jeecg-boot/; \ proxy_redirect off; \ proxy_set_header Host jeecg-boot-system; \ proxy_set_header Host 8.145.61.64; \ proxy_set_header X-Real-IP \$remote_addr; \ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; \ } \ Dockerfile.cloud
@@ -7,7 +7,7 @@ location /jeecgboot/ { \ proxy_pass http://8.145.61.64:8080/; \ proxy_redirect off; \ proxy_set_header Host jeecg-boot-system; \ proxy_set_header Host 8.145.61.64; \ proxy_set_header X-Real-IP \$remote_addr; \ proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; \ } \ src/components/Tinymce/src/Editor.vue
@@ -193,6 +193,10 @@ // toolbar_groups: true, skin: skinName.value, skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value, // 修复插件路径问题 - 使用绝对路径从 node_modules 加载插件 base_url: '/', // 显式指定插件路径,避免路径拼接错误 plugins_url: '/node_modules/tinymce/plugins', images_upload_handler: (blobInfo, process) => new Promise((resolve, reject) => { let params = { src/design/ant/index.less
@@ -4,6 +4,7 @@ @import './btn.less'; // update-end--author:liaozhiyang---date:20240130---for:【issues/5857】Button color类型颜色失效 // @import './table.less'; @import './select.less'; // TODO beta.11 fix .ant-col { src/design/ant/select.less
New file @@ -0,0 +1,132 @@ @import (reference) '../color.less'; // 下拉选择框选项悬停样式 .ant-select-dropdown { .ant-select-item { // 默认状态 &-option { position: relative; transition: all 0.3s ease; // 悬停状态 - 显示完整信息 &:hover { background-color: @primary-1; color: @primary-color; // 当选项内容过长时,显示完整内容 .ant-select-item-option-content { white-space: normal !important; word-break: break-all; max-height: none !important; overflow: visible !important; text-overflow: unset !important; display: block; } // 添加悬停提示效果 &::after { content: attr(title); position: absolute; left: 100%; top: 0; margin-left: 8px; background: rgba(0, 0, 0, 0.8); color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 9999; pointer-events: none; opacity: 0; transform: translateX(-10px); transition: all 0.3s ease; } &:hover::after { opacity: 1; transform: translateX(0); } } // 选中状态 &-selected { background-color: @primary-1; color: @primary-color; font-weight: 500; } } } // 多选模式下的标签样式 .ant-select-selection-overflow { .ant-select-selection-item { max-width: 100%; .ant-select-selection-item-content { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } } } } // 附件选择框特殊样式 .fujianSection { .ant-select { .ant-select-selector { border-radius: 4px; border: 1px solid #d9d9d9; &:hover { border-color: @primary-color; } &:focus { border-color: @primary-color; box-shadow: 0 0 0 2px fade(@primary-color, 20%); } } // 下拉箭头样式 .ant-select-arrow { color: @text-color-secondary; } } // 附件选项特殊样式 .ant-select-dropdown { .ant-select-item { &-option { // 附件选项悬停时显示完整文件名 &:hover { .ant-select-item-option-content { position: relative; z-index: 1; background: @primary-1; padding: 4px 8px; border-radius: 2px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); } } } } } } // 响应式处理 @media (max-width: 768px) { .ant-select-dropdown { .ant-select-item { &-option { &:hover { .ant-select-item-option-content { white-space: normal; word-break: break-word; } } } } } } src/views/copywriting/CopywritingList.vue
@@ -64,6 +64,7 @@ import { useModal } from '/@/components/Modal'; import { useListPage } from '/@/hooks/system/useListPage'; import { useRouter } from 'vue-router'; import { message } from 'ant-design-vue'; import CopywritingModal from './components/CopywritingModal.vue'; import { columns, searchFormSchema, superQuerySchema } from './Copywriting.data'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Copywriting.api'; @@ -146,13 +147,13 @@ // }); // } /** * 编辑事件 - 跳转到下载页面 * 编辑事件 - 直接跳转到生成页面 */ function handleEdit(record: Recordable) { try { // 传递语义词、签约排名和标题数据到下载页面 // 直接跳转到生成页面,传递当前页面的参数 router.push({ path: '/copywriting/download/index', path: '/copywriting/generated/index', query: { semanticWord: record.semanticWord?.word || '', ranking: record.semanticWord?.ranking || '', @@ -163,9 +164,8 @@ }, }); } catch (error) { console.error('路由跳转失败:', error); // 如果路由跳转失败,尝试使用完整路径 window.location.href = '/#/copywriting/download'; console.error('跳转失败:', error); message.error('跳转失败'); } } /** src/views/copywriting/generated/index.vue
@@ -4,6 +4,18 @@ <span class="info-item">签约排名: {{ ranking || '暂无数据' }}</span> <span class="info-item">预警通知: 名次小于等于2,或发布后2小时内没提名。</span> </div> <!-- 附件选择区域 --> <div class="fujianSection"> <div class="attachment-row"> <label class="label">选择附件:</label> <a-select v-model:value="selectedAttachment" placeholder="请选择附件" class="select" :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="page-container"> <!-- 左侧智能文案区域 --> <div class="left-panel"> @@ -53,29 +65,34 @@ import { saveOrUpdate, aiCreateCopyWriting } 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: [], // 客户资料附件集合 loading: false, }; }, data() { return { editorContent: '', semanticWord: '', ranking: '', contractId: '', id: '', platform: '', title: '', wenanyaoqiu: '', louchu: '', youshang: '', wenti: '', jianli: [], // 客户资料附件集合 loading: false, // 附件选择相关状态 selectedAttachment: '', attachmentFiles: [], attachmentLoading: false, }; }, mounted() { // 从路由参数获取数据 this.semanticWord = this.$route.query.semanticWord || ''; @@ -83,7 +100,9 @@ this.contractId = this.$route.query.contractId || ''; this.id = this.$route.query.id || ''; this.title = this.$route.query.title || ''; this.editorContent = this.$route.query.text || ''; console.log('semanticWord,ranking,contractId,id,title,text', this.semanticWord, this.ranking, this.contractId, this.id, this.title, this.editorContent); // 从路由参数获取附件信息 const selectedAttachmentId = this.$route.query.selectedAttachmentId; const selectedAttachmentName = this.$route.query.selectedAttachmentName; @@ -99,9 +118,14 @@ ]; console.log('已加载客户资料附件:', this.jianli); } // 如果有合同ID,加载附件列表 if (this.contractId) { this.loadAttachmentFiles(this.contractId); } }, methods: { // 保存方法 // 保存方法 - 调用编辑接口修改text内容 async handleSave() { if (!this.editorContent) { message.warning('请先填写文案内容'); @@ -111,6 +135,7 @@ this.loading = true; try { const params = { id: this.id || '', // 编辑接口需要ID参数 title: this.title || this.semanticWord || '智能文案', // 标题优先使用列表页的title,其次使用语义词 status: '1', // 1:已创建 platform: this.platform || '', // 推荐平台 @@ -118,7 +143,8 @@ text: this.editorContent, // 文案内容 }; await saveOrUpdate(params, false); // 直接调用编辑接口 await defHttp.post({ url: '/copywriting/copywriting/edit', params }); message.success('保存成功'); // 保存成功后返回列表页 this.$router.push('/copywriting/copywritingList'); @@ -265,6 +291,83 @@ handleCancel() { this.$router.back(); }, /** * 加载附件数据 */ async loadAttachmentFiles(contractId) { this.attachmentLoading = true; try { const result = await defHttp.get({ url: '/contract/contract/queryContractFileByMainId', params: { id: contractId }, }); console.log('附件文件数据:', result); if (result && Array.isArray(result)) { // 过滤出附件文件(根据fileType为'合同附件') const attachmentFileList = [] result.forEach(file => { if (file.fileType === '合同附件' && file.appendixFile) { const files = file.appendixFile.split(','); // .map((file) => ({ // id: file.id, // name: file.appendixFile || '附件文件', // url: file.appendixFile, // })); files.forEach((fileName, index) => { const uniqueId = `${file.id}_${index}`; attachmentFileList.push({ id: uniqueId, name: fileName.split('/').pop(), url: fileName, originalId: file.id }); }); } }); this.attachmentFiles = attachmentFileList; // 如果有附件,默认选择第一个 if (attachmentFileList.length > 0) { this.selectedAttachment = attachmentFileList[0].id; // 更新客户资料附件 this.jianli = [ { id: attachmentFileList[0].id, name: attachmentFileList[0].name, url: attachmentFileList[0].url || '', }, ]; } } } 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> @@ -287,6 +390,31 @@ 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; } /* 左侧面板 */ .left-panel { width: 350px; src/views/semanticwordPD/SemanticWordList.vue
@@ -28,73 +28,44 @@ <!--字段回显插槽--> <!--字段回显插槽--> <template v-slot:bodyCell="{ column, record, index, text }"> <template v-if="column.dataIndex === 'semanticWord.word'"> {{ record.semanticWord?.word || '' }} <template v-if="column.dataIndex === 'word'"> {{ record.word || '' }} </template> <template v-else-if="column.dataIndex === 'semanticWord.startDate'"> {{ record.semanticWord?.startDate || '' }} <template v-else-if="column.dataIndex === 'startDate'"> {{ record.startDate || '' }} </template> <template v-else-if="column.dataIndex === 'semanticWord.endDate'"> {{ record.semanticWord?.endDate || '' }} <template v-else-if="column.dataIndex === 'endDate'"> {{ record.endDate || '' }} </template> <template v-else-if="column.dataIndex === 'semanticWord.lookTime'"> {{ record.semanticWord?.lookTime || '' }} <template v-else-if="column.dataIndex === 'lookTime'"> {{ record.lookTime || '' }} </template> <template v-else-if="column.dataIndex === 'semanticWord.ranking'"> {{ record.semanticWord?.ranking || '' }} <template v-else-if="column.dataIndex === 'ranking'"> {{ record.ranking || '' }} </template> <template v-else-if="column.dataIndex === 'semanticWord.nowNo'"> {{ record.semanticWord?.nowNo || '' }} <template v-else-if="column.dataIndex === 'nowNo'"> {{ record.nowNo || '' }} </template> <template v-else-if="column.dataIndex === 'semanticWord.changer'"> {{ record.semanticWord?.changer || '' }} <template v-else-if="column.dataIndex === 'changer'"> {{ record.changer || '' }} </template> </template> </BasicTable> <!-- 表单区域 --> <SemanticWordModal @register="registerModal" @success="handleSuccess"></SemanticWordModal> <!-- 下载模态框 --> <a-modal v-model:visible="showDownloadModal" title="选择附件并生成" width="600px" :footer="null" @cancel="handleModalCancel"> <div class="download-modal-content"> <!-- 顶部信息行 --> <div class="info-row"> <span class="info-item">语义词: {{ currentRecord?.word || '暂无数据' }}</span> <span class="info-item">签约排名: {{ currentRecord?.ranking || '暂无数据' }}</span> </div> <!-- 附件下拉选择 --> <div class="attachment-row"> <label class="label">选择附件:</label> <a-select v-model:value="selectedAttachment" placeholder="请选择附件" class="select" :loading="loading"> <a-select-option v-for="file in attachmentFiles" :key="file.id" :value="file.id"> {{ file.name }} </a-select-option> </a-select> </div> <!-- 按钮与备注 --> <div class="button-row"> <a-button type="primary" @click="handleGenerate" :loading="generating">一键生成</a-button> <a-button @click="handleModalCancel">取消</a-button> <span class="note">注: 调用豆包接口</span> </div> </div> </a-modal> </div> </template> <script lang="ts" name="semanticword-semanticWord" setup> import { ref, reactive } from 'vue'; import { reactive } from 'vue'; import { useRouter } from 'vue-router'; import { BasicTable, TableAction } from '/@/components/Table'; import { useModal } from '/@/components/Modal'; import { useListPage } from '/@/hooks/system/useListPage'; import SemanticWordModal from './components/SemanticWordModal.vue'; import { columns, searchFormSchema, superQuerySchema } from './SemanticWord.data'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SemanticWord.api'; import { list, batchDelete, getImportUrl, getExportUrl } from './SemanticWord.api'; import { getDateByPicker } from '/@/utils'; import { defHttp } from '/@/utils/http/axios'; import { message } from 'ant-design-vue'; //日期个性化选择 @@ -102,16 +73,9 @@ const queryParam = reactive<any>({}); const router = useRouter(); // 下载模态框相关状态 const showDownloadModal = ref(false); const currentRecord = ref<any>(null); const selectedAttachment = ref<string>(''); const attachmentFiles = ref<any[]>([]); const loading = ref(false); const generating = ref(false); //注册model const [registerModal, { openModal }] = useModal(); const [registerModal] = useModal(); //注册table数据 const { tableContext } = useListPage({ @@ -171,92 +135,26 @@ } /** * 加载附件数据 * 编辑事件 - 直接跳转到生成页面 */ const loadAttachmentFiles = async (contractId: string) => { loading.value = true; function handleEdit(record: Recordable) { try { const result = await defHttp.get({ url: '/contract/contract/queryContractFileByMainId', params: { id: contractId }, // 直接跳转到生成页面,传递当前页面的参数 router.push({ path: '/copywriting/generated/index', query: { semanticWord: record.word || '', ranking: record.ranking || '', id: record.id || '', contractId: record.contractId || '', title: record.title || '', // 补充传递title参数 }, }); console.log('附件文件数据:', result); if (result && Array.isArray(result)) { // 过滤出附件文件(根据fileType为'合同附件') const attachmentFileList = result .filter((file: any) => file.fileType === '合同附件') .map((file: any) => ({ id: file.id, name: file.appendixFile || '附件文件', url: file.appendixFile, })); attachmentFiles.value = attachmentFileList; // 如果有附件,默认选择第一个 if (attachmentFileList.length > 0) { selectedAttachment.value = attachmentFileList[0].id; } } } catch (error) { console.error('加载附件文件失败:', error); message.error('加载附件文件失败'); } finally { loading.value = false; } }; /** * 编辑事件 - 显示下载模态框 */ async function handleEdit(record: Recordable) { currentRecord.value = record; showDownloadModal.value = true; // 加载附件数据 if (record.contractId) { await loadAttachmentFiles(record.contractId); console.error('跳转失败:', error); message.error('跳转失败'); } } /** * 模态框取消事件 */ function handleModalCancel() { showDownloadModal.value = false; currentRecord.value = null; selectedAttachment.value = ''; attachmentFiles.value = []; } /** * 一键生成事件 */ const handleGenerate = async () => { if (!selectedAttachment.value) { message.warning('请先选择附件'); return; } // 获取选中的附件信息 const selectedFile = attachmentFiles.value.find((file) => file.id === selectedAttachment.value); // 跳转到生成页面,传递当前页面的参数和附件信息 router.push({ path: '/copywriting/generated/index', query: { semanticWord: currentRecord.value?.word || '', ranking: currentRecord.value?.ranking || '', id: currentRecord.value?.id || '', contractId: currentRecord.value?.contractId || '', selectedAttachmentId: selectedAttachment.value, selectedAttachmentName: selectedFile?.name || '', selectedAttachmentUrl: selectedFile?.url || '', }, }); }; /** * 详情 */ @@ -331,127 +229,4 @@ width: 100%; } .download-modal-content { padding: 16px 0; .info-row { display: flex; justify-content: space-between; margin-bottom: 20px; padding: 12px 16px; background-color: #f5f5f5; border-radius: 6px; .info-item { flex: 1; font-size: 14px; color: #333; } } .attachment-row { display: flex; align-items: center; margin-bottom: 20px; padding: 0 16px; .label { margin-right: 10px; font-weight: 500; color: #333; } .select { width: 300px; } } .button-row { display: flex; align-items: center; padding: 0 16px; .note { margin-left: 20px; font-size: 12px; color: #999; } } } .contract-file-select-modal { .contract-file-card { cursor: pointer; transition: all 0.3s ease; border: 1px solid #d9d9d9; &.selected { border-color: #1890ff; background-color: #f0f8ff; } &:hover { border-color: #40a9ff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .file-info { display: flex; align-items: center; padding: 8px 0; .file-details { flex: 1; .file-name { font-weight: 500; color: #262626; margin-bottom: 4px; font-size: 14px; } .file-meta { display: flex; gap: 16px; font-size: 12px; color: #8c8c8c; margin-bottom: 4px; .file-type { text-transform: uppercase; font-weight: 500; } .file-size { font-weight: 500; } .upload-time { color: #bfbfbf; } } .contract-info { display: flex; gap: 16px; font-size: 12px; color: #1890ff; .contract-type { font-weight: 500; } .status { font-weight: 500; } } } } } .empty-state { text-align: center; padding: 40px 0; } } </style>