feat(project):重构项目管理模块并新增订单管理功能

- 移除项目表单内联代码,改为独立的 ProjectForm 组件- 引入 ProjectDetailDrawer 组件用于展示项目详情- 移除旧的项目编辑对话框及相关逻辑
- 新增订单管理页面,支持查询、新增、编辑、删除订单
- 实现订单列表分页及多条件筛选功能
- 添加订单导出功能
- 实现项目详情抽屉在订单模块中的复用
-优化项目查询逻辑,移除冗余的地区与行业选项处理- 更新项目表单交互方式,通过抽屉和独立表单组件进行操作
dev_1.0.0
chenhao 2025-11-12 20:52:38 +08:00
parent 51d24b5dec
commit dbacc884be
8 changed files with 2711 additions and 548 deletions

View File

@ -0,0 +1,53 @@
import request from '@/utils/request'
// 查询订单管理列表
export function listOrder(query) {
return request({
url: '/project/order/vue/list',
method: 'get',
params: query
})
}
// 查询订单管理详细信息
export function getOrder(id) {
return request({
url: '/project/order/vue/' + id,
method: 'get'
})
}
// 新增订单管理
export function addOrder(data) {
return request({
url: '/project/order/vue',
method: 'post',
data: data
})
}
// 修改订单管理
export function updateOrder(data) {
return request({
url: '/project/order/vue',
method: 'put',
data: data
})
}
// 删除订单管理
export function delOrder(id) {
return request({
url: '/project/order/vue/' + id,
method: 'delete'
})
}
// 导出订单管理
export function exportOrder(query) {
return request({
url: '/project/order/vue/export',
method: 'post',
data: query
})
}

View File

@ -0,0 +1,577 @@
<template>
<div class="product-config">
<div class="section-title">配置信息</div>
<!-- 软件产品 -->
<div class="product-section">
<div class="add-btn-div">
<h3>软件产品</h3>
<el-button type="primary" size="small" icon="el-icon-plus" @click="addProduct('software')" :disabled="isActionDisabled">添加</el-button>
</div>
<el-table :data="softwareList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="产品编码" width="120" prop="productBomCode">
<template slot-scope="scope">
<el-input v-model="scope.row.productBomCode" readonly size="small" @click.native="!isActionDisabled && selectProduct('software', scope.$index)"></el-input>
</template>
</el-table-column>
<el-table-column label="产品型号" width="150" prop="model">
<template slot-scope="scope">
<el-input v-model="scope.row.model" readonly size="small" @click.native="!isActionDisabled && selectProduct('software', scope.$index)"></el-input>
</template>
</el-table-column>
<el-table-column label="描述" width="250" prop="productDesc">
<template slot-scope="scope">
<el-input v-model="scope.row.productDesc" type="textarea" :rows="2" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="数量" width="100" prop="quantity">
<template slot-scope="scope">
<el-input v-model="scope.row.quantity" type="number" :min="0" size="small" @input="calculateRow(scope.row)" style="width: 100%;" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="目录单价(¥)" width="120" prop="cataloguePrice">
<template slot-scope="scope">
<el-input v-model="scope.row.cataloguePriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="指导折扣(%)" width="100" prop="guidanceDiscount">
<template slot-scope="scope">
<el-input v-model="scope.row.guidanceDiscountFormat" readonly size="small">
</el-input>
</template>
</el-table-column>
<el-table-column label="折扣(%)" width="100" prop="discount">
<template slot-scope="scope">
<el-input v-model="scope.row.discountFormat" size="small" @blur="handleDiscountChange(scope.row)" style="width: 100%;" :readonly="readonly" :disabled="disabled">
</el-input>
</template>
</el-table-column>
<el-table-column label="单价(¥)" width="120" prop="price">
<template slot-scope="scope">
<el-input v-model="scope.row.priceFormat" size="small" @blur="handlePriceChange(scope.row)" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="总价(¥)" width="120" prop="allPrice">
<template slot-scope="scope">
<el-input v-model="scope.row.allPriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="目录总价(¥)" width="130" prop="catalogueAllPrice">
<template slot-scope="scope">
<el-input v-model="scope.row.catalogueAllPriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="税率(%)" width="100" prop="taxRate">
<template slot-scope="scope">
<el-input v-model="scope.row.taxRate" type="number" :min="0" :max="100" size="small" style="width: 100%;" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="备注" width="150" prop="remark">
<template slot-scope="scope">
<el-input v-model="scope.row.remark" size="small" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="80" fixed="right" align="center">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteProduct('software', scope.$index)" style="color: #F56C6C;" :disabled="isActionDisabled">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 硬件产品 -->
<div class="product-section">
<div class="add-btn-div">
<h3>硬件产品</h3>
<el-button type="primary" size="small" icon="el-icon-plus" @click="addProduct('hardware')" :disabled="isActionDisabled">添加</el-button>
</div>
<el-table :data="hardwareList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="产品编码" width="120" prop="productBomCode">
<template slot-scope="scope">
<el-input v-model="scope.row.productBomCode" readonly size="small" @click.native="!isActionDisabled && selectProduct('hardware', scope.$index)"></el-input>
</template>
</el-table-column>
<el-table-column label="产品型号" width="150" prop="model">
<template slot-scope="scope">
<el-input v-model="scope.row.model" readonly size="small" @click.native="!isActionDisabled && selectProduct('hardware', scope.$index)"></el-input>
</template>
</el-table-column>
<el-table-column label="描述" width="250" prop="productDesc">
<template slot-scope="scope">
<el-input v-model="scope.row.productDesc" type="textarea" :rows="2" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="数量" width="100" prop="quantity">
<template slot-scope="scope">
<el-input v-model="scope.row.quantity" type="number" :min="0" size="small" @input="calculateRow(scope.row)" style="width: 100%;" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="目录单价(¥)" width="120" prop="cataloguePrice">
<template slot-scope="scope">
<el-input v-model="scope.row.cataloguePriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="指导折扣(%)" width="100" prop="guidanceDiscount">
<template slot-scope="scope">
<el-input v-model="scope.row.guidanceDiscountFormat" readonly size="small">
</el-input>
</template>
</el-table-column>
<el-table-column label="折扣(%)" width="100" prop="discount">
<template slot-scope="scope">
<el-input v-model="scope.row.discountFormat" size="small" @blur="handleDiscountChange(scope.row)" style="width: 100%;" :readonly="readonly" :disabled="disabled">
</el-input>
</template>
</el-table-column>
<el-table-column label="单价(¥)" width="120" prop="price">
<template slot-scope="scope">
<el-input v-model="scope.row.priceFormat" size="small" @blur="handlePriceChange(scope.row)" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="总价(¥)" width="120" prop="allPrice">
<template slot-scope="scope">
<el-input v-model="scope.row.allPriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="目录总价(¥)" width="130" prop="catalogueAllPrice">
<template slot-scope="scope">
<el-input v-model="scope.row.catalogueAllPriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="税率(%)" width="100" prop="taxRate">
<template slot-scope="scope">
<el-input v-model="scope.row.taxRate" type="number" :min="0" :max="100" size="small" style="width: 100%;" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="备注" width="150" prop="remark">
<template slot-scope="scope">
<el-input v-model="scope.row.remark" size="small" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="80" fixed="right" align="center">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteProduct('hardware', scope.$index)" style="color: #F56C6C;" :disabled="isActionDisabled">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 服务产品 -->
<div class="product-section">
<div class="add-btn-div">
<h3>服务产品</h3>
<el-button type="primary" size="small" icon="el-icon-plus" @click="addProduct('maintenance')" :disabled="isActionDisabled">添加</el-button>
</div>
<el-table :data="maintenanceList" border style="width: 100%">
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="产品编码" width="120" prop="productBomCode">
<template slot-scope="scope">
<el-input v-model="scope.row.productBomCode" readonly size="small" @click.native="!isActionDisabled && selectProduct('maintenance', scope.$index)"></el-input>
</template>
</el-table-column>
<el-table-column label="产品型号" width="150" prop="model">
<template slot-scope="scope">
<el-input v-model="scope.row.model" readonly size="small" @click.native="!isActionDisabled && selectProduct('maintenance', scope.$index)"></el-input>
</template>
</el-table-column>
<el-table-column label="描述" width="250" prop="productDesc">
<template slot-scope="scope">
<el-input v-model="scope.row.productDesc" type="textarea" :rows="2" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="数量" width="100" prop="quantity">
<template slot-scope="scope">
<el-input v-model="scope.row.quantity" type="number" :min="0" size="small" @input="calculateRow(scope.row)" style="width: 100%;" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="目录单价(¥)" width="120" prop="cataloguePrice">
<template slot-scope="scope">
<el-input v-model="scope.row.cataloguePriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="指导折扣(%)" width="100" prop="guidanceDiscount">
<template slot-scope="scope">
<el-input v-model="scope.row.guidanceDiscountFormat" readonly size="small">
</el-input>
</template>
</el-table-column>
<el-table-column label="折扣(%)" width="100" prop="discount">
<template slot-scope="scope">
<el-input v-model="scope.row.discountFormat" size="small" @blur="handleDiscountChange(scope.row)" style="width: 100%;" :readonly="readonly" :disabled="disabled">
</el-input>
</template>
</el-table-column>
<el-table-column label="单价(¥)" width="120" prop="price">
<template slot-scope="scope">
<el-input v-model="scope.row.priceFormat" size="small" @blur="handlePriceChange(scope.row)" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="总价(¥)" width="120" prop="allPrice">
<template slot-scope="scope">
<el-input v-model="scope.row.allPriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="目录总价(¥)" width="130" prop="catalogueAllPrice">
<template slot-scope="scope">
<el-input v-model="scope.row.catalogueAllPriceFormat" readonly size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="税率(%)" width="100" prop="taxRate">
<template slot-scope="scope">
<el-input v-model="scope.row.taxRate" type="number" :min="0" :max="100" size="small" style="width: 100%;" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="备注" width="150" prop="remark">
<template slot-scope="scope">
<el-input v-model="scope.row.remark" size="small" :readonly="readonly" :disabled="disabled"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="80" fixed="right" align="center">
<template slot-scope="scope">
<el-button type="text" size="small" @click="deleteProduct('maintenance', scope.$index)" style="color: #F56C6C;" :disabled="isActionDisabled">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 产品选择对话框 -->
<select-product
:visible.sync="selectProductVisible"
:product-type="currentProductType"
@product-selected="handleProductSelected"
/>
</div>
</template>
<script>
import SelectProduct from '@/views/system/product/selectProduct.vue';
export default {
name: 'ProductConfig',
components: {
SelectProduct
},
props: {
value: {
type: Object,
default: () => ({
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
})
},
disabled: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
}
},
data() {
return {
softwareList: [],
hardwareList: [],
maintenanceList: [],
selectProductVisible: false,
currentProductType: '1', // 1: 2: 11,22,99:
currentEditType: '', // software, hardware, maintenance
currentEditIndex: -1
};
},
computed: {
isActionDisabled() {
return this.disabled || this.readonly;
}
},
watch: {
value: {
handler(val) {
if (val) {
this.initData(val);
}
},
immediate: true,
deep: true
}
},
methods: {
initData(data) {
//
this.softwareList = (data.softwareProjectProductInfoList || []).map(item => this.formatProduct(item));
//
this.hardwareList = (data.hardwareProjectProductInfoList || []).map(item => this.formatProduct(item));
//
this.maintenanceList = (data.maintenanceProjectProductInfoList || []).map(item => this.formatProduct(item));
},
formatProduct(item) {
return {
...item,
quantity: item.quantity || 0,
taxRate: item.taxRate || 13,
cataloguePriceFormat: this.formatAmount(item.cataloguePrice),
guidanceDiscountFormat: item.guidanceDiscount ? this.preciseCurrencyRound(item.guidanceDiscount * 100, 2) : '',
discountFormat: item.discount ? this.preciseCurrencyRound(item.discount * 100, 2) : '',
priceFormat: this.formatAmount(item.price),
allPriceFormat: this.formatAmount(item.allPrice),
catalogueAllPriceFormat: this.formatAmount(item.catalogueAllPrice)
};
},
addProduct(type) {
const newProduct = {
id: null,
productBomCode: '',
model: '',
productDesc: '',
quantity: 0,
cataloguePrice: 0,
cataloguePriceFormat: '',
guidanceDiscount: 0,
guidanceDiscountFormat: '',
discount: 0,
discountFormat: '',
price: 0,
priceFormat: '',
allPrice: 0,
allPriceFormat: '',
catalogueAllPrice: 0,
catalogueAllPriceFormat: '',
taxRate: 13,
remark: ''
};
this.calculateRow(newProduct);
if (type === 'software') {
this.softwareList.push(newProduct);
} else if (type === 'hardware') {
this.hardwareList.push(newProduct);
} else if (type === 'maintenance') {
this.maintenanceList.push(newProduct);
}
this.emitChange();
},
deleteProduct(type, index) {
this.$confirm('是否确认删除该产品?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (type === 'software') {
this.softwareList.splice(index, 1);
} else if (type === 'hardware') {
this.hardwareList.splice(index, 1);
} else if (type === 'maintenance') {
this.maintenanceList.splice(index, 1);
}
this.emitChange();
this.$message.success('删除成功');
}).catch(() => {});
},
selectProduct(type, index) {
this.currentEditType = type;
this.currentEditIndex = index;
//
if (type === 'software') {
this.currentProductType = '1';
} else if (type === 'hardware') {
this.currentProductType = '2';
} else if (type === 'maintenance') {
this.currentProductType = '11,22,99';
}
this.selectProductVisible = true;
},
handleProductSelected(product) {
let list = [];
if (this.currentEditType === 'software') {
list = this.softwareList;
} else if (this.currentEditType === 'hardware') {
list = this.hardwareList;
} else if (this.currentEditType === 'maintenance') {
list = this.maintenanceList;
}
const row = list[this.currentEditIndex];
row.productBomCode = product.productCode;
row.model = product.model;
row.productDesc = product.description;
row.cataloguePrice = product.cataloguePrice;
row.cataloguePriceFormat = this.formatAmount(product.cataloguePrice);
row.guidanceDiscount = product.guidanceDiscount;
row.guidanceDiscountFormat = product.guidanceDiscount ? this.preciseCurrencyRound(product.guidanceDiscount * 100, 2) : '';
row.discount = product.guidanceDiscount || 0;
row.discountFormat = product.guidanceDiscount ? this.preciseCurrencyRound(product.guidanceDiscount * 100, 2) : '';
this.calculateRow(row);
this.emitChange();
},
handleDiscountChange(row) {
// 0-100
let value = parseFloat(row.discountFormat);
if (isNaN(value)) {
value = 0;
} else if (value < 0) {
value = 0;
} else if (value > 100) {
value = 100;
}
row.discountFormat = value;
this.calculateRow(row);
},
handlePriceChange(row) {
//
let price = parseFloat(String(row.priceFormat).replace(/,/g, ''));
if (isNaN(price)) {
price = 0;
}
row.price = this.preciseCurrencyRound(price, 2);
row.priceFormat = this.formatAmount(row.price);
// 0
if (row.cataloguePrice && row.cataloguePrice > 0) {
const discount = row.price / row.cataloguePrice;
row.discount = this.preciseCurrencyRound(discount, 4); // 4
row.discountFormat = this.preciseCurrencyRound(discount * 100, 2); // 2
} else {
// 00
row.discount = 0;
row.discountFormat = 0;
}
//
row.allPrice = this.preciseCurrencyRound(row.price * row.quantity, 2);
row.allPriceFormat = this.formatAmount(row.allPrice);
row.catalogueAllPrice = this.preciseCurrencyRound(row.cataloguePrice * row.quantity, 2);
row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice);
this.emitChange();
},
calculateRow(row) {
if (this.isActionDisabled){
return;
}
// = *
const discount = row.discountFormat ? row.discountFormat / 100 : 0;
row.discount = this.preciseCurrencyRound(discount, 4);
row.price = this.preciseCurrencyRound(row.cataloguePrice * discount, 2);
row.priceFormat = this.formatAmount(row.price);
// = *
row.allPrice = this.preciseCurrencyRound(row.price * row.quantity, 2);
row.allPriceFormat = this.formatAmount(row.allPrice);
// = *
row.catalogueAllPrice = this.preciseCurrencyRound(row.cataloguePrice * row.quantity, 2);
row.catalogueAllPriceFormat = this.formatAmount(row.catalogueAllPrice);
this.emitChange();
},
formatAmount(value) {
if (value === null || value === undefined || value === '') return '';
return Number(value).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
},
preciseCurrencyRound(value, decimals = 2) {
return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
},
emitChange() {
const data = {
softwareProjectProductInfoList: this.softwareList.map(item => ({
...item,
cataloguePriceFormat: undefined,
guidanceDiscountFormat: undefined,
discountFormat: undefined,
priceFormat: undefined,
allPriceFormat: undefined,
catalogueAllPriceFormat: undefined
})),
hardwareProjectProductInfoList: this.hardwareList.map(item => ({
...item,
cataloguePriceFormat: undefined,
guidanceDiscountFormat: undefined,
discountFormat: undefined,
priceFormat: undefined,
allPriceFormat: undefined,
catalogueAllPriceFormat: undefined
})),
maintenanceProjectProductInfoList: this.maintenanceList.map(item => ({
...item,
cataloguePriceFormat: undefined,
guidanceDiscountFormat: undefined,
discountFormat: undefined,
priceFormat: undefined,
allPriceFormat: undefined,
catalogueAllPriceFormat: undefined
}))
};
this.$emit('input', data);
this.$emit('change', data);
},
getData() {
return {
softwareProjectProductInfoList: this.softwareList,
hardwareProjectProductInfoList: this.hardwareList,
maintenanceProjectProductInfoList: this.maintenanceList
};
}
}
};
</script>
<style scoped lang="scss">
.product-config {
margin-top: 20px;
.section-title {
font-weight: bold;
font-size: 18px;
color: #0075ff;
margin-bottom: 20px;
}
.product-section {
margin-bottom: 30px;
.add-btn-div {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
h3 {
margin: 0;
font-size: 16px;
font-weight: bold;
}
}
}
::v-deep .el-table {
font-size: 12px;
.el-input__inner,
.el-textarea__inner {
font-size: 12px;
}
.el-input-number {
width: 100%;
}
}
}
</style>

