611 lines
22 KiB
Vue
611 lines
22 KiB
Vue
<template>
|
||
<el-dialog
|
||
:model-value="visible"
|
||
:title="isEdit ? '编辑项目' : '新增项目'"
|
||
width="960px"
|
||
:close-on-click-modal="false"
|
||
@update:model-value="handleClose"
|
||
@opened="handleOpened"
|
||
>
|
||
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px" label-position="right">
|
||
<!-- 项目类型:决定是否需要维护下游(供应商)信息 -->
|
||
<el-form-item label="项目类型" prop="project_type">
|
||
<el-radio-group v-model="form.project_type">
|
||
<el-radio-button :value="1">单一项目</el-radio-button>
|
||
<el-radio-button :value="2">上下游项目</el-radio-button>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">项目概况</el-divider>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="项目名称" prop="project_name">
|
||
<el-input v-model="form.project_name" placeholder="请输入项目名称" maxlength="100" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="项目编号" prop="project_no">
|
||
<el-input v-model="form.project_no" placeholder="请输入项目编号" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="负责人" prop="owner_user_id">
|
||
<el-select v-model="form.owner_user_id" filterable placeholder="请选择负责人" style="width: 100%">
|
||
<el-option v-for="u in userOptions" :key="u.id" :label="u.name" :value="String(u.id)" />
|
||
</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="i in PROJECT_STATUS_OPTIONS" :key="i.value" :label="i.label" :value="Number(i.value)" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col v-if="isBackToBack" :span="12">
|
||
<el-form-item label="项目毛利">
|
||
<span class="profit-value">¥{{ formatMoney(profitAmount) }}</span>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="24">
|
||
<el-form-item label="项目地址" prop="address">
|
||
<el-input v-model="form.address" placeholder="请输入项目地址" maxlength="255" />
|
||
</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="请输入备注(选填)" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 单一项目不存在上下游概念,只展示客户信息;上下游项目才区分上游(客户侧) -->
|
||
<el-divider content-position="left">{{ isBackToBack ? "上游信息" : "客户信息" }}</el-divider>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="客户" prop="customer_id">
|
||
<el-select
|
||
v-model="form.customer_id"
|
||
filterable
|
||
remote
|
||
clearable
|
||
:remote-method="searchCustomers"
|
||
:loading="customerLoading"
|
||
placeholder="输入客户名称搜索"
|
||
style="width: 100%"
|
||
>
|
||
<el-option v-for="c in customerOptions" :key="c.id" :label="c.customer_name" :value="c.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item :label="isBackToBack ? '上游金额' : '项目金额'" prop="upstream_amount">
|
||
<el-input v-model="form.upstream_amount" placeholder="对客户的收入金额">
|
||
<template #append>元</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item :label="isBackToBack ? '上游对接人' : '对接人'" prop="upstream_contact">
|
||
<!-- 对接人从该客户已有联系人中带出(自动带出电话),不支持手工输入 -->
|
||
<el-select
|
||
v-model="form.upstream_contact"
|
||
filterable
|
||
clearable
|
||
:loading="upstreamContactLoading"
|
||
:disabled="!form.customer_id"
|
||
:placeholder="form.customer_id ? '选择客户联系人' : '请先选择客户'"
|
||
style="width: 100%"
|
||
@change="onUpstreamContactPick"
|
||
>
|
||
<el-option v-for="c in upstreamContacts" :key="c.id" :label="c.contact_name" :value="c.contact_name">
|
||
<span>{{ c.contact_name }}</span>
|
||
<span class="opt-meta">
|
||
{{ [c.position || c.department, c.mobile || c.phone].filter(Boolean).join(" · ") }}
|
||
</span>
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item :label="isBackToBack ? '上游联系电话' : '联系电话'" prop="upstream_phone">
|
||
<el-input v-model="form.upstream_phone" placeholder="客户方对接人电话" maxlength="20" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 下游信息:仅上下游项目需要维护 -->
|
||
<template v-if="isBackToBack">
|
||
<el-divider content-position="left">下游信息</el-divider>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="供应商/客户" prop="supplier_name">
|
||
<el-autocomplete
|
||
v-model="form.supplier_name"
|
||
clearable
|
||
value-key="supplier_name"
|
||
:fetch-suggestions="querySuppliers"
|
||
placeholder="输入名称搜索供应商库 / 客户库,没有则直接填写"
|
||
style="width: 100%"
|
||
@focus="searchSuppliers(form.supplier_name || '')"
|
||
@select="applySupplier"
|
||
@blur="() => applySupplier(null)"
|
||
>
|
||
<template #default="{ item }">
|
||
<span>{{ item.supplier_name }}</span>
|
||
<el-tag size="small" effect="plain" :type="Number(item.ref_type) === 1 ? 'success' : 'warning'">
|
||
{{ Number(item.ref_type) === 1 ? "客户" : "供应商" }}
|
||
</el-tag>
|
||
</template>
|
||
</el-autocomplete>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="下游金额" prop="downstream_amount">
|
||
<el-input v-model="form.downstream_amount" placeholder="支付给供应商的成本">
|
||
<template #append>元</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="下游对接人" prop="downstream_contact">
|
||
<!-- 对接人从下游主体(供应商 / 客户)已有联系人中带出,不支持手工输入 -->
|
||
<el-select
|
||
v-model="form.downstream_contact"
|
||
filterable
|
||
clearable
|
||
:loading="downstreamContactLoading"
|
||
:disabled="!form.supplier_id"
|
||
:placeholder="form.supplier_id ? '选择对方联系人' : '请先选择供应商/客户'"
|
||
style="width: 100%"
|
||
@change="onDownstreamContactPick"
|
||
>
|
||
<el-option v-for="c in downstreamContacts" :key="c.id" :label="c.contact_name" :value="c.contact_name">
|
||
<span>{{ c.contact_name }}</span>
|
||
<span class="opt-meta">
|
||
{{ [c.position || c.department, c.mobile || c.phone].filter(Boolean).join(" · ") }}
|
||
</span>
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="下游联系电话" prop="downstream_phone">
|
||
<el-input v-model="form.downstream_phone" placeholder="供应商方对接人电话" maxlength="20" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</template>
|
||
</el-form>
|
||
|
||
<!-- <el-divider content-position="left">
|
||
产品清单
|
||
<span class="tip">保存项目时,产品参数将自动写入「产品管理」</span>
|
||
</el-divider>
|
||
<ProductList :products="form.products" /> -->
|
||
|
||
<template #footer>
|
||
<el-button @click="handleClose">取消</el-button>
|
||
<el-button type="primary" :loading="submitting" @click="handleSubmit">保存</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref, reactive, watch } from "vue";
|
||
import { ElMessage } from "element-plus";
|
||
import { createProject, updateProject } from "@/api/crmPipeline";
|
||
import { getProductList } from "@/api/crmProduct";
|
||
import { getCrmCustomerList, getCrmSupplierList } from "@/api/crm";
|
||
import { listContacts } from "@/api/contact";
|
||
import { getAllUsers } from "@/api/user";
|
||
import { useAuthStore } from "@/stores/auth";
|
||
import { PROJECT_STATUS_OPTIONS, formatMoney } from "../../dict";
|
||
import ProductList from "../../contract/components/ProductList.vue";
|
||
|
||
const props = defineProps({
|
||
visible: { type: Boolean, default: false },
|
||
editData: { type: Object, default: null },
|
||
});
|
||
const emit = defineEmits(["update:visible", "success"]);
|
||
|
||
const authStore = useAuthStore();
|
||
const formRef = ref();
|
||
const submitting = ref(false);
|
||
const isEdit = ref(false);
|
||
const internalId = ref(null);
|
||
const userOptions = ref([]);
|
||
const customerOptions = ref([]);
|
||
const customerLoading = ref(false);
|
||
|
||
const defaultForm = () => ({
|
||
project_name: "",
|
||
project_no: "",
|
||
// 项目类型:1=单一项目(仅上游) 2=上下游项目(上游客户 + 下游供应商)
|
||
project_type: 1,
|
||
customer_id: "",
|
||
customer_name: "",
|
||
supplier_id: 0,
|
||
supplier_ref_type: 0,
|
||
supplier_name: "",
|
||
supplier_industry: "",
|
||
owner_user_id: "",
|
||
owner_user_name: "",
|
||
industry: "",
|
||
upstream_amount: "",
|
||
downstream_amount: "",
|
||
upstream_contact: "",
|
||
upstream_phone: "",
|
||
downstream_contact: "",
|
||
downstream_phone: "",
|
||
status: 1,
|
||
start_date: "",
|
||
end_date: "",
|
||
address: "",
|
||
remark: "",
|
||
products: [],
|
||
});
|
||
|
||
const form = reactive(defaultForm());
|
||
|
||
/** 是否上下游项目:需要维护下游(供应商)信息 */
|
||
const isBackToBack = computed(() => Number(form.project_type) === 2);
|
||
|
||
/** 项目毛利:上游金额 − 下游金额(仅上下游项目有意义) */
|
||
const profitAmount = computed(
|
||
() => (Number(form.upstream_amount) || 0) - (Number(form.downstream_amount) || 0)
|
||
);
|
||
|
||
/** 切换为单一项目时清空下游信息,避免残留数据造成误解 */
|
||
watch(
|
||
() => form.project_type,
|
||
(val) => {
|
||
if (Number(val) === 2) return;
|
||
form.supplier_id = 0;
|
||
form.supplier_ref_type = 0;
|
||
form.supplier_name = "";
|
||
form.supplier_industry = "";
|
||
form.downstream_amount = "";
|
||
form.downstream_contact = "";
|
||
form.downstream_phone = "";
|
||
}
|
||
);
|
||
|
||
/** 解析项目产品清单:兼容 JSON 字符串 / 数组 / 空,并补全行内 __key(el-table 的 row-key) */
|
||
function parseProducts(val) {
|
||
let arr = [];
|
||
if (Array.isArray(val)) {
|
||
arr = val;
|
||
} else if (typeof val === "string" && val.trim()) {
|
||
try {
|
||
const parsed = JSON.parse(val);
|
||
if (Array.isArray(parsed)) arr = parsed;
|
||
} catch {
|
||
arr = [];
|
||
}
|
||
}
|
||
// 缺失 __key 时多行会被当作同一行,导致回显不出来
|
||
return arr.map((r, i) => ({
|
||
...r,
|
||
__key: r && r.__key ? r.__key : `row_${i}_${Date.now()}`,
|
||
}));
|
||
}
|
||
|
||
/**
|
||
* 兜底回显:存量项目 / 商机转化项目的 products 可能从未写入(null 或空串),
|
||
* 此时从「产品管理」回查归属本项目的产品,保证编辑时能看到产品清单。
|
||
*/
|
||
async function loadProjectProducts(projectId) {
|
||
try {
|
||
// 后端 pageSize 上限为 100
|
||
const res = await getProductList({ project_id: projectId, page: 1, pageSize: 100 });
|
||
const list = res?.data?.list || [];
|
||
form.products = list.map((p, i) => ({
|
||
__key: `row_p${p.id}_${i}`,
|
||
name: p.product_name || "",
|
||
product_id: p.id,
|
||
category: p.category || "",
|
||
spec: p.spec || "",
|
||
unit: p.unit || "",
|
||
quantity: 1,
|
||
price: Number(p.price) || 0,
|
||
cost_price: Number(p.cost_price) || 0,
|
||
tax_rate: Number(p.tax_rate) || 0,
|
||
remark: p.remark || "",
|
||
}));
|
||
} catch (e) {
|
||
form.products = [];
|
||
}
|
||
}
|
||
|
||
const rules = {
|
||
project_name: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
|
||
};
|
||
|
||
watch(
|
||
() => props.visible,
|
||
(val) => {
|
||
if (val) {
|
||
if (props.editData) {
|
||
isEdit.value = true;
|
||
internalId.value = props.editData.id;
|
||
Object.assign(form, defaultForm(), props.editData);
|
||
form.status = Number(props.editData.status) || 1;
|
||
form.project_type = Number(props.editData.project_type) || 1;
|
||
// 存量项目兼容:只有旧「项目金额 / 对接人」时回填到上游侧
|
||
if (!Number(form.upstream_amount) && Number(props.editData.amount)) {
|
||
form.upstream_amount = props.editData.amount;
|
||
}
|
||
if (!form.upstream_contact) form.upstream_contact = props.editData.contact_person || "";
|
||
if (!form.upstream_phone) form.upstream_phone = props.editData.contact_phone || "";
|
||
// 解析项目已关联的产品清单(后端返回 JSON 字符串)
|
||
const rawProducts = props.editData.products;
|
||
form.products = parseProducts(rawProducts);
|
||
// 从未写入过产品清单(null / 空串,而非 "[]")时,从产品管理回查兜底回显
|
||
const neverSet =
|
||
rawProducts === null || rawProducts === undefined || String(rawProducts).trim() === "";
|
||
if (neverSet && form.products.length === 0 && props.editData.id) {
|
||
loadProjectProducts(props.editData.id);
|
||
}
|
||
} else {
|
||
isEdit.value = false;
|
||
internalId.value = null;
|
||
Object.assign(form, defaultForm());
|
||
form.owner_user_id = authStore.user?.id ? String(authStore.user.id) : "";
|
||
form.owner_user_name = authStore.user?.name || "";
|
||
}
|
||
loadUsers();
|
||
searchCustomers(form.customer_name || "");
|
||
// 对接人候选随所选企业加载(编辑回显时也需带上,赋值未变化时不触发 watch)
|
||
loadUpstreamContacts();
|
||
loadDownstreamContacts();
|
||
}
|
||
}
|
||
);
|
||
|
||
async function loadUsers() {
|
||
if (userOptions.value.length) return;
|
||
try {
|
||
const res = await getAllUsers();
|
||
const data = res?.data || {};
|
||
const list = Array.isArray(data) ? data : data.list || [];
|
||
userOptions.value = list.map((u) => ({ id: u.uid || u.id, name: u.name || u.account || `用户${u.uid || u.id}` }));
|
||
} catch (e) {
|
||
userOptions.value = [];
|
||
}
|
||
}
|
||
|
||
async function searchCustomers(keyword) {
|
||
customerLoading.value = true;
|
||
try {
|
||
const res = await getCrmCustomerList({ page: 1, pageSize: 20, keyword: keyword || "" });
|
||
const data = res?.data || {};
|
||
customerOptions.value = (data.list || []).map((c) => ({ id: c.id, customer_name: c.customer_name }));
|
||
} catch (e) {
|
||
customerOptions.value = [];
|
||
} finally {
|
||
customerLoading.value = false;
|
||
}
|
||
}
|
||
|
||
/* --------------------------- 下游主体(供应商 / 客户) --------------------------- */
|
||
|
||
const supplierOptions = ref([]);
|
||
|
||
/**
|
||
* 下游主体候选:同时检索供应商库与客户库。
|
||
* 同一家单位在系统里可能以客户身份建档,因此下游既支持选供应商,也支持选客户。
|
||
*/
|
||
async function searchSuppliers(keyword) {
|
||
const kw = String(keyword || "").trim();
|
||
try {
|
||
const [supRes, cusRes] = await Promise.all([
|
||
getCrmSupplierList({ page: 1, pageSize: 50, keyword: kw }),
|
||
getCrmCustomerList({ page: 1, pageSize: 50, keyword: kw }),
|
||
]);
|
||
const suppliers = (supRes?.data?.list || []).map((s) => ({
|
||
id: s.id,
|
||
ref_type: 2,
|
||
supplier_name: s.supplier_name || "",
|
||
}));
|
||
const customers = (cusRes?.data?.list || []).map((c) => ({
|
||
id: c.id,
|
||
ref_type: 1,
|
||
supplier_name: c.customer_name || "",
|
||
}));
|
||
supplierOptions.value = [...suppliers, ...customers];
|
||
} catch {
|
||
supplierOptions.value = [];
|
||
}
|
||
}
|
||
|
||
/** 供应商名称输入建议:本地模糊过滤已加载候选(自由输入同样生效) */
|
||
function querySuppliers(query, cb) {
|
||
const kw = String(query || "").trim().toLowerCase();
|
||
const list = (supplierOptions.value || []).filter(
|
||
(s) => !kw || String(s.supplier_name || "").toLowerCase().includes(kw)
|
||
);
|
||
// 附加 value 字段:兼容 autocomplete 默认以 value 作为选中值
|
||
cb(list.slice(0, 50).map((s) => ({ ...s, value: s.supplier_name })));
|
||
}
|
||
|
||
/**
|
||
* 选中的库中主体:记录来源类型(1客户 / 2供应商)与 ID,并写入标准名称;
|
||
* 手工输入且未命中库时清空关联,仅按名称保存。
|
||
*/
|
||
function applySupplier(item) {
|
||
if (item && item.id) {
|
||
form.supplier_id = item.id;
|
||
form.supplier_ref_type = Number(item.ref_type) || 0;
|
||
form.supplier_name = item.supplier_name;
|
||
return;
|
||
}
|
||
const name = String(form.supplier_name || "").trim();
|
||
const hit = supplierOptions.value.find((s) => s.supplier_name === name);
|
||
if (hit) {
|
||
form.supplier_id = hit.id;
|
||
form.supplier_ref_type = Number(hit.ref_type) || 0;
|
||
} else {
|
||
form.supplier_id = 0;
|
||
form.supplier_ref_type = 0;
|
||
}
|
||
}
|
||
|
||
/* --------------------------- 对接人(取自企业联系人) --------------------------- */
|
||
|
||
const upstreamContacts = ref([]);
|
||
const upstreamContactLoading = ref(false);
|
||
const downstreamContacts = ref([]);
|
||
const downstreamContactLoading = ref(false);
|
||
|
||
/** 查询某企业下的联系人:relatedType 1=客户 2=供应商 */
|
||
async function fetchContactsOf(relatedType, relatedID) {
|
||
try {
|
||
const res = await listContacts({
|
||
related_type: relatedType,
|
||
related_id: relatedID,
|
||
page: 1,
|
||
pageSize: 100,
|
||
});
|
||
return res?.data?.list || [];
|
||
} catch {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
/** 上游对接人候选:所选客户(上游)的联系人 */
|
||
async function loadUpstreamContacts() {
|
||
const id = Number(form.customer_id) || 0;
|
||
if (!id) {
|
||
upstreamContacts.value = [];
|
||
return;
|
||
}
|
||
upstreamContactLoading.value = true;
|
||
try {
|
||
upstreamContacts.value = await fetchContactsOf(1, id);
|
||
} finally {
|
||
upstreamContactLoading.value = false;
|
||
}
|
||
}
|
||
|
||
/** 下游对接人候选:所选下游主体(供应商 / 客户)的联系人 */
|
||
async function loadDownstreamContacts() {
|
||
const id = Number(form.supplier_id) || 0;
|
||
if (!id) {
|
||
downstreamContacts.value = [];
|
||
return;
|
||
}
|
||
downstreamContactLoading.value = true;
|
||
try {
|
||
downstreamContacts.value = await fetchContactsOf(Number(form.supplier_ref_type) === 1 ? 1 : 2, id);
|
||
} finally {
|
||
downstreamContactLoading.value = false;
|
||
}
|
||
}
|
||
|
||
/** 选择上游对接人:自动带出联系电话 */
|
||
function onUpstreamContactPick(name) {
|
||
const hit = upstreamContacts.value.find((c) => c.contact_name === name);
|
||
if (hit) form.upstream_phone = hit.mobile || hit.phone || "";
|
||
}
|
||
|
||
/** 选择下游对接人:自动带出联系电话 */
|
||
function onDownstreamContactPick(name) {
|
||
const hit = downstreamContacts.value.find((c) => c.contact_name === name);
|
||
if (hit) form.downstream_phone = hit.mobile || hit.phone || "";
|
||
}
|
||
|
||
// 客户 / 下游主体变化时刷新对接人候选(不清空已选值,兼容历史手工数据)
|
||
watch(() => form.customer_id, loadUpstreamContacts);
|
||
watch(() => [form.supplier_id, form.supplier_ref_type], loadDownstreamContacts);
|
||
|
||
function handleClose() {
|
||
emit("update:visible", false);
|
||
}
|
||
|
||
function handleOpened() {
|
||
formRef.value?.clearValidate();
|
||
}
|
||
|
||
async function handleSubmit() {
|
||
if (!formRef.value) return;
|
||
try {
|
||
await formRef.value.validate();
|
||
} catch (e) {
|
||
return;
|
||
}
|
||
const owner = userOptions.value.find((u) => String(u.id) === String(form.owner_user_id));
|
||
if (owner) form.owner_user_name = owner.name;
|
||
submitting.value = true;
|
||
try {
|
||
const upstream = Number(form.upstream_amount) || 0;
|
||
// 仅提交新字段(旧 amount / contact_* 不再使用,由后端按上游侧同步)
|
||
const payload = {
|
||
project_name: form.project_name,
|
||
project_no: form.project_no,
|
||
project_type: Number(form.project_type) || 1,
|
||
customer_id: Number(form.customer_id) || 0,
|
||
customer_name: form.customer_name,
|
||
supplier_id: Number(form.supplier_id) || 0,
|
||
supplier_ref_type: Number(form.supplier_ref_type) || 0,
|
||
supplier_name: form.supplier_name,
|
||
supplier_industry: form.supplier_industry,
|
||
owner_user_id: form.owner_user_id,
|
||
owner_user_name: form.owner_user_name,
|
||
industry: form.industry,
|
||
upstream_amount: upstream,
|
||
downstream_amount: isBackToBack.value ? Number(form.downstream_amount) || 0 : 0,
|
||
amount: upstream, // 兼容旧字段
|
||
status: Number(form.status) || 1,
|
||
start_date: form.start_date,
|
||
end_date: form.end_date,
|
||
upstream_contact: form.upstream_contact,
|
||
upstream_phone: form.upstream_phone,
|
||
downstream_contact: isBackToBack.value ? form.downstream_contact : "",
|
||
downstream_phone: isBackToBack.value ? form.downstream_phone : "",
|
||
address: form.address,
|
||
remark: form.remark,
|
||
products: JSON.stringify(form.products || []),
|
||
};
|
||
if (internalId.value) {
|
||
await updateProject(internalId.value, payload);
|
||
ElMessage.success("更新成功");
|
||
} else {
|
||
await createProject(payload);
|
||
ElMessage.success("创建成功");
|
||
}
|
||
emit("success");
|
||
handleClose();
|
||
} catch (e) {
|
||
ElMessage.error(e.message || "操作失败");
|
||
} finally {
|
||
submitting.value = false;
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.tip {
|
||
font-size: 12px;
|
||
color: var(--el-text-color-secondary);
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.type-tip {
|
||
margin-left: 8px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-secondary);
|
||
}
|
||
|
||
.profit-value {
|
||
font-weight: 600;
|
||
color: var(--el-color-danger);
|
||
}
|
||
|
||
.opt-meta {
|
||
margin-left: 8px;
|
||
font-size: 12px;
|
||
color: var(--el-text-color-secondary);
|
||
}
|
||
</style>
|