Browse Source

feat(线索管理): 添加线索详细信息管理功能

yz 1 month ago
parent
commit
bf905aec55
2 changed files with 604 additions and 0 deletions
  1. 136 0
      src/api/order/lead-detail.js
  2. 468 0
      src/views/order/lead/index.vue

+ 136 - 0
src/api/order/lead-detail.js

@@ -0,0 +1,136 @@
+import request from '@/router/axios'
+
+/**
+ * 线索详细信息记录类型定义
+ * @typedef {Object} LeadDetailRecord
+ * @property {string} id - 详细信息ID
+ * @property {string} createUser - 创建用户ID
+ * @property {string} createDept - 创建部门ID
+ * @property {string} createTime - 创建时间
+ * @property {string} updateUser - 更新用户ID
+ * @property {string} updateTime - 更新时间
+ * @property {number} status - 状态
+ * @property {number} isDeleted - 是否删除 0-未删除 1-已删除
+ * @property {string|number} leadId - 线索ID
+ * @property {string} detailText - 详细信息内容
+ */
+
+/**
+ * 线索详细信息查询参数类型定义
+ * @typedef {Object} LeadDetailQueryParams
+ * @property {string|number} leadId - 线索ID
+ * @property {number} [size] - 每页大小
+ * @property {number} [current] - 当前页码
+ */
+
+/**
+ * 线索详细信息新增参数类型定义
+ * @typedef {Object} LeadDetailAddParams
+ * @property {string|number} leadId - 线索ID
+ * @property {string} detailText - 详细信息内容
+ */
+
+/**
+ * 线索详细信息更新参数类型定义
+ * @typedef {Object} LeadDetailUpdateParams
+ * @property {string|number} id - 详细信息ID
+ * @property {string|number} leadId - 线索ID
+ * @property {string} detailText - 详细信息内容
+ */
+
+/**
+ * API响应类型定义
+ * @typedef {Object} ApiResponse
+ * @property {number} code - 响应码
+ * @property {boolean} success - 是否成功
+ * @property {string} msg - 响应消息
+ * @property {any} data - 响应数据
+ */
+
+/**
+ * 分页结果类型定义
+ * @typedef {Object} PageResult
+ * @property {LeadDetailRecord[]} records - 记录列表
+ * @property {number} total - 总记录数
+ * @property {number} size - 每页大小
+ * @property {number} current - 当前页码
+ * @property {number} pages - 总页数
+ * @property {any[]} orders - 排序
+ * @property {boolean} optimizeCountSql - 是否优化计数SQL
+ * @property {boolean} hitCount - 是否命中计数
+ * @property {any} countId - 计数ID
+ * @property {any} maxLimit - 最大限制
+ * @property {boolean} searchCount - 是否搜索计数
+ */
+
+/**
+ * 获取线索详细信息列表
+ * @param {number} current - 当前页码
+ * @param {number} size - 每页大小
+ * @param {LeadDetailQueryParams} params - 查询参数
+ * @returns {Promise<ApiResponse<PageResult>>} 分页查询结果
+ */
+export const getList = (current, size, params) => {
+  return request({
+    url: '/api/blade-factory/api/factory/lead-detail',
+    method: 'get',
+    params: {
+      ...params,
+      current,
+      size
+    }
+  })
+}
+
+/**
+ * 获取线索详细信息详情
+ * @param {string|number} detailId - 详细信息ID
+ * @returns {Promise<ApiResponse<LeadDetailRecord>>} 详细信息详情
+ */
+export const getDetail = (detailId) => {
+  return request({
+    url: `/api/blade-factory/api/factory/lead-detail/${detailId}`,
+    method: 'get'
+  })
+}
+
+/**
+ * 新增线索详细信息
+ * @param {LeadDetailAddParams} params - 新增参数
+ * @returns {Promise<ApiResponse<boolean>>} 操作结果
+ */
+export const add = (params) => {
+  return request({
+    url: '/api/blade-factory/api/factory/lead-detail',
+    method: 'post',
+    data: params
+  })
+}
+
+/**
+ * 修改线索详细信息
+ * @param {LeadDetailUpdateParams} params - 修改参数
+ * @returns {Promise<ApiResponse<boolean>>} 操作结果
+ */
+export const update = (params) => {
+  return request({
+    url: '/api/blade-factory/api/factory/lead-detail',
+    method: 'put',
+    data: params
+  })
+}
+
+/**
+ * 删除线索详细信息
+ * @param {string} ids - 要删除的ID列表,多个ID用逗号分隔
+ * @returns {Promise<ApiResponse<boolean>>} 操作结果
+ */
+export const remove = (ids) => {
+  return request({
+    url: '/api/blade-factory/api/factory/lead-detail/remove',
+    method: 'post',
+    params: {
+      ids
+    }
+  })
+}