View File

@ -0,0 +1,470 @@
<template>
<el-drawer
title="项目详情"
:visible.sync="localVisible"
direction="rtl"
size="70%"
@close="handleClose"
:with-header="true"
>
<div class="drawer-container">
<el-form ref="form" :model="form" label-width="120px">
<el-tabs v-model="activeTab">
<el-tab-pane label="项目信息" name="projectInfo">
<div class="tab-content">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="form.projectCode" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="form.projectName" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="BG" prop="bgProperty">
<el-select v-model="form.bgProperty" :disabled="true" style="width: 100%;">
<el-option
v-for="dict in dict.type.bg_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行业" prop="industryType">
<el-select v-model="form.industryType" :disabled="true" style="width: 100%;">
<el-option
v-for="item in industryOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="代表处" prop="agentName">
<el-input v-model="form.agentName" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="项目阶段" prop="projectStage">
<el-select v-model="form.projectStage" :disabled="true" style="width: 100%;">
<el-option
v-for="dict in dict.type.project_stage"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目把握度" prop="projectGraspDegree">
<el-select v-model="form.projectGraspDegree" :disabled="true" style="width: 100%;">
<el-option label="A" value="A" />
<el-option label="B" value="B" />
<el-option label="C" value="C" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="汇智负责人" prop="hzSupportUserName">
<el-input v-model="form.hzSupportUserName" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="最终客户" prop="customerName">
<el-input v-model="form.customerName" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户联系人" prop="customerUserName">
<el-input v-model="form.customerUserName" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最终客户 TEL" prop="customerPhone">
<el-input v-model="form.customerPhone" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="运作方" prop="operateInstitution">
<el-select v-model="form.operateInstitution" :disabled="true" style="width: 100%;">
<el-option
v-for="dict in dict.type.operate_institution"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="新华三联系人" prop="h3cPerson">
<el-input v-model="form.h3cPerson" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="新华三TEL" prop="h3cPhone">
<el-input v-model="form.h3cPhone" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="form.partnerName" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商联系人" prop="partnerUserName">
<el-input v-model="form.partnerUserName" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商 TEL" prop="contactWay">
<el-input v-model="form.contactWay" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="预计金额(元)" prop="estimatedAmount">
<el-input v-model="form.estimatedAmount" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计下单时间" prop="estimatedOrderTime">
<el-date-picker clearable
v-model="form.estimatedOrderTime"
type="date"
value-format="yyyy-MM-dd"
style="width: 100%;"
readonly>
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="POC测试" prop="poc">
<el-select v-model="form.poc" :disabled="true" style="width: 100%;">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否国产" prop="countryProduct">
<el-select v-model="form.countryProduct" :disabled="true" style="width: 100%;">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="竞争对手" >
<el-input :value="form.competitor" readonly />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="关键技术问题" prop="keyProblem">
<el-input v-model="form.keyProblem" type="textarea" readonly />
</el-form-item>
<el-form-item label="项目简述" prop="projectDesc">
<el-input v-model="form.projectDesc" type="textarea" readonly />
</el-form-item>
<el-form-item label="服务器配置" prop="serverConfiguration">
<el-input v-model="form.serverConfiguration" type="textarea" readonly />
</el-form-item>
</div>
</el-tab-pane>
<el-tab-pane label="配置信息" name="productConfig">
<div class="tab-content">
<product-config :value="form.productConfig" :readonly="true" />
</div>
</el-tab-pane>
<el-tab-pane label="工作进度" name="workProgress">
<div class="tab-content">
<el-table :data="form.projectWorkProgressList" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="更新内容" prop="workContent" />
<el-table-column label="更新人员" prop="userName" width="120" align="center" />
<el-table-column label="更新时间" prop="workTime" width="180" align="center">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.workTime) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="POC 记录" name="pocRecord" v-if="form.poc === '1'">
<div class="tab-content">
<el-row>
<el-col :span="24">
<el-form-item label="服务器配置" prop="projectPocInfo.serverConfig">
<el-input v-model="form.projectPocInfo.serverConfig" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="云桌面版本" prop="projectPocInfo.vdiVersion">
<el-input v-model="form.projectPocInfo.vdiVersion" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="配置终端" prop="projectPocInfo.terminalConfig">
<el-input v-model="form.projectPocInfo.terminalConfig" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作系统" prop="projectPocInfo.operateSystem">
<el-input v-model="form.projectPocInfo.operateSystem" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="H3C接口人" prop="projectPocInfo.h3cPerson">
<el-input v-model="form.projectPocInfo.h3cPerson" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="H3C TEL" prop="projectPocInfo.h3cPhone">
<el-input v-model="form.projectPocInfo.h3cPhone" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="汇智接口人" prop="projectPocInfo.hzInterfacePerson">
<el-input v-model="form.projectPocInfo.hzInterfacePerson" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="汇智 TEL" prop="projectPocInfo.hzInterfacePhone">
<el-input v-model="form.projectPocInfo.hzInterfacePhone" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="研发接口人" prop="projectPocInfo.processPerson">
<el-input v-model="form.projectPocInfo.processPerson" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="研发 TEL" prop="projectPocInfo.processPhone">
<el-input v-model="form.projectPocInfo.processPhone" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="现场接口人" prop="projectPocInfo.handlePerson">
<el-input v-model="form.projectPocInfo.handlePerson" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="现场 TEL" prop="projectPocInfo.handlePhone">
<el-input v-model="form.projectPocInfo.handlePhone" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="启动时间" prop="projectPocInfo.startDate">
<el-date-picker v-model="form.projectPocInfo.startDate" type="date" readonly style="width:100%"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计完成时间" prop="projectPocInfo.planFinishTime">
<el-date-picker v-model="form.projectPocInfo.planFinishTime" type="date" readonly style="width:100%"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实际完成时间" prop="projectPocInfo.realFinishTime">
<el-date-picker v-model="form.projectPocInfo.realFinishTime" type="date" readonly style="width:100%"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<h4 class="form-header">测试进展</h4>
<el-table :data="form.projectPocInfo.projectPocInfoDetailList" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="测试进展" prop="testProgress" />
<el-table-column label="更新人员" prop="createByName" width="120" align="center" />
<el-table-column label="更新时间" prop="createTime" width="180" align="center">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="操作日志" name="operationLog">
<div class="tab-content">
<el-table :data="form.projectOperateLogList" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="操作人员" prop="operateUserName" width="120" align="center" />
<el-table-column label="操作内容" prop="operateLog" />
<el-table-column label="操作时间" prop="operateTime" width="180" align="center">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.operateTime) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</el-form>
</div>
</el-drawer>
</template>
<script>
import { getProject } from "@/api/project/info";
import ProductConfig from "./ProductConfig.vue";
export default {
name: "ProjectDetailDrawer",
components: { ProductConfig },
dicts: ['bg_type', 'bg_yys', 'bg_hysy', 'project_stage', 'operate_institution'],
props: {
visible: {
type: Boolean,
default: false,
},
projectId: {
type: [Number, String],
default: null,
},
},
data() {
return {
form: {
productConfig: {},
projectWorkProgressList: [],
projectOperateLogList: [],
projectPocInfo: {
projectPocInfoDetailList: []
}
},
activeTab: 'projectInfo',
localVisible: this.visible,
industryOptions: [],
};
},
watch: {
visible(val) {
this.localVisible = val;
if (val && this.projectId) {
this.getDetails();
}
},
projectId(val) {
if (val && this.localVisible) {
this.getDetails();
}
}
},
methods: {
getDetails() {
getProject(this.projectId).then(response => {
const projectData = response.data.project;
// Initialize nested objects to avoid "cannot read property of undefined"
projectData.productConfig = {
softwareProjectProductInfoList: projectData.softwareProjectProductInfoList || [],
hardwareProjectProductInfoList: projectData.hardwareProjectProductInfoList || [],
maintenanceProjectProductInfoList: projectData.maintenanceProjectProductInfoList || []
};
projectData.projectWorkProgressList = projectData.projectWorkProgressList || [];
projectData.projectOperateLogList = projectData.projectOperateLogList || [];
projectData.projectPocInfo = projectData.projectPocInfo || { projectPocInfoDetailList: [] };
projectData.projectPocInfo.projectPocInfoDetailList = projectData.projectPocInfo.projectPocInfoDetailList || [];
this.form = projectData;
this.setupIndustryOptions();
});
},
setupIndustryOptions() {
if (this.form.bgProperty === 'YYS') {
this.industryOptions = this.dict.type.bg_yys;
} else if (this.form.bgProperty) {
this.industryOptions = this.dict.type.bg_hysy;
} else {
this.industryOptions = [];
}
},
handleClose() {
this.form = {
productConfig: {},
projectWorkProgressList: [],
projectOperateLogList: [],
projectPocInfo: {
projectPocInfoDetailList: []
}
};
this.activeTab = 'projectInfo';
this.$emit("update:visible", false);
},
// A simple parseTime function placeholder if not globally available
parseTime(time, pattern) {
if (time === undefined || time === null) {
return "";
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
let date;
if (typeof time === 'object') {
date = time;
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time);
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000;
}
date = new Date(time);
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
};
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
if (result.length > 0 && value < 10) {
value = '0' + value;
}
return value || 0;
});
return time_str;
}
},
};
</script>
<style scoped>
.drawer-container {
padding: 0 20px;
height: 100%;
}
.tab-content {
height: calc(100vh - 120px); /* Adjust based on header height */
overflow-y: auto;
padding: 15px;
}
.el-drawer__header {
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,748 @@
<template>
<div>
<!-- 添加或修改项目管理对话框 -->
<el-dialog :title="title" :visible.sync="localVisible" width="80%" append-to-body :close-on-click-modal="false" @close="cancel">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-tabs v-model="activeTab">
<el-tab-pane label="项目信息" name="projectInfo">
<div style="max-height: 60vh; overflow-y: auto; padding: 15px;">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="保存后自动生成" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="form.projectName" placeholder="请输入项目名称" maxlength="40" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="BG" prop="bgProperty">
<el-select v-model="form.bgProperty" placeholder="请选择BG" @change="handleBgChange" style="width: 100%;" :disabled="isFormDisabled">
<el-option
v-for="dict in dict.type.bg_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行业" prop="industryType">
<el-select v-model="form.industryType" placeholder="请选择行业" style="width: 100%;" :disabled="isFormDisabled">
<el-option
v-for="item in industryOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="代表处" prop="agentName">
<el-input v-model="form.agentName" placeholder="请选择代表处" readonly :disabled="isFormDisabled" @click.native="!isFormDisabled && openSelectAgent()" />
<input type="hidden" v-model="form.agentCode" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="项目阶段" prop="projectStage">
<el-select v-model="form.projectStage" placeholder="请选择项目阶段" style="width: 100%;" :disabled="isFormDisabled">
<el-option
v-for="dict in dict.type.project_stage"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目把握度" prop="projectGraspDegree">
<el-select v-model="form.projectGraspDegree" placeholder="请选择项目把握度" style="width: 100%;" :disabled="isFormDisabled">
<el-option label="A" value="A" />
<el-option label="B" value="B" />
<el-option label="C" value="C" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="汇智负责人" prop="hzSupportUserName">
<el-input v-model="form.hzSupportUserName" placeholder="请选择汇智负责人" readonly :disabled="isFormDisabled" @click.native="!isFormDisabled && openSelectPeople()" />
<input type="hidden" v-model="form.hzSupportUser" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="最终客户" prop="customerName">
<el-input v-model="form.customerName" placeholder="请选择最终客户" readonly :disabled="isFormDisabled" @click.native="!isFormDisabled && openSelectCustomer()" />
<input type="hidden" v-model="form.customerCode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户联系人" prop="customerUserName">
<el-input v-model="form.customerUserName" placeholder="请输入客户联系人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最终客户 TEL" prop="customerPhone">
<el-input v-model="form.customerPhone" placeholder="请输入最终客户电话" maxlength="11" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="运作方" prop="operateInstitution">
<el-select v-model="form.operateInstitution" placeholder="请选择运作方" @change="handleOperateInstitutionChange" style="width: 100%;" :disabled="isFormDisabled">
<el-option
v-for="dict in dict.type.operate_institution"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="新华三联系人" prop="h3cPerson">
<el-input v-model="form.h3cPerson" placeholder="请输入新华三联系人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="新华三TEL" prop="h3cPhone">
<el-input v-model="form.h3cPhone" placeholder="请输入新华三电话" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="form.partnerName" placeholder="请选择进货商" readonly :disabled="isFormDisabled" @click.native="!isFormDisabled && openSelectPartner()" />
<input type="hidden" v-model="form.partnerCode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商联系人" prop="partnerUserName">
<el-input v-model="form.partnerUserName" placeholder="请输入进货商联系人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商 TEL" prop="contactWay">
<el-input v-model="form.contactWay" placeholder="请输入进货商电话" maxlength="11" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="预计金额(元)" prop="estimatedAmount">
<el-input v-model="form.estimatedAmount" placeholder="请输入预计金额" @blur="formatEstimatedAmount" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计下单时间" prop="estimatedOrderTime">
<el-date-picker clearable
v-model="form.estimatedOrderTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择预计下单时间"
style="width: 100%;"
:disabled="isFormDisabled">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="POC测试" prop="poc">
<el-select v-model="form.poc" placeholder="请选择POC" style="width: 100%;" :disabled="isFormDisabled">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否国产" prop="countryProduct">
<el-select v-model="form.countryProduct" placeholder="请选择" style="width: 100%;" :disabled="isFormDisabled">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="竞争对手" >
<div :disabled="isFormDisabled">
<el-checkbox-group v-model="form.competitorList" :disabled="isFormDisabled">
<el-checkbox label="华为" value="华为"></el-checkbox>
<el-checkbox label="锐捷" value="锐捷"></el-checkbox>
<el-checkbox label="深信服" value="深信服"></el-checkbox>
<el-checkbox label="中兴" value="中兴"></el-checkbox>
<el-checkbox label="噢易云" value="噢易云"></el-checkbox>
</el-checkbox-group>
</div>
<div style="margin-top: 10px;">
<el-input
v-model="form.otherCompetitor"
placeholder="其他竞争对手"
style="width: 400px;"
@click.native.stop
@mousedown.native.stop
:disabled="isFormDisabled"
/>
</div>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="关键技术问题" prop="keyProblem">
<el-input v-model="form.keyProblem" type="textarea" placeholder="请输入关键技术问题" maxlength="500" :disabled="isFormDisabled" />
</el-form-item>
<el-form-item label="项目简述" prop="projectDesc">
<el-input v-model="form.projectDesc" type="textarea" placeholder="请输入项目简述" maxlength="500" :disabled="isFormDisabled" />
</el-form-item>
<el-form-item label="服务器配置" prop="serverConfiguration">
<el-input v-model="form.serverConfiguration" type="textarea" placeholder="请输入服务器配置" maxlength="500" :disabled="isFormDisabled" />
</el-form-item>
</div>
</el-tab-pane>
<el-tab-pane label="配置信息" name="productConfig">
<div style="max-height: 60vh; overflow-y: auto; padding: 15px;">
<!-- 产品配置信息 -->
<product-config v-model="form.productConfig" ref="productConfig" :disabled="isFormDisabled" />
</div>
</el-tab-pane>
<el-tab-pane label="工作进度" name="workProgress">
<div style="max-height: 60vh; overflow-y: auto; padding: 15px;">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAddWorkLog" class="mb8" :disabled="isFormDisabled">添加</el-button>
<el-table :data="form.projectWorkProgressList">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="更新内容">
<template slot-scope="scope">
<el-input v-model="scope.row.workContent" type="textarea" :rows="2" :disabled="!!scope.row.id || isFormDisabled" />
</template>
</el-table-column>
<el-table-column label="更新人员" prop="userName" width="120" align="center" />
<el-table-column label="更新时间" prop="workTime" width="180" align="center">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.workTime) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="POC 记录" name="pocRecord" v-if="form.poc === '1'">
<div style="max-height: 60vh; overflow-y: auto; padding: 15px;">
<el-row>
<el-col :span="24">
<el-form-item label="服务器配置" prop="projectPocInfo.serverConfig">
<el-input v-model="form.projectPocInfo.serverConfig" placeholder="请输入服务器配置" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="云桌面版本" prop="projectPocInfo.vdiVersion">
<el-input v-model="form.projectPocInfo.vdiVersion" placeholder="请输入云桌面版本" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="配置终端" prop="projectPocInfo.terminalConfig">
<el-input v-model="form.projectPocInfo.terminalConfig" placeholder="请输入配置终端" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="操作系统" prop="projectPocInfo.operateSystem">
<el-input v-model="form.projectPocInfo.operateSystem" placeholder="请输入操作系统" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="H3C接口人" prop="projectPocInfo.h3cPerson">
<el-input v-model="form.projectPocInfo.h3cPerson" placeholder="请输入H3C接口人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="H3C TEL" prop="projectPocInfo.h3cPhone">
<el-input v-model="form.projectPocInfo.h3cPhone" placeholder="请输入H3C TEL" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="汇智接口人" prop="projectPocInfo.hzInterfacePerson">
<el-input v-model="form.projectPocInfo.hzInterfacePerson" placeholder="请输入汇智接口人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="汇智 TEL" prop="projectPocInfo.hzInterfacePhone">
<el-input v-model="form.projectPocInfo.hzInterfacePhone" placeholder="请输入汇智 TEL" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="研发接口人" prop="projectPocInfo.processPerson">
<el-input v-model="form.projectPocInfo.processPerson" placeholder="请输入研发接口人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="研发 TEL" prop="projectPocInfo.processPhone">
<el-input v-model="form.projectPocInfo.processPhone" placeholder="请输入研发 TEL" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="现场接口人" prop="projectPocInfo.handlePerson">
<el-input v-model="form.projectPocInfo.handlePerson" placeholder="请输入现场接口人" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="现场 TEL" prop="projectPocInfo.handlePhone">
<el-input v-model="form.projectPocInfo.handlePhone" placeholder="请输入现场 TEL" :disabled="isFormDisabled" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="启动时间" prop="projectPocInfo.startDate">
<el-date-picker v-model="form.projectPocInfo.startDate" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width:100%" :disabled="isFormDisabled"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计完成时间" prop="projectPocInfo.planFinishTime">
<el-date-picker v-model="form.projectPocInfo.planFinishTime" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width:100%" :disabled="isFormDisabled"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实际完成时间" prop="projectPocInfo.realFinishTime">
<el-date-picker v-model="form.projectPocInfo.realFinishTime" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width:100%" :disabled="isFormDisabled"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<h4 class="form-header">测试进展</h4>
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAddPocDetail" class="mb8" :disabled="isFormDisabled">添加</el-button>
<el-table :data="form.projectPocInfo.projectPocInfoDetailList">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="测试进展">
<template slot-scope="scope">
<el-input v-model="scope.row.testProgress" type="textarea" :rows="2" :disabled="isFormDisabled" />
</template>
</el-table-column>
<el-table-column label="更新人员" prop="createByName" width="120" align="center" />
<el-table-column label="更新时间" prop="createTime" width="180" align="center">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="操作日志" name="operationLog">
<div style="max-height: 60vh; overflow-y: auto; padding: 15px;">
<el-table :data="form.projectOperateLogList">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="操作人员" prop="operateUserName" width="120" align="center" />
<el-table-column label="操作内容" prop="operateLog" />
<el-table-column label="操作时间" prop="operateTime" width="180" align="center">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.operateTime) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button v-if="!isFormDisabled" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<select-agent :visible.sync="selectAgentVisible" @agent-selected="handleAgentSelected" />
<select-customer :visible.sync="selectCustomerVisible" @customer-selected="handleCustomerSelected" />
<select-partner :visible.sync="selectPartnerVisible" @partner-selected="handlePartnerSelected" />
<select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected" />
</div>
</template>
<script>
import { getProject, addProject, updateProject } from "@/api/project/info";
import { listAreas } from "@/api/system/area";
import SelectAgent from "../../system/agent/selectAgent.vue";
import SelectCustomer from "../../system/customer/selectCustomer.vue";
import SelectPartner from "../../system/partner/selectPartner.vue";
import SelectUser from "@/views/system/user/selectUser";
import ProductConfig from "./ProductConfig.vue";
export default {
name: "ProjectForm",
components: {
SelectAgent,
SelectCustomer,
SelectPartner,
SelectUser,
ProductConfig,
},
dicts: ['bg_type', 'bg_yys', 'bg_hysy', 'project_stage', 'operate_institution'],
props: {
visible: {
type: Boolean,
default: false,
},
projectId: {
type: [Number, String],
default: null,
},
},
data() {
const pocRequiredValidator = (rule, value, callback) => {
if (this.form.poc === '1' && !value) {
callback(new Error(rule.message));
} else {
callback();
}
};
return {
title: "",
localVisible: this.visible,
isFormDisabled: false,
activeTab: 'projectInfo',
provinceOptions: [],
cityOptions: [],
industryOptions: [],
selectAgentVisible: false,
selectCustomerVisible: false,
selectPartnerVisible: false,
selectUserVisible: false,
form: {
competitorList: [],
otherCompetitor: '',
productConfig: {
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
},
projectWorkProgressList: [],
projectOperateLogList: [],
projectPocInfo: {
projectPocInfoDetailList: []
}
},
rules: {
projectName: [
{ required: true, message: "项目名称不能为空", trigger: "blur" }
],
bgProperty: [
{ required: true, message: "BG不能为空", trigger: "change" }
],
industryType: [
{ required: true, message: "行业不能为空", trigger: "change" }
],
agentName: [
{ required: true, message: "代表处不能为空", trigger: "change" }
],
projectStage: [
{ required: true, message: "项目阶段不能为空", trigger: "change" }
],
projectGraspDegree: [
{ required: true, message: "项目把握度不能为空", trigger: "change" }
],
customerName: [
{ required: true, message: "最终客户不能为空", trigger: "change" }
],
operateInstitution: [
{ required: true, message: "运作方不能为空", trigger: "change" }
],
estimatedAmount: [
{ required: true, message: "预计金额不能为空", trigger: "blur" }
],
projectDesc: [
{ required: true, message: "项目简述不能为空", trigger: "blur" }
],
h3cPerson: [
{ validator: (rule, value, callback) => {
if (this.form.operateInstitution === 'h3c' && !value) {
callback(new Error("新华三联系人不能为空"));
} else {
callback();
}
}, trigger: "blur" }
],
h3cPhone: [
{ validator: (rule, value, callback) => {
if (this.form.operateInstitution === 'h3c' && !value) {
callback(new Error("新华三TEL不能为空"));
} else {
callback();
}
}, trigger: "blur" }
],
'projectPocInfo.serverConfig': [{required: true, validator: pocRequiredValidator, message: "服务器配置不能为空", trigger: "blur" }],
'projectPocInfo.vdiVersion': [{required: true, validator: pocRequiredValidator, message: "云桌面版本不能为空", trigger: "blur" }],
'projectPocInfo.terminalConfig': [{required: true, validator: pocRequiredValidator, message: "配置终端不能为空", trigger: "blur" }],
'projectPocInfo.operateSystem': [{ required: true,validator: pocRequiredValidator, message: "操作系统不能为空", trigger: "blur" }],
'projectPocInfo.h3cPerson': [{ required: true,validator: pocRequiredValidator, message: "H3C接口人不能为空", trigger: "blur" }],
'projectPocInfo.h3cPhone': [{ required: true,validator: pocRequiredValidator, message: "H3C TEL不能为空", trigger: "blur" }],
'projectPocInfo.hzInterfacePerson': [{ required: true,validator: pocRequiredValidator, message: "汇智接口人不能为空", trigger: "blur" }],
'projectPocInfo.hzInterfacePhone': [{ required: true,validator: pocRequiredValidator, message: "汇智 TEL不能为空", trigger: "blur" }],
'projectPocInfo.processPerson': [{ required: true,validator: pocRequiredValidator, message: "研发接口人不能为空", trigger: "blur" }],
'projectPocInfo.processPhone': [{ required: true,validator: pocRequiredValidator, message: "研发 TEL不能为空", trigger: "blur" }],
'projectPocInfo.handlePerson': [{ required: true,validator: pocRequiredValidator, message: "现场接口人不能为空", trigger: "blur" }],
'projectPocInfo.handlePhone': [{ required: true,validator: pocRequiredValidator, message: "现场 TEL不能为空", trigger: "blur" }],
'projectPocInfo.startDate': [{ required: true,validator: pocRequiredValidator, message: "启动时间不能为空", trigger: "change" }],
'projectPocInfo.planFinishTime': [{ required: true,validator: pocRequiredValidator, message: "预计完成时间不能为空", trigger: "change" }],
}
};
},
watch: {
visible(val) {
this.localVisible = val;
if (val) {
this.getProvinceList();
if (this.projectId) {
this.handleUpdate();
} else {
this.handleAdd();
}
}
}
},
methods: {
getProvinceList() {
listAreas(0).then(response => {
this.provinceOptions = response;
});
},
handleProvinceChange(provinceName) {
this.form.city = null;
const selectedProvince = this.provinceOptions.find(p => p.n === provinceName);
if (selectedProvince) {
this.cityOptions = selectedProvince.s;
} else {
this.cityOptions = [];
}
},
handleBgChange(bgValue) {
this.form.industryType = null;
if (bgValue === 'YYS') {
this.industryOptions = this.dict.type.bg_yys;
} else if (bgValue) {
this.industryOptions = this.dict.type.bg_hysy;
} else {
this.industryOptions = [];
}
},
cancel() {
this.localVisible = false;
this.$emit("update:visible", false);
this.reset();
},
reset() {
this.form = {
id: null,
projectCode: null,
projectName: null,
bgProperty: null,
industryType: null,
agentName: null,
agentCode: null,
projectStage: null,
projectGraspDegree: null,
hzSupportUserName: null,
hzSupportUser: null,
customerName: null,
customerCode: null,
customerUserName: null,
customerPhone: null,
operateInstitution: null,
h3cPerson: null,
h3cPhone: null,
partnerName: null,
partnerCode: null,
partnerUserName: null,
contactWay: null,
estimatedAmount: null,
estimatedOrderTime: null,
poc: '0',
competitorList: [],
otherCompetitor: '',
countryProduct: null,
keyProblem: null,
projectDesc: null,
serverConfiguration: null,
productConfig: {
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
},
projectWorkProgressList: [],
projectOperateLogList: [],
projectPocInfo: {
projectPocInfoDetailList: []
},
createAt: null,
updatedAt: null
};
this.activeTab = 'projectInfo';
this.isFormDisabled = false;
this.cityOptions = [];
this.industryOptions = [];
this.resetForm("form");
},
handleAdd() {
this.reset();
this.title = "添加项目管理";
},
handleUpdate() {
this.reset();
getProject(this.projectId).then(response => {
this.form = response.data.project;
this.isFormDisabled = !response.data.canUpdate;
this.$set(this.form, 'otherCompetitor', this.form.otherCompetitor || '');
this.$set(this.form, 'competitorList', this.form.competitorList || []);
if (this.form.competitor) {
const allCompetitors = this.form.competitor.split(",");
const predefinedCompetitors = ['华为', '锐捷', '深信服', '中兴', '噢易云'];
this.$set(this.form, 'otherCompetitor', allCompetitors.filter(c => !predefinedCompetitors.includes(c)).join(','));
this.$set(this.form, 'competitorList', allCompetitors.filter(c => predefinedCompetitors.includes(c)));
}
if (this.form.province) {
const selectedProvince = this.provinceOptions.find(p => p.n === this.form.province);
if (selectedProvince) {
this.cityOptions = selectedProvince.s;
}
}
if (this.form.bgProperty) {
if (this.form.bgProperty === 'YYS') {
this.industryOptions = this.dict.type.bg_yys;
} else {
this.industryOptions = this.dict.type.bg_hysy;
}
}
this.$set(this.form, 'productConfig', {
softwareProjectProductInfoList: this.form.softwareProjectProductInfoList || [],
hardwareProjectProductInfoList: this.form.hardwareProjectProductInfoList || [],
maintenanceProjectProductInfoList: this.form.maintenanceProjectProductInfoList || []
});
this.$set(this.form, 'projectWorkProgressList', this.form.projectWorkProgressList || []);
this.$set(this.form, 'projectOperateLogList', this.form.projectOperateLogList || []);
this.$set(this.form, 'projectPocInfo', this.form.projectPocInfo || { projectPocInfoDetailList: [] });
this.$set(this.form.projectPocInfo, 'projectPocInfoDetailList', this.form.projectPocInfo.projectPocInfoDetailList || []);
this.title = "修改项目管理";
});
},
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
let finalCompetitorList = [...this.form.competitorList];
if (this.form.otherCompetitor) {
finalCompetitorList = finalCompetitorList.concat(this.form.otherCompetitor.split(',').map(s => s.trim()).filter(s => s.length > 0));
}
this.form.competitor = finalCompetitorList.join(',');
if (this.form.productConfig) {
this.form.softwareProjectProductInfoList = this.form.productConfig.softwareProjectProductInfoList || [];
this.form.hardwareProjectProductInfoList = this.form.productConfig.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = this.form.productConfig.maintenanceProjectProductInfoList || [];
}
if (this.form.id != null) {
updateProject(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.localVisible = false;
this.$emit("update:visible", false);
this.$emit("success");
});
} else {
addProject(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.localVisible = false;
this.$emit("update:visible", false);
this.$emit("success");
});
}
} else {
this.$modal.msgError("请检查表单和poc记录必填项");
}
});
},
formatEstimatedAmount() {
if (this.form.estimatedAmount) {
this.form.estimatedAmount = this.form.estimatedAmount.replace(/[^0-9.]/g,'').replace(/(\..*)\./g, '$1');
}
},
handleOperateInstitutionChange(value) {
if (value === 'h3c') {
this.form.partnerName = '新华三';
this.form.partnerCode = null;
}
if (this.$refs.form) {
this.$nextTick(() => {
this.$refs.form.validateField('h3cPerson');
this.$refs.form.validateField('h3cPhone');
});
}
},
openSelectAgent() {
this.selectAgentVisible = true;
},
handleAgentSelected(agent) {
this.form.agentName = agent.agentName;
this.form.agentCode = agent.agentCode;
this.selectAgentVisible = false;
},
openSelectCustomer() {
this.selectCustomerVisible = true;
},
handleCustomerSelected(customer) {
this.form.customerName = customer.customerName;
this.form.customerCode = customer.customerCode;
this.form.customerUserName = customer.contactPerson;
this.form.customerPhone = customer.contactPhone;
this.selectCustomerVisible = false;
},
openSelectPartner() {
this.selectPartnerVisible = true;
},
handlePartnerSelected(partner) {
this.form.partnerName = partner.partnerName;
this.form.partnerCode = partner.partnerCode;
this.form.partnerUserName = partner.contactPerson;
this.form.contactWay = partner.contactPhone;
this.selectPartnerVisible = false;
},
openSelectPeople() {
this.selectUserVisible = true;
},
handleUserSelected(user) {
this.form.hzSupportUserName = user.userName;
this.form.hzSupportUser = user.userId;
this.selectUserVisible = false;
},
handleAddWorkLog() {
if (!this.form.projectWorkProgressList) {
this.$set(this.form, 'projectWorkProgressList', []);
}
this.form.projectWorkProgressList.push({
workContent: '',
userName: '',
workTime: ''
});
},
handleAddPocDetail() {
if (!this.form.projectPocInfo.projectPocInfoDetailList) {
this.$set(this.form.projectPocInfo, 'projectPocInfoDetailList', []);
}
this.form.projectPocInfo.projectPocInfoDetailList.push({
testProgress: '',
createByName: '',
createTime: ''
});
},
}
};
</script>

