增加招投标模块
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="bid-approval" v-loading="loading">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">立项审批</h3>
|
||||
<el-button type="primary" :icon="Promotion" :disabled="disabled" @click="openSubmit">
|
||||
提交立项审批
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="list.length === 0" description="暂无审批记录" :image-size="80" />
|
||||
|
||||
<el-timeline v-else class="approval-timeline">
|
||||
<el-timeline-item
|
||||
v-for="row in list"
|
||||
:key="row.id"
|
||||
:type="statusType(row.approval_status)"
|
||||
:timestamp="formatDateTime(row.submit_time)"
|
||||
placement="top"
|
||||
>
|
||||
<el-card shadow="never" class="approval-card">
|
||||
<div class="approval-row">
|
||||
<el-tag :type="approvalStatusTag(row.approval_status)" size="small">
|
||||
{{ approvalStatusText(row.approval_status) }}
|
||||
</el-tag>
|
||||
<span class="approval-type">{{ approvalTypeText(row.approval_type) }}</span>
|
||||
<span class="approval-user">提交人:{{ row.submit_user_name || "-" }}</span>
|
||||
</div>
|
||||
<div v-if="row.approval_users" class="approval-row muted">
|
||||
审批人:{{ row.approval_users }}
|
||||
</div>
|
||||
<div v-if="row.comment" class="approval-comment">{{ row.comment }}</div>
|
||||
<div v-if="row.complete_time" class="approval-row muted">
|
||||
完成时间:{{ formatDateTime(row.complete_time) }}
|
||||
</div>
|
||||
<div
|
||||
v-if="row.approval_status === 1 && !disabled"
|
||||
class="approval-actions"
|
||||
>
|
||||
<el-button type="success" size="small" @click="handleApprove(row, true)">通过</el-button>
|
||||
<el-button type="danger" size="small" @click="handleApprove(row, false)">驳回</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<el-dialog v-model="dialogVisible" title="提交立项审批" width="560px" :close-on-click-modal="false">
|
||||
<el-form :model="form" label-width="100px">
|
||||
<el-form-item label="审批类型">
|
||||
<el-select v-model="form.approval_type" style="width: 100%">
|
||||
<el-option
|
||||
v-for="opt in approvalTypeOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批人">
|
||||
<el-input v-model="form.approval_users" placeholder="多个审批人用逗号分隔" />
|
||||
</el-form-item>
|
||||
<el-form-item label="说明">
|
||||
<el-input v-model="form.comment" type="textarea" :rows="3" placeholder="立项说明(选填)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="handleSubmit">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Promotion } from "@element-plus/icons-vue";
|
||||
import { getBidApprovalList, createBidApproval, approveBidApproval } from "@/api/crmBidding";
|
||||
import {
|
||||
approvalStatusText,
|
||||
approvalStatusTag,
|
||||
approvalTypeOptions,
|
||||
formatDateTime,
|
||||
} from "@/views/apps/crm/dict";
|
||||
|
||||
const props = defineProps({
|
||||
bidId: { type: [Number, String], required: true },
|
||||
disabled: { type: Boolean, default: false },
|
||||
});
|
||||
const emit = defineEmits(["count", "approved"]);
|
||||
|
||||
const loading = ref(false);
|
||||
const list = ref([]);
|
||||
const dialogVisible = ref(false);
|
||||
const submitting = ref(false);
|
||||
|
||||
const form = reactive({ approval_type: "1", approval_users: "", comment: "" });
|
||||
|
||||
defineExpose({ reload: loadList });
|
||||
|
||||
onMounted(loadList);
|
||||
|
||||
async function loadList() {
|
||||
if (!props.bidId) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getBidApprovalList({ bid_id: props.bidId });
|
||||
list.value = res?.data?.list || [];
|
||||
emit("count", list.value.length);
|
||||
} catch {
|
||||
list.value = [];
|
||||
emit("count", 0);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openSubmit() {
|
||||
form.approval_type = "1";
|
||||
form.approval_users = "";
|
||||
form.comment = "";
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
submitting.value = true;
|
||||
try {
|
||||
await createBidApproval({
|
||||
bid_id: props.bidId,
|
||||
approval_type: form.approval_type,
|
||||
approval_users: form.approval_users,
|
||||
comment: form.comment,
|
||||
});
|
||||
ElMessage.success("已提交,投标状态转为「立项审批中」");
|
||||
dialogVisible.value = false;
|
||||
loadList();
|
||||
emit("approved");
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "提交失败");
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleApprove(row, pass) {
|
||||
let comment = "";
|
||||
try {
|
||||
const { value } = await ElMessageBox.prompt(
|
||||
pass ? "填写审批意见(可留空)" : "填写驳回原因",
|
||||
pass ? "审批通过" : "审批驳回",
|
||||
{ inputPlaceholder: "审批意见", inputType: "textarea" }
|
||||
);
|
||||
comment = value || "";
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await approveBidApproval(row.id, { pass, comment });
|
||||
ElMessage.success(pass ? "已通过,投标转为「投标中」" : "已驳回,投标退回草稿");
|
||||
loadList();
|
||||
emit("approved");
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || "操作失败");
|
||||
}
|
||||
}
|
||||
|
||||
function approvalTypeText(v) {
|
||||
const hit = approvalTypeOptions.find((i) => String(i.value) === String(v));
|
||||
return hit ? hit.label : "-";
|
||||
}
|
||||
|
||||
function statusType(v) {
|
||||
const map = { 1: "primary", 2: "success", 3: "danger" };
|
||||
return map[Number(v)] || "info";
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.approval-timeline {
|
||||
padding-left: 8px;
|
||||
}
|
||||
.approval-card {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.approval-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.approval-row.muted {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
.approval-type {
|
||||
font-weight: 500;
|
||||
}
|
||||
.approval-user {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.approval-comment {
|
||||
margin: 6px 0;
|
||||
padding: 8px;
|
||||
background: var(--el-bg-color-page);
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
.approval-actions {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user