+ 468 - 0
src/views/order/lead/index.vue

@@ -12,6 +12,24 @@
                 </el-button>
             </template>
 
+            <template slot="menu" slot-scope="{row}">
+                <el-button type="text" size="small" icon="el-icon-view" @click="handleView(row)"
+                    v-if="permission.order_lead_view">
+                    查看
+                </el-button>
+                <el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)"
+                    v-if="permission.order_lead_edit">
+                    编辑
+                </el-button>
+                <el-button type="text" size="small" icon="el-icon-document" @click="handleViewDetail(row)">
+                    详细信息
+                </el-button>
+                <el-button type="text" size="small" icon="el-icon-delete" @click="handleRowDelete(row)"
+                    v-if="permission.order_lead_delete">
+                    删除
+                </el-button>
+            </template>
+
             <template slot-scope="{row}" slot="status">
                 <el-tag :type="getStatusType(row.status)">
                     {{ getStatusText(row.status) }}
@@ -30,11 +48,55 @@
                 </span>
             </template>
         </avue-crud>
+
+        <!-- 线索详细信息弹窗 -->
+        <el-dialog
+            title="线索详细信息管理"
+            :visible.sync="detailDialogVisible"
+            width="80%"
+            :close-on-click-modal="false"
+            :destroy-on-close="true"
+            append-to-body>
+            <div v-if="currentLead">
+                <el-descriptions :column="3" border class="lead-info-desc">
+                    <el-descriptions-item label="线索编码">{{ currentLead.leadCode }}</el-descriptions-item>
+                    <el-descriptions-item label="客户名称">{{ currentLead.customerName }}</el-descriptions-item>
+                    <el-descriptions-item label="联系人">{{ currentLead.contactName }}</el-descriptions-item>
+                    <el-descriptions-item label="线索标题" :span="3">{{ currentLead.title }}</el-descriptions-item>
+                </el-descriptions>
+            </div>
+            
+            <avue-crud 
+                :option="detailOption" 
+                :data="detailData" 
+                ref="detailCrud" 
+                v-model="detailForm" 
+                :page.sync="detailPage"
+                :permission="detailPermissionList" 
+                :before-open="detailBeforeOpen" 
+                :table-loading="detailLoading" 
+                @row-del="detailRowDel"
+                @row-update="detailRowUpdate" 
+                @row-save="detailRowSave" 
+                @selection-change="detailSelectionChange" 
+                @current-change="detailCurrentChange" 
+                @size-change="detailSizeChange"
+                @refresh-change="detailRefreshChange" 
+                @on-load="detailOnLoad">
+                <template slot="menuLeft">
+                    <el-button type="danger" size="small" plain icon="el-icon-delete" 
+                        @click="handleDetailDelete" :disabled="detailSelectionList.length === 0">
+                        删除
+                    </el-button>
+                </template>
+            </avue-crud>
+        </el-dialog>
     </basic-container>
 </template>
 
 <script>
 import { getList, add, update, remove, getDetail } from '@/api/order/lead'
+import { getList as getDetailList, add as addDetail, update as updateDetail, remove as removeDetail, getDetail as getDetailDetail } from '@/api/order/lead-detail'
 import { getCustomerList } from '@/api/common/index'
 import { mapGetters } from 'vuex'
 
