feat(approve): 新增订单审批功能模块
- 添加审批页面布局组件 ApproveLayout.vue - 实现订单审批主页面 Approve.vue - 创建配置信息展示组件 ConfigInfo.vue - 增加审批相关 API 接口方法 - 更新环境变量配置文件标题 - 修改首页标题显示内容- 调整路由配置结构dev_1.0.0
|
|
@ -1,5 +1,5 @@
|
|||
# 页面标题
|
||||
VUE_APP_TITLE = 若依管理系统
|
||||
VUE_APP_TITLE = UNISSENSE-OMS
|
||||
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# 页面标题
|
||||
VUE_APP_TITLE = 若依管理系统
|
||||
VUE_APP_TITLE = UNISSENSE-OMS
|
||||
|
||||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# 页面标题
|
||||
VUE_APP_TITLE = 若依管理系统
|
||||
VUE_APP_TITLE = UNISSENSE-OMS
|
||||
|
||||
BABEL_ENV = production
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 1.1 KiB |
|
|
@ -6,7 +6,8 @@
|
|||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
<!-- <title><%= webpackConfig.name %></title>-->
|
||||
<title>汇智OMS订单管理系统</title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
html,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询订单列表
|
||||
export function listOrder(query) {
|
||||
return request({
|
||||
url: '/project/order/vue/approve/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 提交审批(同意或驳回)
|
||||
export function approveOrder(data) {
|
||||
return request({
|
||||
url: '/project/order/order/approve',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ export function listOrder(query) {
|
|||
// 查询订单管理详细信息
|
||||
export function getOrder(id) {
|
||||
return request({
|
||||
url: '/project/order/vue/' + id,
|
||||
url: '/project/order/h5/approve/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 509 KiB After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 509 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 2.4 KiB |
|
|
@ -2,11 +2,15 @@
|
|||
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<div v-if="logo" style="width: 30px; position: absolute; overflow: hidden;">
|
||||
<img :src="logo" class="sidebar-logo"/>
|
||||
</div>
|
||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<div v-if="logo" style="width: 30px; position: absolute; overflow: hidden;">
|
||||
<img :src="logo" class="sidebar-logo"/>
|
||||
</div>
|
||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
|
|
@ -66,8 +70,7 @@ export default {
|
|||
width: 100%;
|
||||
|
||||
& .sidebar-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
vertical-align: middle;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,37 +88,8 @@ export const constantRoutes = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/manage',
|
||||
component: Layout,
|
||||
redirect: 'noRedirect',
|
||||
name: 'Manage',
|
||||
meta: {
|
||||
title: '档案管理',
|
||||
icon: 'documentation'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'order',
|
||||
component: () => import('@/views/manage/order/index'),
|
||||
name: 'ManageOrder',
|
||||
meta: { title: '合同档案', icon: 'documentation' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/project/order',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/project/order/index'),
|
||||
name: 'ProjectOrder',
|
||||
meta: { title: '订单管理', icon: 'order' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
path: '/service-query',
|
||||
component: () => import('@/views/manage/service/index'),
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ module.exports = {
|
|||
/**
|
||||
* 网页标题
|
||||
*/
|
||||
title: process.env.VUE_APP_TITLE,
|
||||
|
||||
// title: process.env.VUE_APP_TITLE,
|
||||
title: '汇智OMS订单管理系统',
|
||||
/**
|
||||
* 侧边栏主题 深色主题theme-dark,浅色主题theme-light
|
||||
*/
|
||||
|
|
@ -23,7 +23,7 @@ module.exports = {
|
|||
* 是否显示 tagsView
|
||||
*/
|
||||
tagsView: true,
|
||||
|
||||
|
||||
/**
|
||||
* 显示页签图标
|
||||
*/
|
||||
|
|
@ -52,5 +52,5 @@ module.exports = {
|
|||
/**
|
||||
* 底部版权文本内容
|
||||
*/
|
||||
footerContent: 'Copyright © 2018-2025 RuoYi. All Rights Reserved.'
|
||||
footerContent: 'Copyright © 2018-2025 Unissense. All Rights Reserved.'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<div class="approve-layout">
|
||||
<header class="approve-layout-header">
|
||||
<div class="header-center">
|
||||
<h1>{{ title }}</h1>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<img src="@/assets/images/companyLogo.png" alt="Company Logo" class="company-logo" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="approve-layout-content">
|
||||
<slot></slot>
|
||||
</main>
|
||||
|
||||
<footer class="approve-layout-footer">
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ApproveLayout',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '审批页面'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.approve-layout {
|
||||
min-height: 100vh;
|
||||
background-color: #F8F5F0;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.approve-layout-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid ; /* Separator */
|
||||
|
||||
.header-center {
|
||||
flex-grow: 1;
|
||||
text-align: left; /* Changed to left */
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: #606266; /* Lighter color */
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
height: 40px; /* Adjust as needed */
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.approve-layout-footer {
|
||||
text-align: left;
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid; /* Separator */
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,487 @@
|
|||
<template>
|
||||
<div class="approve-container">
|
||||
<ApproveLayout title="紫光汇智信息技术有限公司">
|
||||
<template #default>
|
||||
<div style="display: flex;align-items: center;justify-content: center;">
|
||||
<span id="projectNameBox" style="margin-left: 10px;color: black;font-size: 30px">
|
||||
紫光汇智信息技术有限公司进货供货订单
|
||||
</span>
|
||||
</div>
|
||||
<el-form ref="orderForm" :model="order" label-width="120px" class="mb20">
|
||||
<h3 class="section-title">订单信息</h3>
|
||||
<order-info-view
|
||||
:order-data.sync="order"
|
||||
:is-readonly="true"
|
||||
/>
|
||||
</el-form>
|
||||
|
||||
<h3 class="section-title">配置信息</h3>
|
||||
<config-info
|
||||
:order-data="order"
|
||||
:can-edit-discount="canEditDiscount"
|
||||
:hide-price="hidePrice"
|
||||
@discount-change="handleDiscountChange"
|
||||
@tax-rate-change="handleTaxRateChange"
|
||||
class="mb20"
|
||||
/>
|
||||
|
||||
<!-- Tab页:附件信息和流转过程 -->
|
||||
<el-tabs v-model="activeTab" class="approve-tabs">
|
||||
<el-tab-pane v-if="order.processTemplate !== '1'" label="附件信息" name="attachment">
|
||||
<el-table :data="attachmentList" border>
|
||||
<el-table-column label="附件类型" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
{{ getAttachmentType(scope.$index) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件名称" align="center" prop="fileName" />
|
||||
<el-table-column label="上传人" align="center" prop="uploadUserName" width="120" />
|
||||
<el-table-column label="上传时间" align="center" width="180">
|
||||
<template slot-scope="scope">
|
||||
{{ formatDate(scope.row.uploadTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="scope.row.id && scope.row.id !== -1"
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="previewFile(scope.row)"
|
||||
>预览</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.id && scope.row.id !== -1"
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="downloadFile(scope.row)"
|
||||
>下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="流转过程" name="process">
|
||||
<div class="process-container">
|
||||
<el-tabs v-model="activeVersionTab" type="card" v-if="uniqueVersions.length > 0">
|
||||
<el-tab-pane
|
||||
v-for="version in uniqueVersions"
|
||||
:key="version"
|
||||
:label="'版本号Rev.' + version"
|
||||
:name="version">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="log in groupedApproveLogs[version]"
|
||||
:key="log.id"
|
||||
:timestamp="log.approveTime"
|
||||
placement="top">
|
||||
<el-card>
|
||||
<h4>{{ log.approveOpinion }}</h4>
|
||||
<p><b>操作人:</b> {{ log.approveUserName }} ({{ log.roleName }})</p>
|
||||
<p><b>接收人:</b> {{ getReceiverName(log) }}</p>
|
||||
<p><b>审批状态:</b> <el-tag :type="getStatusTagType(log.approveStatus)" size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div v-else>暂无流转过程数据。</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<template #footer>
|
||||
<p>{{ order.projectCode }}-{{ order.orderCode }}-Rev.{{ order.versionCode }}</p>
|
||||
</template>
|
||||
</ApproveLayout>
|
||||
|
||||
<!-- 审批意见弹窗 -->
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
:visible.sync="opinionDialogVisible"
|
||||
width="500px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-form ref="opinionForm" :model="opinionForm" :rules="opinionRules" label-width="100px">
|
||||
<el-form-item label="审批意见" prop="approveOpinion">
|
||||
<el-input
|
||||
v-model="opinionForm.approveOpinion"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入审批意见"
|
||||
:disabled="submitLoading"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="opinionDialogVisible = false" :disabled="submitLoading">取 消</el-button>
|
||||
<el-button type="primary" @click="submitOpinion" :loading="submitLoading">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getOrder } from "@/api/project/order";
|
||||
import { approveOrder } from "@/api/approve/order";
|
||||
import ConfigInfo from './ConfigInfo.vue';
|
||||
import ApproveLayout from '@/views/approve/ApproveLayout.vue';
|
||||
|
||||
export default {
|
||||
name: "Approve",
|
||||
components: {
|
||||
ApproveLayout,
|
||||
OrderInfoView: () => import('@/components/order/OrderInfo.vue'),
|
||||
ConfigInfo
|
||||
},
|
||||
props: {
|
||||
orderId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
order: {},
|
||||
todo: {}, // 当前审批任务信息
|
||||
approveLog: [], // 审批流转记录
|
||||
activeTab: 'attachment', // 默认显示附件信息
|
||||
activeVersionTab: null, // 当前激活的版本Tab
|
||||
selectedDiscount: 1, // 选择的现金折扣
|
||||
opinionDialogVisible: false,
|
||||
dialogTitle: '',
|
||||
approveType: '', // 'approve' 或 'reject'
|
||||
submitLoading: false, // 提交loading状态
|
||||
opinionForm: {
|
||||
approveOpinion: '',
|
||||
},
|
||||
opinionRules: {
|
||||
approveOpinion: [
|
||||
{ required: true, message: '审批意见不能为空', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 附件列表
|
||||
attachmentList() {
|
||||
if (!this.order.contractTableData || !this.order.versionCode) {
|
||||
return [];
|
||||
}
|
||||
return this.order.contractTableData[this.order.versionCode] || [];
|
||||
},
|
||||
// 获取所有唯一的版本号
|
||||
uniqueVersions() {
|
||||
if (!this.approveLog || this.approveLog.length === 0) return [];
|
||||
const versions = [...new Set(this.approveLog.map(log => log.extendField1))];
|
||||
return versions.sort((a, b) => b - a); // 降序排列
|
||||
},
|
||||
// 按版本号分组的审批记录
|
||||
groupedApproveLogs() {
|
||||
if (!this.approveLog || this.approveLog.length === 0) return {};
|
||||
return this.approveLog.reduce((acc, log) => {
|
||||
const version = log.extendField1;
|
||||
if (!acc[version]) acc[version] = [];
|
||||
acc[version].push(log);
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
// 是否可以编辑现金折扣(只有商务或财务审批时可以)
|
||||
canEditDiscount() {
|
||||
if (!this.todo || !this.todo.taskName) return false;
|
||||
const taskName = this.todo.taskName;
|
||||
return taskName.startsWith('商务') || taskName.startsWith('财务');
|
||||
},
|
||||
// 是否隐藏目录单价和折扣列(省代审批时隐藏)
|
||||
hidePrice() {
|
||||
if (!this.todo || !this.todo.taskName) return false;
|
||||
const taskName = this.todo.taskName;
|
||||
return taskName.includes('省代');
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
orderId: {
|
||||
immediate: true,
|
||||
handler(id) {
|
||||
if (id) {
|
||||
this.getOrderDetails();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getOrderDetails() {
|
||||
getOrder(this.orderId).then(response => {
|
||||
this.order = { ...response.data.projectOrderInfo };
|
||||
this.approveLog = response.data.approveLog || [];
|
||||
this.todo = response.data.todo || {};
|
||||
// 如果是总代模式,默认显示流转过程tab
|
||||
if (this.order.processTemplate === '1') {
|
||||
this.activeTab = 'process';
|
||||
}
|
||||
|
||||
// 设置默认激活的版本Tab
|
||||
if (this.uniqueVersions.length > 0) {
|
||||
this.activeVersionTab = String(this.uniqueVersions[0]);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 处理现金折扣变化
|
||||
handleDiscountChange(value) {
|
||||
this.selectedDiscount = value;
|
||||
},
|
||||
// 处理税率变化
|
||||
handleTaxRateChange(product) {
|
||||
// 验证税率范围
|
||||
if (product.taxRate !== null && product.taxRate !== undefined) {
|
||||
// 限制为最多2位小数
|
||||
product.taxRate = Math.round(product.taxRate * 100) / 100;
|
||||
|
||||
if (product.taxRate < 0) {
|
||||
product.taxRate = 0;
|
||||
this.$modal.msgWarning('税率不能小于0');
|
||||
} else if (product.taxRate > 100) {
|
||||
product.taxRate = 100;
|
||||
this.$modal.msgWarning('税率不能大于100');
|
||||
}
|
||||
}
|
||||
},
|
||||
handleApprove() {
|
||||
this.approveType = 'approve';
|
||||
this.dialogTitle = '同意审批';
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.opinionDialogVisible = true;
|
||||
},
|
||||
handleReject() {
|
||||
this.approveType = 'reject';
|
||||
this.dialogTitle = '驳回审批';
|
||||
this.opinionForm.approveOpinion = '';
|
||||
this.opinionDialogVisible = true;
|
||||
},
|
||||
submitOpinion() {
|
||||
this.$refs.opinionForm.validate(valid => {
|
||||
if (valid) {
|
||||
// 开始loading
|
||||
this.submitLoading = true;
|
||||
|
||||
// 构造审批数据
|
||||
const { taxRateData, ...todoData } = this.todo;
|
||||
const data = {
|
||||
...todoData,
|
||||
approveOpinion: this.opinionForm.approveOpinion,
|
||||
approveStatus: this.approveType === 'approve' ? 3 : 2,
|
||||
variables: {
|
||||
comment: this.opinionForm.approveOpinion,
|
||||
approveBtn: this.approveType === 'approve' ? 1 : 0,
|
||||
allPriceCountValue: this.selectedDiscount
|
||||
}
|
||||
};
|
||||
|
||||
// 如果有总代进货金额,添加到variables中
|
||||
if (this.order.actualPurchaseAmount) {
|
||||
data.variables.actualPurchaseAmount = this.order.actualPurchaseAmount;
|
||||
}
|
||||
|
||||
// 如果是商务或财务审批,收集税率数据
|
||||
if (this.canEditDiscount) {
|
||||
const taxRateList = this.collectTaxRateData();
|
||||
if (taxRateList && taxRateList.length > 0) {
|
||||
for (let i = 0; i < taxRateList.length; i++) {
|
||||
for (let key in taxRateList[i]) {
|
||||
data['taxRateData[' + i + '].' + key] = taxRateList[i][key];
|
||||
// 验证税率范围
|
||||
if (key === 'taxRate' && (taxRateList[i][key] > 100 || taxRateList[i][key] < 0)) {
|
||||
this.$modal.msgError("税率区间为0-100");
|
||||
this.submitLoading = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交审批
|
||||
approveOrder(data).then(() => {
|
||||
this.$modal.msgSuccess(this.approveType === 'approve' ? '审批成功' : '驳回成功');
|
||||
this.submitLoading = false;
|
||||
this.opinionDialogVisible = false;
|
||||
this.closeDialog(true);
|
||||
}).catch(() => {
|
||||
this.$modal.msgError(this.approveType === 'approve' ? '审批失败' : '驳回失败');
|
||||
this.submitLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 收集税率数据
|
||||
collectTaxRateData() {
|
||||
const taxRateList = [];
|
||||
|
||||
// 收集软件产品税率
|
||||
if (this.order.softwareProjectProductInfoList) {
|
||||
this.order.softwareProjectProductInfoList.forEach(product => {
|
||||
if (product.id) {
|
||||
taxRateList.push({
|
||||
productId: product.id,
|
||||
projectId: this.order.projectId,
|
||||
taxRate: product.taxRate || 13
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 收集硬件产品税率
|
||||
if (this.order.hardwareProjectProductInfoList) {
|
||||
this.order.hardwareProjectProductInfoList.forEach(product => {
|
||||
if (product.id) {
|
||||
taxRateList.push({
|
||||
productId: product.id,
|
||||
projectId: this.order.projectId,
|
||||
taxRate: product.taxRate || 13
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 收集维保产品税率
|
||||
if (this.order.maintenanceProjectProductInfoList) {
|
||||
this.order.maintenanceProjectProductInfoList.forEach(product => {
|
||||
if (product.id) {
|
||||
taxRateList.push({
|
||||
productId: product.id,
|
||||
projectId: this.order.projectId,
|
||||
taxRate: product.taxRate || 13
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return taxRateList;
|
||||
},
|
||||
closeDialog(isSuccess = false) {
|
||||
this.$emit('close', isSuccess);
|
||||
},
|
||||
// 获取附件类型
|
||||
getAttachmentType(index) {
|
||||
const types = ['商务折扣审批', '合同', '补充附件'];
|
||||
return types[index] || '其他';
|
||||
},
|
||||
// 格式化日期
|
||||
formatDate(date) {
|
||||
if (!date) return '';
|
||||
return this.parseTime(date, '{y}-{m}-{d}');
|
||||
},
|
||||
// 时间解析函数
|
||||
parseTime(time, pattern) {
|
||||
if (!time) 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(/{([ymdhisa])+}/g, (result, key) => {
|
||||
const value = formatObj[key];
|
||||
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value]; }
|
||||
return value.toString().padStart(2, '0');
|
||||
});
|
||||
return time_str;
|
||||
},
|
||||
// 获取状态标签类型
|
||||
getStatusTagType(status) {
|
||||
const typeMap = {
|
||||
'1': 'info', // 提交审批
|
||||
'2': 'danger', // 驳回
|
||||
'3': 'success' // 批准
|
||||
};
|
||||
return typeMap[String(status)] || 'info';
|
||||
},
|
||||
// 获取状态文本
|
||||
getStatusText(status) {
|
||||
const textMap = {
|
||||
'1': '提交审批',
|
||||
'2': '驳回',
|
||||
'3': '批准'
|
||||
};
|
||||
return textMap[String(status)] || '提交审批';
|
||||
},
|
||||
// 获取接收人姓名
|
||||
getReceiverName(log) {
|
||||
if (log.taskName === '公司领导') {
|
||||
return log.applyUserName || '';
|
||||
}
|
||||
return log.nextAllApproveUserName || '';
|
||||
},
|
||||
// 预览文件
|
||||
previewFile(file) {
|
||||
const filePath = file.filePath;
|
||||
const fileName = file.fileName;
|
||||
const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
||||
|
||||
if (['.png', '.jpg', '.jpeg', '.pdf'].includes(ext)) {
|
||||
const url = `${process.env.VUE_APP_BASE_API}/project/order/file/view?filePath=${encodeURIComponent(filePath)}&fileName=${encodeURIComponent(fileName)}`;
|
||||
window.open(url);
|
||||
} else {
|
||||
this.downloadFile(file);
|
||||
}
|
||||
},
|
||||
// 下载文件
|
||||
downloadFile(file) {
|
||||
const url = `${process.env.VUE_APP_BASE_API}/project/order/file/download?filePath=${encodeURIComponent(file.filePath)}&fileName=${encodeURIComponent(file.fileName)}`;
|
||||
window.location.href = url;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.approve-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.mb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Tab样式 */
|
||||
.approve-tabs {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 流转过程容器 */
|
||||
.process-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 审批意见弹窗样式 */
|
||||
::v-deep .el-dialog__body {
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,434 @@
|
|||
<template>
|
||||
<div class="config-info-container">
|
||||
<div v-if="order.softwareProjectProductInfoList && order.softwareProjectProductInfoList.length > 0">
|
||||
<h3>软件产品</h3>
|
||||
<table class="product-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-seq">序号</th>
|
||||
<th class="col-code">产品编码</th>
|
||||
<th class="col-model">产品型号</th>
|
||||
<th class="col-desc">描述</th>
|
||||
<th class="col-qty">数量</th>
|
||||
<th v-if="!hidePrice" class="col-price">目录单价(¥)</th>
|
||||
<th v-if="!hidePrice" class="col-discount">折扣</th>
|
||||
<th class="col-price">单价(¥)</th>
|
||||
<th class="col-discount">现金折扣</th>
|
||||
<th class="col-price">总价(¥)</th>
|
||||
<th class="col-price">折后总价(¥)</th>
|
||||
<th class="col-tax">税率(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(product, index) in order.softwareProjectProductInfoList" :key="'sw-' + product.id">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ product.productBomCode }}</td>
|
||||
<td>{{ product.model }}</td>
|
||||
<td>{{ product.productDesc }}</td>
|
||||
<td>{{ product.quantity }}</td>
|
||||
<td v-if="!hidePrice">{{ formatCurrency(product.cataloguePrice) }}</td>
|
||||
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
||||
<td>{{ formatCurrency(product.price) }}</td>
|
||||
<td>{{ selectedDiscountLabel }}</td>
|
||||
<td>{{ formatCurrency(product.allPrice) }}</td>
|
||||
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-if="canEditDiscount"
|
||||
v-model.number="product.taxRate"
|
||||
type="number"
|
||||
size="small"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
@input="handleTaxRateChange(product)"
|
||||
/>
|
||||
<span v-else>{{ product.taxRate || '-' }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td :colspan="hidePrice ? 8 : 10" style="text-align: right;"><strong>软件折后小计:</strong></td>
|
||||
<td colspan="2"><strong>{{ formatCurrency(softwareDiscountedTotal) }}</strong></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="order.hardwareProjectProductInfoList && order.hardwareProjectProductInfoList.length > 0">
|
||||
<h3>硬件产品</h3>
|
||||
<table class="product-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-seq">序号</th>
|
||||
<th class="col-code">产品编码</th>
|
||||
<th class="col-model">产品型号</th>
|
||||
<th class="col-desc">描述</th>
|
||||
<th class="col-qty">数量</th>
|
||||
<th v-if="!hidePrice" class="col-price">目录单价(¥)</th>
|
||||
<th v-if="!hidePrice" class="col-discount">折扣</th>
|
||||
<th class="col-price">单价(¥)</th>
|
||||
<th class="col-discount">现金折扣</th>
|
||||
<th class="col-price">总价(¥)</th>
|
||||
<th class="col-price">折后总价(¥)</th>
|
||||
<th class="col-tax">税率(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(product, index) in order.hardwareProjectProductInfoList" :key="'hw-' + product.id">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ product.productBomCode }}</td>
|
||||
<td>{{ product.model }}</td>
|
||||
<td>{{ product.productDesc }}</td>
|
||||
<td>{{ product.quantity }}</td>
|
||||
<td v-if="!hidePrice">{{ formatCurrency(product.cataloguePrice) }}</td>
|
||||
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
||||
<td>{{ formatCurrency(product.price) }}</td>
|
||||
<td>{{ selectedDiscountLabel }}</td>
|
||||
<td>{{ formatCurrency(product.allPrice) }}</td>
|
||||
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-if="canEditDiscount"
|
||||
v-model.number="product.taxRate"
|
||||
type="number"
|
||||
size="small"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
@input="handleTaxRateChange(product)"
|
||||
/>
|
||||
<span v-else>{{ product.taxRate || '-' }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td :colspan="hidePrice ? 8 : 10" style="text-align: right;"><strong>硬件折后小计:</strong></td>
|
||||
<td colspan="2"><strong>{{ formatCurrency(hardwareDiscountedTotal) }}</strong></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="order.maintenanceProjectProductInfoList && order.maintenanceProjectProductInfoList.length > 0">
|
||||
<h3>维保产品</h3>
|
||||
<table class="product-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-seq">序号</th>
|
||||
<th class="col-code">产品编码</th>
|
||||
<th class="col-model">产品型号</th>
|
||||
<th class="col-desc">描述</th>
|
||||
<th class="col-qty">数量</th>
|
||||
<th v-if="!hidePrice" class="col-price">目录单价(¥)</th>
|
||||
<th v-if="!hidePrice" class="col-discount">折扣</th>
|
||||
<th class="col-price">单价(¥)</th>
|
||||
<th class="col-discount">现金折扣</th>
|
||||
<th class="col-price">总价(¥)</th>
|
||||
<th class="col-price">折后总价(¥)</th>
|
||||
<th class="col-tax">税率(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(product, index) in order.maintenanceProjectProductInfoList" :key="'m-' + product.id">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ product.productBomCode }}</td>
|
||||
<td>{{ product.model }}</td>
|
||||
<td>{{ product.productDesc }}</td>
|
||||
<td>{{ product.quantity }}</td>
|
||||
<td v-if="!hidePrice">{{ formatCurrency(product.cataloguePrice) }}</td>
|
||||
<td v-if="!hidePrice">{{ product.discount ? (product.discount * 100).toFixed(2) + '%' : '-' }}</td>
|
||||
<td>{{ formatCurrency(product.price) }}</td>
|
||||
<td>{{ selectedDiscountLabel }}</td>
|
||||
<td>{{ formatCurrency(product.allPrice) }}</td>
|
||||
<td>{{ formatCurrency(getDiscountedAllPrice(product, selectedDiscount)) }}</td>
|
||||
<td>
|
||||
<el-input
|
||||
v-if="canEditDiscount"
|
||||
v-model.number="product.taxRate"
|
||||
type="number"
|
||||
size="small"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
@input="handleTaxRateChange(product)"
|
||||
/>
|
||||
<span v-else>{{ product.taxRate || '-' }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td :colspan="hidePrice ? 8 : 10" style="text-align: right;"><strong>服务折后小计:</strong></td>
|
||||
<td colspan="2"><strong>{{ formatCurrency(maintenanceDiscountedTotal) }}</strong></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="summary-section">
|
||||
<el-row type="flex" justify="end" align="middle" class="summary-row">
|
||||
|
||||
<el-col :span="6" type="flex" justify="end">
|
||||
<span class="summary-label">总价合计</span> <span class="summary-value-right"> {{
|
||||
formatCurrency(grandTotal)
|
||||
}}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="end" align="middle" class="summary-row">
|
||||
<el-col :span="18" style="text-align: center;">
|
||||
<span style="margin-right: 5px;">现金折扣</span>
|
||||
<el-select
|
||||
v-model="selectedDiscount"
|
||||
placeholder="请选择折扣率"
|
||||
size="small"
|
||||
style="width: 120px;"
|
||||
:disabled="!canEditDiscount"
|
||||
@change="handleDiscountChange">
|
||||
<el-option
|
||||
v-for="item in discountOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<span class="summary-label"> 折后总价合计</span> <span
|
||||
class="summary-value-right"> {{ formatCurrency(finalTotal) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ConfigInfo",
|
||||
props: {
|
||||
orderData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({
|
||||
softwareProjectProductInfoList: [],
|
||||
hardwareProjectProductInfoList: [],
|
||||
maintenanceProjectProductInfoList: []
|
||||
})
|
||||
},
|
||||
canEditDiscount: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hidePrice: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedDiscount: 1,
|
||||
discountOptions: [
|
||||
{value: 1, label: '100%'},
|
||||
{value: 0.988, label: '98.8%'},
|
||||
{value: 0.985, label: '98.5%'}
|
||||
]
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'orderData.discountFold': {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
// 从order.discountFold获取折扣值,如果为空则默认为1
|
||||
this.selectedDiscount = newVal || 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
order() {
|
||||
return this.orderData || {
|
||||
softwareProjectProductInfoList: [],
|
||||
hardwareProjectProductInfoList: [],
|
||||
maintenanceProjectProductInfoList: []
|
||||
};
|
||||
},
|
||||
selectedDiscountLabel() {
|
||||
const option = this.discountOptions.find(opt => opt.value === this.selectedDiscount);
|
||||
return option ? option.label : '';
|
||||
},
|
||||
softwareTotal() {
|
||||
return this.calculateTotal(this.order.softwareProjectProductInfoList, 1);
|
||||
},
|
||||
hardwareTotal() {
|
||||
return this.calculateTotal(this.order.hardwareProjectProductInfoList, 1);
|
||||
},
|
||||
maintenanceTotal() {
|
||||
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, 1);
|
||||
},
|
||||
softwareDiscountedTotal() {
|
||||
return this.calculateTotal(this.order.softwareProjectProductInfoList, this.selectedDiscount);
|
||||
},
|
||||
hardwareDiscountedTotal() {
|
||||
return this.calculateTotal(this.order.hardwareProjectProductInfoList, this.selectedDiscount);
|
||||
},
|
||||
maintenanceDiscountedTotal() {
|
||||
return this.calculateTotal(this.order.maintenanceProjectProductInfoList, this.selectedDiscount);
|
||||
},
|
||||
grandTotal() {
|
||||
return this.softwareTotal + this.hardwareTotal + this.maintenanceTotal;
|
||||
},
|
||||
finalTotal() {
|
||||
return this.softwareDiscountedTotal + this.hardwareDiscountedTotal + this.maintenanceDiscountedTotal;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDiscountChange(value) {
|
||||
this.$emit('discount-change', value);
|
||||
},
|
||||
handleTaxRateChange(product) {
|
||||
// 验证税率范围并限制小数位数
|
||||
if (product.taxRate !== null && product.taxRate !== undefined) {
|
||||
// 限制为最多2位小数
|
||||
product.taxRate = Math.round(product.taxRate * 100) / 100;
|
||||
}
|
||||
// 税率变化时通知父组件
|
||||
this.$emit('tax-rate-change', product);
|
||||
},
|
||||
getDiscountedAllPrice(product, discount) {
|
||||
if (discount === 1) {
|
||||
return product.allPrice;
|
||||
}
|
||||
const discountedUnitPrice = product.price * discount;
|
||||
const roundedDiscountedUnitPrice = Math.round(discountedUnitPrice * 100) / 100;
|
||||
return roundedDiscountedUnitPrice * product.quantity;
|
||||
},
|
||||
calculateTotal(productList, discount) {
|
||||
if (!productList) return 0;
|
||||
return productList.reduce((sum, product) => sum + (this.getDiscountedAllPrice(product, discount) || 0), 0);
|
||||
},
|
||||
formatCurrency(value) {
|
||||
if (value == null) return '0.00';
|
||||
return Number(value).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.config-info-container {
|
||||
font-family: 'Arial', sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #333;
|
||||
border-bottom: 2px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.product-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 10px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.product-table th, .product-table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Column widths for alignment */
|
||||
.col-seq {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.col-code {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.col-model {
|
||||
width: 12%;
|
||||
}
|
||||
|
||||
.col-desc {
|
||||
width: 14%;
|
||||
}
|
||||
|
||||
.col-qty {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.col-price {
|
||||
width: 9%;
|
||||
}
|
||||
|
||||
.col-discount {
|
||||
width: 7%;
|
||||
}
|
||||
|
||||
.col-tax {
|
||||
width: 7%;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
/* 税率输入框样式 */
|
||||
.col-tax >>> .el-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.col-tax >>> .el-input__inner {
|
||||
text-align: center;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.product-table th {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.product-table tbody tr:nth-child(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.product-table tfoot {
|
||||
font-weight: bold;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.summary-section {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
border-top: 2px solid #ccc;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.summary-row {
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
font-weight: bold;
|
||||
color: #d9534f;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.summary-value-right {
|
||||
font-weight: bold;
|
||||
color: #d9534f;
|
||||
text-align: right; /* Ensure right alignment */
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="合同编号" prop="orderCode">
|
||||
<el-input
|
||||
v-model="queryParams.orderCode"
|
||||
placeholder="请输入合同编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目名称" prop="projectName">
|
||||
<el-input
|
||||
v-model="queryParams.projectName"
|
||||
placeholder="请输入项目名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="queryParams.projectCode"
|
||||
placeholder="请输入项目编号"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="汇智负责人" prop="dutyName">
|
||||
<el-input
|
||||
v-model="queryParams.dutyName"
|
||||
placeholder="请输入汇智负责人"
|
||||
clearable
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批节点" prop="approveNode">
|
||||
<el-input
|
||||
v-model="queryParams.approveNode"
|
||||
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-row :gutter="10" class="mb8">
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="orderList">
|
||||
<el-table-column label="合同编号" align="center" prop="orderCode" />
|
||||
<el-table-column label="项目名称" align="center" prop="projectName" />
|
||||
<el-table-column label="项目编号" align="center" prop="projectCode" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="订单金额" align="center" prop="actualPurchaseAmount" />
|
||||
<el-table-column label="汇智负责人" align="center" prop="dutyName" />
|
||||
<el-table-column label="审批节点" align="center" prop="approveNode" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleApprove(scope.row)"
|
||||
>审批</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 审批弹窗 -->
|
||||
<el-dialog
|
||||
title="订单审批"
|
||||
:visible.sync="approveDialogVisible"
|
||||
custom-class="approve-dialog"
|
||||
width="80%"
|
||||
top="5vh"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<approve-dialog
|
||||
v-if="approveDialogVisible"
|
||||
ref="approveDialog"
|
||||
:order-id="currentOrderId"
|
||||
@close="closeApproveDialog"
|
||||
/>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="approveDialogVisible = false">取 消</el-button>
|
||||
<el-button type="danger" @click="handleReject">驳 回</el-button>
|
||||
<el-button type="primary" @click="handleApproveConfirm">同 意</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listOrder } from "@/api/approve/order";
|
||||
import ApproveDialog from './Approve.vue';
|
||||
|
||||
export default {
|
||||
name: "Order",
|
||||
components: {
|
||||
ApproveDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// ...其他data属性...
|
||||
approveDialogVisible: false,
|
||||
currentOrderId: null,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 订单表格数据
|
||||
orderList: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
orderCode: null,
|
||||
projectName: null,
|
||||
projectCode: null,
|
||||
customerName: null,
|
||||
dutyName: null,
|
||||
approveNode: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询订单列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listOrder(this.queryParams).then(response => {
|
||||
this.orderList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 表单重置
|
||||
resetForm(formName) {
|
||||
if (this.$refs[formName]) {
|
||||
this.$refs[formName].resetFields();
|
||||
}
|
||||
},
|
||||
/** 审批按钮操作 */
|
||||
handleApprove(row) {
|
||||
this.currentOrderId = row.id;
|
||||
this.approveDialogVisible = true;
|
||||
},
|
||||
/** 关闭审批弹窗 */
|
||||
closeApproveDialog() {
|
||||
this.approveDialogVisible = false;
|
||||
this.currentOrderId = null;
|
||||
this.getList(); // 刷新列表
|
||||
},
|
||||
/** 同意按钮操作 */
|
||||
handleApproveConfirm() {
|
||||
if (this.$refs.approveDialog) {
|
||||
this.$refs.approveDialog.handleApprove();
|
||||
}
|
||||
},
|
||||
/** 驳回按钮操作 */
|
||||
handleReject() {
|
||||
if (this.$refs.approveDialog) {
|
||||
this.$refs.approveDialog.handleReject();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 审批弹窗样式 */
|
||||
::v-deep .approve-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 90vh;
|
||||
}
|
||||
|
||||
::v-deep .approve-dialog .el-dialog__body {
|
||||
max-height: calc(90vh - 120px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-login-footer">
|
||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>Copyright © 2018-2025 unissense All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@
|
|||
<div v-else>暂无合同信息数据。</div>
|
||||
</div>
|
||||
</div>
|
||||
<input id="uploadInput" type="file" accept=".pdf,.jpg,.png" style="display: none" @change="handleUploadFile"/>
|
||||
<input id="uploadInput0" type="file" accept=".pdf,.jpg,.png" style="display: none" @change="handleUploadFile"/>
|
||||
<input id="uploadInput1" type="file" accept=".pdf,.jpg,.png" style="display: none" @change="handleUploadFile"/>
|
||||
<input id="uploadInput2" type="file" accept=".zip,.rar,.jpg,.png" style="display: none" @change="handleUploadFile"/>
|
||||
<input id="fileSort" type="hidden" v-model="fileSort"/>
|
||||
|
|
@ -443,12 +443,12 @@ export default {
|
|||
},
|
||||
saveDraft() {
|
||||
if (!this.form.projectCode) {
|
||||
this.msgError("项目编号为必填");
|
||||
this.$modal.msgError("项目编号为必填");
|
||||
return;
|
||||
}
|
||||
const checkDiscount = (list) => !list || list.every(item => item.discount === null || item.discount === undefined || item.discount <= 1);
|
||||
if (!checkDiscount(this.form.softwareProjectProductInfoList) || !checkDiscount(this.form.hardwareProjectProductInfoList) || !checkDiscount(this.form.maintenanceProjectProductInfoList)) {
|
||||
this.msgError("折扣不能大于100%");
|
||||
this.$modal.msgError("折扣不能大于100%");
|
||||
return;
|
||||
}
|
||||
this.form.orderStatus = '0';
|
||||
|
|
@ -457,12 +457,12 @@ export default {
|
|||
submitForApproval() {
|
||||
const hasBusinessApprovalFile = this.currentContractFiles && this.currentContractFiles.length > 0 && this.currentContractFiles[0].id !== -1;
|
||||
if (!hasBusinessApprovalFile) {
|
||||
this.msgError("请补充商务审批文件");
|
||||
this.$modal.msgError("请补充商务审批文件");
|
||||
return;
|
||||
}
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) this.selectCommitTypeVisible = true;
|
||||
else this.msgError("请完善表单");
|
||||
else this.$modal.msgError("请完善表单");
|
||||
});
|
||||
},
|
||||
handleCommitTypeSelected(data) {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-register-footer">
|
||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>Copyright © 2018-2025 unissense All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ function resolve(dir) {
|
|||
|
||||
const CompressionPlugin = require('compression-webpack-plugin')
|
||||
|
||||
const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
|
||||
const name = process.env.VUE_APP_TITLE || 'UNISSENSE-OMS' // 网页标题
|
||||
|
||||
const baseUrl = 'http://localhost:28080' // 后端接口
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ spring:
|
|||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
profiles:
|
||||
active: prod
|
||||
active: dev
|
||||
# 文件上传
|
||||
servlet:
|
||||
multipart:
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
<div class="signup-footer">
|
||||
<div class="pull-left">
|
||||
Copyright © 2018-2025 ruoyi.vip All Rights Reserved. <br>
|
||||
Copyright © 2018-2025 unissense All Rights Reserved. <br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,18 @@ public class VueProjectOrderInfoController extends BaseController {
|
|||
@RequiresPermissions(value = {"project:order:list", "project:order:approve"}, logical = Logical.OR)
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ProjectOrderInfo projectOrderInfo) {
|
||||
if (StringUtils.isNotEmpty(projectOrderInfo.getApprove())) {
|
||||
projectOrderInfo.setApprove(ShiroUtils.getUserId().toString());
|
||||
}
|
||||
startPage();
|
||||
List<ProjectOrderInfo> list = projectOrderInfoService.selectProjectOrderInfoList(projectOrderInfo);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单管理列表
|
||||
*/
|
||||
@RequiresPermissions(value = {"project:order:list", "project:order:approve"}, logical = Logical.OR)
|
||||
@GetMapping("/approve/list")
|
||||
public TableDataInfo listApprove(ProjectOrderInfo projectOrderInfo) {
|
||||
projectOrderInfo.setApprove(ShiroUtils.getUserId().toString());
|
||||
startPage();
|
||||
List<ProjectOrderInfo> list = projectOrderInfoService.selectProjectOrderInfoList(projectOrderInfo);
|
||||
return getDataTable(list);
|
||||
|
|
@ -56,7 +65,7 @@ public class VueProjectOrderInfoController extends BaseController {
|
|||
/**
|
||||
* 获取订单管理详细信息
|
||||
*/
|
||||
@RequiresPermissions("project:order:query")
|
||||
@RequiresPermissions("project:order:list")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
Map<String,Object> mmap=new HashMap<>();
|
||||
|
|
@ -84,7 +93,7 @@ public class VueProjectOrderInfoController extends BaseController {
|
|||
mmap.put("updateFile", (ShiroUtils.getSubject().hasRole("sale_assistant")||ShiroUtils.getSubject().hasRole("business") ||ShiroUtils.getSysUser().isAdmin()) && updateFlag);
|
||||
mmap.put("uploadFinalFile", (ShiroUtils.getSubject().hasRole("business") || ShiroUtils.getSysUser().isAdmin()) &&
|
||||
ProjectOrderInfo.OrderStatus.APPROVE_COMPLETE.getCode().equals(projectOrderInfo.getOrderStatus()));
|
||||
|
||||
mmap.put("todo", todoService.selectTodo(todo));
|
||||
|
||||
|
||||
return AjaxResult.success(mmap);
|
||||
|
|
|
|||