View File

@ -26,7 +26,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="BG" prop="bgProperty"> <el-form-item label="BG" prop="bgProperty">
<el-select v-model="queryParams.bgProperty" placeholder="请选择BG" clearable @change="handleSearchBgChange"> <el-select v-model="queryParams.bgProperty" placeholder="请选择BG" clearable>
<el-option <el-option
v-for="dict in dict.type.bg_type" v-for="dict in dict.type.bg_type"
:key="dict.value" :key="dict.value"
@ -147,7 +147,7 @@
<el-table-column label="项目编号" align="center" prop="projectCode" width="100" /> <el-table-column label="项目编号" align="center" prop="projectCode" width="100" />
<el-table-column label="项目名称" align="center" prop="projectName" width="300"> <el-table-column label="项目名称" align="center" prop="projectName" width="300">
<template slot-scope="scope"> <template slot-scope="scope">
<a @click="viewDetail(scope.row.id)" class="link-type">{{ scope.row.projectName }}</a> <a @click="handleView(scope.row)" class="link-type">{{ scope.row.projectName }}</a>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="最终客户" align="center" prop="customerName" width="200" /> <el-table-column label="最终客户" align="center" prop="customerName" width="200" />
@ -226,242 +226,30 @@
@pagination="getList" @pagination="getList"
/> />
<!-- 添加或修改项目管理对话框 --> <!-- 项目详情抽屉 -->
<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body :close-on-click-modal="false"> <project-detail-drawer :visible.sync="drawerVisible" :project-id="currentProjectId" />
<el-form ref="form" :model="form" :rules="rules" label-width="120px" style="max-height: 60vh; overflow-y: auto; padding-right: 20px;">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="保存后自动生成" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="form.projectName" placeholder="请输入项目名称" maxlength="40" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="BG" prop="bgProperty">
<el-select v-model="form.bgProperty" placeholder="请选择BG" @change="handleBgChange" style="width: 100%;">
<el-option
v-for="dict in dict.type.bg_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行业" prop="industryType">
<el-select v-model="form.industryType" placeholder="请选择行业" style="width: 100%;">
<el-option
v-for="item in industryOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="代表处" prop="agentName">
<el-input v-model="form.agentName" placeholder="请选择代表处" readonly @click.native="openSelectAgent" />
<input type="hidden" v-model="form.agentCode" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="项目阶段" prop="projectStage">
<el-select v-model="form.projectStage" placeholder="请选择项目阶段" style="width: 100%;">
<el-option
v-for="dict in dict.type.project_stage"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目把握度" prop="projectGraspDegree">
<el-select v-model="form.projectGraspDegree" placeholder="请选择项目把握度" style="width: 100%;">
<el-option label="A" value="A" />
<el-option label="B" value="B" />
<el-option label="C" value="C" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="汇智负责人" prop="hzSupportUserName">
<el-input v-model="form.hzSupportUserName" placeholder="请选择汇智负责人" readonly @click.native="openSelectPeople" />
<input type="hidden" v-model="form.hzSupportUser" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="最终客户" prop="customerName">
<el-input v-model="form.customerName" placeholder="请选择最终客户" readonly @click.native="openSelectCustomer" />
<input type="hidden" v-model="form.customerCode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户联系人" prop="customerUserName">
<el-input v-model="form.customerUserName" placeholder="请输入客户联系人" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最终客户 TEL" prop="customerPhone">
<el-input v-model="form.customerPhone" placeholder="请输入最终客户电话" maxlength="11" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="运作方" prop="operateInstitution">
<el-select v-model="form.operateInstitution" placeholder="请选择运作方" @change="handleOperateInstitutionChange" style="width: 100%;">
<el-option
v-for="dict in dict.type.operate_institution"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="新华三联系人" prop="h3cPerson">
<el-input v-model="form.h3cPerson" placeholder="请输入新华三联系人" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="新华三TEL" prop="h3cPhone">
<el-input v-model="form.h3cPhone" placeholder="请输入新华三电话" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="form.partnerName" placeholder="请选择进货商" readonly @click.native="openSelectPartner" />
<input type="hidden" v-model="form.partnerCode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商联系人" prop="partnerUserName">
<el-input v-model="form.partnerUserName" placeholder="请输入进货商联系人" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商 TEL" prop="contactWay">
<el-input v-model="form.contactWay" placeholder="请输入进货商电话" maxlength="11" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="预计金额(元)" prop="estimatedAmount">
<el-input v-model="form.estimatedAmount" placeholder="请输入预计金额" @blur="formatEstimatedAmount" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计下单时间" prop="estimatedOrderTime">
<el-date-picker clearable
v-model="form.estimatedOrderTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择预计下单时间"
style="width: 100%;">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="POC测试" prop="poc">
<el-select v-model="form.poc" placeholder="请选择POC" style="width: 100%;">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否国产" prop="countryProduct">
<el-select v-model="form.countryProduct" placeholder="请选择" style="width: 100%;">
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="竞争对手" >
<div>
<el-checkbox-group v-model="form.competitorList">
<el-checkbox label="华为" value="华为"></el-checkbox>
<el-checkbox label="锐捷" value="锐捷"></el-checkbox>
<el-checkbox label="深信服" value="深信服"></el-checkbox>
<el-checkbox label="中兴" value="中兴"></el-checkbox>
<el-checkbox label="噢易云" value="噢易云"></el-checkbox>
</el-checkbox-group>
</div>
<div style="margin-top: 10px;">
<el-input
v-model="form.otherCompetitor"
placeholder="其他竞争对手"
style="width: 400px;"
@click.native.stop
@mousedown.native.stop
/>
</div>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="关键技术问题" prop="keyProblem">
<el-input v-model="form.keyProblem" type="textarea" placeholder="请输入关键技术问题" maxlength="500" />
</el-form-item>
<el-form-item label="项目简述" prop="projectDesc">
<el-input v-model="form.projectDesc" type="textarea" placeholder="请输入项目简述" maxlength="500" />
</el-form-item>
<el-form-item label="服务器配置" prop="serverConfiguration">
<el-input v-model="form.serverConfiguration" type="textarea" placeholder="请输入服务器配置" maxlength="500" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<select-agent :visible.sync="selectAgentVisible" @agent-selected="handleAgentSelected" /> <!-- 项目表单 -->
<select-customer :visible.sync="selectCustomerVisible" @customer-selected="handleCustomerSelected" /> <project-form
<select-partner :visible.sync="selectPartnerVisible" @partner-selected="handlePartnerSelected" /> :visible.sync="projectFormVisible"
<select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected" /> :project-id="selectedProjectId"
@success="handleFormSuccess"
/>
</div> </div>
</template> </template>
<script> <script>
import { listProject, getProject, delProject, addProject, updateProject, exportProject } from "@/api/project/info"; import { listProject, delProject, exportProject } from "@/api/project/info";
import { listAreas } from "@/api/system/area"; import ProjectDetailDrawer from "./ProjectDetailDrawer.vue";
import SelectAgent from "../../system/agent/selectAgent.vue"; import ProjectForm from "./ProjectForm.vue";
import SelectCustomer from "../../system/customer/selectCustomer.vue";
import SelectPartner from "../../system/partner/selectPartner.vue";
import SelectUser from "@/views/system/user/selectUser"; // Reusing the existing SelectUser component
export default { export default {
name: "Project", name: "Project",
components: { components: {
SelectAgent, ProjectDetailDrawer,
SelectCustomer, ProjectForm,
SelectPartner,
SelectUser
}, },
dicts: ['bg_type', 'bg_yys', 'bg_hysy', 'project_stage', 'operate_institution'], dicts: ['bg_type', 'bg_yys', 'bg_hysy', 'project_stage'],
data() { data() {
return { return {
// //
@ -478,10 +266,12 @@ export default {
total: 0, total: 0,
// //
projectList: [], projectList: [],
// //
title: "", drawerVisible: false,
// currentProjectId: null,
open: false, //
projectFormVisible: false,
selectedProjectId: null,
// //
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@ -503,91 +293,10 @@ export default {
lastWorkUpdateTimeEnd: null, lastWorkUpdateTimeEnd: null,
}, },
dateRange: [], dateRange: [],
//
provinceOptions: [],
//
cityOptions: [],
//
searchProvinceOptions: [],
//
searchCityOptions: [],
//
industryOptions: [],
//
searchIndustryOptions: [],
//
selectAgentVisible: false,
selectCustomerVisible: false,
selectPartnerVisible: false,
selectUserVisible: false,
selectProductVisible: false, //
currentProductRowIndex: null, //
currentProductListType: null, // (software, hardware, maintenance)
//
form: {
competitorList: [], // Initialize as array for checkboxes
otherCompetitor: null, // For the "" competitor input
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: [],
},
//
rules: {
projectName: [
{ required: true, message: "项目名称不能为空", trigger: "blur" }
],
bgProperty: [
{ required: true, message: "BG不能为空", trigger: "change" }
],
industryType: [
{ required: true, message: "行业不能为空", trigger: "change" }
],
agentName: [
{ required: true, message: "代表处不能为空", trigger: "change" }
],
projectStage: [
{ required: true, message: "项目阶段不能为空", trigger: "change" }
],
projectGraspDegree: [
{ required: true, message: "项目把握度不能为空", trigger: "change" }
],
customerName: [
{ required: true, message: "最终客户不能为空", trigger: "change" }
],
operateInstitution: [
{ required: true, message: "运作方不能为空", trigger: "change" }
],
estimatedAmount: [
{ required: true, message: "预计金额不能为空", trigger: "blur" }
],
projectDesc: [
{ required: true, message: "项目简述不能为空", trigger: "blur" }
],
h3cPerson: [
{ validator: (rule, value, callback) => {
if (this.form.operateInstitution === 'h3c' && !value) {
callback(new Error("新华三联系人不能为空"));
} else {
callback();
}
}, trigger: "blur" }
],
h3cPhone: [
{ validator: (rule, value, callback) => {
if (this.form.operateInstitution === 'h3c' && !value) {
callback(new Error("新华三TEL不能为空"));
} else {
callback();
}
}, trigger: "blur" }
],
}
}; };
}, },
created() { created() {
this.getList(); this.getList();
this.getProvinceList(); // For selectAgent/selectCustomer/selectPartner
this.handleSearchBgChange(this.queryParams.bgProperty); // Initialize search industry options
}, },
watch: { watch: {
dateRange(val) { dateRange(val) {
@ -611,6 +320,22 @@ export default {
} }
}, },
}, },
computed: {
searchIndustryOptions() {
const yys = this.dict.type.bg_yys || [];
const hysy = this.dict.type.bg_hysy || [];
const combined = [...yys, ...hysy];
const uniqueMap = new Map();
combined.forEach(item => {
if (!uniqueMap.has(item.value)) {
uniqueMap.set(item.value, item);
}
});
return Array.from(uniqueMap.values());
}
},
methods: { methods: {
/** 查询项目管理列表 */ /** 查询项目管理列表 */
getList() { getList() {
@ -621,55 +346,6 @@ export default {
this.loading = false; this.loading = false;
}); });
}, },
/** 查询省份列表 (for selectAgent/selectCustomer/selectPartner) */
getProvinceList() {
listAreas(0).then(response => {
this.provinceOptions = response;
this.searchProvinceOptions = response;
});
},
/** 搜索省份选择改变时 */
handleSearchProvinceChange(provinceName) {
this.queryParams.city = null;
const selectedProvince = this.searchProvinceOptions.find(p => p.n === provinceName);
if (selectedProvince) {
this.searchCityOptions = selectedProvince.s;
} else {
this.searchCityOptions = [];
}
},
/** 省份选择改变时 (for add/edit dialog) */
handleProvinceChange(provinceName) {
this.form.city = null;
const selectedProvince = this.provinceOptions.find(p => p.n === provinceName);
if (selectedProvince) {
this.cityOptions = selectedProvince.s;
} else {
this.cityOptions = [];
}
},
/** 搜索BG选择改变时 */
handleSearchBgChange(bgValue) {
this.queryParams.industryType = null;
if (bgValue === 'YYS') {
this.searchIndustryOptions = this.dict.type.bg_yys;
} else if (bgValue) { // Only update if a BG is selected
this.searchIndustryOptions = this.dict.type.bg_hysy;
} else {
this.searchIndustryOptions = []; // Clear if no BG selected
}
},
/** BG选择改变时 (for add/edit dialog) */
handleBgChange(bgValue) {
this.form.industryType = null;
if (bgValue === 'YYS') {
this.industryOptions = this.dict.type.bg_yys;
} else if (bgValue) { // Only update if a BG is selected
this.industryOptions = this.dict.type.bg_hysy;
} else {
this.industryOptions = []; // Clear if no BG selected
}
},
/** 时间类型选择改变时 */ /** 时间类型选择改变时 */
handleTimeTypeChange() { handleTimeTypeChange() {
this.dateRange = []; // Clear date range when time type changes this.dateRange = []; // Clear date range when time type changes
@ -678,52 +354,6 @@ export default {
this.queryParams.lastWorkUpdateTimeStart = null; this.queryParams.lastWorkUpdateTimeStart = null;
this.queryParams.lastWorkUpdateTimeEnd = null; this.queryParams.lastWorkUpdateTimeEnd = null;
}, },
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
projectCode: null,
projectName: null,
bgProperty: null,
industryType: null,
agentName: null,
agentCode: null,
projectStage: null,
projectGraspDegree: null,
hzSupportUserName: null,
hzSupportUser: null,
customerName: null,
customerCode: null,
customerUserName: null,
customerPhone: null,
operateInstitution: null,
h3cPerson: null,
h3cPhone: null,
partnerName: null,
partnerCode: null,
partnerUserName: null,
contactWay: null,
estimatedAmount: null,
estimatedOrderTime: null,
poc: '0', // Default to ''
competitorList: [],
otherCompetitor: '', //
countryProduct: null,
keyProblem: null,
projectDesc: null,
serverConfiguration: null,
createAt: null,
updatedAt: null
};
this.cityOptions = [];
this.industryOptions = [];
this.resetForm("form");
},
/** 搜索按钮操作 */ /** 搜索按钮操作 */
handleQuery() { handleQuery() {
this.queryParams.pageNum = 1; this.queryParams.pageNum = 1;
@ -739,8 +369,6 @@ export default {
this.queryParams.lastWorkUpdateTimeStart = null; this.queryParams.lastWorkUpdateTimeStart = null;
this.queryParams.lastWorkUpdateTimeEnd = null; this.queryParams.lastWorkUpdateTimeEnd = null;
this.queryParams.industryType = null; // Clear industry type this.queryParams.industryType = null; // Clear industry type
this.searchIndustryOptions = []; // Clear search industry options
this.handleSearchBgChange(this.queryParams.bgProperty); // Re-initialize search industry options based on default BG
this.handleQuery(); this.handleQuery();
}, },
// //
@ -751,74 +379,18 @@ export default {
}, },
/** 新增按钮操作 */ /** 新增按钮操作 */
handleAdd() { handleAdd() {
this.reset(); this.selectedProjectId = null;
this.open = true; this.projectFormVisible = true;
this.title = "添加项目管理";
}, },
/** 修改按钮操作 */ /** 修改按钮操作 */
handleUpdate(row) { handleUpdate(row) {
this.reset(); this.selectedProjectId = row.id;
const id = row.id || this.ids this.projectFormVisible = true;
getProject(id).then(response => {
this.form = response.data;
// otherCompetitor competitorList
this.$set(this.form, 'otherCompetitor', '');
this.$set(this.form, 'competitorList', []);
// Handle competitorList and otherCompetitor
if (this.form.competitor) {
const allCompetitors = this.form.competitor.split(",");
const predefinedCompetitors = ['华为', '锐捷', '深信服', '中兴', '噢易云'];
this.$set(this.form, 'otherCompetitor', allCompetitors.filter(c => !predefinedCompetitors.includes(c)).join(','));
this.$set(this.form, 'competitorList', allCompetitors.filter(c => predefinedCompetitors.includes(c)));
}
// Populate city options if province is already set
if (this.form.province) {
const selectedProvince = this.provinceOptions.find(p => p.n === this.form.province);
if (selectedProvince) {
this.cityOptions = selectedProvince.s;
}
}
// Populate industry options if bgProperty is already set
if (this.form.bgProperty) {
if (this.form.bgProperty === 'YYS') {
this.industryOptions = this.dict.type.bg_yys;
} else {
this.industryOptions = this.dict.type.bg_hysy;
}
}
this.open = true;
this.title = "修改项目管理";
});
}, },
/** 提交按钮 */ /** 表单提交成功 */
submitForm() { handleFormSuccess() {
this.$refs["form"].validate(valid => { this.projectFormVisible = false;
if (valid) {
// Combine competitorList and otherCompetitor before submitting
let finalCompetitorList = [...this.form.competitorList];
if (this.form.otherCompetitor) {
finalCompetitorList = finalCompetitorList.concat(this.form.otherCompetitor.split(',').map(s => s.trim()).filter(s => s.length > 0));
}
this.form.competitorList = finalCompetitorList;
if (this.form.id != null) {
updateProject(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList(); this.getList();
});
} else {
addProject(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
}, },
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(row) { handleDelete(row) {
@ -850,75 +422,10 @@ export default {
} }
return ''; return '';
}, },
/** 金额输入框失去焦点时格式化 */
formatEstimatedAmount() {
if (this.form.estimatedAmount) {
this.form.estimatedAmount = this.form.estimatedAmount.replace(/[^0-9.]/g,'').replace(/(\..*)\./g, '$1');
}
},
/** 运作方选择改变时 */
handleOperateInstitutionChange(value) {
// ""
if (value === 'h3c') {
this.form.partnerName = '新华三';
this.form.partnerCode = null;
}
// TEL
if (this.$refs.form) {
this.$nextTick(() => {
this.$refs.form.validateField('h3cPerson');
this.$refs.form.validateField('h3cPhone');
});
}
},
/** 打开选择代表处对话框 */
openSelectAgent() {
this.selectAgentVisible = true;
},
/** 处理代表处选择 */
handleAgentSelected(agent) {
this.form.agentName = agent.agentName;
this.form.agentCode = agent.agentCode;
this.selectAgentVisible = false;
},
/** 打开选择客户对话框 */
openSelectCustomer() {
this.selectCustomerVisible = true;
},
/** 处理客户选择 */
handleCustomerSelected(customer) {
this.form.customerName = customer.customerName;
this.form.customerCode = customer.customerCode;
this.form.customerUserName = customer.contactPerson;
this.form.customerPhone = customer.contactPhone;
this.selectCustomerVisible = false;
},
/** 打开选择进货商对话框 */
openSelectPartner() {
this.selectPartnerVisible = true;
},
/** 处理进货商选择 */
handlePartnerSelected(partner) {
this.form.partnerName = partner.partnerName;
this.form.partnerCode = partner.partnerCode;
this.form.partnerUserName = partner.contactPerson;
this.form.contactWay = partner.contactPhone;
this.selectPartnerVisible = false;
},
/** 打开选择汇智负责人对话框 */
openSelectPeople() {
this.selectUserVisible = true;
},
/** 处理汇智负责人选择 */
handleUserSelected(user) {
this.form.hzSupportUserName = user.userName;
this.form.hzSupportUser = user.userId;
this.selectUserVisible = false;
},
/** 查看项目详情 */ /** 查看项目详情 */
viewDetail(id) { handleView(row) {
// This would typically open a new route or a larger dialog for project details this.currentProjectId = row.id;
this.$modal.alert("查看项目详情ID: " + id); this.drawerVisible = true;
}, },
/** 生成订单 */ /** 生成订单 */
openOrder(id, canGenerate) { openOrder(id, canGenerate) {
@ -927,7 +434,6 @@ export default {
return; return;
} }
this.$modal.alert("生成订单项目ID: " + id); this.$modal.alert("生成订单项目ID: " + id);
// This would typically open a new route or a larger dialog for order creation
}, },
} }
}; };

