Files
yunzerwebsiteallinone/backend/src/views/apps/crm/customer/components/edit.vue
T
2026-09-11 00:22:49 +08:00

468 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<el-dialog
:model-value="visible"
:title="dialogTitle"
width="680px"
:close-on-click-modal="false"
@update:model-value="handleClose"
@closed="handleClosed"
>
<!-- 步骤条 -->
<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-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="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>
<!-- 步骤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>
<!-- 步骤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>
<!-- 步骤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>
<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, 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";
const props = defineProps({
visible: { type: Boolean, default: false },
editData: { type: Object, default: null },
});
const emit = defineEmits(["update:visible", "success"]);
const formRef = ref();
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: "",
status: "1",
is_draft: 0,
remark: "",
invoice_title: "",
tax_number: "",
registered_address: "",
registered_phone: "",
bank_name: "",
bank_account: "",
});
const form = reactive(defaultForm());
// 各步骤的验证规则
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,
(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());
}
}
}
);
function handleClose() {
emit("update:visible", false);
}
function handleClosed() {
formRef.value?.resetFields();
activeStep.value = 0;
}
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: isDraft ? 1 : 0 };
if (internalId.value) {
await updateCrmCustomer(internalId.value, payload);
ElMessage.success(isDraft ? "草稿已保存" : "更新成功");
} else {
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");
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>