@@ -84,6 +146,118 @@ export default {
     data() {
         return {
             /**
+             * 详细信息表格配置
+             */
+            detailOption: {
+                height: 'auto',
+                calcHeight: 30,
+                tip: false,
+                searchShow: false,
+                border: true,
+                index: true,
+                viewBtn: true,
+                selection: true,
+                dialogClickModal: false,
+                column: [
+                    {
+                        label: '详细信息内容',
+                        prop: 'detailText',
+                        type: 'textarea',
+                        span: 24,
+                        minRows: 4,
+                        maxRows: 8,
+                        rules: [{
+                            required: true,
+                            message: '请输入详细信息内容',
+                            trigger: 'blur'
+                        }, {
+                            min: 10,
+                            message: '详细信息内容至少10个字符',
+                            trigger: 'blur'
+                        }],
+                        overHidden: true,
+                        width: 400
+                    },
+                    {
+                        label: '创建时间',
+                        prop: 'createTime',
+                        type: 'datetime',
+                        format: 'yyyy-MM-dd HH:mm:ss',
+                        valueFormat: 'yyyy-MM-dd HH:mm:ss',
+                        addDisplay: false,
+                        editDisplay: false,
+                        viewDisplay: true,
+                        width: 180
+                    },
+                    {
+                        label: '更新时间',
+                        prop: 'updateTime',
+                        type: 'datetime',
+                        format: 'yyyy-MM-dd HH:mm:ss',
+                        valueFormat: 'yyyy-MM-dd HH:mm:ss',
+                        addDisplay: false,
+                        editDisplay: false,
+                        viewDisplay: true,
+                        width: 180
+                    }
+                ]
+            },
+            
+            /**
+             * 详细信息权限列表
+             */
+            detailPermissionList: {
+                addBtn: true,
+                viewBtn: true,
+                delBtn: false,
+                editBtn: true,
+            },
+
+            /**
+             * 详细信息分页信息
+             * @type {{total: number, currentPage: number, pageSize: number}}
+             */
+            detailPage: {
+                total: 0,
+                currentPage: 1,
+                pageSize: 10
+            },
+            
+            /**
+             * 详细信息加载状态
+             * @type {boolean}
+             */
+            detailLoading: false,
+            /**
+             * 详细信息弹窗显示状态
+             * @type {boolean}
+             */
+            detailDialogVisible: false,
+            
+            /**
+             * 当前选中的线索
+             * @type {LeadRecord|null}
+             */
+            currentLead: null,
+            
+            /**
+             * 详细信息表格数据
+             * @type {LeadDetailRecord[]}
+             */
+            detailData: [],
+            
+            /**
+             * 详细信息表单数据
+             * @type {LeadDetailRecord}
+             */
+            detailForm: {},
+            
+            /**
+             * 详细信息选中的数据列表
+             * @type {LeadDetailRecord[]}
+             */
+            detailSelectionList: [],
+            /**
              * 表格数据
              * @type {LeadRecord[]}
              */
@@ -389,6 +563,17 @@ export default {
                 ids.push(ele.id)
             })
             return ids.join(',')
+        },
+        /**
+         * 详细信息选中的ID列表
+         * @returns {string} 逗号分隔的ID字符串
+         */
+        detailIds() {
+            const ids = []
+            this.detailSelectionList.forEach(ele => {
+                ids.push(ele.id)
+            })
+            return ids.join(',')
         }
     },
     methods: {
@@ -779,6 +964,285 @@ export default {
          */
         refreshChange() {
             this.onLoad(this.page, this.query)
+        },
+
+        /**
+         * 查看线索详细信息
+         * @param {LeadRecord} row - 行数据
+         * @returns {Promise<void>}
+         */
+        async handleViewDetail(row) {
+            this.currentLead = row
+            this.detailDialogVisible = true
+            this.detailPage.currentPage = 1
+            await this.detailOnLoad(this.detailPage)
+        },
+        
+        /**
+         * 查看详情
+         * @param {LeadRecord} row - 行数据
+         * @returns {void}
+         */
+        handleView(row) {
+            this.form = { ...row }
+            this.$refs.crud.rowView(row)
+        },
+        
+        /**
+         * 编辑
+         * @param {LeadRecord} row - 行数据
+         * @returns {void}
+         */
+        handleEdit(row) {
+            this.form = { ...row }
+            this.$refs.crud.rowEdit(row)
+        },
+        
+        /**
+         * 删除单行
+         * @param {LeadRecord} row - 行数据
+         * @returns {Promise<void>}
+         */
+        async handleRowDelete(row) {
+            try {
+                await this.$confirm(`确定删除线索 "${row.title}" 吗?`, '提示', {
+                    confirmButtonText: '确定',
+                    cancelButtonText: '取消',
+                    type: 'warning'
+                })
+        
+                const res = await remove(row.id)
+                if (res.data && res.data.success) {
+                    this.$message.success('删除成功')
+                    this.onLoad(this.page)
+                } else {
+                    this.$message.error(res.data ? res.data.msg : '删除失败')
+
+                }
+            } catch (error) {
+                if (error !== 'cancel') {
+                    console.error('删除失败:', error)
+                    this.$message.error('删除失败,请稍后重试')
+                }
+            }
+        },
+        // ========== 详细信息相关方法 ==========
+        
+        /**
+         * 详细信息数据加载
+         * @param {Object} page - 分页参数
+         * @param {Object} params - 查询参数
+         * @returns {Promise<void>}
+         */
+        async detailOnLoad(page, params = {}) {
+            if (!this.currentLead) return
+            
+            this.detailLoading = true
+            try {
+                const queryParams = {
+                    ...params,
+                    leadId: this.currentLead.id
+                }
+        
+                const res = await getDetailList(page.currentPage, page.pageSize, queryParams)
+                if (res.data && res.data.success) {
+                    const data = res.data.data
+                    this.detailData = data.records || []
+                    this.detailPage.total = data.total || 0
+                } else {
+                    this.detailData = []
+                    this.detailPage.total = 0
+                    this.$message.error(res.data ? res.data.msg : '获取详细信息失败')
+
+                }
+            } catch (error) {
+                this.detailData = []
+                this.detailPage.total = 0
+                console.error('获取详细信息失败:', error)
+                this.$message.error('获取详细信息失败,请稍后重试')
+            } finally {
+                this.detailLoading = false
+            }
+        },
+        
+        /**
+         * 详细信息新增前的回调
+         * @param {Function} done - 完成回调
+         * @param {string} type - 操作类型
+         * @returns {Promise<void>}
+         */
+        async detailBeforeOpen(done, type) {
+            if (['edit', 'view'].includes(type)) {
+                try {
+                    const res = await getDetailDetail(this.detailForm.id)
+                    if (res.data && res.data.success) {
+                        this.detailForm = res.data.data
+                    } else {
+                        this.$message.error('获取详情失败')
+                        return
+                    }
+                } catch (error) {
+                    console.error('获取详情失败:', error)
+                    this.$message.error('获取详情失败')
+                    return
+                }
+            } else if (type === 'add') {
+                // 新增时设置线索ID
+                this.detailForm.leadId = this.currentLead.id
+            }
+            done()
+        },
+        
+        /**
+         * 详细信息新增
+         * @param {LeadDetailRecord} row - 表单数据
+         * @param {Function} done - 完成回调
+         * @param {Function} loading - 加载状态回调
+         * @returns {Promise<void>}
+         */
+        async detailRowSave(row, done, loading) {
+            try {
+                loading()
+                const params = {
+                    leadId: this.currentLead.id,
+                    detailText: row.detailText
+                }
+                const res = await addDetail(params)
+                if (res.data && res.data.success) {
+                    this.$message.success('添加成功')
+                    this.detailOnLoad(this.detailPage)
+                    done()
+                } else {
+                    this.$message.error(res.data ? res.data.msg : '添加失败')
+                    loading()
+                }
+            } catch (error) {
+                console.error('添加失败:', error)
+                this.$message.error('添加失败,请稍后重试')
+                loading()
+            }
+        },
+        
+        /**
+         * 详细信息修改
+         * @param {LeadDetailRecord} row - 表单数据
+         * @param {number} index - 行索引
+         * @param {Function} done - 完成回调
+         * @param {Function} loading - 加载状态回调
+         * @returns {Promise<void>}
+         */
+        async detailRowUpdate(row, index, done, loading) {
+            try {
+                loading()
+                const params = {
+                    id: row.id,
+                    leadId: this.currentLead.id,
+                    detailText: row.detailText
+                }
+                const res = await updateDetail(params)
+                if (res.data && res.data.success) {
+                    this.$message.success('修改成功')
+                    this.detailOnLoad(this.detailPage)
+                    done()
+                } else {
+                    this.$message.error(res.data ? res.data.msg : '修改失败')
+                    loading()
+                }
+            } catch (error) {
+                console.error('修改失败:', error)
+                this.$message.error('修改失败,请稍后重试')
+                loading()
+            }
+        },
+        
+        /**
+         * 详细信息删除
+         * @param {LeadDetailRecord} row - 行数据
+         * @param {number} index - 行索引
+         * @returns {Promise<void>}
+         */
+        async detailRowDel(row, index) {
+            try {
+                const res = await removeDetail(row.id)
+                if (res.data && res.data.success) {
+                    this.$message.success('删除成功')
+                    this.detailOnLoad(this.detailPage)
+                } else {
+                    this.$message.error(res.data ? res.data.msg : '删除失败')
+                }
+            } catch (error) {
+                console.error('删除失败:', error)
+                this.$message.error('删除失败,请稍后重试')
+            }
+        },
+        
+        /**
+         * 详细信息选择变更
+         * @param {LeadDetailRecord[]} list - 选中的数据列表
+         * @returns {void}
+         */
+        detailSelectionChange(list) {
+            this.detailSelectionList = list
+        },
+        
+        /**
+         * 详细信息当前页变更
+         * @param {number} currentPage - 当前页码
+         * @returns {void}
+         */
+        detailCurrentChange(currentPage) {
+            this.detailPage.currentPage = currentPage
+        },
+        
+        /**
+         * 详细信息页大小变更
+         * @param {number} pageSize - 页大小
+         * @returns {void}
+         */
+        detailSizeChange(pageSize) {
+            this.detailPage.pageSize = pageSize
+        },
+        
+        /**
+         * 详细信息刷新变更
+         * @param {Object} page - 分页参数
+         * @returns {void}
+         */
+        detailRefreshChange(page) {
+            this.detailOnLoad(page)
+        },
+        
+        /**
+         * 批量删除详细信息
+         * @returns {Promise<void>}
+         */
+        async handleDetailDelete() {
+            if (this.detailSelectionList.length === 0) {
+                this.$message.warning('请选择要删除的数据')
+                return
+            }
+        
+            try {
+                await this.$confirm('确定删除选中的详细信息吗?', '提示', {
+                    confirmButtonText: '确定',
+                    cancelButtonText: '取消',
+                    type: 'warning'
+                })
+        
+                const res = await removeDetail(this.detailIds)
+                if (res.data && res.data.success) {
+                    this.$message.success('删除成功')
+                    this.detailOnLoad(this.detailPage)
+                    this.$refs.detailCrud.toggleSelection()
+                } else {
+                    this.$message.error(res.data ? res.data.msg : '删除失败')
+                }
+            } catch (error) {
+                if (error !== 'cancel') {
+                    console.error('删除失败:', error)
+                    this.$message.error('删除失败,请稍后重试')
+                }
+            }
         }
     },
     mounted() {
@@ -801,6 +1265,10 @@ export default {
     font-weight: bold;
 }
 
+.lead-info-desc {
+    margin-bottom: 20px;
+}
+
 // 表格中的优先级和状态样式
 ::v-deep .el-table {
     .el-tag {