View File

@ -0,0 +1,495 @@
<template>
<!-- 添加或修改订单管理对话框 -->
<el-dialog :title="title" :visible.sync="internalVisible" width="1400px" append-to-body @close="handleClose">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-tabs v-model="activeTab">
<el-tab-pane label="基础信息" name="basic">
<el-row>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="form.projectName" placeholder="选择项目后带入" readonly>
<el-button slot="append" icon="el-icon-search" @click="handleSelectProject"></el-button>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="版本号" prop="versionCode">
<el-input-number v-model="form.versionCode" :min="1" label="版本号"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="form.projectCode" placeholder="选择项目后带入" readonly/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="最终客户" prop="customerName">
<el-input v-model="form.customerName" placeholder="选择项目后带入" readonly/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="BG" prop="bgProperty">
<el-select v-model="form.bgProperty" placeholder="请选择BG" @change="handleBgChange">
<el-option v-for="dict in bgOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行业" prop="industryType">
<el-select v-model="form.industryType" placeholder="请先选择BG">
<el-option v-for="dict in industryOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="代表处" prop="agentName">
<el-input v-model="form.agentName" placeholder="选择项目后带入" readonly/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商接口人" prop="businessPerson">
<el-input v-model="form.businessPerson" placeholder="请输入进货商接口人"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Email" prop="businessEmail">
<el-input v-model="form.businessEmail" placeholder="请输入Email"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="businessPhone">
<el-input v-model="form.businessPhone" placeholder="请输入联系方式"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同编号" prop="orderCode">
<el-input v-model="form.orderCode" placeholder="自动生成" readonly/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="执行单有效截止时间" prop="orderEndTime">
<el-date-picker v-model="form.orderEndTime" type="date" value-format="yyyy-MM-dd" placeholder="审批完成后自动计算" disabled></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="币种" prop="currencyType">
<el-select v-model="form.currencyType" placeholder="请选择币种">
<el-option v-for="dict in currencyOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总代出货金额" prop="shipmentAmount">
<el-input v-model="form.shipmentAmount" placeholder="请输入金额"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="要求到货时间" prop="deliveryTime">
<el-date-picker clearable size="small" v-model="form.deliveryTime" type="date" value-format="yyyy-MM-dd" placeholder="选择要求到货时间">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="公司直发" prop="companyDelivery">
<el-select v-model="form.companyDelivery" placeholder="请选择">
<el-option v-for="dict in companyDeliveryOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="下单通路" prop="orderChannel">
<el-select v-model="form.orderChannel" placeholder="请选择" @change="handleChannelChange">
<el-option label="总代" value="1"></el-option>
<el-option label="直签" value="2"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" v-if="form.orderChannel == '1'">
<el-form-item label="总代" prop="zd">
<el-input value="广州佳都技术有限公司" readonly/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供货商" prop="supplier">
<el-input v-model="form.supplier" readonly/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="汇智责任人" prop="dutyName">
<el-input v-model="form.dutyName" placeholder="请选择责任人">
<el-button slot="append" icon="el-icon-search" @click="handleSelectUser"></el-button>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Email" prop="dutyEmail">
<el-input v-model="form.dutyEmail" placeholder="请输入Email"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="dutyPhone">
<el-input v-model="form.dutyPhone" placeholder="请输入联系方式"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="form.partnerName" placeholder="请选择进货商">
<el-button slot="append" icon="el-icon-search" @click="handleSelectPartner"></el-button>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="进货商类型" prop="level">
<el-select v-model="form.level" placeholder="选择后带入" disabled>
<el-option v-for="dict in partnerLevelOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="进货商联系人" prop="partnerUserName">
<el-input v-model="form.partnerUserName" placeholder="请输入进货商联系人"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Email" prop="partnerEmail">
<el-input v-model="form.partnerEmail" placeholder="请输入Email"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="partnerPhone">
<el-input v-model="form.partnerPhone" placeholder="请输入联系方式"/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="收货地址" prop="notifierAddress">
<el-input v-model="form.notifierAddress" placeholder="请输入收货地址"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="收货人" prop="notifier">
<el-input v-model="form.notifier" placeholder="请输入收货人"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Email" prop="notifierEmail">
<el-input v-model="form.notifierEmail" placeholder="请输入Email"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="notifierPhone">
<el-input v-model="form.notifierPhone" placeholder="请输入联系方式"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="付款方式" prop="paymentMethod">
<el-select v-model="form.paymentMethod" placeholder="请选择付款方式" @change="handlePaymentMethodChange">
<el-option v-for="item in paymentMethodOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="付款比例" prop="paymentRatio">
<el-input-number v-model="form.paymentRatio" :min="0" :max="100" placeholder="请输入付款比例" @change="updatePaymentDescription"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="付款条件" prop="paymentDescription">
<el-input v-model="form.paymentDescription" type="textarea" :rows="3" readonly/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="其他特别说明" prop="remark">
<el-input v-model="form.remark" placeholder="请输入其他特别说明"/>
</el-form-item>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="配置信息" name="config">
<product-config v-model="form" :disabled="!isEdit" />
</el-tab-pane>
<el-tab-pane label="流转过程" name="flow">
<!-- 流转过程 -->
</el-tab-pane>
<el-tab-pane label="备货信息" name="stock">
<!-- 备货信息 -->
</el-tab-pane>
<el-tab-pane label="物流信息" name="delivery">
<!-- 物流信息 -->
</el-tab-pane>
<el-tab-pane label="合同信息" name="contract">
<!-- 合同信息 -->
</el-tab-pane>
</el-tabs>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { getOrder, addOrder, updateOrder } from "@/api/project/order";
import { getDicts } from "@/api/system/dict/data";
import ProductConfig from '@/views/project/info/ProductConfig.vue';
export default {
name: "OrderDetail",
components: {
ProductConfig
},
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ""
},
orderId: {
type: Number,
default: null
}
},
data() {
return {
//
form: {},
//
internalVisible: this.visible,
// Tab
activeTab: 'basic',
//
bgOptions: [],
industryOptions: [],
currencyOptions: [],
companyDeliveryOptions: [],
partnerLevelOptions: [],
paymentMethodOptions: [],
//
rules: {
projectName: [{ required: true, message: "项目名称不能为空", trigger: "blur" }],
versionCode: [{ required: true, message: "版本号不能为空", trigger: "blur" }],
projectCode: [{ required: true, message: "项目编号不能为空", trigger: "blur" }],
customerName: [{ required: true, message: "最终客户不能为空", trigger: "blur" }],
bgProperty: [{ required: true, message: "BG不能为空", trigger: "change" }],
industryType: [{ required: true, message: "行业不能为空", trigger: "change" }],
agentName: [{ required: true, message: "代表处不能为空", trigger: "blur" }],
businessPerson: [{ required: true, message: "进货商接口人不能为空", trigger: "blur" }],
businessPhone: [{ required: true, message: "联系方式不能为空", trigger: "blur" }],
currencyType: [{ required: true, message: "币种不能为空", trigger: "change" }],
shipmentAmount: [{ required: true, message: "总代出货金额不能为空", trigger: "blur" }],
deliveryTime: [{ required: true, message: "要求到货时间不能为空", trigger: "blur" }],
orderChannel: [{ required: true, message: "下单通路不能为空", trigger: "change" }],
supplier: [{ required: true, message: "供货商不能为空", trigger: "blur" }],
dutyPhone: [{ required: true, message: "联系方式不能为空", trigger: "blur" }],
partnerName: [{ required: true, message: "进货商不能为空", trigger: "blur" }],
level: [{ required: true, message: "进货商类型不能为空", trigger: "change" }],
partnerPhone: [{ required: true, message: "联系方式不能为空", trigger: "blur" }],
notifierAddress: [{ required: true, message: "收货地址不能为空", trigger: "blur" }],
notifierPhone: [{ required: true, message: "联系方式不能为空", trigger: "blur" }],
paymentMethod: [{ required: true, message: "付款方式不能为空", trigger: "change" }],
paymentRatio: [{ required: true, message: "付款比例不能为空", trigger: "blur" }],
}
};
},
computed: {
isEdit() {
return this.orderId != null;
}
},
watch: {
visible(val) {
this.internalVisible = val;
if (val) {
this.handleOpen();
}
}
},
created() {
this.getDicts("bg_type").then(response => { this.bgOptions = response.data; });
this.getDicts("currency_type").then(response => { this.currencyOptions = response.data; });
this.getDicts("company_delivery").then(response => { this.companyDeliveryOptions = response.data; });
this.getDicts("identify_level").then(response => { this.partnerLevelOptions = response.data; });
},
methods: {
//
handleOpen() {
this.reset();
if (this.isEdit) {
getOrder(this.orderId).then(response => {
this.form = response.data;
//
this.handleBgChange(this.form.bgProperty);
this.handleChannelChange(this.form.orderChannel);
this.handlePaymentMethodChange(this.form.paymentMethod);
});
} else {
//
this.form.dutyName = this.$store.state.user.name;
this.form.dutyEmail = this.$store.state.user.email;
this.form.dutyPhone = this.$store.state.user.phonenumber;
}
},
//
handleClose() {
this.$emit('update:visible', false);
},
//
cancel() {
this.handleClose();
},
//
reset() {
this.form = {
id: null,
projectName: null,
versionCode: 1,
projectCode: null,
customerName: null,
bgProperty: null,
industryType: null,
agentName: null,
businessPerson: null,
businessEmail: null,
businessPhone: null,
orderCode: null,
orderEndTime: null,
currencyType: null,
shipmentAmount: null,
deliveryTime: null,
companyDelivery: '0',
orderChannel: null,
supplier: null,
dutyName: null,
dutyEmail: null,
dutyPhone: null,
partnerName: null,
level: null,
partnerUserName: null,
partnerEmail: null,
partnerPhone: null,
notifierAddress: null,
notifier: null,
notifierEmail: null,
notifierPhone: null,
paymentMethod: null,
paymentRatio: 0,
paymentDescription: null,
remark: null,
softwareProjectProductInfoList: [],
hardwareProjectProductInfoList: [],
maintenanceProjectProductInfoList: []
};
this.activeTab = 'basic';
this.resetForm("form");
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
const action = this.isEdit ? updateOrder : addOrder;
action(this.form).then(response => {
this.msgSuccess(this.isEdit ? "修改成功" : "新增成功");
this.handleClose();
this.$emit('success');
});
}
});
},
/** BG改变事件 */
handleBgChange(value) {
this.form.industryType = null;
const dictType = value === 'YYS' ? 'bg_yys' : 'bg_hysy';
getDicts(dictType).then(response => {
this.industryOptions = response.data;
});
},
/** 下单通路改变事件 */
handleChannelChange(value) {
this.form.supplier = value === '1' ? '广州佳都技术有限公司' : '紫光汇智信息技术有限公司';
this.form.paymentMethod = null;
this.form.paymentRatio = 0;
this.form.paymentDescription = null;
if (value === '1') { //
this.paymentMethodOptions = [
{ label: '全款支付,无需预付款', value: '1-1' },
{ label: '全款支付,需单独备货生产,预付订单总货款百分比', value: '1-2' }
];
} else if (value === '2') { //
this.paymentMethodOptions = [
{ label: '全款支付', value: '2-1' },
{ label: '全款支付,需单独备货生产,预付订单总货款百分比', value: '2-2' },
{ label: '商业汇票支付,预付订单总货款百分比', value: '2-3' }
];
} else {
this.paymentMethodOptions = [];
}
},
/** 付款方式改变事件 */
handlePaymentMethodChange(value) {
const descriptionTemplates = {
'1-2': '总代预付{ratio}%订单金额作为备货押金后开始备货生产备货完成供货商发起付款通知后总代需1个月内支付尾款完成提货否则备货押金不予退还。',
'2-2': '进货商预付{ratio}%订单金额作为备货押金后开始备货生产备货完成供货商发起付款通知后进货商需3个工作日内付全部剩余款项即可享受订单约定的现金折扣。',
'2-3': '进货商预付{ratio}%订单金额作为备货押金后开始备货生产备货完成供货商发起付款通知后供货商需在1个月内提交剩余款项额度的商业汇票完成提货否则备货押金不予退还。'
};
let description = '';
let paymentRatio = 0;
switch (value) {
case '1-1':
description = '备货完成供货商发起付款通知后,总代按照订单金额支付全款。';
paymentRatio = 100;
break;
case '1-2':
paymentRatio = 30;
description = descriptionTemplates['1-2'].replace('{ratio}', paymentRatio);
break;
case '2-1':
description = '备货完成供货商发起付款通知后进货商需3个工作日内付订单全部款项即可享受订单约定的现金折扣。';
paymentRatio = 100;
break;
case '2-2':
paymentRatio = 30;
description = descriptionTemplates['2-2'].replace('{ratio}', paymentRatio);
break;
case '2-3':
paymentRatio = 30;
description = descriptionTemplates['2-3'].replace('{ratio}', paymentRatio);
break;
}
this.form.paymentDescription = description;
this.form.paymentRatio = paymentRatio;
},
/** 更新付款条件描述 */
updatePaymentDescription() {
const paymentMethod = this.form.paymentMethod;
const ratio = this.form.paymentRatio;
const descriptionTemplates = {
'1-2': '总代预付{ratio}%订单金额作为备货押金后开始备货生产备货完成供货商发起付款通知后总代需1个月内支付尾款完成提货否则备货押金不予退还。',
'2-2': '进货商预付{ratio}%订单金额作为备货押金后开始备货生产备货完成供货商发起付款通知后进货商需3个工作日内付全部剩余款项即可享受订单约定的现金折扣。',
'2-3': '进货商预付{ratio}%订单金额作为备货押金后开始备货生产备货完成供货商发起付款通知后供货商需在1个月内提交剩余款项额度的商业汇票完成提货否则备货押金不予退还。'
};
if (descriptionTemplates[paymentMethod]) {
this.form.paymentDescription = descriptionTemplates[paymentMethod].replace('{ratio}', ratio);
}
},
/** 选择项目按钮操作 */
handleSelectProject() {
this.msgWarning("选择项目功能待实现");
},
/** 选择用户按钮操作 */
handleSelectUser() {
this.msgWarning("选择用户功能待实现");
},
/** 选择合作伙伴按钮操作 */
handleSelectPartner() {
this.msgWarning("选择合作伙伴功能待实现");
}
}
};
</script>

