更新各项内容
This commit is contained in:
@@ -86,11 +86,6 @@ export function assignPoolCustomer(data) {
|
||||
return request({ url: "/backend/crm/pool/assign", method: "post", data });
|
||||
}
|
||||
|
||||
/** 移出公海:ids 客户ID集合 */
|
||||
export function removeFromPool(data) {
|
||||
return request({ url: "/backend/crm/pool/remove", method: "post", data });
|
||||
}
|
||||
|
||||
/** 移入公海:ids 客户ID集合,reason 移入原因 */
|
||||
export function moveToPool(data) {
|
||||
return request({ url: "/backend/crm/pool/move", method: "post", data });
|
||||
|
||||
@@ -80,17 +80,32 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="座机" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入座机号(选填)" />
|
||||
<el-input v-model="form.phone" placeholder="请输入座机号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱(选填)" />
|
||||
<el-input v-model="form.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="微信" prop="wechat">
|
||||
<el-input v-model="form.wechat" placeholder="请输入微信号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input v-model="form.qq" placeholder="请输入QQ号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="钉钉" prop="dingtalk">
|
||||
<el-input v-model="form.dingtalk" placeholder="请输入钉钉号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="部门" prop="department">
|
||||
<el-input v-model="form.department" placeholder="请输入所属部门(选填)" />
|
||||
<el-input v-model="form.department" placeholder="请输入所属部门" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -109,13 +124,29 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="在职状态" prop="status">
|
||||
<el-switch
|
||||
v-model="form.status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="在职"
|
||||
inactive-text="离职"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="家庭住址" prop="home_address">
|
||||
<el-input v-model="form.home_address" placeholder="请输入家庭住址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="如:关键决策人、对接注意事项(选填)"
|
||||
placeholder="如:关键决策人、对接注意事项"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -162,9 +193,14 @@ const defaultForm = () => ({
|
||||
mobile: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
wechat: "",
|
||||
qq: "",
|
||||
dingtalk: "",
|
||||
department: "",
|
||||
position: "",
|
||||
home_address: "",
|
||||
is_primary: 0,
|
||||
status: 1,
|
||||
remark: "",
|
||||
});
|
||||
|
||||
@@ -175,10 +211,14 @@ const rules = {
|
||||
related_id: [{ required: true, message: "请选择关联对象", trigger: "change" }],
|
||||
contact_name: [{ required: true, message: "请输入联系人姓名", trigger: "blur" }],
|
||||
mobile: [
|
||||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: "手机号格式不正确",
|
||||
// 手机号非必填,填写时才校验格式
|
||||
validator: (rule, value, callback) => {
|
||||
const val = (value || "").trim();
|
||||
if (!val) return callback();
|
||||
if (!/^1[3-9]\d{9}$/.test(val)) return callback(new Error("手机号格式不正确"));
|
||||
callback();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<el-table-column label="姓名" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<div class="name-cell-wrapper">
|
||||
<span class="name-cell">{{ row.contact_name }}</span>
|
||||
<span class="name-cell name-link" @click="openDetail(row)">{{ row.contact_name }}</span>
|
||||
<el-tag v-if="Number(row.is_primary) === 1" type="danger" size="small" effect="plain">
|
||||
主联系人
|
||||
</el-tag>
|
||||
@@ -63,8 +63,6 @@
|
||||
<template #default="{ row }">{{ genderText(row.gender) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" width="130" />
|
||||
<el-table-column prop="phone" label="座机" width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="email" label="邮箱" min-width="170" show-overflow-tooltip />
|
||||
<el-table-column label="关联类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="Number(row.related_type) === 1 ? 'primary' : 'warning'">
|
||||
@@ -118,6 +116,51 @@
|
||||
:edit-data="currentEditData"
|
||||
@success="fetchList"
|
||||
/>
|
||||
|
||||
<!-- 联系人详情 -->
|
||||
<el-drawer
|
||||
v-model="detailVisible"
|
||||
:title="detailData?.contact_name || '联系人详情'"
|
||||
direction="rtl"
|
||||
size="540px"
|
||||
>
|
||||
<div v-if="detailData">
|
||||
<el-descriptions title="基本信息" :column="2" border size="small">
|
||||
<el-descriptions-item label="姓名">{{ detailData.contact_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ genderText(detailData.gender) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联类型">
|
||||
<el-tag size="small" :type="Number(detailData.related_type) === 1 ? 'primary' : 'warning'">
|
||||
{{ relatedTypeText(detailData.related_type) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联对象">{{ detailData.related_name || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="部门">{{ detailData.department || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="职位">{{ detailData.position || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ detailData.mobile || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="座机">{{ detailData.phone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">{{ detailData.email || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="微信">{{ detailData.wechat || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">{{ detailData.qq || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="钉钉">{{ detailData.dingtalk || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="主联系人">
|
||||
<el-tag v-if="Number(detailData.is_primary) === 1" type="danger" size="small">是</el-tag>
|
||||
<span v-else>否</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="在职状态">
|
||||
<el-tag :type="Number(detailData.status) === 1 ? 'success' : 'info'" size="small">
|
||||
{{ Number(detailData.status) === 1 ? '在职' : '离职' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="家庭住址" :span="2">
|
||||
{{ detailData.home_address || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detailData.remark || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间" :span="2">
|
||||
{{ formatDateTime(detailData.update_time) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -139,6 +182,10 @@ const tableData = ref([]);
|
||||
const editVisible = ref(false);
|
||||
const currentEditData = ref(null);
|
||||
|
||||
// 详情
|
||||
const detailVisible = ref(false);
|
||||
const detailData = ref(null);
|
||||
|
||||
const filters = reactive({
|
||||
keyword: "",
|
||||
related_type: "",
|
||||
@@ -193,6 +240,11 @@ function openCreate() {
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
detailData.value = { ...row };
|
||||
detailVisible.value = true;
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
currentEditData.value = { ...row };
|
||||
editVisible.value = true;
|
||||
@@ -277,6 +329,15 @@ async function handleDelete(row) {
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.name-link {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
|
||||
@@ -1,188 +1,254 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="isEdit ? '编辑客户' : '新增客户'"
|
||||
width="760px"
|
||||
:title="dialogTitle"
|
||||
width="680px"
|
||||
:close-on-click-modal="false"
|
||||
@update:model-value="handleClose"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="110px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="customer_name">
|
||||
<el-input v-model="form.customer_name" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户类型" prop="customer_type">
|
||||
<el-select v-model="form.customer_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in CUSTOMER_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户等级" prop="customer_level">
|
||||
<el-select v-model="form.customer_level" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in levelOptions('customer')"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industry">
|
||||
<el-input v-model="form.industry" placeholder="如:互联网、制造业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合作开始" prop="register_time">
|
||||
<el-date-picker
|
||||
v-model="form.register_time"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%"
|
||||
<!-- 步骤条 -->
|
||||
<div class="steps-wrapper">
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<el-step title="基本信息" />
|
||||
<el-step title="联系信息" />
|
||||
<el-step title="开票信息" />
|
||||
<el-step title="备注信息" />
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="allRules"
|
||||
label-width="100px"
|
||||
label-position="right"
|
||||
style="margin-top: 24px"
|
||||
>
|
||||
<!-- 步骤1:基本信息 -->
|
||||
<div v-show="activeStep === 0">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="customer_name">
|
||||
<el-input v-model="form.customer_name" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户类型" prop="customer_type">
|
||||
<el-select v-model="form.customer_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in CUSTOMER_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合作到期" prop="expire_time">
|
||||
<el-date-picker
|
||||
v-model="form.expire_time"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%"
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户等级" prop="customer_level">
|
||||
<el-select v-model="form.customer_level" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in levelOptions('customer')"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="客户地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in STATUS_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industry">
|
||||
<el-input v-model="form.industry" placeholder="如:互联网、制造业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册资本" prop="registered_capital">
|
||||
<el-input v-model="form.registered_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实缴资本" prop="paid_capital">
|
||||
<el-input v-model="form.paid_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成立日期" prop="establish_date">
|
||||
<el-date-picker v-model="form.establish_date" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="行政区划" prop="administrative_division">
|
||||
<el-input v-model="form.administrative_division" placeholder="省/市/区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业类型" prop="enterprise_type">
|
||||
<el-select v-model="form.enterprise_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="有限责任公司" value="有限责任公司" />
|
||||
<el-option label="股份有限公司" value="股份有限公司" />
|
||||
<el-option label="合伙企业" value="合伙企业" />
|
||||
<el-option label="个人独资企业" value="个人独资企业" />
|
||||
<el-option label="国有企业" value="国有企业" />
|
||||
<el-option label="集体企业" value="集体企业" />
|
||||
<el-option label="外商投资企业" value="外商投资企业" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人资质" prop="taxpayer_qualification">
|
||||
<el-select v-model="form.taxpayer_qualification" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="一般纳税人" value="一般纳税人" />
|
||||
<el-option label="小规模纳税人" value="小规模纳税人" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in STATUS_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="经营范围" prop="business_scope">
|
||||
<el-input v-model="form.business_scope" type="textarea" :rows="3" placeholder="请输入经营范围(选填)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="联系信息" name="contact">
|
||||
<el-form :model="form" label-width="110px" label-position="right">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人">
|
||||
<el-input v-model="form.contact_person" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="form.contact_phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系邮箱">
|
||||
<el-input v-model="form.contact_email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 步骤2:联系信息 -->
|
||||
<div v-show="activeStep === 1">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人" prop="contact_person">
|
||||
<el-input v-model="form.contact_person" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contact_phone">
|
||||
<el-input v-model="form.contact_phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系邮箱" prop="contact_email">
|
||||
<el-input v-model="form.contact_email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="客户地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="开票信息" name="invoice">
|
||||
<el-form :model="form" label-width="110px" label-position="right">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发票抬头">
|
||||
<el-input v-model="form.invoice_title" placeholder="请输入发票抬头" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人识别号">
|
||||
<el-input v-model="form.tax_number" placeholder="请输入纳税人识别号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="注册地址">
|
||||
<el-input v-model="form.registered_address" placeholder="请输入注册地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册电话">
|
||||
<el-input v-model="form.registered_phone" placeholder="请输入注册电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行名称">
|
||||
<el-input v-model="form.bank_name" placeholder="请输入开户行名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行账号">
|
||||
<el-input v-model="form.bank_account" placeholder="请输入银行账号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 步骤3:开票信息(发票+银行合并) -->
|
||||
<div v-show="activeStep === 2">
|
||||
<el-divider content-position="left">发票信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发票抬头" prop="invoice_title">
|
||||
<el-input v-model="form.invoice_title" placeholder="请输入发票抬头" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人识别号" prop="tax_number">
|
||||
<el-input v-model="form.tax_number" placeholder="请输入纳税人识别号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="注册地址" prop="registered_address">
|
||||
<el-input v-model="form.registered_address" placeholder="请输入注册地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册电话" prop="registered_phone">
|
||||
<el-input v-model="form.registered_phone" placeholder="请输入注册电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left">银行信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行名称" prop="bank_name">
|
||||
<el-input v-model="form.bank_name" placeholder="请输入开户行名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行账号" prop="bank_account">
|
||||
<el-input v-model="form.bank_account" placeholder="请输入开户行账号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="备注" name="remark">
|
||||
<el-form :model="form" label-width="110px" label-position="right">
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="8"
|
||||
placeholder="请输入备注信息(选填)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- 步骤4:备注信息 -->
|
||||
<div v-show="activeStep === 3">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
placeholder="请输入备注信息(选填)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="final-step-tip">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
<span>确认所有信息无误后,点击「保存」完成创建。</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSave">保存</el-button>
|
||||
<div class="dialog-footer">
|
||||
<div class="footer-left">
|
||||
<el-button v-if="activeStep > 0" @click="prevStep">上一步</el-button>
|
||||
<el-button v-if="activeStep < 3" type="primary" @click="nextStep"
|
||||
>下一步</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button
|
||||
v-if="!isEdit || form.is_draft === 1"
|
||||
:loading="submitting"
|
||||
@click="handleSaveDraft"
|
||||
>
|
||||
保存草稿
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="activeStep === 3"
|
||||
type="primary"
|
||||
:loading="submitting"
|
||||
@click="handleSave(false)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, nextTick } from "vue";
|
||||
import { ref, reactive, computed, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { InfoFilled } from "@element-plus/icons-vue";
|
||||
import { createCrmCustomer, updateCrmCustomer } from "@/api/crm";
|
||||
import {
|
||||
CUSTOMER_TYPE_OPTIONS,
|
||||
STATUS_OPTIONS,
|
||||
levelOptions,
|
||||
} from "../../dict";
|
||||
import { CUSTOMER_TYPE_OPTIONS, STATUS_OPTIONS, levelOptions } from "../../dict";
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
@@ -192,23 +258,36 @@ const props = defineProps({
|
||||
const emit = defineEmits(["update:visible", "success"]);
|
||||
|
||||
const formRef = ref();
|
||||
const activeTab = ref("basic");
|
||||
const activeStep = ref(0);
|
||||
const submitting = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const internalId = ref(null);
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
if (isEdit.value) {
|
||||
return form.is_draft === 1 ? "编辑客户(草稿)" : "编辑客户";
|
||||
}
|
||||
return "新增客户";
|
||||
});
|
||||
|
||||
const defaultForm = () => ({
|
||||
customer_name: "",
|
||||
customer_type: "1",
|
||||
customer_level: "3",
|
||||
industry: "",
|
||||
registered_capital: "",
|
||||
paid_capital: "",
|
||||
establish_date: "",
|
||||
administrative_division: "",
|
||||
enterprise_type: "有限责任公司",
|
||||
taxpayer_qualification: "一般纳税人",
|
||||
business_scope: "",
|
||||
address: "",
|
||||
contact_person: "",
|
||||
contact_phone: "",
|
||||
contact_email: "",
|
||||
address: "",
|
||||
register_time: "",
|
||||
expire_time: "",
|
||||
status: "1",
|
||||
is_draft: 0,
|
||||
remark: "",
|
||||
invoice_title: "",
|
||||
tax_number: "",
|
||||
@@ -220,33 +299,57 @@ const defaultForm = () => ({
|
||||
|
||||
const form = reactive(defaultForm());
|
||||
|
||||
const rules = {
|
||||
customer_name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
|
||||
customer_type: [{ required: true, message: "请选择客户类型", trigger: "change" }],
|
||||
contact_phone: [
|
||||
{
|
||||
pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8}|\d{7,8})$/,
|
||||
message: "联系电话格式不正确",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
contact_email: [{ type: "email", message: "邮箱格式不正确", trigger: "blur" }],
|
||||
};
|
||||
// 各步骤的验证规则
|
||||
const stepRules = [
|
||||
// 步骤1:基本信息
|
||||
{
|
||||
customer_name: [
|
||||
{ required: true, message: "请输入客户名称", trigger: "blur" },
|
||||
],
|
||||
customer_type: [
|
||||
{ required: true, message: "请选择客户类型", trigger: "change" },
|
||||
],
|
||||
},
|
||||
// 步骤2:联系信息
|
||||
{
|
||||
contact_phone: [
|
||||
{
|
||||
pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8}|\d{7,8})$/,
|
||||
message: "联系电话格式不正确",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
contact_email: [{ type: "email", message: "邮箱格式不正确", trigger: "blur" }],
|
||||
},
|
||||
// 步骤3:开票信息(无必填)
|
||||
{},
|
||||
// 步骤4:备注信息(无必填)
|
||||
{},
|
||||
];
|
||||
|
||||
const allRules = computed(() => {
|
||||
const merged = {};
|
||||
stepRules.forEach((rules) => {
|
||||
Object.assign(merged, rules);
|
||||
});
|
||||
return merged;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
async (val) => {
|
||||
if (!val) return;
|
||||
activeTab.value = "basic";
|
||||
isEdit.value = !!props.editData;
|
||||
internalId.value = props.editData?.id ?? null;
|
||||
if (props.editData) {
|
||||
Object.assign(form, defaultForm(), props.editData);
|
||||
} else {
|
||||
Object.assign(form, defaultForm());
|
||||
(val) => {
|
||||
if (val) {
|
||||
activeStep.value = 0;
|
||||
internalId.value = null;
|
||||
if (props.editData) {
|
||||
isEdit.value = true;
|
||||
internalId.value = props.editData.id;
|
||||
Object.assign(form, defaultForm(), props.editData);
|
||||
} else {
|
||||
isEdit.value = false;
|
||||
Object.assign(form, defaultForm());
|
||||
}
|
||||
}
|
||||
await nextTick();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -256,33 +359,109 @@ function handleClose() {
|
||||
|
||||
function handleClosed() {
|
||||
formRef.value?.resetFields();
|
||||
activeTab.value = "basic";
|
||||
activeStep.value = 0;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!formRef.value) return;
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
} catch (e) {
|
||||
activeTab.value = "basic";
|
||||
return;
|
||||
async function nextStep() {
|
||||
const currentRules = stepRules[activeStep.value];
|
||||
if (Object.keys(currentRules).length > 0) {
|
||||
try {
|
||||
await formRef.value.validateField(Object.keys(currentRules));
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (activeStep.value < 3) {
|
||||
activeStep.value++;
|
||||
}
|
||||
}
|
||||
|
||||
function prevStep() {
|
||||
if (activeStep.value > 0) {
|
||||
activeStep.value--;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave(isDraft) {
|
||||
if (!formRef.value) return;
|
||||
if (isDraft) {
|
||||
if (!form.customer_name || form.customer_name.trim() === "") {
|
||||
ElMessage.warning("客户名称不能为空");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
} catch (e) {
|
||||
for (let i = 0; i < stepRules.length; i++) {
|
||||
const fields = Object.keys(stepRules[i]);
|
||||
for (const field of fields) {
|
||||
if (!form[field] || form[field] === "") {
|
||||
activeStep.value = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
const payload = { ...form, is_draft: 0 };
|
||||
const payload = { ...form, is_draft: isDraft ? 1 : 0 };
|
||||
if (internalId.value) {
|
||||
await updateCrmCustomer(internalId.value, payload);
|
||||
ElMessage.success("更新成功");
|
||||
ElMessage.success(isDraft ? "草稿已保存" : "更新成功");
|
||||
} else {
|
||||
await createCrmCustomer(payload);
|
||||
ElMessage.success("新增成功");
|
||||
const res = await createCrmCustomer(payload);
|
||||
ElMessage.success(isDraft ? "草稿已保存" : "创建成功");
|
||||
if (res && res.data && res.data.id) {
|
||||
internalId.value = res.data.id;
|
||||
isEdit.value = true;
|
||||
}
|
||||
}
|
||||
emit("success");
|
||||
handleClose();
|
||||
if (!isDraft) {
|
||||
handleClose();
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || "操作失败");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSaveDraft() {
|
||||
handleSave(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.steps-wrapper {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.footer-left,
|
||||
.footer-right {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.final-step-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 24px;
|
||||
padding: 12px 16px;
|
||||
background: var(--el-color-info-light-9);
|
||||
border-radius: 4px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,520 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
@update:model-value="emit('update:visible', $event)"
|
||||
title="智能添加客户"
|
||||
width="780px"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<!-- 第一步:输入名称 -->
|
||||
<div v-if="step === 1" class="step-input">
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="客户名称" required>
|
||||
<el-input
|
||||
v-model="companyName"
|
||||
placeholder="请输入客户全称或简称"
|
||||
@keyup.enter="handleGenerate"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-alert
|
||||
title="AI将根据公司名称自动生成企业信息、联系方式、开票信息等,生成后可手动修改"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
</el-form>
|
||||
<div class="step-actions">
|
||||
<el-button type="primary" :icon="Cpu" :loading="generating" @click="handleGenerate">
|
||||
智能生成
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二步:确认编辑 -->
|
||||
<div v-else class="step-confirm">
|
||||
<div class="confirm-header">
|
||||
<el-alert
|
||||
title="AI已生成以下信息,请检查确认,可直接修改错误内容,确认无误后点击保存"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-form ref="formRef" :model="form" label-width="110px" class="smart-form">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称">
|
||||
<el-input v-model="form.customer_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户类型">
|
||||
<el-select v-model="form.customer_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in CUSTOMER_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户等级">
|
||||
<el-select v-model="form.customer_level" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in levelOptions('customer')"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业">
|
||||
<el-input v-model="form.industry" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业类型">
|
||||
<el-select v-model="form.enterprise_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="有限责任公司" value="有限责任公司" />
|
||||
<el-option label="股份有限公司" value="股份有限公司" />
|
||||
<el-option label="合伙企业" value="合伙企业" />
|
||||
<el-option label="个人独资企业" value="个人独资企业" />
|
||||
<el-option label="国有企业" value="国有企业" />
|
||||
<el-option label="集体企业" value="集体企业" />
|
||||
<el-option label="外商投资企业" value="外商投资企业" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人资质">
|
||||
<el-select v-model="form.taxpayer_qualification" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="一般纳税人" value="一般纳税人" />
|
||||
<el-option label="小规模纳税人" value="小规模纳税人" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册资本">
|
||||
<el-input v-model="form.registered_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实缴资本">
|
||||
<el-input v-model="form.paid_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成立日期">
|
||||
<el-date-picker v-model="form.establish_date" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="行政区划">
|
||||
<el-input v-model="form.administrative_division" placeholder="如:江苏省连云港市海州区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="form.status" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in STATUS_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="公司地址">
|
||||
<el-input v-model="form.address" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="经营范围">
|
||||
<el-input v-model="form.business_scope" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">联系信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系人">
|
||||
<el-input v-model="form.contact_person" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="form.contact_phone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="form.contact_email" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">开票信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发票抬头">
|
||||
<el-input v-model="form.invoice_title" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="税号">
|
||||
<el-input v-model="form.tax_number" placeholder="18位统一社会信用代码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户银行">
|
||||
<el-input v-model="form.bank_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="银行账号">
|
||||
<el-input v-model="form.bank_account" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册地址">
|
||||
<el-input v-model="form.registered_address" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册电话">
|
||||
<el-input v-model="form.registered_phone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">备注</el-divider>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 显示原始AI响应数据 -->
|
||||
<el-divider content-position="left">AI响应数据</el-divider>
|
||||
<el-form-item label="查询信息" v-if="genMeta.model || (genMeta.tools && genMeta.tools.length)">
|
||||
<div class="ai-meta">
|
||||
<div class="ai-meta-row">
|
||||
<span class="ai-meta-label">使用模型:</span>
|
||||
<span>{{ genMeta.model || '—' }}</span>
|
||||
</div>
|
||||
<div class="ai-meta-row">
|
||||
<span class="ai-meta-label">调用工具:</span>
|
||||
<template v-if="genMeta.tools && genMeta.tools.length">
|
||||
<el-tag
|
||||
v-for="t in genMeta.tools"
|
||||
:key="t.server_id + '-' + t.tool_name"
|
||||
size="small"
|
||||
type="info"
|
||||
class="ai-tool-tag"
|
||||
>{{ t.server_name }} / {{ t.tool_name }}</el-tag>
|
||||
</template>
|
||||
<span v-else>无</span>
|
||||
</div>
|
||||
<div class="ai-meta-row" v-if="genMeta.found !== undefined">
|
||||
<span class="ai-meta-label">查到结果:</span>
|
||||
<el-tag v-if="genMeta.found" size="small" type="success">已查到工商信息</el-tag>
|
||||
<el-tag v-else size="small" type="warning">未查到(请手动补全)</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细数据">
|
||||
<div class="ai-data-box">
|
||||
<pre>{{ formattedAiData }}</pre>
|
||||
</div>
|
||||
<div class="form-tip">此数据为AI返回的原始响应,仅作查看参考,已自动填充可编辑字段到上方表单</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button v-if="step === 2" @click="step = 1">返回修改名称</el-button>
|
||||
<el-button
|
||||
v-if="step === 2"
|
||||
type="primary"
|
||||
:loading="saving"
|
||||
@click="handleSave"
|
||||
>确认保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Cpu } from '@element-plus/icons-vue'
|
||||
import { smartGenerateCompany } from '@/api/ai'
|
||||
import { createCrmCustomer } from '@/api/crm'
|
||||
import {
|
||||
CUSTOMER_TYPE_OPTIONS,
|
||||
STATUS_OPTIONS,
|
||||
levelOptions,
|
||||
} from '../../dict'
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
type: { type: String, default: 'customer' } // 当前仅支持 customer
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:visible', 'success'])
|
||||
|
||||
const step = ref(1)
|
||||
const companyName = ref('')
|
||||
const generating = ref(false)
|
||||
const saving = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
const defaultForm = () => ({
|
||||
customer_name: '',
|
||||
customer_type: '1',
|
||||
customer_level: '3',
|
||||
industry: '',
|
||||
enterprise_type: '有限责任公司',
|
||||
taxpayer_qualification: '一般纳税人',
|
||||
status: '1',
|
||||
registered_capital: '',
|
||||
paid_capital: '',
|
||||
establish_date: '',
|
||||
administrative_division: '',
|
||||
business_scope: '',
|
||||
address: '',
|
||||
contact_person: '',
|
||||
contact_phone: '',
|
||||
contact_email: '',
|
||||
invoice_title: '',
|
||||
tax_number: '',
|
||||
bank_name: '',
|
||||
bank_account: '',
|
||||
registered_address: '',
|
||||
registered_phone: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const form = reactive(defaultForm())
|
||||
|
||||
// 本次查询使用的模型与 MCP 工具(后端返回)
|
||||
const genMeta = ref({ model: '', tools: [], found: undefined })
|
||||
|
||||
// 原始AI响应数据(JSON格式字符串)
|
||||
const rawAiData = ref('')
|
||||
|
||||
// formattedAiData 直接显示 rawAiData 的内容
|
||||
const formattedAiData = ref('')
|
||||
watch(rawAiData, (newVal) => {
|
||||
formattedAiData.value = newVal
|
||||
}, { immediate: true })
|
||||
|
||||
async function handleGenerate() {
|
||||
if (!companyName.value.trim()) {
|
||||
ElMessage.warning('请输入公司名称')
|
||||
return
|
||||
}
|
||||
|
||||
generating.value = true
|
||||
try {
|
||||
const res = await smartGenerateCompany({
|
||||
company_name: companyName.value.trim(),
|
||||
type: props.type
|
||||
})
|
||||
if (res.code === 200) {
|
||||
// 记录本次查询使用的模型与 MCP 工具,便于前端透明展示
|
||||
genMeta.value = {
|
||||
model: res.data.model || '',
|
||||
tools: res.data.tools || [],
|
||||
found: res.data.found
|
||||
}
|
||||
if (res.data.parsed && res.data.data) {
|
||||
const data = res.data.data
|
||||
form.customer_name = data.customer_name || data.name || companyName.value.trim()
|
||||
form.customer_type = data.customer_type || '1'
|
||||
form.customer_level = data.customer_level || '3'
|
||||
form.status = data.status || '1'
|
||||
form.industry = data.industry || ''
|
||||
form.enterprise_type = data.enterprise_type || ''
|
||||
form.taxpayer_qualification = data.taxpayer_qualification || ''
|
||||
form.registered_capital = data.registered_capital || ''
|
||||
form.paid_capital = data.paid_capital || ''
|
||||
form.establish_date = data.establish_date || ''
|
||||
form.administrative_division = data.administrative_division || ''
|
||||
form.business_scope = data.business_scope || ''
|
||||
form.address = data.address || ''
|
||||
form.contact_person = data.contact_person || ''
|
||||
form.contact_phone = data.contact_phone || ''
|
||||
form.contact_email = data.contact_email || ''
|
||||
form.invoice_title = data.invoice_title || form.customer_name
|
||||
form.tax_number = data.tax_number || ''
|
||||
form.bank_name = data.bank_name || ''
|
||||
form.bank_account = data.bank_account || ''
|
||||
form.registered_address = data.registered_address || ''
|
||||
form.registered_phone = data.registered_phone || ''
|
||||
form.remark = data.remark || ''
|
||||
// 保存原始AI响应数据(使用后端返回的原始数据)
|
||||
if (res.data.raw) {
|
||||
rawAiData.value = res.data.raw
|
||||
} else {
|
||||
rawAiData.value = JSON.stringify(data, null, 2)
|
||||
}
|
||||
} else {
|
||||
// 解析失败,显示AI原始返回内容
|
||||
let rawContent = res.data.raw || '无原始数据'
|
||||
let errorMsg = res.data.error || '未知错误'
|
||||
rawAiData.value = rawContent
|
||||
if (rawContent.length > 1500) {
|
||||
rawContent = rawContent.substring(0, 1500) + '...'
|
||||
}
|
||||
ElMessage.error({
|
||||
message: `AI生成失败:${errorMsg}\n\n原始响应:\n${rawContent}`,
|
||||
showClose: true,
|
||||
duration: 0
|
||||
})
|
||||
form.customer_name = companyName.value.trim()
|
||||
}
|
||||
step.value = 2
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '生成失败')
|
||||
} finally {
|
||||
generating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.customer_name.trim()) {
|
||||
ElMessage.warning('客户名称不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = {
|
||||
customer_name: form.customer_name,
|
||||
customer_type: form.customer_type,
|
||||
customer_level: form.customer_level,
|
||||
status: form.status,
|
||||
industry: form.industry,
|
||||
enterprise_type: form.enterprise_type,
|
||||
taxpayer_qualification: form.taxpayer_qualification,
|
||||
registered_capital: form.registered_capital,
|
||||
paid_capital: form.paid_capital,
|
||||
establish_date: form.establish_date,
|
||||
administrative_division: form.administrative_division,
|
||||
business_scope: form.business_scope,
|
||||
address: form.address,
|
||||
contact_person: form.contact_person,
|
||||
contact_phone: form.contact_phone,
|
||||
contact_email: form.contact_email,
|
||||
invoice_title: form.invoice_title,
|
||||
tax_number: form.tax_number,
|
||||
bank_name: form.bank_name,
|
||||
bank_account: form.bank_account,
|
||||
registered_address: form.registered_address,
|
||||
registered_phone: form.registered_phone,
|
||||
remark: form.remark,
|
||||
is_draft: 0
|
||||
}
|
||||
|
||||
await createCrmCustomer(payload)
|
||||
|
||||
ElMessage.success('保存成功')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
step.value = 1
|
||||
companyName.value = ''
|
||||
Object.assign(form, defaultForm())
|
||||
rawAiData.value = ''
|
||||
formattedAiData.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-input {
|
||||
padding: 20px 0;
|
||||
|
||||
.step-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.step-confirm {
|
||||
.confirm-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.smart-form {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.ai-data-box {
|
||||
background: var(--el-fill-color-lighter);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ai-meta {
|
||||
background: var(--el-fill-color-lighter);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
.ai-meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 6px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-meta-label {
|
||||
flex-shrink: 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.ai-tool-tag {
|
||||
margin-right: 6px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -16,6 +16,7 @@
|
||||
移入公海
|
||||
</el-button>
|
||||
<el-button type="primary" :icon="Plus" @click="openCreate">新增客户</el-button>
|
||||
<el-button type="success" :icon="Cpu" @click="smartAddVisible = true">智能添加</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +148,7 @@
|
||||
v-model="detailVisible"
|
||||
:title="detailData?.customer_name || '客户详情'"
|
||||
direction="rtl"
|
||||
size="560px"
|
||||
size="700px"
|
||||
>
|
||||
<div v-if="detailData" class="detail-content">
|
||||
<el-descriptions title="基本信息" :column="2" border size="small">
|
||||
@@ -167,9 +168,14 @@
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">{{ detailData.owner_name || '未分配' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="合作开始">{{ formatDate(detailData.register_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="合作到期">{{ formatDate(detailData.expire_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="注册资本">{{ detailData.registered_capital ? detailData.registered_capital + ' 万元' : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实缴资本">{{ detailData.paid_capital ? detailData.paid_capital + ' 万元' : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成立日期">{{ formatDate(detailData.establish_date) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="行政区划">{{ detailData.administrative_division || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业类型">{{ detailData.enterprise_type || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="纳税人资质">{{ detailData.taxpayer_qualification || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户地址" :span="2">{{ detailData.address || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="经营范围" :span="2">{{ detailData.business_scope || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ formatDateTime(detailData.create_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ formatDateTime(detailData.update_time) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@@ -250,6 +256,8 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" width="130" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="wechat" label="微信" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="qq" label="QQ" width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="department" label="部门" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="position" label="职位" width="130" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
@@ -274,6 +282,12 @@
|
||||
@success="loadContacts"
|
||||
/>
|
||||
|
||||
<SmartAddCompany
|
||||
v-model:visible="smartAddVisible"
|
||||
type="customer"
|
||||
@success="fetchList"
|
||||
/>
|
||||
|
||||
<!-- 移入公海 -->
|
||||
<el-dialog v-model="poolVisible" title="移入客户公海" width="460px" :close-on-click-modal="false">
|
||||
<el-alert
|
||||
@@ -304,7 +318,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Plus, Search, Refresh } from "@element-plus/icons-vue";
|
||||
import { Plus, Search, Refresh, Cpu } from "@element-plus/icons-vue";
|
||||
import {
|
||||
getCrmCustomerList,
|
||||
deleteCrmCustomer,
|
||||
@@ -313,6 +327,7 @@ import {
|
||||
import { listContacts, deleteContact } from "@/api/contact";
|
||||
import CustomerEdit from "./components/edit.vue";
|
||||
import ContactEdit from "../contact/components/edit.vue";
|
||||
import SmartAddCompany from "./components/smartAddCompany.vue";
|
||||
import {
|
||||
CUSTOMER_TYPE_OPTIONS,
|
||||
STATUS_OPTIONS,
|
||||
@@ -343,6 +358,7 @@ const contactList = ref([]);
|
||||
const currentRow = ref(null);
|
||||
const contactEditVisible = ref(false);
|
||||
const contactEditData = ref(null);
|
||||
const smartAddVisible = ref(false);
|
||||
|
||||
// 移入公海
|
||||
const poolVisible = ref(false);
|
||||
@@ -601,4 +617,20 @@ async function submitMovePool() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 4px 8px;
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 130px;
|
||||
min-width: 130px;
|
||||
color: var(--el-text-color-regular);
|
||||
font-weight: 500;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__content) {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="handleClaim([row])">领取</el-button>
|
||||
<el-button link type="primary" size="small" @click="openAssign([row])">分配</el-button>
|
||||
<el-button link type="warning" size="small" @click="handleRemove([row])">移出公海</el-button>
|
||||
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -233,7 +232,6 @@ import {
|
||||
getPoolList,
|
||||
claimPoolCustomer,
|
||||
assignPoolCustomer,
|
||||
removeFromPool,
|
||||
deleteCrmCustomer,
|
||||
} from "@/api/crm";
|
||||
import { getAllUsers } from "@/api/user";
|
||||
@@ -391,9 +389,11 @@ async function submitAssign() {
|
||||
}
|
||||
assignSubmitting.value = true;
|
||||
try {
|
||||
const user = userOptions.value.find((u) => u.id === assignUserId.value);
|
||||
await assignPoolCustomer({
|
||||
ids: assignTargets.value.map((item) => item.id),
|
||||
owner_user_id: assignUserId.value,
|
||||
owner_user_name: user?.name || "",
|
||||
});
|
||||
ElMessage.success("分配成功");
|
||||
assignVisible.value = false;
|
||||
@@ -406,26 +406,7 @@ async function submitAssign() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------- 移出 / 删除 ---------------------------- */
|
||||
async function handleRemove(rows) {
|
||||
if (!rows.length) return;
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定将选中的 ${rows.length} 个客户移出公海吗?`,
|
||||
"移出公海",
|
||||
{ type: "warning" }
|
||||
);
|
||||
await removeFromPool({ ids: rows.map((item) => item.id) });
|
||||
ElMessage.success("已移出公海");
|
||||
multipleSelection.value = [];
|
||||
fetchList();
|
||||
} catch (e) {
|
||||
if (e !== "cancel") {
|
||||
ElMessage.error(e.message || "操作失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------- 删除 -------------------------------- */
|
||||
async function handleDelete(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
|
||||
@@ -1,182 +1,252 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
:title="isEdit ? '编辑供应商' : '新增供应商'"
|
||||
width="760px"
|
||||
:title="dialogTitle"
|
||||
width="680px"
|
||||
:close-on-click-modal="false"
|
||||
@update:model-value="handleClose"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="110px"
|
||||
label-position="right"
|
||||
>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商名称" prop="supplier_name">
|
||||
<el-input v-model="form.supplier_name" placeholder="请输入供应商名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商类型" prop="supplier_type">
|
||||
<el-select v-model="form.supplier_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in SUPPLIER_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商等级" prop="supplier_level">
|
||||
<el-select v-model="form.supplier_level" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in levelOptions('supplier')"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industry">
|
||||
<el-input v-model="form.industry" placeholder="如:机械设备、电子元器件" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合作开始" prop="register_time">
|
||||
<el-date-picker
|
||||
v-model="form.register_time"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%"
|
||||
<!-- 步骤条 -->
|
||||
<div class="steps-wrapper">
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<el-step title="基本信息" />
|
||||
<el-step title="联系信息" />
|
||||
<el-step title="开票信息" />
|
||||
<el-step title="备注信息" />
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="allRules"
|
||||
label-width="100px"
|
||||
label-position="right"
|
||||
style="margin-top: 24px"
|
||||
>
|
||||
<!-- 步骤1:基本信息 -->
|
||||
<div v-show="activeStep === 0">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商名称" prop="supplier_name">
|
||||
<el-input v-model="form.supplier_name" placeholder="请输入供应商名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商类型" prop="supplier_type">
|
||||
<el-select v-model="form.supplier_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in SUPPLIER_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合作到期" prop="expire_time">
|
||||
<el-date-picker
|
||||
v-model="form.expire_time"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期"
|
||||
style="width: 100%"
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商等级" prop="supplier_level">
|
||||
<el-select v-model="form.supplier_level" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in levelOptions('supplier')"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="供应商地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in STATUS_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industry">
|
||||
<el-input v-model="form.industry" placeholder="如:机械设备、电子元器件" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册资本" prop="registered_capital">
|
||||
<el-input v-model="form.registered_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实缴资本" prop="paid_capital">
|
||||
<el-input v-model="form.paid_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成立日期" prop="establish_date">
|
||||
<el-date-picker v-model="form.establish_date" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="行政区划" prop="administrative_division">
|
||||
<el-input v-model="form.administrative_division" placeholder="省/市/区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业类型" prop="enterprise_type">
|
||||
<el-select v-model="form.enterprise_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="有限责任公司" value="有限责任公司" />
|
||||
<el-option label="股份有限公司" value="股份有限公司" />
|
||||
<el-option label="合伙企业" value="合伙企业" />
|
||||
<el-option label="个人独资企业" value="个人独资企业" />
|
||||
<el-option label="国有企业" value="国有企业" />
|
||||
<el-option label="集体企业" value="集体企业" />
|
||||
<el-option label="外商投资企业" value="外商投资企业" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人资质" prop="taxpayer_qualification">
|
||||
<el-select v-model="form.taxpayer_qualification" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="一般纳税人" value="一般纳税人" />
|
||||
<el-option label="小规模纳税人" value="小规模纳税人" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in STATUS_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="经营范围" prop="business_scope">
|
||||
<el-input v-model="form.business_scope" type="textarea" :rows="3" placeholder="请输入经营范围(选填)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="联系信息" name="contact">
|
||||
<el-form :model="form" label-width="110px" label-position="right">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人">
|
||||
<el-input v-model="form.contact_person" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="form.contact_phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系邮箱">
|
||||
<el-input v-model="form.contact_email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 步骤2:联系信息 -->
|
||||
<div v-show="activeStep === 1">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人" prop="contact_person">
|
||||
<el-input v-model="form.contact_person" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contact_phone">
|
||||
<el-input v-model="form.contact_phone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系邮箱" prop="contact_email">
|
||||
<el-input v-model="form.contact_email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="供应商地址" prop="address">
|
||||
<el-input v-model="form.address" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="开票信息" name="invoice">
|
||||
<el-form :model="form" label-width="110px" label-position="right">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发票抬头">
|
||||
<el-input v-model="form.invoice_title" placeholder="请输入发票抬头" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人识别号">
|
||||
<el-input v-model="form.tax_number" placeholder="请输入纳税人识别号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="注册地址">
|
||||
<el-input v-model="form.registered_address" placeholder="请输入注册地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册电话">
|
||||
<el-input v-model="form.registered_phone" placeholder="请输入注册电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行名称">
|
||||
<el-input v-model="form.bank_name" placeholder="请输入开户行名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行账号">
|
||||
<el-input v-model="form.bank_account" placeholder="请输入银行账号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 步骤3:开票信息(发票+银行合并) -->
|
||||
<div v-show="activeStep === 2">
|
||||
<el-divider content-position="left">发票信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发票抬头" prop="invoice_title">
|
||||
<el-input v-model="form.invoice_title" placeholder="请输入发票抬头" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人识别号" prop="tax_number">
|
||||
<el-input v-model="form.tax_number" placeholder="请输入纳税人识别号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="注册地址" prop="registered_address">
|
||||
<el-input v-model="form.registered_address" placeholder="请输入注册地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册电话" prop="registered_phone">
|
||||
<el-input v-model="form.registered_phone" placeholder="请输入注册电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left">银行信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行名称" prop="bank_name">
|
||||
<el-input v-model="form.bank_name" placeholder="请输入开户行名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行账号" prop="bank_account">
|
||||
<el-input v-model="form.bank_account" placeholder="请输入开户行账号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="备注" name="remark">
|
||||
<el-form :model="form" label-width="110px" label-position="right">
|
||||
<el-form-item label="备注">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="8"
|
||||
placeholder="请输入备注信息(选填)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!-- 步骤4:备注信息 -->
|
||||
<div v-show="activeStep === 3">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
placeholder="请输入备注信息(选填)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="final-step-tip">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
<span>确认所有信息无误后,点击「保存」完成创建。</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSave">保存</el-button>
|
||||
<div class="dialog-footer">
|
||||
<div class="footer-left">
|
||||
<el-button v-if="activeStep > 0" @click="prevStep">上一步</el-button>
|
||||
<el-button v-if="activeStep < 3" type="primary" @click="nextStep"
|
||||
>下一步</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button
|
||||
v-if="!isEdit || form.is_draft === 1"
|
||||
:loading="submitting"
|
||||
@click="handleSaveDraft"
|
||||
>
|
||||
保存草稿
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="activeStep === 3"
|
||||
type="primary"
|
||||
:loading="submitting"
|
||||
@click="handleSave(false)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, nextTick } from "vue";
|
||||
import { ref, reactive, computed, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { InfoFilled } from "@element-plus/icons-vue";
|
||||
import { createCrmSupplier, updateCrmSupplier } from "@/api/crm";
|
||||
import { SUPPLIER_TYPE_OPTIONS, STATUS_OPTIONS, levelOptions } from "../../dict";
|
||||
|
||||
@@ -188,23 +258,36 @@ const props = defineProps({
|
||||
const emit = defineEmits(["update:visible", "success"]);
|
||||
|
||||
const formRef = ref();
|
||||
const activeTab = ref("basic");
|
||||
const activeStep = ref(0);
|
||||
const submitting = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const internalId = ref(null);
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
if (isEdit.value) {
|
||||
return form.is_draft === 1 ? "编辑供应商(草稿)" : "编辑供应商";
|
||||
}
|
||||
return "新增供应商";
|
||||
});
|
||||
|
||||
const defaultForm = () => ({
|
||||
supplier_name: "",
|
||||
supplier_type: "1",
|
||||
supplier_level: "3",
|
||||
industry: "",
|
||||
registered_capital: "",
|
||||
paid_capital: "",
|
||||
establish_date: "",
|
||||
administrative_division: "",
|
||||
enterprise_type: "有限责任公司",
|
||||
taxpayer_qualification: "一般纳税人",
|
||||
business_scope: "",
|
||||
address: "",
|
||||
contact_person: "",
|
||||
contact_phone: "",
|
||||
contact_email: "",
|
||||
address: "",
|
||||
register_time: "",
|
||||
expire_time: "",
|
||||
status: "1",
|
||||
is_draft: 0,
|
||||
remark: "",
|
||||
invoice_title: "",
|
||||
tax_number: "",
|
||||
@@ -216,33 +299,57 @@ const defaultForm = () => ({
|
||||
|
||||
const form = reactive(defaultForm());
|
||||
|
||||
const rules = {
|
||||
supplier_name: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
|
||||
supplier_type: [{ required: true, message: "请选择供应商类型", trigger: "change" }],
|
||||
contact_phone: [
|
||||
{
|
||||
pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8}|\d{7,8})$/,
|
||||
message: "联系电话格式不正确",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
contact_email: [{ type: "email", message: "邮箱格式不正确", trigger: "blur" }],
|
||||
};
|
||||
// 各步骤的验证规则
|
||||
const stepRules = [
|
||||
// 步骤1:基本信息
|
||||
{
|
||||
supplier_name: [
|
||||
{ required: true, message: "请输入供应商名称", trigger: "blur" },
|
||||
],
|
||||
supplier_type: [
|
||||
{ required: true, message: "请选择供应商类型", trigger: "change" },
|
||||
],
|
||||
},
|
||||
// 步骤2:联系信息
|
||||
{
|
||||
contact_phone: [
|
||||
{
|
||||
pattern: /^(1[3-9]\d{9}|0\d{2,3}-?\d{7,8}|\d{7,8})$/,
|
||||
message: "联系电话格式不正确",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
contact_email: [{ type: "email", message: "邮箱格式不正确", trigger: "blur" }],
|
||||
},
|
||||
// 步骤3:开票信息(无必填)
|
||||
{},
|
||||
// 步骤4:备注信息(无必填)
|
||||
{},
|
||||
];
|
||||
|
||||
const allRules = computed(() => {
|
||||
const merged = {};
|
||||
stepRules.forEach((rules) => {
|
||||
Object.assign(merged, rules);
|
||||
});
|
||||
return merged;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
async (val) => {
|
||||
if (!val) return;
|
||||
activeTab.value = "basic";
|
||||
isEdit.value = !!props.editData;
|
||||
internalId.value = props.editData?.id ?? null;
|
||||
if (props.editData) {
|
||||
Object.assign(form, defaultForm(), props.editData);
|
||||
} else {
|
||||
Object.assign(form, defaultForm());
|
||||
(val) => {
|
||||
if (val) {
|
||||
activeStep.value = 0;
|
||||
internalId.value = null;
|
||||
if (props.editData) {
|
||||
isEdit.value = true;
|
||||
internalId.value = props.editData.id;
|
||||
Object.assign(form, defaultForm(), props.editData);
|
||||
} else {
|
||||
isEdit.value = false;
|
||||
Object.assign(form, defaultForm());
|
||||
}
|
||||
}
|
||||
await nextTick();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -252,33 +359,109 @@ function handleClose() {
|
||||
|
||||
function handleClosed() {
|
||||
formRef.value?.resetFields();
|
||||
activeTab.value = "basic";
|
||||
activeStep.value = 0;
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!formRef.value) return;
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
} catch (e) {
|
||||
activeTab.value = "basic";
|
||||
return;
|
||||
async function nextStep() {
|
||||
const currentRules = stepRules[activeStep.value];
|
||||
if (Object.keys(currentRules).length > 0) {
|
||||
try {
|
||||
await formRef.value.validateField(Object.keys(currentRules));
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (activeStep.value < 3) {
|
||||
activeStep.value++;
|
||||
}
|
||||
}
|
||||
|
||||
function prevStep() {
|
||||
if (activeStep.value > 0) {
|
||||
activeStep.value--;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave(isDraft) {
|
||||
if (!formRef.value) return;
|
||||
if (isDraft) {
|
||||
if (!form.supplier_name || form.supplier_name.trim() === "") {
|
||||
ElMessage.warning("供应商名称不能为空");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
} catch (e) {
|
||||
for (let i = 0; i < stepRules.length; i++) {
|
||||
const fields = Object.keys(stepRules[i]);
|
||||
for (const field of fields) {
|
||||
if (!form[field] || form[field] === "") {
|
||||
activeStep.value = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
const payload = { ...form, is_draft: 0 };
|
||||
const payload = { ...form, is_draft: isDraft ? 1 : 0 };
|
||||
if (internalId.value) {
|
||||
await updateCrmSupplier(internalId.value, payload);
|
||||
ElMessage.success("更新成功");
|
||||
ElMessage.success(isDraft ? "草稿已保存" : "更新成功");
|
||||
} else {
|
||||
await createCrmSupplier(payload);
|
||||
ElMessage.success("新增成功");
|
||||
const res = await createCrmSupplier(payload);
|
||||
ElMessage.success(isDraft ? "草稿已保存" : "创建成功");
|
||||
if (res && res.data && res.data.id) {
|
||||
internalId.value = res.data.id;
|
||||
isEdit.value = true;
|
||||
}
|
||||
}
|
||||
emit("success");
|
||||
handleClose();
|
||||
if (!isDraft) {
|
||||
handleClose();
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || "操作失败");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSaveDraft() {
|
||||
handleSave(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.steps-wrapper {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.footer-left,
|
||||
.footer-right {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.final-step-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 24px;
|
||||
padding: 12px 16px;
|
||||
background: var(--el-color-info-light-9);
|
||||
border-radius: 4px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,520 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="visible"
|
||||
@update:model-value="emit('update:visible', $event)"
|
||||
title="智能添加供应商"
|
||||
width="780px"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleClosed"
|
||||
>
|
||||
<!-- 第一步:输入名称 -->
|
||||
<div v-if="step === 1" class="step-input">
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="供应商名称" required>
|
||||
<el-input
|
||||
v-model="companyName"
|
||||
placeholder="请输入供应商全称或简称"
|
||||
@keyup.enter="handleGenerate"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-alert
|
||||
title="AI将根据公司名称自动生成企业信息、联系方式、开票信息等,生成后可手动修改"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
</el-form>
|
||||
<div class="step-actions">
|
||||
<el-button type="primary" :icon="Cpu" :loading="generating" @click="handleGenerate">
|
||||
智能生成
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二步:确认编辑 -->
|
||||
<div v-else class="step-confirm">
|
||||
<div class="confirm-header">
|
||||
<el-alert
|
||||
title="AI已生成以下信息,请检查确认,可直接修改错误内容,确认无误后点击保存"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-form ref="formRef" :model="form" label-width="110px" class="smart-form">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商名称">
|
||||
<el-input v-model="form.supplier_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商类型">
|
||||
<el-select v-model="form.supplier_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in SUPPLIER_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="供应商等级">
|
||||
<el-select v-model="form.supplier_level" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in levelOptions('supplier')"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业">
|
||||
<el-input v-model="form.industry" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业类型">
|
||||
<el-select v-model="form.enterprise_type" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="有限责任公司" value="有限责任公司" />
|
||||
<el-option label="股份有限公司" value="股份有限公司" />
|
||||
<el-option label="合伙企业" value="合伙企业" />
|
||||
<el-option label="个人独资企业" value="个人独资企业" />
|
||||
<el-option label="国有企业" value="国有企业" />
|
||||
<el-option label="集体企业" value="集体企业" />
|
||||
<el-option label="外商投资企业" value="外商投资企业" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税人资质">
|
||||
<el-select v-model="form.taxpayer_qualification" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="一般纳税人" value="一般纳税人" />
|
||||
<el-option label="小规模纳税人" value="小规模纳税人" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="form.status" placeholder="请选择" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in STATUS_OPTIONS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册资本">
|
||||
<el-input v-model="form.registered_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实缴资本">
|
||||
<el-input v-model="form.paid_capital" placeholder="万元">
|
||||
<template #append>万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成立日期">
|
||||
<el-date-picker v-model="form.establish_date" type="date" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="行政区划">
|
||||
<el-input v-model="form.administrative_division" placeholder="如:江苏省连云港市海州区" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="经营范围">
|
||||
<el-input v-model="form.business_scope" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="公司地址">
|
||||
<el-input v-model="form.address" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">联系信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系人">
|
||||
<el-input v-model="form.contact_person" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="form.contact_phone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="form.contact_email" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">开票信息</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发票抬头">
|
||||
<el-input v-model="form.invoice_title" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="税号">
|
||||
<el-input v-model="form.tax_number" placeholder="18位统一社会信用代码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户银行">
|
||||
<el-input v-model="form.bank_name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="银行账号">
|
||||
<el-input v-model="form.bank_account" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册地址">
|
||||
<el-input v-model="form.registered_address" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册电话">
|
||||
<el-input v-model="form.registered_phone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">备注</el-divider>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 显示原始AI响应数据 -->
|
||||
<el-divider content-position="left">AI响应数据</el-divider>
|
||||
<el-form-item label="查询信息" v-if="genMeta.model || (genMeta.tools && genMeta.tools.length)">
|
||||
<div class="ai-meta">
|
||||
<div class="ai-meta-row">
|
||||
<span class="ai-meta-label">使用模型:</span>
|
||||
<span>{{ genMeta.model || '—' }}</span>
|
||||
</div>
|
||||
<div class="ai-meta-row">
|
||||
<span class="ai-meta-label">调用工具:</span>
|
||||
<template v-if="genMeta.tools && genMeta.tools.length">
|
||||
<el-tag
|
||||
v-for="t in genMeta.tools"
|
||||
:key="t.server_id + '-' + t.tool_name"
|
||||
size="small"
|
||||
type="info"
|
||||
class="ai-tool-tag"
|
||||
>{{ t.server_name }} / {{ t.tool_name }}</el-tag>
|
||||
</template>
|
||||
<span v-else>无</span>
|
||||
</div>
|
||||
<div class="ai-meta-row" v-if="genMeta.found !== undefined">
|
||||
<span class="ai-meta-label">查到结果:</span>
|
||||
<el-tag v-if="genMeta.found" size="small" type="success">已查到工商信息</el-tag>
|
||||
<el-tag v-else size="small" type="warning">未查到(请手动补全)</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细数据">
|
||||
<div class="ai-data-box">
|
||||
<pre>{{ formattedAiData }}</pre>
|
||||
</div>
|
||||
<div class="form-tip">此数据为AI返回的原始响应,仅作查看参考,已自动填充可编辑字段到上方表单</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button v-if="step === 2" @click="step = 1">返回修改名称</el-button>
|
||||
<el-button
|
||||
v-if="step === 2"
|
||||
type="primary"
|
||||
:loading="saving"
|
||||
@click="handleSave"
|
||||
>确认保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Cpu } from '@element-plus/icons-vue'
|
||||
import { smartGenerateCompany } from '@/api/ai'
|
||||
import { createCrmSupplier } from '@/api/crm'
|
||||
import {
|
||||
SUPPLIER_TYPE_OPTIONS,
|
||||
STATUS_OPTIONS,
|
||||
levelOptions,
|
||||
} from '../../dict'
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
type: { type: String, default: 'supplier' } // 当前仅支持 supplier
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:visible', 'success'])
|
||||
|
||||
const step = ref(1)
|
||||
const companyName = ref('')
|
||||
const generating = ref(false)
|
||||
const saving = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
const defaultForm = () => ({
|
||||
supplier_name: '',
|
||||
supplier_type: '1',
|
||||
supplier_level: '3',
|
||||
industry: '',
|
||||
enterprise_type: '有限责任公司',
|
||||
taxpayer_qualification: '一般纳税人',
|
||||
status: '1',
|
||||
registered_capital: '',
|
||||
paid_capital: '',
|
||||
establish_date: '',
|
||||
administrative_division: '',
|
||||
business_scope: '',
|
||||
address: '',
|
||||
contact_person: '',
|
||||
contact_phone: '',
|
||||
contact_email: '',
|
||||
invoice_title: '',
|
||||
tax_number: '',
|
||||
bank_name: '',
|
||||
bank_account: '',
|
||||
registered_address: '',
|
||||
registered_phone: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const form = reactive(defaultForm())
|
||||
|
||||
// 本次查询使用的模型与 MCP 工具(后端返回)
|
||||
const genMeta = ref({ model: '', tools: [], found: undefined })
|
||||
|
||||
// 原始AI响应数据(JSON格式字符串)
|
||||
const rawAiData = ref('')
|
||||
|
||||
// formattedAiData 直接显示 rawAiData 的内容
|
||||
const formattedAiData = ref('')
|
||||
watch(rawAiData, (newVal) => {
|
||||
formattedAiData.value = newVal
|
||||
}, { immediate: true })
|
||||
|
||||
async function handleGenerate() {
|
||||
if (!companyName.value.trim()) {
|
||||
ElMessage.warning('请输入公司名称')
|
||||
return
|
||||
}
|
||||
|
||||
generating.value = true
|
||||
try {
|
||||
const res = await smartGenerateCompany({
|
||||
company_name: companyName.value.trim(),
|
||||
type: props.type
|
||||
})
|
||||
if (res.code === 200) {
|
||||
// 记录本次查询使用的模型与 MCP 工具,便于前端透明展示
|
||||
genMeta.value = {
|
||||
model: res.data.model || '',
|
||||
tools: res.data.tools || [],
|
||||
found: res.data.found
|
||||
}
|
||||
if (res.data.parsed && res.data.data) {
|
||||
const data = res.data.data
|
||||
form.supplier_name = data.supplier_name || data.name || companyName.value.trim()
|
||||
form.supplier_type = data.supplier_type || '1'
|
||||
form.supplier_level = data.supplier_level || '3'
|
||||
form.status = data.status || '1'
|
||||
form.industry = data.industry || ''
|
||||
form.enterprise_type = data.enterprise_type || ''
|
||||
form.taxpayer_qualification = data.taxpayer_qualification || ''
|
||||
form.registered_capital = data.registered_capital || ''
|
||||
form.paid_capital = data.paid_capital || ''
|
||||
form.establish_date = data.establish_date || ''
|
||||
form.administrative_division = data.administrative_division || ''
|
||||
form.business_scope = data.business_scope || ''
|
||||
form.address = data.address || ''
|
||||
form.contact_person = data.contact_person || ''
|
||||
form.contact_phone = data.contact_phone || ''
|
||||
form.contact_email = data.contact_email || ''
|
||||
form.invoice_title = data.invoice_title || form.supplier_name
|
||||
form.tax_number = data.tax_number || ''
|
||||
form.bank_name = data.bank_name || ''
|
||||
form.bank_account = data.bank_account || ''
|
||||
form.registered_address = data.registered_address || ''
|
||||
form.registered_phone = data.registered_phone || ''
|
||||
form.remark = data.remark || ''
|
||||
// 保存原始AI响应数据(使用后端返回的原始数据)
|
||||
if (res.data.raw) {
|
||||
rawAiData.value = res.data.raw
|
||||
} else {
|
||||
rawAiData.value = JSON.stringify(data, null, 2)
|
||||
}
|
||||
} else {
|
||||
// 解析失败,显示AI原始返回内容
|
||||
let rawContent = res.data.raw || '无原始数据'
|
||||
let errorMsg = res.data.error || '未知错误'
|
||||
rawAiData.value = rawContent
|
||||
if (rawContent.length > 1500) {
|
||||
rawContent = rawContent.substring(0, 1500) + '...'
|
||||
}
|
||||
ElMessage.error({
|
||||
message: `AI生成失败:${errorMsg}\n\n原始响应:\n${rawContent}`,
|
||||
showClose: true,
|
||||
duration: 0
|
||||
})
|
||||
form.supplier_name = companyName.value.trim()
|
||||
}
|
||||
step.value = 2
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '生成失败')
|
||||
} finally {
|
||||
generating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if (!form.supplier_name.trim()) {
|
||||
ElMessage.warning('供应商名称不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = {
|
||||
supplier_name: form.supplier_name,
|
||||
supplier_type: form.supplier_type,
|
||||
supplier_level: form.supplier_level,
|
||||
status: form.status,
|
||||
industry: form.industry,
|
||||
enterprise_type: form.enterprise_type,
|
||||
taxpayer_qualification: form.taxpayer_qualification,
|
||||
registered_capital: form.registered_capital,
|
||||
paid_capital: form.paid_capital,
|
||||
establish_date: form.establish_date,
|
||||
administrative_division: form.administrative_division,
|
||||
business_scope: form.business_scope,
|
||||
address: form.address,
|
||||
contact_person: form.contact_person,
|
||||
contact_phone: form.contact_phone,
|
||||
contact_email: form.contact_email,
|
||||
invoice_title: form.invoice_title,
|
||||
tax_number: form.tax_number,
|
||||
bank_name: form.bank_name,
|
||||
bank_account: form.bank_account,
|
||||
registered_address: form.registered_address,
|
||||
registered_phone: form.registered_phone,
|
||||
remark: form.remark,
|
||||
is_draft: 0
|
||||
}
|
||||
|
||||
await createCrmSupplier(payload)
|
||||
|
||||
ElMessage.success('保存成功')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
} catch (e) {
|
||||
ElMessage.error(e.message || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleClosed() {
|
||||
step.value = 1
|
||||
companyName.value = ''
|
||||
Object.assign(form, defaultForm())
|
||||
rawAiData.value = ''
|
||||
formattedAiData.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.step-input {
|
||||
padding: 20px 0;
|
||||
|
||||
.step-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.step-confirm {
|
||||
.confirm-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.smart-form {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.ai-data-box {
|
||||
background: var(--el-fill-color-lighter);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ai-meta {
|
||||
background: var(--el-fill-color-lighter);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
.ai-meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 6px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-meta-label {
|
||||
flex-shrink: 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.ai-tool-tag {
|
||||
margin-right: 6px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,6 +8,7 @@
|
||||
<div class="header-actions">
|
||||
<el-button :icon="Refresh" @click="fetchList">刷新</el-button>
|
||||
<el-button type="primary" :icon="Plus" @click="openCreate">新增供应商</el-button>
|
||||
<el-button type="success" :icon="Cpu" @click="smartAddVisible = true">智能添加</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -155,11 +156,18 @@
|
||||
{{ statusText(detailData.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合作开始">{{ formatDate(detailData.register_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="合作到期">{{ formatDate(detailData.expire_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="注册资本">{{ detailData.registered_capital ? detailData.registered_capital + ' 万元' : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实缴资本">{{ detailData.paid_capital ? detailData.paid_capital + ' 万元' : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成立日期">{{ formatDate(detailData.establish_date) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="行政区划">{{ detailData.administrative_division || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业类型">{{ detailData.enterprise_type || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="纳税人资质">{{ detailData.taxpayer_qualification || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="供应商地址" :span="2">
|
||||
{{ detailData.address || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="经营范围" :span="2">
|
||||
{{ detailData.business_scope || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ formatDateTime(detailData.create_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ formatDateTime(detailData.update_time) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
@@ -234,6 +242,8 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" width="130" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="wechat" label="微信" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="qq" label="QQ" width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="department" label="部门" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="position" label="职位" width="130" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
@@ -257,13 +267,19 @@
|
||||
:default-related="contactRelated"
|
||||
@success="loadContacts"
|
||||
/>
|
||||
|
||||
<SmartAddCompany
|
||||
v-model:visible="smartAddVisible"
|
||||
type="supplier"
|
||||
@success="fetchList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Plus, Search, Refresh } from "@element-plus/icons-vue";
|
||||
import { Plus, Search, Refresh, Cpu } from "@element-plus/icons-vue";
|
||||
import {
|
||||
getCrmSupplierList,
|
||||
deleteCrmSupplier,
|
||||
@@ -272,6 +288,7 @@ import {
|
||||
import { listContacts, deleteContact } from "@/api/contact";
|
||||
import SupplierEdit from "./components/edit.vue";
|
||||
import ContactEdit from "../contact/components/edit.vue";
|
||||
import SmartAddCompany from "./components/smartAddCompany.vue";
|
||||
import {
|
||||
SUPPLIER_TYPE_OPTIONS,
|
||||
STATUS_OPTIONS,
|
||||
@@ -299,6 +316,7 @@ const contactList = ref([]);
|
||||
const currentRow = ref(null);
|
||||
const contactEditVisible = ref(false);
|
||||
const contactEditData = ref(null);
|
||||
const smartAddVisible = ref(false);
|
||||
|
||||
const filters = reactive({
|
||||
keyword: "",
|
||||
@@ -530,4 +548,20 @@ async function handleContactDelete(row) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 4px 8px;
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 130px;
|
||||
min-width: 130px;
|
||||
color: var(--el-text-color-regular);
|
||||
font-weight: 500;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__content) {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -225,6 +225,32 @@
|
||||
|
||||
<!-- 显示原始AI响应数据 -->
|
||||
<el-divider content-position="left">AI响应数据</el-divider>
|
||||
<el-form-item label="查询信息" v-if="genMeta.model || (genMeta.tools && genMeta.tools.length)">
|
||||
<div class="ai-meta">
|
||||
<div class="ai-meta-row">
|
||||
<span class="ai-meta-label">使用模型:</span>
|
||||
<span>{{ genMeta.model || '—' }}</span>
|
||||
</div>
|
||||
<div class="ai-meta-row">
|
||||
<span class="ai-meta-label">调用工具:</span>
|
||||
<template v-if="genMeta.tools && genMeta.tools.length">
|
||||
<el-tag
|
||||
v-for="t in genMeta.tools"
|
||||
:key="t.server_id + '-' + t.tool_name"
|
||||
size="small"
|
||||
type="info"
|
||||
class="ai-tool-tag"
|
||||
>{{ t.server_name }} / {{ t.tool_name }}</el-tag>
|
||||
</template>
|
||||
<span v-else>无</span>
|
||||
</div>
|
||||
<div class="ai-meta-row" v-if="genMeta.found !== undefined">
|
||||
<span class="ai-meta-label">查到结果:</span>
|
||||
<el-tag v-if="genMeta.found" size="small" type="success">已查到工商信息</el-tag>
|
||||
<el-tag v-else size="small" type="warning">未查到(请手动补全)</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细数据">
|
||||
<div class="ai-data-box">
|
||||
<pre>{{ formattedAiData }}</pre>
|
||||
@@ -326,6 +352,9 @@ const defaultForm = () => ({
|
||||
|
||||
const form = reactive(defaultForm())
|
||||
|
||||
// 本次查询使用的模型与 MCP 工具(后端返回)
|
||||
const genMeta = ref({ model: '', tools: [], found: undefined })
|
||||
|
||||
// 原始AI响应数据(JSON格式字符串)
|
||||
const rawAiData = ref('')
|
||||
|
||||
@@ -348,6 +377,12 @@ async function handleGenerate() {
|
||||
type: props.type
|
||||
})
|
||||
if (res.code === 200) {
|
||||
// 记录本次查询使用的模型与 MCP 工具,便于前端透明展示
|
||||
genMeta.value = {
|
||||
model: res.data.model || '',
|
||||
tools: res.data.tools || [],
|
||||
found: res.data.found
|
||||
}
|
||||
if (res.data.parsed && res.data.data) {
|
||||
const data = res.data.data
|
||||
const nameField = props.type === 'supplier' ? 'supplier_name' : 'customer_name'
|
||||
@@ -506,5 +541,37 @@ function handleClosed() {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ai-meta {
|
||||
background: var(--el-fill-color-lighter);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
.ai-meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 6px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-meta-label {
|
||||
flex-shrink: 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.ai-tool-tag {
|
||||
margin-right: 6px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user