更新各项内容

This commit is contained in:
2026-09-11 00:22:49 +08:00
parent 1304e71b7a
commit b9cdabe045
24 changed files with 3109 additions and 469 deletions
@@ -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>