View File

@ -0,0 +1,312 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="合同编号" prop="orderCode">
<el-input v-model="queryParams.orderCode" placeholder="请输入合同编号" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="最终客户" prop="customerName">
<el-input v-model="queryParams.customerName" placeholder="请输入最终客户" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="订单状态" prop="orderStatus">
<el-select v-model="queryParams.orderStatus" placeholder="请选择订单状态" clearable size="small">
<el-option v-for="dict in orderStatusOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"/>
</el-select>
</el-form-item>
<el-form-item label="代表处" prop="agentName">
<el-input v-model="queryParams.agentName" placeholder="请输入代表处" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="汇智负责人" prop="dutyName">
<el-input v-model="queryParams.dutyName" placeholder="请输入项目负责人" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="进货商" prop="partnerName">
<el-input v-model="queryParams.partnerName" placeholder="请输入进货商" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="时间选择">
<el-select v-model="queryParams.timeType" size="small" style="width: 140px">
<el-option label="到货时间" value="deliveryTime"></el-option>
<el-option label="下单时间" value="estimatedOrderTime"></el-option>
<el-option label="执行单截止时间" value="orderEndTime"></el-option>
</el-select>
<el-date-picker
v-model="dateRange"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['project:order:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['project:order:remove']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['project:order:export']"></el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="orderList" @selection-change="handleSelectionChange" @sort-change="handleSortChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="项目编号" align="center" prop="projectCode" width="100" />
<el-table-column label="项目名称" align="center" prop="projectName" width="300" :show-overflow-tooltip="true">
<template slot-scope="scope">
<a @click="handleViewProject(scope.row)" class="link-type">{{ scope.row.projectName }}</a>
</template>
</el-table-column>
<el-table-column label="合同编号" align="center" prop="orderCode" width="200" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span class="link-type" @click="handleUpdate(scope.row)">{{ scope.row.orderCode }}</span>
</template>
</el-table-column>
<el-table-column label="最终客户" align="center" prop="customerName" width="200" :show-overflow-tooltip="true"/>
<el-table-column label="金额(¥)" align="center" prop="shipmentAmount" width="100" sortable="custom">
<template slot-scope="scope">
<span>{{
formatAmount(scope.row.shipmentAmount)
}}</span>
</template>
</el-table-column>
<el-table-column label="订单状态" align="center" prop="orderStatus" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.order_status" :value="scope.row.orderStatus"/>
</template>
</el-table-column>
<el-table-column label="代表处" align="center" prop="agentName" width="100" :show-overflow-tooltip="true"/>
<el-table-column label="汇智负责人" align="center" prop="dutyName" width="100"/>
<el-table-column label="物流状态" align="center" width="100" />
<el-table-column label="进货商" align="center" prop="partnerName" width="200" :show-overflow-tooltip="true"/>
<el-table-column label="到货时间" align="center" prop="deliveryTime" width="200" sortable="custom">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.deliveryTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="下单时间" align="center" prop="estimatedOrderTime" width="200" sortable="custom">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.estimatedOrderTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="执行单截止时间" align="center" prop="orderEndTime" width="200" sortable="custom">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.orderEndTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200">
<template slot-scope="scope">
<div v-if="scope.row.showLog">
<el-button size="mini" type="text" icon="el-icon-view" @click="viewApproveLog(scope.row)"></el-button>
</div>
<div v-else>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:order:edit']"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['project:order:remove']"></el-button>
</div>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 订单详情对话框 -->
<order-detail :visible.sync="open" :title="title" :order-id="currentOrderId" @success="getList"></order-detail>
<!-- 项目详情抽屉 -->
<project-detail-drawer :visible.sync="projectDrawerVisible" :project-id="currentProjectId" />
</div>
</template>
<script>
import { listOrder, delOrder, exportOrder } from "@/api/project/order";
import OrderDetail from './OrderDetail.vue';
import ProjectDetailDrawer from '../info/ProjectDetailDrawer.vue';
export default {
name: "Order",
components: {
OrderDetail,
ProjectDetailDrawer
},
dicts: ['order_status'],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
orderList: [],
//
dateRange: [],
// --- ---
//
title: "",
//
open: false,
// ID
currentOrderId: null,
// --- ---
projectDrawerVisible: false,
currentProjectId: null,
// --- ---
queryParams: {
pageNum: 1,
pageSize: 10,
projectCode: null,
projectName: null,
orderCode: null,
customerName: null,
orderStatus: null,
agentName: null,
dutyName: null,
partnerName: null,
timeType: 'deliveryTime',
params: {}
}
};
},
created() {
this.getList();
},
methods: {
/** 查询订单管理列表 */
getList() {
this.loading = true;
let query = { ...this.queryParams };
query.params = {};
if (this.dateRange && this.dateRange.length === 2) {
const timeType = this.queryParams.timeType;
const beginKey = `begin${timeType.charAt(0).toUpperCase() + timeType.slice(1)}`;
const endKey = `end${timeType.charAt(0).toUpperCase() + timeType.slice(1)}`;
query.params[beginKey] = this.dateRange[0];
query.params[endKey] = this.dateRange[1];
}
delete query.timeType;
listOrder(query).then(response => {
this.orderList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.currentOrderId = null;
this.open = true;
this.title = "添加订单";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.currentOrderId = row.id || this.ids[0];
this.open = true;
this.title = "修改订单";
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$confirm('是否确认删除订单管理编号为"' + ids + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delOrder(ids);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有订单管理数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.exportLoading = true;
return exportOrder(queryParams);
}).then(response => {
this.download(response.msg);
this.exportLoading = false;
})
},
/** 排序触发事件 */
handleSortChange(column) {
this.queryParams.orderByColumn = column.prop;
if (column.order === 'ascending') {
this.queryParams.isAsc = 'asc';
} else if (column.order === 'descending') {
this.queryParams.isAsc = 'desc';
} else {
this.queryParams.isAsc = null;
this.queryParams.orderByColumn = null;
}
this.getList();
},
/** 格式化金额 */
formatAmount(value) {
if (value === null || value === undefined || value === '') return '0.00';
return Number(value).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
},
/** 查看项目详情 */
handleViewProject(row) {
this.currentProjectId = row.projectId;
this.projectDrawerVisible = true;
},
/** 查看审批历史 */
viewApproveLog(row) {
// this.$router.push({ path: '/project/order/approveLog/' + row.id });
this.msgWarning("查看审批历史功能待实现");
}
}
};
</script>

