484 lines
15 KiB
Vue
484 lines
15 KiB
Vue
<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 class="clickable-steps">
|
||
<el-step title="基本信息" class="step-clickable" @click="goStep(0)" />
|
||
<el-step title="联系信息" class="step-clickable" @click="goStep(1)" />
|
||
<el-step title="开票信息" class="step-clickable" @click="goStep(2)" />
|
||
<el-step title="备注信息" class="step-clickable" @click="goStep(3)" />
|
||
</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-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="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 { createCrmSupplier, updateCrmSupplier } from "@/api/crm";
|
||
import { SUPPLIER_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 = () => ({
|
||
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: "",
|
||
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:基本信息
|
||
{
|
||
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,
|
||
(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--;
|
||
}
|
||
}
|
||
|
||
/** 点击上方步骤条直接跳转(无需按上一步 / 下一步) */
|
||
function goStep(index) {
|
||
const target = Math.max(0, Math.min(3, Number(index) || 0));
|
||
if (target === activeStep.value) return;
|
||
activeStep.value = target;
|
||
}
|
||
|
||
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: isDraft ? 1 : 0 };
|
||
if (internalId.value) {
|
||
await updateCrmSupplier(internalId.value, payload);
|
||
ElMessage.success(isDraft ? "草稿已保存" : "更新成功");
|
||
} else {
|
||
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");
|
||
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;
|
||
}
|
||
|
||
/* 步骤条可点击跳转 */
|
||
.clickable-steps {
|
||
:deep(.step-clickable),
|
||
:deep(.el-step__head),
|
||
:deep(.el-step__main) {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.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>
|