liubp
2025-11-27 56b25369f561dcb0bdcf7af11d59c51d703a95f5
添加财务详情页面,修改编辑页面必填项
5个文件已修改
2个文件已添加
446 ■■■■■ 已修改文件
.env.development 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/ContractCW/ContractList.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/desc/detailCW/data.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/desc/detailCW/index.vue 371 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/desc/editDL/index.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/desc/editkehu/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/page/form/stephetong/Step3.vue 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.development
@@ -6,14 +6,14 @@
# 跨域代理,您可以配置多个 ,请注意,没有换行符
VITE_PROXY = [["/jeecgboot","http://192.168.31.222:8080/jeecg-boot"],["/upload","http://192.168.31.222:3300/upload"]]
# VITE_PROXY = [["/jeecgboot","http://8.145.61.64:8080/jeecg-boot"],["/upload","http://8.145.61.64:3300/upload"]]
# VITE_PROXY = [["/jeecgboot","http://192.168.31.222:8080/jeecg-boot"],["/upload","http://192.168.31.222:3300/upload"]]
VITE_PROXY = [["/jeecgboot","http://8.145.61.64:8080/jeecg-boot"],["/upload","http://8.145.61.64:3300/upload"]]
# VITE_PROXY = [["/jeecgboot","http://172.19.0.1:8080/jeecg-boot"],["/upload","http://localhost:3300/upload"]]
#后台接口全路径地址(必填)
VITE_GLOB_DOMAIN_URL=http://192.168.31.222:8080/jeecg-boot
# VITE_GLOB_DOMAIN_URL=http://8.145.61.64:8080/jeecg-boot
# VITE_GLOB_DOMAIN_URL=http://192.168.31.222:8080/jeecg-boot
VITE_GLOB_DOMAIN_URL=http://8.145.61.64:8080/jeecg-boot
# VITE_GLOB_DOMAIN_URL=http://172.19.0.1:8080/jeecg-boot
src/views/ContractCW/ContractList.vue
@@ -306,9 +306,20 @@
        tooltip: isReviewed ? '该合同已审核,无需重复审核' : undefined,
        // auth: 'contract:contract:audit' // 暂时移除权限控制测试
      },
      {
        label: '详情',
        onClick: handleDetail.bind(null, record),
      },
    ];
  }
  function handleDetail(record: Recordable) {
    router.push({
      path: '/page-demo/desc/detailCW',
      query: { id: record.id },
    });
  }
  // /**
  //  * 下拉操作栏
  //  */