View File

@ -1,5 +1,6 @@
<template> <template>
<el-dialog title="选择产品" :visible.sync="visible" width="1000px" append-to-body @close="handleClose"> <el-dialog title="选择产品" :visible.sync="visible" width="60%" append-to-body @close="handleClose">
<div style="max-height: 60vh; overflow-y: auto;">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
<el-form-item label="产品编码" prop="productCode"> <el-form-item label="产品编码" prop="productCode">
<el-input <el-input
@ -38,6 +39,7 @@
:limit.sync="queryParams.pageSize" :limit.sync="queryParams.pageSize"
@pagination="getList" @pagination="getList"
/> />
</div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button> <el-button @click="handleClose"> </el-button>
@ -74,19 +76,19 @@ export default {
pageSize: 10, pageSize: 10,
productCode: null, productCode: null,
model: null, model: null,
productType: this.productType // Pass productType to query params type: this.productType // Pass productType to query params
}, },
}; };
}, },
watch: { watch: {
visible(val) { visible(val) {
if (val) { if (val) {
this.queryParams.productType = this.productType; // Update productType when dialog opens this.queryParams.type = this.productType; // Update productType when dialog opens
this.getList(); this.getList();
} }
}, },
productType(val) { productType(val) {
this.queryParams.productType = val; this.queryParams.type = val;
if (this.visible) { if (this.visible) {
this.getList(); this.getList();
} }