feat(quotation): 新增报价单详情页面并完善相关功能
- 新增报价单详情页面实现查看功能 - 添加选择报价单组件支持项目关联报价单 - 更新权限配置将base模块改为sip模块 - 在项目表中新增合作商系统用户ID字段 - 完善报价单控制器的权限注解 - 优化报价单导出Excel格式和样式 - 修复查询条件中的表别名问题 - 添加省代服务产品类型支持dev_1.0.1^2
parent
50ee54d6ef
commit
a7f7a29d74
|
|
@ -0,0 +1,143 @@
|
|||
<template>
|
||||
<el-drawer
|
||||
title="报价单详情"
|
||||
:visible.sync="visible"
|
||||
direction="rtl"
|
||||
size="80%"
|
||||
:before-close="handleClose"
|
||||
append-to-body
|
||||
>
|
||||
<div class="detail-container" v-loading="loading">
|
||||
<!-- Basic Info -->
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-descriptions :column="2" border size="medium">
|
||||
<el-descriptions-item label="报价单号">{{ form.quotationCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报价单名称">{{ form.quotationName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="币种">
|
||||
<dict-tag :options="dict.type.currency_type" :value="form.amountType"/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="代表处">{{ agentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">{{ form.customerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ form.quotationStatus }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ parseTime(form.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ form.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div v-if="form.quotationCode || catalogueTotalPrice > 0 || discountedTotalPrice > 0" style="margin-top: 20px;">
|
||||
<el-row type="flex" justify="space-between" style="margin-bottom: 20px; font-size: 14px;">
|
||||
<el-col :span="24" style="text-align: right;">
|
||||
<span v-if="catalogueTotalPrice > 0" style="margin-right: 20px;">
|
||||
<span style="font-weight: bold;">目录总价:</span>{{ formatAmount(catalogueTotalPrice) }}
|
||||
</span>
|
||||
<span v-if="discountedTotalPrice > 0">
|
||||
<span style="font-weight: bold;">折后总价:</span>{{ formatAmount(discountedTotalPrice) }}
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- Config Info -->
|
||||
<product-config :value="form" readonly />
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getQuotation } from "@/api/base/quotation";
|
||||
import ProductConfig from "@/views/project/info/ProductConfig";
|
||||
|
||||
export default {
|
||||
name: "QuotationDetail",
|
||||
components: { ProductConfig },
|
||||
dicts: ['currency_type'],
|
||||
props: {
|
||||
agentOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
form: {
|
||||
softwareProjectProductInfoList: [],
|
||||
hardwareProjectProductInfoList: [],
|
||||
maintenanceProjectProductInfoList: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
agentName() {
|
||||
if (!this.form.agentCode || !this.agentOptions) return this.form.agentCode;
|
||||
const agent = this.agentOptions.find(item => item.agentCode === this.form.agentCode);
|
||||
return agent ? agent.agentName : this.form.agentCode;
|
||||
},
|
||||
catalogueTotalPrice() {
|
||||
let total = 0;
|
||||
const lists = [
|
||||
this.form.softwareProjectProductInfoList,
|
||||
this.form.hardwareProjectProductInfoList,
|
||||
this.form.maintenanceProjectProductInfoList
|
||||
];
|
||||
lists.forEach(list => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
total += Number(item.catalogueAllPrice) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
return total;
|
||||
},
|
||||
discountedTotalPrice() {
|
||||
let total = 0;
|
||||
const lists = [
|
||||
this.form.softwareProjectProductInfoList,
|
||||
this.form.hardwareProjectProductInfoList,
|
||||
this.form.maintenanceProjectProductInfoList
|
||||
];
|
||||
lists.forEach(list => {
|
||||
if (list && list.length > 0) {
|
||||
list.forEach(item => {
|
||||
total += Number(item.allPrice) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
return total;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(id) {
|
||||
this.visible = true;
|
||||
this.getDetail(id);
|
||||
},
|
||||
getDetail(id) {
|
||||
this.loading = true;
|
||||
getQuotation(id).then(response => {
|
||||
this.form = response.data;
|
||||
// Ensure lists are arrays
|
||||
this.form.softwareProjectProductInfoList = this.form.softwareProjectProductInfoList || [];
|
||||
this.form.hardwareProjectProductInfoList = this.form.hardwareProjectProductInfoList || [];
|
||||
this.form.maintenanceProjectProductInfoList = this.form.maintenanceProjectProductInfoList || [];
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleClose(done) {
|
||||
this.visible = false;
|
||||
if (done) done();
|
||||
},
|
||||
formatAmount(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return Number(value).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.detail-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['base:quotation:add']"
|
||||
v-hasPermi="['sip:quotation:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['base:quotation:edit']"
|
||||
v-hasPermi="['sip:quotation:update']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['base:quotation:remove']"
|
||||
v-hasPermi="['sip:quotation:delete']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
|
|
@ -103,35 +103,34 @@
|
|||
type="text"
|
||||
icon="el-icon-view"
|
||||
@click="handleDetail(scope.row)"
|
||||
v-hasPermi="['base:quotation:query']"
|
||||
>详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['base:quotation:edit']"
|
||||
v-hasPermi="['sip:quotation:update']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document-copy"
|
||||
@click="handleCopy(scope.row)"
|
||||
v-hasPermi="['base:quotation:add']"
|
||||
v-hasPermi="['sip:quotation:add']"
|
||||
>复制创建</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['base:quotation:remove']"
|
||||
v-hasPermi="['sip:quotation:delete']"
|
||||
>删除</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-download"
|
||||
@click="handleExport(scope.row)"
|
||||
v-hasPermi="['base:quotation:export']"
|
||||
v-hasPermi="['sip:quotation:export']"
|
||||
>导出</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<el-dialog title="选择报价单" :visible.sync="visible" :close-on-click-modal="false" width="900px" append-to-body @close="handleClose">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" @submit.native.prevent>
|
||||
<el-form-item label="报价单号" prop="quotationCode">
|
||||
<el-input
|
||||
v-model="queryParams.quotationCode"
|
||||
placeholder="请输入报价单号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="报价单名称" prop="quotationName">
|
||||
<el-input
|
||||
v-model="queryParams.quotationName"
|
||||
placeholder="请输入报价单名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</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-table v-loading="loading" :data="quotationList" @row-click="handleRowClick" highlight-current-row>
|
||||
<el-table-column label="报价单号" align="center" prop="quotationCode" />
|
||||
<el-table-column label="报价单名称" align="center" prop="quotationName" />
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||
<el-table-column label="报价金额" align="center" prop="discountAmount" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listQuotation } from "@/api/base/quotation";
|
||||
|
||||
export default {
|
||||
name: "SelectQuotation",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
createBy: {
|
||||
type: String,
|
||||
default: "-1",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 报价单表格数据
|
||||
quotationList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
createBy: this.createBy,
|
||||
quotationCode: null,
|
||||
quotationName: null,
|
||||
orderByColumn: 'createTime',
|
||||
isAsc: 'desc'
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.queryParams.createBy = this.createBy||"-1";
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 查询报价单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listQuotation(this.queryParams).then(response => {
|
||||
this.quotationList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 行点击事件 */
|
||||
handleRowClick(row) {
|
||||
this.$emit("quotation-selected", row);
|
||||
this.handleClose();
|
||||
},
|
||||
/** 关闭按钮 */
|
||||
handleClose() {
|
||||
this.$emit("update:visible", false);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -433,7 +433,7 @@
|
|||
</el-dialog>
|
||||
|
||||
<select-agent :visible.sync="selectAgentVisible" @agent-selected="handleAgentSelected" />
|
||||
<select-quotation :visible.sync="selectQuotationVisible" @quotation-selected="handleQuotationSelected" />
|
||||
<select-quotation :visible.sync="selectQuotationVisible" :create-by="form.partnerSystemUserId" @quotation-selected="handleQuotationSelected" />
|
||||
<select-customer :visible.sync="selectCustomerVisible" @customer-selected="handleCustomerSelected" />
|
||||
<select-partner :visible.sync="selectPartnerVisible" @partner-selected="handlePartnerSelected" />
|
||||
<select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected" />
|
||||
|
|
@ -683,6 +683,7 @@ export default {
|
|||
partnerName: null,
|
||||
partnerCode: null,
|
||||
partnerUserName: null,
|
||||
partnerSystemUserId: null,
|
||||
contactWay: null,
|
||||
estimatedAmount: null,
|
||||
estimatedOrderTime: null,
|
||||
|
|
@ -834,6 +835,7 @@ export default {
|
|||
if (value === 'h3c') {
|
||||
this.form.partnerName = '新华三';
|
||||
this.form.partnerCode = null;
|
||||
this.form.partnerSystemUserId = null;
|
||||
}
|
||||
if (this.$refs.form) {
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -923,6 +925,8 @@ export default {
|
|||
this.form.partnerCode = partner.partnerCode;
|
||||
this.form.partnerUserName = partner.contactPerson;
|
||||
this.form.contactWay = partner.contactPhone;
|
||||
this.$set(this.form,'partnerSystemUserId', partner.systemUserId);
|
||||
|
||||
this.selectPartnerVisible = false;
|
||||
},
|
||||
openSelectPeople() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
|
|||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.sip.domain.Quotation;
|
||||
import com.ruoyi.sip.service.IQuotationService;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -28,6 +29,7 @@ public class QuotationController extends BaseController {
|
|||
|
||||
|
||||
@GetMapping("/list")
|
||||
@RequiresPermissions("sip:quotation:list")
|
||||
public TableDataInfo list(Quotation quotation) {
|
||||
startPage();
|
||||
List<Quotation> list = quotationService.queryAll(quotation);
|
||||
|
|
@ -43,6 +45,7 @@ public class QuotationController extends BaseController {
|
|||
|
||||
|
||||
@PostMapping("/insert")
|
||||
@RequiresPermissions("sip:quotation:add")
|
||||
public AjaxResult add(@RequestBody Quotation quotation) {
|
||||
return toAjax(quotationService.insert(quotation));
|
||||
}
|
||||
|
|
@ -50,6 +53,7 @@ public class QuotationController extends BaseController {
|
|||
|
||||
|
||||
@PutMapping("/update")
|
||||
@RequiresPermissions("sip:quotation:update")
|
||||
public AjaxResult edit(@RequestBody Quotation quotation) {
|
||||
return toAjax(quotationService.update(quotation));
|
||||
}
|
||||
|
|
@ -57,6 +61,7 @@ public class QuotationController extends BaseController {
|
|||
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@RequiresPermissions("sip:quotation:delete")
|
||||
public AjaxResult remove(@PathVariable("id") Integer id) {
|
||||
return toAjax(quotationService.deleteById(id));
|
||||
}
|
||||
|
|
@ -66,10 +71,12 @@ public class QuotationController extends BaseController {
|
|||
|
||||
*/
|
||||
@DeleteMapping("/remove/batch/{ids}")
|
||||
@RequiresPermissions("sip:quotation:delete")
|
||||
public AjaxResult batchRemove(@PathVariable("ids") Integer[] ids) {
|
||||
return AjaxResult.success(quotationService.batchRemove(ids));
|
||||
}
|
||||
@GetMapping("/export/single/{id}")
|
||||
@RequiresPermissions("sip:quotation:export")
|
||||
public AjaxResult exportSingle(@PathVariable("id") Integer id) {
|
||||
return AjaxResult.success(quotationService.exportSingle(id));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ public class ProjectInfo extends BaseEntity
|
|||
|
||||
private List<OmsFileLog> projectFileList;
|
||||
private String fileId;
|
||||
private String partnerSystemUserId;
|
||||
private Integer quotationId;
|
||||
private List<Integer> quotationIdList;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class Quotation extends BaseEntity {
|
|||
* 项目编号
|
||||
*/
|
||||
private String projectCode;
|
||||
private String projectName;
|
||||
/**
|
||||
* 报价金额
|
||||
*/
|
||||
|
|
@ -78,6 +79,8 @@ public class Quotation extends BaseEntity {
|
|||
private List<QuotationProductInfo> hardwareProjectProductInfoList;
|
||||
// @Excel(name = "服务")
|
||||
private List<QuotationProductInfo> maintenanceProjectProductInfoList;
|
||||
//省代
|
||||
private List<QuotationProductInfo> provinceProductInfoList;
|
||||
|
||||
@Getter
|
||||
public enum QuotationStatusEnum {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
|
|||
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||||
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
|
||||
import com.ruoyi.common.annotation.DataScope;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.PageUtils;
|
||||
import com.ruoyi.common.utils.ShiroUtils;
|
||||
|
|
@ -69,6 +70,7 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
* @return 对象列表
|
||||
*/
|
||||
@Override
|
||||
@DataScope(deptAlias = "u", userAlias = "u")
|
||||
public List<Quotation> queryAll(Quotation quotation) {
|
||||
List<Quotation> dataList = quotationMapper.queryAll(quotation);
|
||||
PageUtils.clearPage();
|
||||
|
|
@ -95,7 +97,14 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>()));
|
||||
maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.OTHER.getType(), new ArrayList<>()));
|
||||
quotation.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList);
|
||||
quotation.setProvinceProductInfoList(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.PROVINCE_SERVICE.getType(),new ArrayList<>()));
|
||||
ProjectInfo projectInfo = new ProjectInfo();
|
||||
projectInfo.setQuotationId(quotation.getId());
|
||||
List<ProjectInfo> projectInfos = projectInfoService.selectProjectInfoList(projectInfo);
|
||||
quotation.setProjectCode(projectInfos.stream().map(ProjectInfo::getProjectCode).collect(Collectors.joining(",")));
|
||||
quotation.setProjectName(projectInfos.stream().map(ProjectInfo::getProjectName).collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
return quotation;
|
||||
}
|
||||
|
||||
|
|
@ -187,46 +196,42 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
row1.add("");
|
||||
}
|
||||
rows.add(row1);
|
||||
rows.add(Collections.emptyList());
|
||||
|
||||
// Calculate Date
|
||||
String validDate = LocalDate.now().plusDays(7).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
|
||||
// Helper to create padded row
|
||||
List<Object> row2 = new ArrayList<>();
|
||||
row2.add("项目名称:");
|
||||
row2.add(quotation.getQuotationName());
|
||||
row2.add("");
|
||||
row2.add("*项目名称:");
|
||||
row2.add(quotation.getProjectName());
|
||||
row2.add("");
|
||||
row2.add("");
|
||||
row2.add("");
|
||||
row2.add(""); // Padding
|
||||
row2.add("*本报价单有效期至:");
|
||||
row2.add(validDate);
|
||||
row2.add("*本报价单有效期至:"+validDate);
|
||||
rows.add(row2);
|
||||
|
||||
List<Object> row3 = new ArrayList<>();
|
||||
row3.add("");
|
||||
row3.add("*项目ID:");
|
||||
row3.add(quotation.getProjectCode());
|
||||
row3.add("");
|
||||
row3.add("");
|
||||
row3.add("(项目名称和ID在商务申请时必填)");
|
||||
row3.add("");
|
||||
row3.add("");
|
||||
row3.add("*云桌面完整报价单必须包含部署服务、现场维保、省代集成服务,此三项由省代进行补充报价,不能缺项");
|
||||
rows.add(row3);
|
||||
|
||||
List<Object> row4 = new ArrayList<>();
|
||||
row4.add("");
|
||||
row4.add("国家/地区 :");
|
||||
row4.add("中国大陆");
|
||||
row4.add("");
|
||||
row4.add("");
|
||||
row4.add("");
|
||||
row4.add("");
|
||||
row4.add("*因上游CPU、内存、存储波动较大,封标前3天与汇智区域接口人邮件确定商务折扣和供货周期,否则报价单无效");
|
||||
rows.add(row4);
|
||||
|
||||
List<Object> row5 = new ArrayList<>();
|
||||
row5.add("备注:");
|
||||
row5.add(quotation.getRemark());
|
||||
rows.add(row5);
|
||||
|
||||
rows.add(Collections.emptyList());
|
||||
|
||||
|
|
@ -236,18 +241,25 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
"目录单价(RMB)", "推荐折扣", "折扣单价(RMB)", "总价(RMB)",
|
||||
"目录总价(RMB)", "CID信息", "备注"
|
||||
);
|
||||
// 记录列标题行索引,使其变灰
|
||||
// 记录列标题行索引
|
||||
int headerRowIndex = rows.size();
|
||||
Set<Integer> coloredRowIndices = new HashSet<>();
|
||||
coloredRowIndices.add(rows.size());
|
||||
// 标题列填充灰色
|
||||
coloredRowIndices.add(headerRowIndex);
|
||||
rows.add(new ArrayList<>(headers));
|
||||
|
||||
// 3. 第三部分:数据分组
|
||||
Set<Integer> aquaRowIndices = new HashSet<>();
|
||||
AtomicInteger sectionCounter = new AtomicInteger(1);
|
||||
|
||||
addSection(rows, "软件", quotation.getSoftwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
addSection(rows, "硬件", quotation.getHardwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
addSection(rows, "服务", quotation.getMaintenanceProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
// 添加默认分组:云桌面服务器
|
||||
QuotationProductInfo defaultItem = new QuotationProductInfo();
|
||||
|
||||
addSection(rows, "云桌面服务器","VOI/VDI服务器", Collections.singletonList(defaultItem), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
|
||||
addSection(rows, "云桌面软件", "Workspace/learningspace/NEX",quotation.getSoftwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
addSection(rows, "云终端", "VOI/VDI终端",quotation.getHardwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
addSection(rows, "省代服务(若从省代出货需计算)","省代服务", quotation.getProvinceProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter);
|
||||
|
||||
ExcelUtil<Object> util = new ExcelUtil<>(Object.class);
|
||||
String fileName = util.encodingFilename("报价单_" + quotation.getQuotationCode());
|
||||
|
|
@ -257,15 +269,19 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||||
contentWriteCellStyle.setWrapped(true);
|
||||
contentWriteCellStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
contentWriteCellStyle.setHorizontalAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.LEFT);
|
||||
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
|
||||
|
||||
EasyExcel.write(filePath)
|
||||
.registerWriteHandler(horizontalCellStyleStrategy)
|
||||
.registerWriteHandler(new CustomColumnWidthStrategy())
|
||||
.registerWriteHandler(new CustomColumnWidthStrategy(headerRowIndex))
|
||||
.registerWriteHandler(new com.alibaba.excel.write.handler.CellWriteHandler() {
|
||||
private org.apache.poi.ss.usermodel.CellStyle coloredStyle;
|
||||
private org.apache.poi.ss.usermodel.CellStyle coloredLeftStyle;
|
||||
private org.apache.poi.ss.usermodel.CellStyle aquaStyle;
|
||||
private org.apache.poi.ss.usermodel.CellStyle centerStyle;
|
||||
private org.apache.poi.ss.usermodel.CellStyle infoStyle;
|
||||
private org.apache.poi.ss.usermodel.CellStyle redInfoStyle;
|
||||
|
||||
@Override
|
||||
public void beforeCellCreate(com.alibaba.excel.write.metadata.holder.WriteSheetHolder writeSheetHolder, com.alibaba.excel.write.metadata.holder.WriteTableHolder writeTableHolder, org.apache.poi.ss.usermodel.Row row, com.alibaba.excel.metadata.Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
|
||||
|
|
@ -289,6 +305,8 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
centerStyle = workbook.createCellStyle();
|
||||
centerStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
|
||||
centerStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
centerStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE.getIndex());
|
||||
centerStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
font.setFontHeightInPoints((short) 16);
|
||||
|
|
@ -297,21 +315,75 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
cell.setCellStyle(centerStyle);
|
||||
// Merge cells for title (0-10, Logo is at 11 usually, but let's merge fewer if needed, or 0-10)
|
||||
writeSheetHolder.getSheet().addMergedRegionUnsafe(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 10));
|
||||
}
|
||||
} else if (cell.getRowIndex() < headerRowIndex) {
|
||||
// Info rows (0 to headerRowIndex-1) - Excluding 0 handled above?
|
||||
// Actually handle 0 separately or here. 0 is handled above.
|
||||
if (cell.getRowIndex() == 0) return;
|
||||
|
||||
if (coloredRowIndices.contains(cell.getRowIndex())) {
|
||||
if (coloredStyle == null) {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
coloredStyle = workbook.createCellStyle();
|
||||
coloredStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
coloredStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
coloredStyle.setWrapText(true);
|
||||
coloredStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
coloredStyle.setFont(font);
|
||||
// Check for Red Font Conditions
|
||||
boolean isRed = false;
|
||||
if (cell.getRowIndex() == 2 && (cell.getColumnIndex() == 3 || cell.getColumnIndex() == 6)) {
|
||||
isRed = true;
|
||||
} else if (cell.getRowIndex() == 3 && (cell.getColumnIndex() == 6 || cell.getColumnIndex() == 3)) {
|
||||
isRed = true;
|
||||
} else if (cell.getRowIndex() == 4 && cell.getColumnIndex() == 6) {
|
||||
isRed = true;
|
||||
}
|
||||
|
||||
if (isRed) {
|
||||
if (redInfoStyle == null) {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
redInfoStyle = workbook.createCellStyle();
|
||||
redInfoStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE.getIndex());
|
||||
redInfoStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
redInfoStyle.setWrapText(false);
|
||||
redInfoStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
||||
font.setColor(org.apache.poi.ss.usermodel.IndexedColors.RED.getIndex());
|
||||
redInfoStyle.setFont(font);
|
||||
}
|
||||
cell.setCellStyle(redInfoStyle);
|
||||
} else {
|
||||
if (infoStyle == null) {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
infoStyle = workbook.createCellStyle();
|
||||
infoStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE.getIndex());
|
||||
infoStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
infoStyle.setWrapText(false);
|
||||
infoStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
}
|
||||
cell.setCellStyle(infoStyle);
|
||||
}
|
||||
|
||||
} else if (coloredRowIndices.contains(cell.getRowIndex())) {
|
||||
if (cell.getRowIndex() > headerRowIndex) {
|
||||
if (coloredLeftStyle == null) {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
coloredLeftStyle = workbook.createCellStyle();
|
||||
coloredLeftStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
coloredLeftStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
coloredLeftStyle.setWrapText(true);
|
||||
coloredLeftStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
coloredLeftStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.LEFT);
|
||||
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
coloredLeftStyle.setFont(font);
|
||||
}
|
||||
cell.setCellStyle(coloredLeftStyle);
|
||||
} else {
|
||||
if (coloredStyle == null) {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
coloredStyle = workbook.createCellStyle();
|
||||
coloredStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
coloredStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
coloredStyle.setWrapText(true);
|
||||
coloredStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
coloredStyle.setFont(font);
|
||||
}
|
||||
cell.setCellStyle(coloredStyle);
|
||||
}
|
||||
cell.setCellStyle(coloredStyle);
|
||||
} else if (aquaRowIndices.contains(cell.getRowIndex())) {
|
||||
if (aquaStyle == null) {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
|
|
@ -319,6 +391,7 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
aquaStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.AQUA.getIndex());
|
||||
aquaStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
||||
aquaStyle.setWrapText(true);
|
||||
aquaStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.LEFT);
|
||||
}
|
||||
cell.setCellStyle(aquaStyle);
|
||||
}
|
||||
|
|
@ -350,7 +423,7 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
quotationMapper.update(quotation);
|
||||
}
|
||||
|
||||
private void addSection(List<List<Object>> rows, String title, List<QuotationProductInfo> list, Set<Integer> coloredRowIndices, Set<Integer> aquaRowIndices, AtomicInteger sectionCounter) {
|
||||
private void addSection(List<List<Object>> rows, String title, String subTitle,List<QuotationProductInfo> list, Set<Integer> coloredRowIndices, Set<Integer> aquaRowIndices, AtomicInteger sectionCounter) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -367,6 +440,18 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
}
|
||||
rows.add(titleRow);
|
||||
|
||||
// 添加标题行 (补齐12列)
|
||||
List<Object> subTitleRow = new ArrayList<>();
|
||||
subTitleRow.add(currentSection+"_"+currentSection);
|
||||
subTitleRow.add(subTitle);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
subTitleRow.add("");
|
||||
}
|
||||
rows.add(subTitleRow);
|
||||
|
||||
|
||||
|
||||
|
||||
double sumAllPrice = 0.0;
|
||||
double sumCatalogueAllPrice = 0.0;
|
||||
|
||||
|
|
@ -374,7 +459,7 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
int index = 1;
|
||||
for (QuotationProductInfo item : list) {
|
||||
List<Object> row = new ArrayList<>();
|
||||
row.add(currentSection + "_" + index++);
|
||||
row.add("");
|
||||
row.add(item.getProductBomCode());
|
||||
row.add(item.getModel());
|
||||
row.add(item.getProductDesc());
|
||||
|
|
@ -400,7 +485,7 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
coloredRowIndices.add(rows.size());
|
||||
List<Object> subTotalRow1 = new ArrayList<>();
|
||||
subTotalRow1.add("");
|
||||
subTotalRow1.add(title);
|
||||
subTotalRow1.add(subTitle);
|
||||
subTotalRow1.add("");
|
||||
subTotalRow1.add("");
|
||||
subTotalRow1.add("");
|
||||
|
|
@ -434,9 +519,19 @@ public class QuotationServiceImpl implements IQuotationService {
|
|||
// 自定义列宽策略:自适应但有最大宽度
|
||||
public static class CustomColumnWidthStrategy extends AbstractColumnWidthStyleStrategy {
|
||||
private static final int MAX_COLUMN_WIDTH = 50;
|
||||
private final int headerRowIndex;
|
||||
|
||||
public CustomColumnWidthStrategy(int headerRowIndex) {
|
||||
this.headerRowIndex = headerRowIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
||||
// 1-6行不参与列宽计算 (小于标题行的行)
|
||||
if (cell.getRowIndex() < headerRowIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHead != null && isHead) {
|
||||
// 如果是表头,不需特别处理,EasyExcel会自动处理,或者这里也可以计算
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -28,70 +28,74 @@
|
|||
<!--通过实体作为筛选条件查询-->
|
||||
<select id="queryAll" resultMap="QuotationMap">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
from oms_quotation
|
||||
t1.id, t1.quotation_code, t1.quotation_name, t1.quotation_amount, t1.discount_amount,
|
||||
t1.quotation_status, t1.create_time, t1.create_by, t1.update_by, t1.update_time, t1.remark,
|
||||
t1.agent_code, t1.amount_type, t1.customer_name
|
||||
from oms_quotation t1
|
||||
LEFT join sys_user u on t1.create_by = u.user_id
|
||||
<where>
|
||||
<if test="id != null">
|
||||
and id = #{id}
|
||||
and t1.id = #{id}
|
||||
</if>
|
||||
<if test="quotationCode != null and quotationCode != ''">
|
||||
and quotation_code = #{quotationCode}
|
||||
and t1.quotation_code = #{quotationCode}
|
||||
</if>
|
||||
<if test="quotationName != null and quotationName != ''">
|
||||
and quotation_name = #{quotationName}
|
||||
and t1.quotation_name = #{quotationName}
|
||||
</if>
|
||||
|
||||
<if test="quotationAmount != null">
|
||||
and quotation_amount = #{quotationAmount}
|
||||
and t1.quotation_amount = #{quotationAmount}
|
||||
</if>
|
||||
<if test="discountAmount != null">
|
||||
and discount_amount = #{discountAmount}
|
||||
and t1.discount_amount = #{discountAmount}
|
||||
</if>
|
||||
<if test="quotationStatus != null and quotationStatus != ''">
|
||||
and quotation_status = #{quotationStatus}
|
||||
and t1.quotation_status = #{quotationStatus}
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
and create_time = #{createTime}
|
||||
and t1.create_time = #{createTime}
|
||||
</if>
|
||||
<if test="createBy != null and createBy != ''">
|
||||
and create_by = #{createBy}
|
||||
and t1.create_by = #{createBy}
|
||||
</if>
|
||||
<if test="updateBy != null and updateBy != ''">
|
||||
and update_by = #{updateBy}
|
||||
and t1.update_by = #{updateBy}
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
and update_time = #{updateTime}
|
||||
and t1.update_time = #{updateTime}
|
||||
</if>
|
||||
<if test="remark != null and remark != ''">
|
||||
and remark = #{remark}
|
||||
and t1.remark = #{remark}
|
||||
</if>
|
||||
|
||||
<if test="agentCode != null and agentCode != ''">
|
||||
and agent_code = #{agentCode}
|
||||
and t1.agent_code = #{agentCode}
|
||||
</if>
|
||||
<if test="amountType != null and amountType != ''">
|
||||
and amount_type = #{amountType}
|
||||
and t1.amount_type = #{amountType}
|
||||
</if>
|
||||
<if test="customerName != null and customerName != ''">
|
||||
and customer_name = #{customerName}
|
||||
and t1.customer_name = #{customerName}
|
||||
</if>
|
||||
<if test="customerName != null and customerName != ''">
|
||||
and customer_name = #{customerName}
|
||||
and t1.customer_name = #{customerName}
|
||||
</if>
|
||||
<if test="createTimeStart != null or createTimeEnd != null">
|
||||
<choose>
|
||||
<when test="createTimeStart != null and createTimeEnd != null">
|
||||
and create_time between date_format(#{createTimeStart}, '%Y-%m-%d 00:00:00') and date_format(#{createTimeEnd}, '%Y-%m-%d 23:59:59')
|
||||
and t1.create_time between date_format(#{createTimeStart}, '%Y-%m-%d 00:00:00') and date_format(#{createTimeEnd}, '%Y-%m-%d 23:59:59')
|
||||
</when>
|
||||
<when test="createTimeStart != null">
|
||||
and create_time <![CDATA[ >= ]]> date_format(#{createTimeStart}, '%Y-%m-%d 00:00:00')
|
||||
and t1.create_time <![CDATA[ >= ]]> date_format(#{createTimeStart}, '%Y-%m-%d 00:00:00')
|
||||
</when>
|
||||
<when test="createTimeEnd != null">
|
||||
and create_time <![CDATA[ <= ]]> date_format(#{createTimeEnd}, '%Y-%m-%d 23:59:59')
|
||||
and t1.create_time <![CDATA[ <= ]]> date_format(#{createTimeEnd}, '%Y-%m-%d 23:59:59')
|
||||
</when>
|
||||
|
||||
</choose>
|
||||
</if>
|
||||
${params.dataScope}
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
t3.user_name as hz_support_user_name,
|
||||
t5.level,
|
||||
t5.contact_email as partner_email,
|
||||
t6.system_user_id as partner_system_user_id,
|
||||
t1.update_time ,
|
||||
ifnull(t4.work_time,t1.update_time) as last_work_update_time
|
||||
from project_info t1
|
||||
|
|
@ -98,6 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
left join sys_user t3 on t1.hz_support_user=t3.user_id
|
||||
left join partner_info t5 on t1.partner_code=t5.partner_code
|
||||
left join (select max(work_time) work_time,project_id from project_work_progress group by project_id) t4 ON t1.id=t4.project_id
|
||||
left join partner_info t6 on t1.partner_code=t6.partner_code
|
||||
|
||||
|
||||
</sql>
|
||||
|
|
|
|||
Loading…
Reference in New Issue