gaoluyang
2025-12-12 e8eda23ee6fe0273619d826ba768ce975a0d8ccf
1.公司-添加商机管理页面
已添加1个文件
已修改4个文件
519 ■■■■ 文件已修改
src/api/salesManagement/opportunityManagement.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/components/formDia.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/opportunityManagement/fileList.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/opportunityManagement/index.vue 463 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/opportunityManagement.js
@@ -27,6 +27,14 @@
    data: data,
  });
}
// æ·»åŠ å•†æœº
export function addDescription(data) {
  return request({
    url: "/businessOpportunity/addDescription",
    method: "post",
    data: data,
  });
}
// åˆ é™¤å•†æœº
export function delOpportunity(ids) {
src/views/productionManagement/productionReporting/components/formDia.vue
@@ -42,6 +42,9 @@
                                v-model="form.schedulingUserId"
                                placeholder="选择人员"
                                style="width: 100%;"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
src/views/salesManagement/opportunityManagement/fileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh" stripe>
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="100" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
  <filePreview ref="filePreviewRef" />
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
const dialogVisible = ref(false)
const tableData = ref([])
const { proxy } = getCurrentInstance();
const filePreviewRef = ref()
const handleClose = () => {
  dialogVisible.value = false
}
const open = (list) => {
  dialogVisible.value = true
  tableData.value = list
}
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
defineExpose({
  open
})
</script>
<style></style>
src/views/salesManagement/opportunityManagement/index.vue
@@ -51,16 +51,16 @@
          </template>
        </el-table-column>
        <el-table-column label="省份" prop="province" show-overflow-tooltip width="120" />
        <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip width="230" />
        <el-table-column label="商机来源" prop="businessSource" show-overflow-tooltip width="150" />
        <el-table-column label="客户描述" prop="description" show-overflow-tooltip min-width="200" />
        <el-table-column label="录入人" prop="entryPerson" show-overflow-tooltip width="120" />
        <el-table-column label="更新日期" prop="updateTime" width="120">
        <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip />
        <el-table-column label="商机来源" prop="businessSource" show-overflow-tooltip />
        <!-- <el-table-column label="客户描述" prop="description" show-overflow-tooltip min-width="200" /> -->
        <el-table-column label="录入人" prop="entryPerson" show-overflow-tooltip />
        <el-table-column label="更新日期" prop="updateTime">
          <template #default="{ row }">
            {{ formatDate(row.updateTime) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" fixed="right" width="130" align="center">
        <el-table-column label="操作" fixed="right" width="220" align="center">
          <template #default="{ row }">
            <el-button
              link
@@ -74,9 +74,25 @@
              link
              type="primary"
              size="small"
              @click="handleAddOperation(row)"
            >
              æ·»åŠ æè¿°
            </el-button>
            <el-button
              link
              type="primary"
              size="small"
              @click="handleDetail(row)"
            >
              è¯¦æƒ…
            </el-button>
            <el-button
              link
              type="primary"
              size="small"
              @click="handleAttachment(row)"
            >
              é™„ä»¶
            </el-button>
          </template>
        </el-table-column>
@@ -96,7 +112,7 @@
    <!-- æ–°å¢ž/编辑对话框 -->
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新建商机' : operationType === 'edit' ? '编辑商机' : '商机详情'"
      :title="operationType === 'add' ? '新建商机' : operationType === 'edit' ? '编辑商机' : operationType === 'addOperation' ? '添加商机' : '商机详情'"
      width="600px"
      @close="closeDialog"
    >
@@ -119,7 +135,7 @@
        </el-form-item>
        
        <el-form-item label="省份" prop="province">
          <el-select v-model="form.province" filterable placeholder="请选择省份" style="width: 100%" :disabled="operationType === 'detail'">
          <el-select v-model="form.province" filterable placeholder="请选择省份" style="width: 100%" :disabled="operationType === 'detail' || operationType === 'addOperation'">
            <el-option
              v-for="item in provinceOptions"
              :key="item.value"
@@ -130,7 +146,7 @@
        </el-form-item>
        
        <el-form-item label="客户名称" prop="customerName">
          <el-select v-model="form.customerName" placeholder="请选择" clearable :disabled="operationType === 'detail'">
          <el-select v-model="form.customerName" placeholder="请选择" clearable :disabled="operationType === 'detail' || operationType === 'addOperation'">
            <el-option v-for="item in customerOption" :key="item.customerName" :label="item.customerName" :value="item.customerName">
              {{
                item.customerName + "——" + item.taxpayerIdentificationNumber
@@ -140,10 +156,10 @@
        </el-form-item>
        
        <el-form-item label="商机来源" prop="businessSource">
          <el-input v-model="form.businessSource" placeholder="请输入商机来源" :disabled="operationType === 'detail'" />
          <el-input v-model="form.businessSource" placeholder="请输入商机来源" :disabled="operationType === 'detail' || operationType === 'addOperation'" />
        </el-form-item>
        
        <el-form-item label="客户描述" prop="description">
        <el-form-item label="客户描述" prop="description" v-if="operationType !== 'detail'">
          <el-input
            v-model="form.description"
            type="textarea"
@@ -151,26 +167,80 @@
            placeholder="请输入客户描述"
            maxlength="500"
            show-word-limit
            :disabled="operationType === 'detail'"
          />
        </el-form-item>
        
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="录入人" prop="entryPerson">
              <el-select v-model="form.entryPerson" placeholder="请选择" clearable @change="changs" :disabled="operationType === 'detail'">
              <el-select v-model="form.entryPerson" placeholder="请选择" clearable @change="changs" :disabled="operationType === 'detail' || operationType === 'addOperation'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="录入日期" prop="entryDateStart">
              <el-date-picker style="width: 100%" v-model="form.entryDateStart" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable :disabled="operationType === 'detail'" />
             <el-form-item label="录入日期" prop="entryDate">
               <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                 type="date" placeholder="请选择" clearable :disabled="operationType === 'detail' || operationType === 'addOperation'" />
             </el-form-item>
           </el-col>
        </el-row>
        <!-- é™„件上传(非详情模式下显示) -->
        <el-row :gutter="30" v-if="operationType !== 'detail'">
          <el-col :span="24">
            <el-form-item label="附件材料:">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                :headers="upload.headers" :data="upload.data" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                :on-success="handleUploadSuccess" :on-remove="handleRemove">
                <el-button type="primary">上传</el-button>
                <template #tip>
                  <div class="el-upload__tip">
                    æ–‡ä»¶æ ¼å¼æ”¯æŒ
                    doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <!-- å˜æ›´è®°å½•时间线(仅在详情模式下显示) -->
      <div v-if="operationType === 'detail'" class="change-history-section">
        <el-divider content-position="left">变更记录</el-divider>
        <el-timeline>
          <el-timeline-item
            v-for="record in changeHistory"
            :key="record.id"
            :timestamp="record.timestamp"
            :type="record.type === 'current' ? 'primary' : record.type === 'update' ? 'success' : 'info'"
            :hollow="record.type === 'current'"
            placement="top"
          >
            <el-card shadow="hover" class="timeline-card">
              <template #header>
                <div class="card-header">
                  <span class="action-type">{{ record.action }}</span>
                  <span class="operator">操作人:{{ record.operator }}</span>
                </div>
              </template>
              <div class="change-content">
                <div class="status-change" v-if="record.status">
                  <span class="label">状态:</span>
                  <el-tag :type="record.type === 'current' ? 'primary' : 'info'" size="small">
                    {{ getStatusLabel(record.status) }}
                  </el-tag>
                </div>
                <div class="description-change" v-if="record.description">
                  <span class="label">客户描述:</span>
                  <span class="description-text">{{ record.description }}</span>
                </div>
              </div>
            </el-card>
          </el-timeline-item>
        </el-timeline>
      </div>
      
      <template #footer>
        <div class="dialog-footer">
@@ -181,6 +251,9 @@
        </div>
      </template>
    </el-dialog>
    <!-- é™„件列表对话框 -->
    <FileList ref="fileListRef" />
  </div>
</template>
@@ -191,14 +264,17 @@
import pagination from '@/components/PIMTable/Pagination.vue'
import useUserStore from '@/store/modules/user'
import dayjs from 'dayjs'
import { getToken } from '@/utils/auth'
import { 
  opportunityListPage, 
  addOpportunity, 
  updateOpportunity, 
  delOpportunity
  delOpportunity,
  addDescription
} from '@/api/salesManagement/opportunityManagement.js'
import { userListNoPage } from '@/api/system/user.js'
import { customerList } from '@/api/salesManagement/salesLedger.js'
import {customerList, getSalesLedgerWithProducts} from '@/api/salesManagement/salesLedger.js'
import FileList from './fileList.vue'
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
@@ -231,14 +307,39 @@
const formRef = ref()
const form = reactive({
  id: undefined,
  status: 'new',
  status: undefined,
  province: '',
  customerName: '',
  businessSource: '',
  description: '',
  entryPerson: userStore.nickName,
  entryDateStart: dayjs().format('YYYY-MM-DD')
  entryDate: dayjs().format('YYYY-MM-DD')
})
// å˜æ›´è®°å½•数据(模拟数据)
const changeHistory = ref([])
// æ–‡ä»¶åˆ—表
const fileList = ref([])
// FileList组件引用
const fileListRef = ref(null)
// ä¸Šä¼ é…ç½®
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ å‚æ•°
  data: { type: 9 }
})
// èŽ·å–çŠ¶æ€æ ‡ç­¾
const getStatusLabel = (statusValue) => {
  const status = statusOptions.find(item => item.value === statusValue)
  return status ? status.label : statusValue
}
// è¡¨å•验证规则
const rules = reactive({
@@ -251,66 +352,66 @@
  entryPerson: [
    { required: true, message: '请选择录入人', trigger: 'change' }
  ],
  entryDateStart: [
  entryDate: [
    { required: true, message: '请选择录入日期', trigger: 'change' }
  ]
})
// çŠ¶æ€é€‰é¡¹
const statusOptions = [
  { value: 'new', label: '新建' },
  { value: 'tracking', label: '项目跟踪' },
  { value: 'contract', label: '合同签约' },
  { value: 'delivery', label: '项目交付' },
  { value: 'acceptance', label: '项目验收' }
  { value: '新建', label: '新建' },
  { value: '项目跟踪', label: '项目跟踪' },
  { value: '合同签约', label: '合同签约' },
  { value: '项目交付', label: '项目交付' },
  { value: '项目验收', label: '项目验收' }
]
// çœä»½é€‰é¡¹ï¼ˆç¤ºä¾‹ï¼‰
const provinceOptions = [
  { value: 'beijing', label: '北京市' },
  { value: 'tianjin', label: '天津市' },
  { value: 'hebei', label: '河北省' },
  { value: 'shanxi', label: '山西省' },
  { value: 'neimenggu', label: '内蒙古自治区' },
  { value: 'liaoning', label: '辽宁省' },
  { value: 'jilin', label: '吉林省' },
  { value: 'heilongjiang', label: '黑龙江省' },
  { value: 'shanghai', label: '上海市' },
  { value: 'jiangsu', label: '江苏省' },
  { value: 'zhejiang', label: '浙江省' },
  { value: 'anhui', label: '安徽省' },
  { value: 'fujian', label: '福建省' },
  { value: 'jiangxi', label: '江西省' },
  { value: 'shandong', label: '山东省' },
  { value: 'henan', label: '河南省' },
  { value: 'hubei', label: '湖北省' },
  { value: 'hunan', label: '湖南省' },
  { value: 'guangdong', label: '广东省' },
  { value: 'guangxi', label: '广西壮族自治区' },
  { value: 'hainan', label: '海南省' },
  { value: 'chongqing', label: '重庆市' },
  { value: 'sichuan', label: '四川省' },
  { value: 'guizhou', label: '贵州省' },
  { value: 'yunnan', label: '云南省' },
  { value: 'xizang', label: '西藏自治区' },
  { value: 'shaanxi', label: '陕西省' },
  { value: 'gansu', label: '甘肃省' },
  { value: 'qinghai', label: '青海省' },
  { value: 'ningxia', label: '宁夏回族自治区' },
  { value: 'xinjiang', label: '新疆维吾尔自治区' },
  { value: 'taiwan', label: '台湾省' },
  { value: 'xianggang', label: '香港特别行政区' },
  { value: 'aomen', label: '澳门特别行政区' }
  { value: '北京市', label: '北京市' },
  { value: '天津市', label: '天津市' },
  { value: '河北省', label: '河北省' },
  { value: '山西省', label: '山西省' },
  { value: '内蒙古自治区', label: '内蒙古自治区' },
  { value: '辽宁省', label: '辽宁省' },
  { value: '吉林省', label: '吉林省' },
  { value: '黑龙江省', label: '黑龙江省' },
  { value: '上海市', label: '上海市' },
  { value: '江苏省', label: '江苏省' },
  { value: '浙江省', label: '浙江省' },
  { value: '安徽省', label: '安徽省' },
  { value: '福建省', label: '福建省' },
  { value: '江西省', label: '江西省' },
  { value: '山东省', label: '山东省' },
  { value: '河南省', label: '河南省' },
  { value: '湖北省', label: '湖北省' },
  { value: '湖南省', label: '湖南省' },
  { value: '广东省', label: '广东省' },
  { value: '广西壮族自治区', label: '广西壮族自治区' },
  { value: '海南省', label: '海南省' },
  { value: '重庆市', label: '重庆市' },
  { value: '四川省', label: '四川省' },
  { value: '贵州省', label: '贵州省' },
  { value: '云南省', label: '云南省' },
  { value: '西藏自治区', label: '西藏自治区' },
  { value: '陕西省', label: '陕西省' },
  { value: '甘肃省', label: '甘肃省' },
  { value: '青海省', label: '青海省' },
  { value: '宁夏回族自治区', label: '宁夏回族自治区' },
  { value: '新疆维吾尔自治区', label: '新疆维吾尔自治区' },
  { value: '台湾省', label: '台湾省' },
  { value: '香港特别行政区', label: '香港特别行政区' },
  { value: '澳门特别行政区', label: '澳门特别行政区' }
]
// èŽ·å–çŠ¶æ€æ ‡ç­¾ç±»åž‹
const getStatusTagType = (status) => {
  const typeMap = {
    'new': 'info',
    'tracking': 'primary',
    'contract': 'warning',
    'delivery': 'success',
    'acceptance': 'success'
    '新建': 'info',
    '项目跟踪': 'primary',
    '合同签约': 'warning',
    '项目交付': 'success',
    '项目验收': 'success'
  }
  return typeMap[status] || 'info'
}
@@ -318,11 +419,11 @@
// èŽ·å–çŠ¶æ€æ–‡æœ¬
const getStatusText = (status) => {
  const textMap = {
    'new': '新建',
    'tracking': '项目跟踪',
    'contract': '合同签约',
    'delivery': '项目交付',
    'acceptance': '项目验收'
    '新建': '新建',
    '项目跟踪': '项目跟踪',
    '合同签约': '合同签约',
    '项目交付': '项目交付',
    '项目验收': '项目验收'
  }
  return textMap[status] || '未知'
}
@@ -419,6 +520,28 @@
  dialogFormVisible.value = true
}
// æ·»åŠ æ“ä½œ
const handleAddOperation = async (row) => {
  operationType.value = 'addOperation'
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨å’Œå®¢æˆ·åˆ—è¡¨
  let userLists = await userListNoPage()
  userList.value = userLists.data
  customerList().then((res) => {
    customerOption.value = res
  })
  // ä½¿ç”¨å½“前行数据作为基础,但只能修改状态和客户描述
  Object.assign(form, row, {
    // ä¿ç•™åŽŸå§‹å•†æœºID,用于关联操作记录
    status: row.status,
    description: '', // æ¸…空客户描述,允许重新填写
    entryPerson: userStore.nickName, // è®¾ç½®å½•入人为当前账号
    entryDate: dayjs().format('YYYY-MM-DD') // è®¾ç½®å½•入时间为当天
  })
  dialogFormVisible.value = true
}
// æŸ¥çœ‹è¯¦æƒ…
const handleDetail = async (row) => {
  operationType.value = 'detail'
@@ -434,7 +557,32 @@
  Object.assign(form, row, {
    entryDateStart: row.updateTime || row.entryDateStart
  })
  // ç”Ÿæˆæ¨¡æ‹Ÿå˜æ›´è®°å½•
  generateChangeHistory(row)
  dialogFormVisible.value = true
}
// ç”Ÿæˆå˜æ›´è®°å½•
const generateChangeHistory = (row) => {
  // ä½¿ç”¨businessDescription数组数据生成变更记录
  const history = []
  if (row.businessDescription && Array.isArray(row.businessDescription)) {
    row.businessDescription.forEach((item, index) => {
      history.push({
        id: item.id || index,
        timestamp: item.entryDate || item.updateTime || item.createTime,
        operator: item.entryPerson || '系统',
        status: item.status,
        description: item.description,
        type: index === 0 ? 'current' : 'info',
        action: index === 0 ? '当前状态' : '历史记录'
      })
    })
  }
  changeHistory.value = history
}
// ç¼–辑商机
@@ -448,9 +596,10 @@
    customerOption.value = res
  })
  
  // ä½¿ç”¨updateTime作为录入时间反显
  // ä½¿ç”¨å½“前账号和当天日期作为默认值
  Object.assign(form, row, {
    entryDateStart: row.updateTime || row.entryDateStart
    entryPerson: userStore.nickName, // è®¾ç½®å½•入人为当前账号
    entryDate: dayjs().format('YYYY-MM-DD') // è®¾ç½®å½•入时间为当天
  })
  dialogFormVisible.value = true
}
@@ -464,11 +613,50 @@
const submitForm = () => {
  formRef.value.validate(valid => {
    if (valid) {
      const api = operationType.value === 'add' ? addOpportunity : updateOpportunity
      // æ”¶é›†é™„件文件的临时ID
      let tempFileIds = []
      if (fileList.value !== null && fileList.value.length > 0) {
        tempFileIds = fileList.value.map(item => item.tempId)
      }
      
      api(form).then(res => {
      let api
      let successMessage
      let submitData
      if (operationType.value === 'add') {
        api = addOpportunity
        successMessage = '新建成功'
        submitData = {
          ...form,
          tempFileIds: tempFileIds,
          type: 9  // å•†æœºç®¡ç†çš„类型标识
        }
      } else if (operationType.value === 'addOperation') {
        api = addDescription
        successMessage = '添加操作成功'
        // æ·»åŠ æ“ä½œæ—¶ä¼ é€’çŠ¶æ€ã€æè¿°ã€å½•å…¥äººã€å½•å…¥æ—¥æœŸã€é™„ä»¶å’Œå•†æœºID
        submitData = {
          status: form.status,
          description: form.description,
          entryPerson: form.entryPerson,
          entryDate: form.entryDate,
          tempFileIds: tempFileIds,
          type: 9,  // å•†æœºç®¡ç†çš„类型标识
          businessOpportunityId: form.id  // ä¼ é€’商机ID
        }
      } else {
        api = updateOpportunity
        successMessage = '修改成功'
        submitData = {
          ...form,
          tempFileIds: tempFileIds,
          type: 9  // å•†æœºç®¡ç†çš„类型标识
        }
      }
      api(submitData).then(res => {
        if (res.code === 200) {
          proxy.$modal.msgSuccess(operationType.value === 'add' ? '新建成功' : '修改成功')
          proxy.$modal.msgSuccess(successMessage)
          closeDialog()
          getList()
        } else {
@@ -515,13 +703,13 @@
const resetForm = () => {
  Object.assign(form, {
    id: undefined,
    status: 'new',
    status: '新建',
    province: '',
    customerName: '',
    businessSource: '',
    description: '',
    entryPerson: userStore.nickName,
    entryDateStart: dayjs().format('YYYY-MM-DD')
    entryDate: dayjs().format('YYYY-MM-DD')
  })
  
  if (formRef.value) {
@@ -533,6 +721,46 @@
const closeDialog = () => {
  dialogFormVisible.value = false
  resetForm()
}
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  // if (file.size > 1024 * 1024 * 10) {
  //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
  //   return false;
  // }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  // è¿™é‡Œå¯ä»¥æ·»åŠ åˆ é™¤æ–‡ä»¶çš„é€»è¾‘
  proxy.$modal.msgSuccess("删除成功");
}
// æŸ¥çœ‹é™„ä»¶
function handleAttachment(row) {
    fileListRef.value.open(row.businessCommonFiles)
}
onMounted(() => {
@@ -571,4 +799,85 @@
    }
  }
}
/* å˜æ›´è®°å½•时间线样式 */
.change-history-section {
  margin-top: 20px;
  .el-divider {
    margin: 20px 0;
  }
  .timeline-card {
    margin: 8px 0;
    border-radius: 8px;
    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 8px 0;
      .action-type {
        font-weight: 600;
        color: #333;
      }
      .operator {
        font-size: 12px;
        color: #666;
      }
    }
    .change-content {
      .status-change, .description-change {
        margin-bottom: 8px;
        .label {
          font-weight: 500;
          color: #666;
          margin-right: 8px;
        }
        .description-text {
          color: #333;
          line-height: 1.5;
        }
      }
    }
  }
  /* æ—¶é—´çº¿æ ·å¼ä¼˜åŒ– */
  :deep(.el-timeline) {
    padding-left: 0;
    .el-timeline-item {
      .el-timeline-item__node {
        background-color: #409eff;
        &.el-timeline-item__node--primary {
          background-color: #409eff;
        }
        &.el-timeline-item__node--success {
          background-color: #67c23a;
        }
        &.el-timeline-item__node--info {
          background-color: #909399;
        }
        &.el-timeline-item__node--hollow {
          background-color: transparent;
          border-color: #409eff;
        }
      }
      .el-timeline-item__timestamp {
        color: #666;
        font-size: 12px;
      }
    }
  }
}
</style>
vite.config.js
@@ -8,7 +8,7 @@
  const { VITE_APP_ENV } = env;
  const baseUrl =
    VITE_APP_ENV == "development"
      ? "http://114.132.189.42:7003" // å¼€å‘环境后端接口
      ? "http://192.168.1.147:7003" // å¼€å‘环境后端接口
      : "http://114.132.189.42:7003"; // ç”Ÿäº§çŽ¯å¢ƒåŽç«¯æŽ¥å£
  return {