liubp
2025-10-28 0c274645f5a044d4489e0dea69080b7aea64f15f
修改端口
Signed-off-by: liubp <1535785116@qq.com>
8个文件已修改
1个文件已添加
612 ■■■■ 已修改文件
.env.docker 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dockerfile 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dockerfile.cloud 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Tinymce/src/Editor.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/design/ant/index.less 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/design/ant/select.less 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/copywriting/CopywritingList.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/copywriting/generated/index.vue 168 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/semanticwordPD/SemanticWordList.vue 287 ●●●● 补丁 | 查看 | 原始文档 | 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>