src/views/demo/page/desc/detailCW/data.tsx
New file
@@ -0,0 +1,7 @@
// 语义词详情页数据
export const keywordData = {
  keyword: '示例语义词',
  rank: '1',
  cooperationPeriod: '2024-01-01 至 2024-12-31',
  status: '进行中',
};
src/views/demo/page/desc/detailCW/index.vue
New file
@@ -0,0 +1,371 @@
<template>
  <PageWrapper title="财务审核详情页" contentBackground>
    <a-card title="基本信息">
      <a-descriptions title="" :column="3">
        <a-descriptions-item label="企业名称">
          <span>{{ formData.customer?.enterpriseName || '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="行业">
          <span>{{ formData.customer?.industry || '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="客户地址">
          <span>{{ formData.customer?.customerAddress || '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="对接人">
          <span>{{ formData.customer?.contactPerson || '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="客户电话">
          <span>{{ formData.customer?.customerPhone || '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="客户邮箱">
          <span>{{ formData.customer?.customerEmail || '-' }}</span>
        </a-descriptions-item>
      </a-descriptions>
    </a-card>
    <a-card title="合同信息">
      <a-descriptions title="" :column="1">
        <a-descriptions-item label="紧急状态">
          <span>{{ formData.emergencyStatus === 1 ? '紧急' : formData.emergencyStatus === 3 ? '正常' : '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="合作周期">
          <span>{{ formData.month ? `${formData.month}月` : '-' }}</span>
        </a-descriptions-item>
        <!-- <a-descriptions-item label="合作开始时间">
          <span>{{ formData.startDate || '-' }}</span>
        </a-descriptions-item>
        <a-descriptions-item label="合作结束时间">
          <span>{{ formData.endDate || '-' }}</span>
        </a-descriptions-item> -->
        <a-descriptions-item label="合同">
          <div v-if="contractFiles.length > 0">
            <div v-for="file in contractFiles" :key="file.uid">
              <a @click="handleDownload(file)" style="margin-top: 4px; display: inline-block">{{ getFileName(file) }}</a>
            </div>
          </div>
          <div v-else>
            <span>-</span>
          </div>
        </a-descriptions-item>
        <a-descriptions-item label="附件">
          <div v-if="attachmentFiles.length > 0">
            <div v-for="file in attachmentFiles" :key="file.uid">
              <a @click="handleDownload(file)" style="margin-top: 4px; display: inline-block">{{ getFileName(file) }}</a>
            </div>
          </div>
          <div v-else>
            <span>-</span>
          </div>
        </a-descriptions-item>
      </a-descriptions>
    </a-card>
    <a-card title="语义词信息">
      <div v-if="semanticWords.length > 0">
        <a-descriptions title="" :column="1">
          <a-descriptions-item v-for="word in semanticWords" :key="word.id" :label="`语义词 ${semanticWords.indexOf(word) + 1}`">
            <div style="display: flex; flex-direction: column; gap: 12px; width: 100%">
              <div style="display: flex; gap: 16px; width: 100%">
                <span style="flex: 1;"><strong>类型:</strong>{{ word.category === '1' ? '品牌' : word.category === '2' ? '地区词' : '全国词' }}</span>
                <span style="flex: 1;"><strong>语义词:</strong>{{ word.word || '-' }}</span>
                <span style="flex: 1;"><strong>露出词:</strong>{{ word.outWord || '-' }}</span>
                <span style="flex: 1;"><strong>价格:</strong>{{ word.price ? `${formatPrice(word.price)}元` : '-' }}</span>
                <span style="flex: 1;"><strong>合作周期:</strong>{{ word.month ? `${word.month}月` : '-' }}</span>
                <span style="flex: 1;"><strong>签约排名:</strong>{{ formatRanking(word.ranking) }}</span>
                <span style="flex: 1;"><strong>验收指标(>=):</strong>{{ word.acceptindicator ? `${word.acceptindicator}%` : '-' }}</span>
              </div>
            </div>
          </a-descriptions-item>
        </a-descriptions>
      </div>
      <div v-else>
        <a-empty description="暂无语义词数据" />
      </div>
    </a-card>
    <!-- 返回按钮 -->
    <div class="action-buttons">
      <a-button type="default" @click="handleBack" class="back-btn">返回</a-button>
    </div>
  </PageWrapper>
</template>
<script lang="ts">
  import { defineComponent, reactive, ref, onMounted } from 'vue';
  import { PageWrapper } from '/@/components/Page';
  import { message } from 'ant-design-vue';
  import { useRoute, useRouter } from 'vue-router';
  import { defHttp } from '/@/utils/http/axios';
  import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
  export default defineComponent({
    name: 'DescDetailKehu',
    components: { PageWrapper },
    setup() {
      const route = useRoute();
      const router = useRouter();
      // 初始数据
      const initialData = {
        id: '',
        createTime: '',
        updateBy: null,
        updateTime: null,
        agentsId: null,
        agentsName: null,
        contractFileList: null,
        contractName: '',
        createBy: '',
        customer: {
          id: '',
          createBy: '',
          createTime: '',
          updateBy: null,
          updateTime: null,
          agentsName: null,
          contactPerson: '',
          contract: null,
          customerAddress: '',
          customerEmail: '',
          customerPhone: '',
          enterpriseName: '',
          industry: '',
          semanticWordList: null,
        },
        customerName: '',
        endDate: '',
        rejectionReasons: null,
        reviewStatus: '',
        semanticWordList: null,
        startDate: '',
        sysOrgCode: '',
        emergencyStatus: 3, // 默认正常状态
        month: null, // 合作周期(月)
      };
      // 响应式表单数据
      const formData = reactive({ ...initialData });
      // 语义词列表
      const semanticWords = ref<any[]>([]);
      // 合同文件列表
      const contractFiles = ref<any[]>([]);
      const attachmentFiles = ref<any[]>([]);
      // 加载客户数据
      const loadCustomerData = async () => {
        const id = route.query.id as string;
        if (id) {
          try {
            // 调用API根据id获取客户数据
            const result = await defHttp.get({
              url: '/contract/contract/queryById',
              params: { id },
            });
            console.log('客户详情数据:', result);
            if (result) {
              const customerData = result;
              // 填充表单数据
              Object.assign(formData, customerData);
              // 确保 emergencyStatus 正确设置(1=紧急,3=正常)
              const emergencyStatus = Number(customerData.emergencyStatus);
              if (emergencyStatus === 1 || emergencyStatus === 3) {
                formData.emergencyStatus = emergencyStatus;
              } else {
                formData.emergencyStatus = 3; // 默认正常状态
              }
              // 加载合同文件数据
              await loadContractFiles(id);
              // 加载语义词数据
              await loadSemanticWords(id);
            } else {
              message.error('加载客户详情数据失败');
            }
          } catch (error) {
            console.error('加载客户详情数据失败:', error);
            message.error('加载客户详情数据失败');
          }
        } else {
          message.error('缺少客户ID参数');
        }
      };
      // 加载合同文件数据
      const loadContractFiles = async (id: string) => {
        try {
          const result = await defHttp.get({
            url: '/contract/contract/queryContractFileByMainId',
            params: { id },
          });
          console.log('合同文件数据:', result);
          // 这里可以根据返回的数据结构处理合同文件
          if (result && Array.isArray(result)) {
            // 根据 fileType 区分合同文件和附件
            const contractFileList: any[] = [];
            const attachmentFileList: any[] = [];
            result.forEach((file: any) => {
              const fileItem = {
                uid: file.id,
                name: file.appendixFile || '文件',
                status: 'done',
                url: file.appendixFile,
              };
              if (file.fileType === '合同文件') {
                contractFileList.push(fileItem);
              } else if (file.fileType === '合同附件') {
                attachmentFileList.push(fileItem);
              }
            });
            contractFiles.value = contractFileList;
            attachmentFiles.value = attachmentFileList;
          }
        } catch (error) {
          console.error('加载合同文件失败:', error);
        }
      };
      // 加载语义词数据
      const loadSemanticWords = async (id: string) => {
        try {
          const result = await defHttp.get({
            url: '/contract/contract/querySemanticWordByMainId',
            params: { id },
          });
          console.log('语义词数据:', result);
          // 这里可以根据返回的数据结构处理语义词
          if (result && Array.isArray(result)) {
            // 将语义词列表保存到响应式变量中
            semanticWords.value = result;
          }
        } catch (error) {
          console.error('加载语义词失败:', error);
        }
      };
      onMounted(() => {
        loadCustomerData();
      });
      // 工具方法:获取文件名(去掉 temp/)
      const getFileName = (file: any) => {
        const name = file.name || '';
        if (name.startsWith('temp/')) {
          return name.substring(5);
        }
        return name;
      };
      // 文件下载功能
      const handleDownload = (file: any) => {
        if (!file.url) {
          message.error('文件链接不存在');
          return;
        }
        try {
          // 创建一个隐藏的a标签进行下载
          const link = document.createElement('a');
          link.href = getFileAccessHttpUrl(file.url);
          link.download = getFileName(file);
          link.target = '_blank';
          link.style.display = 'none';
          // 添加到DOM中
          document.body.appendChild(link);
          // 触发点击事件
          link.click();
          // 清理DOM
          document.body.removeChild(link);
        } catch (error) {
          console.error('文件下载失败:', error);
          message.error('文件下载失败');
        }
      };
      // 返回功能
      const handleBack = () => {
        router.back();
      };
      // 格式化价格
      const formatPrice = (price: number) => {
        if (!price) return '-';
        return price.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
      };
      // 格式化签约排名
      const formatRanking = (ranking: any) => {
        if (ranking === 1 || ranking === 2 || ranking === 3) {
          return ranking.toString();
        } else if (ranking === '保展现不保排名') {
          return '保展现不保排名';
        }
        return '-';
      };
      return {
        formData,
        contractFiles,
        attachmentFiles,
        semanticWords,
        handleDownload,
        handleBack,
        getFileName,
        formatPrice,
        formatRanking,
      };
    },
  });
</script>
<style lang="less" scoped>
  .desc-wrap {
    padding: 16px;
    background-color: @component-background;
  }
  .action-buttons {
    position: fixed;
    right: 24px;
    bottom: 24px;
    display: flex;
    gap: 12px;
    z-index: 1000;
    .back-btn {
      min-width: 80px;
      height: 40px;
      border-radius: 6px;
      font-weight: 500;
      background-color: #f5f5f5;
      border-color: #d9d9d9;
      color: #666;
      &:hover {
        background-color: #e6f7ff;
        border-color: #40a9ff;
        color: #40a9ff;
      }
    }
  }
  // 文件下载链接样式
  a {
    color: #1890ff;
    text-decoration: none;
    cursor: pointer;
    transition: color 0.3s;
    &:hover {
      color: #40a9ff;
      text-decoration: underline;
    }
    &:active {
      color: #096dd9;
    }
  }
</style>
src/views/demo/page/desc/editDL/index.vue
@@ -431,14 +431,7 @@
      const handleSave = async () => {
        try {
          // 基本信息必填校验
          const baseRequiredFields = [
            { value: formData.agentsName, label: '代理商名称' },
            { value: formData.customer?.industry, label: '行业' },
            { value: formData.customer?.customerAddress, label: '客户地址' },
            { value: formData.customer?.contactPerson, label: '对接人' },
            { value: formData.customer?.customerPhone, label: '客户电话' },
            { value: formData.customer?.customerEmail, label: '客户邮箱' },
          ];
          const baseRequiredFields = [{ value: formData.agentsName, label: '代理商名称' }];
          const missingBaseFields = baseRequiredFields
            .filter((item) => !item.value || String(item.value).trim() === '')
            .map((item) => item.label);
src/views/demo/page/desc/editkehu/index.vue
@@ -433,9 +433,9 @@
          // 基本信息必填校验
          const baseRequiredFields = [
            { value: formData.customer?.enterpriseName, label: '企业名称' },
            { value: formData.customer?.industry, label: '行业' },
            { value: formData.customer?.customerAddress, label: '客户地址' },
            { value: formData.customer?.contactPerson, label: '对接人' },
            // { value: formData.customer?.industry, label: '行业' },
            // { value: formData.customer?.customerAddress, label: '客户地址' },
            // { value: formData.customer?.contactPerson, label: '对接人' },
            { value: formData.customer?.customerPhone, label: '客户电话' },
            { value: formData.customer?.customerEmail, label: '客户邮箱' },
          ];
src/views/demo/page/form/stephetong/Step3.vue
@@ -28,6 +28,7 @@
  import { message } from 'ant-design-vue';
  import { queryUserRole } from '/@/api/sys/user';
  import { useUserStore } from '/@/store/modules/user';
  import { split as splitUploadValue } from '/@/utils/index';
  export default defineComponent({
    components: {
@@ -101,6 +102,29 @@
        const raw = typeof value === 'string' ? value.replace(/%/g, '').trim() : value;
        const num = Number(raw);
        return Number.isFinite(num) ? num : undefined;
      };
      const normalizeUploadValue = (value: unknown): string[] => {
        if (!value) {
          return [];
        }
        if (Array.isArray(value)) {
          return value.map((item) => (typeof item === 'string' ? item.trim() : '')).filter(Boolean);
        }
        if (typeof value === 'string') {
          const parsed = splitUploadValue(value);
          const list = Array.isArray(parsed) ? parsed : [parsed];
          return list.map((item) => (typeof item === 'string' ? item.trim() : '')).filter(Boolean);
        }
        return [];
      };
      const buildContractFiles = (rawValue: unknown, fileType: string) => {
        return normalizeUploadValue(rawValue).map((appendixFile) => ({
          contractId: '',
          appendixFile,
          fileType,
        }));
      };
      async function customSubmitFunc() {
@@ -190,6 +214,11 @@
          }
          // 7. 构建最终提交参数
          const contractFileList = [
            ...buildContractFiles(props.step2Data.uploadFile1, '合同文件'),
            ...buildContractFiles(props.step2Data.uploadFile2, '合同附件'),
          ];
          const submitData: ImportCustomerContractWordParams = {
            enterpriseName: props.enterpriseName,
            industry: props.step1Data.industry || '',
@@ -212,10 +241,7 @@
              ...(isAgent
                ? { agentsName: props.enterpriseName }
                : { customerName: props.enterpriseName }),
              contractFileList: [
                { contractId: '', appendixFile: props.step2Data.uploadFile1 || '', fileType: '合同文件' },
                { contractId: '', appendixFile: props.step2Data.uploadFile2 || '', fileType: '合同附件' },
              ],
              contractFileList,
              // 如果是代理商,设置审核状态为8
              ...(reviewStatus !== undefined && { reviewStatus }),
            },