优化合同个管理和产品管理相关功能
This commit is contained in:
@@ -19,6 +19,21 @@
|
||||
<el-input v-model="form.product_no" placeholder="如:P-2026-001" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="行类型" prop="line_type">
|
||||
<el-select v-model="form.line_type" placeholder="请选择" style="width: 100%" @change="onLineTypeChange">
|
||||
<el-option label="成品" value="product" />
|
||||
<el-option label="软件开发" value="dev" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.line_type === 'dev'">
|
||||
<el-form-item label="统一人天单价" prop="dev_unit_price">
|
||||
<el-input-number v-model="form.dev_unit_price" :min="0" :precision="2" :controls="false" style="width: 100%">
|
||||
<template #prefix>¥</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="产品分类" prop="category">
|
||||
<el-select
|
||||
@@ -45,21 +60,29 @@
|
||||
<el-input v-model="form.spec" placeholder="如:型号 / 配置 / 功能模块" maxlength="255" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="24" v-if="form.line_type === 'dev'">
|
||||
<el-form-item label="模块报价" prop="dev_tree">
|
||||
<el-button type="primary" link @click="devVisible = true">
|
||||
📁 编辑模块树({{ (form.dev_tree || []).length }} 项 · ¥{{ formatMoney(treeSellTotal(form.dev_tree, form.dev_unit_price)) }})
|
||||
</el-button>
|
||||
<div class="dev-tip">叶子模块只填「人天」,人天单价统一;父级自动汇总金额</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="form.line_type === 'product'">
|
||||
<el-form-item label="销售单价" prop="price">
|
||||
<el-input-number v-model="form.price" :min="0" :precision="2" :controls="false" style="width: 100%">
|
||||
<template #prefix>¥</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="8" v-if="form.line_type === 'product'">
|
||||
<el-form-item label="成本单价" prop="cost_price">
|
||||
<el-input-number v-model="form.cost_price" :min="0" :precision="2" :controls="false" style="width: 100%">
|
||||
<template #prefix>¥</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="8" v-if="form.line_type === 'product'">
|
||||
<el-form-item label="税率(%)" prop="tax_rate">
|
||||
<el-input-number v-model="form.tax_rate" :min="0" :max="100" :precision="2" :controls="false" style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -78,6 +101,15 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<DevModuleTree
|
||||
v-model="form.dev_tree"
|
||||
:unit-price="form.dev_unit_price"
|
||||
v-model:visible="devVisible"
|
||||
@update:unit-price="(v) => (form.dev_unit_price = v)"
|
||||
:title="form.product_name ? form.product_name + ' - 模块报价' : '软件开发模块报价'"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">保存</el-button>
|
||||
@@ -90,7 +122,9 @@ import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { createProduct, updateProduct } from "@/api/crmProduct";
|
||||
import { getProductCategoryList } from "@/api/crmProductCategory";
|
||||
import { PRODUCT_STATUS_OPTIONS, PRODUCT_UNIT_OPTIONS } from "../../dict";
|
||||
import { PRODUCT_STATUS_OPTIONS, PRODUCT_UNIT_OPTIONS, formatMoney } from "../../dict";
|
||||
import { treeSellTotal } from "../../contract/components/utils";
|
||||
import DevModuleTree from "../../contract/components/DevModuleTree.vue";
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
@@ -103,6 +137,7 @@ const submitting = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const internalId = ref(null);
|
||||
const categoryOptions = ref([]);
|
||||
const devVisible = ref(false);
|
||||
|
||||
const defaultForm = () => ({
|
||||
product_name: "",
|
||||
@@ -110,9 +145,12 @@ const defaultForm = () => ({
|
||||
category: "",
|
||||
unit: "",
|
||||
spec: "",
|
||||
line_type: "product",
|
||||
price: 0,
|
||||
cost_price: 0,
|
||||
tax_rate: 0,
|
||||
dev_unit_price: 0,
|
||||
dev_tree: [],
|
||||
status: 1,
|
||||
remark: "",
|
||||
});
|
||||
@@ -123,6 +161,28 @@ const rules = {
|
||||
product_name: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
|
||||
};
|
||||
|
||||
/** 切换行类型:软件开发确保有模块树容器 */
|
||||
function onLineTypeChange() {
|
||||
if (form.line_type === "dev") {
|
||||
if (!Array.isArray(form.dev_tree)) form.dev_tree = [];
|
||||
if (form.dev_unit_price == null) form.dev_unit_price = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** 安全解析产品管理返回的 dev_tree(可能为字符串或数组) */
|
||||
function normalizeDevTree(v) {
|
||||
if (Array.isArray(v)) return v;
|
||||
if (typeof v === "string" && v) {
|
||||
try {
|
||||
const parsed = JSON.parse(v);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
@@ -133,6 +193,9 @@ watch(
|
||||
Object.assign(form, defaultForm(), props.editData);
|
||||
form.status = Number(props.editData.status);
|
||||
if (isNaN(form.status)) form.status = 1;
|
||||
form.line_type = props.editData.line_type === "dev" ? "dev" : "product";
|
||||
form.dev_unit_price = Number(props.editData.dev_unit_price) || 0;
|
||||
form.dev_tree = normalizeDevTree(props.editData.dev_tree);
|
||||
} else {
|
||||
isEdit.value = false;
|
||||
internalId.value = null;
|
||||
|
||||
Reference in New Issue
Block a user