|
|
@@ -1,42 +1,37 @@
|
|
|
<template>
|
|
|
<div class="app-container">
|
|
|
- <!-- 数据统计 -->
|
|
|
+ <!-- 视图切换标签 -->
|
|
|
+ <el-row class="mb-3">
|
|
|
+ <el-radio-group v-model="activeView" @change="handleViewChange">
|
|
|
+ <el-radio-button value="my">我的报价</el-radio-button>
|
|
|
+ <el-radio-button value="all">全部报价</el-radio-button>
|
|
|
+ <el-radio-button value="submitted">待审批</el-radio-button>
|
|
|
+ <el-radio-button value="accepted">已审批</el-radio-button>
|
|
|
+ <!-- <el-radio-button value="sent">已发送</el-radio-button> -->
|
|
|
+ <!-- <el-radio-button value="expired">已过期</el-radio-button> -->
|
|
|
+ </el-radio-group>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 当前视图统计数据 -->
|
|
|
<el-row :gutter="20" class="mb-3">
|
|
|
- <el-col :span="6">
|
|
|
- <el-statistic title="总数" :value="statistics.total || 0">
|
|
|
- <template #suffix>单</template>
|
|
|
- </el-statistic>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-statistic title="本月新增" :value="statistics.monthNew || 0">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-statistic :title="getViewTitle() + '总数'" :value="currentViewStatistics.total || 0">
|
|
|
<template #suffix>单</template>
|
|
|
</el-statistic>
|
|
|
</el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-statistic title="待审批" :value="statistics.pending || 0">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-statistic title="当前页草稿" :value="currentViewStatistics.draft || 0">
|
|
|
<template #suffix>单</template>
|
|
|
</el-statistic>
|
|
|
</el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-statistic title="总金额" :value="statistics.totalAmount || 0" :precision="2">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-statistic title="当前页金额" :value="currentViewStatistics.totalAmount || 0" :precision="2">
|
|
|
<template #prefix>¥</template>
|
|
|
<template #suffix>万</template>
|
|
|
</el-statistic>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
|
|
|
- <!-- 视图切换标签 -->
|
|
|
- <el-row class="mb-3">
|
|
|
- <el-radio-group v-model="activeView" @change="handleViewChange">
|
|
|
- <el-radio-button value="my">我的报价</el-radio-button>
|
|
|
- <el-radio-button value="all">全部报价</el-radio-button>
|
|
|
- <el-radio-button value="submitted">待审批</el-radio-button>
|
|
|
- <!-- <el-radio-button value="sent">已发送</el-radio-button> -->
|
|
|
- <!-- <el-radio-button value="accepted">已接受</el-radio-button> -->
|
|
|
- <!-- <el-radio-button value="expired">已过期</el-radio-button> -->
|
|
|
- </el-radio-group>
|
|
|
- </el-row>
|
|
|
-
|
|
|
<!-- 查询表单 -->
|
|
|
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="80px">
|
|
|
<el-form-item label="报价单号" prop="quoteNo">
|
|
|
@@ -380,7 +375,6 @@ import {
|
|
|
generatePdf,
|
|
|
generateShare,
|
|
|
createNewVersion,
|
|
|
- statisticsOverview,
|
|
|
approveQuote,
|
|
|
listApproval,
|
|
|
type QuoteQuery,
|
|
|
@@ -392,6 +386,7 @@ import { listUser } from '@/api/system/user';
|
|
|
import type { UserVO } from '@/api/system/user/types';
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
import TinyMceEditor from '@/components/TinyMceEditor/index.vue';
|
|
|
+import logoImg from '@/assets/images/logo.gif';
|
|
|
|
|
|
const router = useRouter();
|
|
|
const { copy, copied, isSupported } = useClipboard();
|
|
|
@@ -426,35 +421,46 @@ const dateRange = ref<string[]>([]);
|
|
|
// 视图切换
|
|
|
const activeView = ref('my');
|
|
|
|
|
|
-// 统计数据
|
|
|
-const statistics = ref({
|
|
|
+// 当前视图统计数据
|
|
|
+const currentViewStatistics = ref({
|
|
|
total: 0,
|
|
|
- monthNew: 0,
|
|
|
- pending: 0,
|
|
|
+ draft: 0,
|
|
|
totalAmount: 0
|
|
|
});
|
|
|
|
|
|
-/** 加载统计数据 */
|
|
|
-const loadStatistics = async () => {
|
|
|
- try {
|
|
|
- const res = await statisticsOverview();
|
|
|
- const data = res.data || {};
|
|
|
- statistics.value = {
|
|
|
- total: data.totalCount || 0,
|
|
|
- monthNew: data.draftCount || 0, // 这里可以根据实际需求调整
|
|
|
- pending: data.submittedCount || 0,
|
|
|
- totalAmount: data.totalAmount ? data.totalAmount / 10000 : 0 // 转换为万元
|
|
|
- };
|
|
|
- } catch (error) {
|
|
|
- console.error('加载统计数据失败:', error);
|
|
|
- // 发生错误时使用默认值
|
|
|
- statistics.value = {
|
|
|
- total: 0,
|
|
|
- monthNew: 0,
|
|
|
- pending: 0,
|
|
|
- totalAmount: 0
|
|
|
- };
|
|
|
- }
|
|
|
+/** 获取视图标题 */
|
|
|
+const getViewTitle = () => {
|
|
|
+ const titles: Record<string, string> = {
|
|
|
+ my: '我的报价',
|
|
|
+ all: '全部报价',
|
|
|
+ submitted: '待审批',
|
|
|
+ accepted: '已审批'
|
|
|
+ };
|
|
|
+ return titles[activeView.value] || '';
|
|
|
+};
|
|
|
+
|
|
|
+/** 计算当前视图统计数据 */
|
|
|
+const calculateCurrentViewStatistics = () => {
|
|
|
+ // 基于当前列表数据计算统计
|
|
|
+ const list = quoteList.value || [];
|
|
|
+
|
|
|
+ // 总数
|
|
|
+ const totalCount = total.value;
|
|
|
+
|
|
|
+ // 草稿状态数量
|
|
|
+ const draftCount = list.filter((item) => item.status === 'draft').length;
|
|
|
+
|
|
|
+ // 计算总金额(单位:元,需转换为万元)
|
|
|
+ const totalAmountValue = list.reduce((sum, item) => {
|
|
|
+ const amount = parseFloat(String(item.finalAmount || 0));
|
|
|
+ return sum + (isNaN(amount) ? 0 : amount);
|
|
|
+ }, 0);
|
|
|
+
|
|
|
+ currentViewStatistics.value = {
|
|
|
+ total: totalCount,
|
|
|
+ draft: draftCount,
|
|
|
+ totalAmount: totalAmountValue / 10000 // 转换为万元
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
// 提交审批对话框
|
|
|
@@ -557,23 +563,33 @@ const getList = async () => {
|
|
|
|
|
|
// 根据视图设置状态过滤
|
|
|
if (activeView.value === 'my') {
|
|
|
- // TODO: 添加当前用户过滤
|
|
|
- queryParams.value.ownerUserId = undefined; // LoginHelper.getUserId()
|
|
|
+ // 只查询当前用户负责的报价单
|
|
|
+ queryParams.value.ownerUserId = userStore.userId as number;
|
|
|
+ queryParams.value.status = undefined;
|
|
|
+ queryParams.value.approvedByUserId = undefined;
|
|
|
+ } else if (activeView.value === 'accepted') {
|
|
|
+ // 查询当前用户已审批过的报价单(包括审批通过和审批拒绝)
|
|
|
+ queryParams.value.ownerUserId = undefined;
|
|
|
+ queryParams.value.status = undefined;
|
|
|
+ queryParams.value.approvedByUserId = userStore.userId as number;
|
|
|
} else if (activeView.value !== 'all') {
|
|
|
+ // 按状态查询(如待审批)
|
|
|
+ queryParams.value.ownerUserId = undefined;
|
|
|
queryParams.value.status = activeView.value;
|
|
|
+ queryParams.value.approvedByUserId = undefined;
|
|
|
} else {
|
|
|
- // 如果没有手动选择状态,则清空状态过滤
|
|
|
- if (activeView.value === 'all') {
|
|
|
- queryParams.value.status = undefined;
|
|
|
- }
|
|
|
+ // 查看全部报价单,清空所有过滤条件
|
|
|
+ queryParams.value.ownerUserId = undefined;
|
|
|
+ queryParams.value.status = undefined;
|
|
|
+ queryParams.value.approvedByUserId = undefined;
|
|
|
}
|
|
|
|
|
|
const res = await listQuote(queryParams.value);
|
|
|
quoteList.value = res.rows;
|
|
|
total.value = res.total;
|
|
|
|
|
|
- // 加载统计数据
|
|
|
- loadStatistics();
|
|
|
+ // 计算当前视图统计数据
|
|
|
+ calculateCurrentViewStatistics();
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
@@ -950,115 +966,103 @@ const generateQuoteTemplate = (quote: QuoteVO): string => {
|
|
|
return sum + (isNaN(subtotal) ? 0 : subtotal);
|
|
|
}, 0) || 0;
|
|
|
|
|
|
+ // 格式化日期(从创建时间获取日期)
|
|
|
+ const formatDate = (dateStr: string): string => {
|
|
|
+ if (!dateStr) return '';
|
|
|
+ const date = new Date(dateStr);
|
|
|
+ const year = date.getFullYear();
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
+ const day = String(date.getDate()).padStart(2, '0');
|
|
|
+ return `${year}/${month}/${day}`;
|
|
|
+ };
|
|
|
+
|
|
|
// 构建产品明细表格行
|
|
|
const productRows =
|
|
|
quote.productList
|
|
|
?.map(
|
|
|
- (product) => `
|
|
|
+ (product, index) => `
|
|
|
<tr>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd;">${product.productName || ''}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${product.productNo || ''}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; text-align: right;">${formatNumber(product.price)}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; text-align: right;">${formatNumber(product.price)}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${product.quantity || 0}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; text-align: center;">${formatNumber(product.discount, 0)}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; text-align: right; font-weight: bold;">${formatNumber(product.subtotal)}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: center;">${index + 1}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000;">${product.productName || ''}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: center;">${product.model || ''}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: center;">${product.unit || ''}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: center;">${product.quantity || 0}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: right;">${formatNumber(product.price)}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: right;">${formatNumber(product.taxAmount)}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: right; font-weight: bold;">${formatNumber(product.subtotal)}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: center;">${product.packageSpec || ''}</td>
|
|
|
+ <td style="padding: 8px; border: 1px solid #000; text-align: center;">${product.deliveryMethod || '自提'}</td>
|
|
|
</tr>
|
|
|
`
|
|
|
)
|
|
|
- .join('') || '<tr><td colspan="7" style="padding: 8px; text-align: center;">暂无产品信息</td></tr>';
|
|
|
+ .join('') || '<tr><td colspan="10" style="padding: 8px; border: 1px solid #000; text-align: center;">暂无产品信息</td></tr>';
|
|
|
|
|
|
return `
|
|
|
- <div style="max-width: 900px; margin: 0 auto; padding: 20px; font-family: Arial, '微软雅黑', sans-serif; position: relative; min-height: 800px;">
|
|
|
+ <div style="max-width: 1200px; margin: 0 auto; padding: 20px; font-family: Arial, '微软雅黑', sans-serif;">
|
|
|
+ <!-- Logo和标题 -->
|
|
|
+ <div style="text-align: right; margin-bottom: 20px;">
|
|
|
+ <img src="${logoImg}" alt="山峡化工" style="height: 60px; vertical-align: middle;" />
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 标题 -->
|
|
|
- <h1 style="text-align: center; font-size: 28px; margin-bottom: 10px;">${quote.customerName}</h1>
|
|
|
- <h2 style="text-align: center; font-size: 24px; margin-bottom: 30px;">报价单</h2>
|
|
|
+ <h1 style="text-align: center; font-size: 32px; margin-bottom: 50px; font-weight: bold;">山峡销售报价单</h1>
|
|
|
|
|
|
<!-- 报价单基本信息 -->
|
|
|
- <table style="width: 100%; border-collapse: collapse; margin-bottom: 20px; border: 1px solid #ddd;">
|
|
|
- <tr>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; width: 25%; background-color: #f5f5f5;"><strong>报价单编号:</strong></td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; width: 25%;">${quote.quoteNo}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; width: 25%; background-color: #f5f5f5;"><strong>报价日期:</strong></td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; width: 25%;">${quote.quoteDate}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; background-color: #f5f5f5;"><strong>报价人:</strong></td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd;">${quote.ownerUserName || ''}</td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd; background-color: #f5f5f5;"><strong>报价人手机号:</strong></td>
|
|
|
- <td style="padding: 8px; border: 1px solid #ddd;">${quote.contactMobile || ''}</td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
-
|
|
|
- <!-- 客户信息 -->
|
|
|
- <h3 style="font-size: 18px; margin-top: 30px; margin-bottom: 15px; border-bottom: 2px solid #333; padding-bottom: 5px;">客户信息:</h3>
|
|
|
- <table style="width: 100%; margin-bottom: 20px;">
|
|
|
- <tr>
|
|
|
- <td style="padding: 5px;"><strong>客户名称:</strong>${quote.customerName}</td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td style="padding: 5px;"><strong>电话:</strong>${quote.contactMobile || ''}</td>
|
|
|
- <td style="padding: 5px;"><strong>邮箱:</strong>${quote.contactEmail || ''}</td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
-
|
|
|
- <!-- 报价说明 -->
|
|
|
- <h3 style="font-size: 18px; margin-top: 30px; margin-bottom: 15px; border-bottom: 2px solid #333; padding-bottom: 5px;">报价说明:</h3>
|
|
|
- <p style="line-height: 1.8; margin-bottom: 20px;">${quote.quoteTitle || ''}</p>
|
|
|
- ${(quote as any).description ? `<p style="line-height: 1.8; margin-bottom: 20px;">${(quote as any).description}</p>` : ''}
|
|
|
+ <div style="margin-bottom: 40px; font-size: 16px; line-height: 2.5; display: flex; justify-content: center;">
|
|
|
+ <div style="width: 90%; max-width: 1000px;">
|
|
|
+ <div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
|
|
+ <div style="flex: 1; text-align: left;">日 期: ${formatDate(quote.createTime)}</div>
|
|
|
+ <div style="flex: 1; text-align: left;">编 号: ${quote.quoteNo}</div>
|
|
|
+ </div>
|
|
|
+ <div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
|
|
|
+ <div style="flex: 1; text-align: left;">需 方: ${quote.customerName}</div>
|
|
|
+ <div style="flex: 1; text-align: left;">供 方: 安庆市山峡化工有限公司</div>
|
|
|
+ </div>
|
|
|
+ <div style="display: flex; justify-content: space-between;">
|
|
|
+ <div style="flex: 1; text-align: left;">联系人: ${quote.contactName || ''}</div>
|
|
|
+ <div style="flex: 1; text-align: left;">联系人: ${quote.ownerUserName || ''} / ${(quote as any).ownerUserMobile || ''}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 报价单详情 -->
|
|
|
- <h3 style="font-size: 18px; margin-top: 30px; margin-bottom: 15px; border-bottom: 2px solid #333; padding-bottom: 5px;">报价单详情:</h3>
|
|
|
- <table style="width: 100%; border-collapse: collapse; margin-bottom: 20px; border: 1px solid #ddd;">
|
|
|
+ <!-- 产品列表、备注、合计(整合为一个表格) -->
|
|
|
+ <table style="width: 100%; border-collapse: collapse; margin-bottom: 20px; border: 2px solid #000;">
|
|
|
<thead>
|
|
|
- <tr style="background-color: #f5f5f5;">
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: left;">产品名称</th>
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: center;">产品编码</th>
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: center;">价格</th>
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: center;">售价</th>
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: center;">数量</th>
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: center;">折扣</th>
|
|
|
- <th style="padding: 10px; border: 1px solid #ddd; text-align: center;">合计</th>
|
|
|
+ <tr style="background-color: #fff;">
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 5%;">序号</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 15%;">物料名称</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 8%;">规格</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 6%;">单位</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 8%;">数量</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 10%;">含税单价</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 10%;">税额/元</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 10%;">含税总价/元</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 14%;">包装规格</th>
|
|
|
+ <th style="padding: 10px; border: 1px solid #000; text-align: center; width: 14%;">送货方式</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
<tbody>
|
|
|
${productRows}
|
|
|
+ <!-- 备注行 -->
|
|
|
+ <tr>
|
|
|
+ <td colspan="10" style="padding: 10px; border: 1px solid #000; background-color: #f9f9f9; text-align: center;">
|
|
|
+ 备注:桶收押金25kg 桶15元/ 个 | 吨桶 450元/ 个
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <!-- 合计行 -->
|
|
|
+ <tr>
|
|
|
+ <td colspan="2" style="padding: 10px; border: 1px solid #000; background-color: #fff; text-align: center;"><strong>合计(元):</strong></td>
|
|
|
+ <td colspan="8" style="padding: 10px; border: 1px solid #000; text-align: right; font-weight: bold; font-size: 18px;">${formatNumber(productTotal)}</td>
|
|
|
+ </tr>
|
|
|
</tbody>
|
|
|
</table>
|
|
|
|
|
|
- <!-- 整单折扣和总金额 -->
|
|
|
- <div style="margin-bottom: 30px;">
|
|
|
- <p style="line-height: 1.8;">整单折扣(%):${formatNumber(quote.discountRate, 2)} 产品总金额(元):${productTotal.toFixed(2)}</p>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 客户确认 -->
|
|
|
- <h3 style="font-size: 18px; margin-top: 30px; margin-bottom: 15px; border-bottom: 2px solid #333; padding-bottom: 5px;">客户确认:</h3>
|
|
|
- <table style="width: 100%; margin-bottom: 40px;">
|
|
|
- <tr>
|
|
|
- <td style="padding: 5px; width: 50%;"><strong>客户签名:</strong></td>
|
|
|
- <td style="padding: 5px; width: 50%;"><strong>客户签名/盖章:</strong></td>
|
|
|
- </tr>
|
|
|
- <tr>
|
|
|
- <td style="padding: 50px 5px; position: relative; height: 100px;">
|
|
|
- <div style="position: relative; height: 100%; border: 1px dashed #ddd; background: #fafafa; text-align: center; line-height: 100px; color: #999;">
|
|
|
- 签名区域
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- <td style="padding: 50px 5px; position: relative; height: 100px;">
|
|
|
- <div style="position: relative; height: 100%; border: 1px dashed #ddd; background: #fafafa; text-align: center; line-height: 100px; color: #999;">
|
|
|
- 盖章区域(点击此处插入图片)
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
-
|
|
|
<!-- 备注信息 -->
|
|
|
${
|
|
|
quote.remark
|
|
|
? `
|
|
|
- <div style="margin-top: 30px; padding: 15px; background-color: #f9f9f9; border-left: 4px solid #409eff;">
|
|
|
- <strong>备注:</strong><br/>
|
|
|
- <p style="margin-top: 10px; line-height: 1.8;">${quote.remark}</p>
|
|
|
+ <div style="margin-top: 30px; padding: 15px;">
|
|
|
+ <p style="margin: 0; line-height: 1.8; white-space: pre-wrap;">${quote.remark.replace(/\n/g, '<br/>')}</p>
|
|
|
</div>
|
|
|
`
|
|
|
: ''
|