提交
This commit is contained in:
1 parent
06956b742b
commit
e59b2aa6cb
104 files changed
+653227
-7621
No files matched your search
@@ -271,7 +271,7 @@
|
||||
<script setup lang="ts" name="ContractApproval">
|
||||
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
|
||||
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { watchDebounced } from '@vueuse/core'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
@@ -479,11 +479,29 @@
|
||||
})
|
||||
}
|
||||
|
||||
const DEFAULT_OPINIONS = ['审批通过', '审批驳回'] as const
|
||||
|
||||
const defaultOpinion = (result: string) => (result === 'rejected' ? '审批驳回' : '审批通过')
|
||||
|
||||
watch(
|
||||
() => approvalForm.value.approvalResult,
|
||||
(result) => {
|
||||
if (
|
||||
!approvalForm.value.approvalOpinion.trim() ||
|
||||
DEFAULT_OPINIONS.includes(
|
||||
approvalForm.value.approvalOpinion as (typeof DEFAULT_OPINIONS)[number]
|
||||
)
|
||||
) {
|
||||
approvalForm.value.approvalOpinion = defaultOpinion(result)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const handleApprove = (row: ApprovalVO) => {
|
||||
approvalForm.value = {
|
||||
id: row.id,
|
||||
approvalResult: 'approved',
|
||||
approvalOpinion: ''
|
||||
approvalOpinion: defaultOpinion('approved')
|
||||
}
|
||||
approvalDialog.visible = true
|
||||
}
|
||||
|
||||
@@ -10,6 +10,22 @@
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-alert
|
||||
v-if="isBatchMode && batchCount > 0"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="batch-action-alert batch-action-alert--success"
|
||||
:title="batchInfoTitle"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="skippedCount > 0"
|
||||
type="error"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="batch-action-alert batch-action-alert--danger"
|
||||
:title="`已跳过 ${skippedCount} 条不可处理的合同`"
|
||||
/>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
@@ -35,14 +51,6 @@
|
||||
<template #label>
|
||||
选择审批人<span class="form-label-tip">支持搜索用户姓名</span>
|
||||
</template>
|
||||
<el-alert
|
||||
v-if="submitPrefillTip"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="已按「合同审批人设置」预填审批人,可修改后提交"
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<el-select
|
||||
v-model="form.approverIds"
|
||||
multiple
|
||||
@@ -158,6 +166,7 @@ const dialogVisible = ref(false)
|
||||
const actionType = ref<'submit' | 'approve' | 'reject' | 'withdraw'>('submit')
|
||||
const contractId = ref<string | number>()
|
||||
const contractIds = ref<(string | number)[]>()
|
||||
const skippedCount = ref(0)
|
||||
|
||||
const form = ref({
|
||||
approvalType: 'sequential',
|
||||
@@ -170,9 +179,11 @@ const form = ref({
|
||||
const approverList = ref<ApproverUser[]>([])
|
||||
const submitLoading = ref(false)
|
||||
const userLoading = ref(false)
|
||||
const submitPrefillTip = ref(false)
|
||||
const formRef = ref<ElFormInstance>()
|
||||
|
||||
const isBatchMode = computed(() => !!(contractIds.value && contractIds.value.length > 0))
|
||||
const batchCount = computed(() => contractIds.value?.length || 0)
|
||||
|
||||
const rules = computed(() => {
|
||||
const baseRules: any = {}
|
||||
if (actionType.value === 'submit') {
|
||||
@@ -195,12 +206,25 @@ const dialogTitle = computed(() => {
|
||||
withdraw: '撤回审批'
|
||||
}
|
||||
let title = titleMap[actionType.value]
|
||||
if (contractIds.value && contractIds.value.length > 0) {
|
||||
title += `(${contractIds.value.length}条)`
|
||||
if (isBatchMode.value) {
|
||||
title += `(${batchCount.value}条)`
|
||||
}
|
||||
return title
|
||||
})
|
||||
|
||||
const batchInfoTitle = computed(() => {
|
||||
if (actionType.value === 'submit') {
|
||||
return `将提交 ${batchCount.value} 条草稿合同(使用同一组审批人与说明)`
|
||||
}
|
||||
if (actionType.value === 'approve') {
|
||||
return `将审批通过 ${batchCount.value} 条合同(使用同一审批意见)`
|
||||
}
|
||||
if (actionType.value === 'reject') {
|
||||
return `将审批驳回 ${batchCount.value} 条合同(使用同一审批意见)`
|
||||
}
|
||||
return `将处理 ${batchCount.value} 条合同`
|
||||
})
|
||||
|
||||
const confirmButtonText = computed(() => {
|
||||
const textMap = {
|
||||
submit: '提交',
|
||||
@@ -214,6 +238,7 @@ const confirmButtonText = computed(() => {
|
||||
const open = async (id: string | number, type: 'submit' | 'approve' | 'reject' | 'withdraw' = 'submit') => {
|
||||
contractId.value = id
|
||||
contractIds.value = undefined
|
||||
skippedCount.value = 0
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
@@ -225,10 +250,12 @@ const open = async (id: string | number, type: 'submit' | 'approve' | 'reject' |
|
||||
|
||||
const openBatch = async (
|
||||
idList: (string | number)[],
|
||||
type: 'submit' | 'approve' | 'reject' | 'withdraw' = 'submit'
|
||||
type: 'submit' | 'approve' | 'reject' | 'withdraw' = 'submit',
|
||||
options?: { skippedCount?: number }
|
||||
) => {
|
||||
contractIds.value = idList
|
||||
contractId.value = undefined
|
||||
skippedCount.value = options?.skippedCount || 0
|
||||
actionType.value = type
|
||||
dialogVisible.value = true
|
||||
resetForm()
|
||||
@@ -238,6 +265,33 @@ const openBatch = async (
|
||||
}
|
||||
}
|
||||
|
||||
const showBatchResultMessage = (options: {
|
||||
actionLabel: string
|
||||
successCount: number
|
||||
failedCount: number
|
||||
skippedCount?: number
|
||||
}) => {
|
||||
const { actionLabel, successCount, failedCount, skippedCount: skip = 0 } = options
|
||||
const skipText = skip > 0 ? `,跳过 ${skip} 条` : ''
|
||||
if (successCount === 0 && failedCount === 0) {
|
||||
$modal.msgWarning(`没有可${actionLabel}的合同${skipText}`)
|
||||
return
|
||||
}
|
||||
if (successCount === 0) {
|
||||
$modal.msgError(`${actionLabel}失败${skipText}`)
|
||||
return
|
||||
}
|
||||
if (failedCount > 0) {
|
||||
$modal.msgWarning(`成功${actionLabel} ${successCount} 条,失败 ${failedCount} 条${skipText}`)
|
||||
return
|
||||
}
|
||||
if (successCount === 1 && skip === 0) {
|
||||
$modal.msgSuccess(`${actionLabel}成功`)
|
||||
return
|
||||
}
|
||||
$modal.msgSuccess(`成功${actionLabel} ${successCount} 条${skipText}`)
|
||||
}
|
||||
|
||||
const loadApproverList = async (nickName?: string) => {
|
||||
userLoading.value = true
|
||||
try {
|
||||
@@ -278,9 +332,7 @@ async function resolvePrefillApproverIds(userList: ApproverUser[]) {
|
||||
}
|
||||
|
||||
async function applyPrefillApprovers() {
|
||||
const prefillIds = await resolvePrefillApproverIds(approverList.value)
|
||||
form.value.approverIds = prefillIds
|
||||
submitPrefillTip.value = prefillIds.length > 0
|
||||
form.value.approverIds = await resolvePrefillApproverIds(approverList.value)
|
||||
}
|
||||
|
||||
const handleSearchUser = (query: string) => {
|
||||
@@ -298,34 +350,73 @@ const getApproverName = (approverId: string | number): string => {
|
||||
|
||||
const handleConfirm = async () => {
|
||||
await formRef.value?.validate()
|
||||
const isBatch = !!(contractIds.value && contractIds.value.length > 0)
|
||||
const isBatch = isBatchMode.value
|
||||
if (!isBatch && (contractId.value == null || contractId.value === '')) {
|
||||
$modal.msgError('合同 ID 缺失,请关闭后重新打开提交审批')
|
||||
$modal.msgError('合同 ID 缺失,请关闭后重新打开')
|
||||
return
|
||||
}
|
||||
submitLoading.value = true
|
||||
try {
|
||||
let shouldClose = true
|
||||
|
||||
if (actionType.value === 'submit') {
|
||||
if (isBatch) {
|
||||
const count = await batchSubmitContract(contractIds.value!, form.value.approverIds, form.value.remark)
|
||||
$modal.msgSuccess(`提交成功,成功 ${count ?? contractIds.value!.length} 条`)
|
||||
showBatchResultMessage({
|
||||
actionLabel: '提交',
|
||||
successCount: count ?? contractIds.value!.length,
|
||||
failedCount: 0,
|
||||
skippedCount: skippedCount.value
|
||||
})
|
||||
} else {
|
||||
await submitContract(contractId.value!, form.value.approverIds, form.value.remark)
|
||||
$modal.msgSuccess('提交成功')
|
||||
}
|
||||
} else if (actionType.value === 'approve') {
|
||||
await approveContract(contractId.value!, form.value.comment)
|
||||
$modal.msgSuccess('审批通过')
|
||||
if (isBatch) {
|
||||
const results = await Promise.allSettled(
|
||||
contractIds.value!.map((id) => approveContract(id, form.value.comment))
|
||||
)
|
||||
const successCount = results.filter((item) => item.status === 'fulfilled').length
|
||||
const failedCount = results.length - successCount
|
||||
showBatchResultMessage({
|
||||
actionLabel: '审批通过',
|
||||
successCount,
|
||||
failedCount,
|
||||
skippedCount: skippedCount.value
|
||||
})
|
||||
shouldClose = successCount > 0
|
||||
} else {
|
||||
await approveContract(contractId.value!, form.value.comment)
|
||||
$modal.msgSuccess('审批通过')
|
||||
}
|
||||
} else if (actionType.value === 'reject') {
|
||||
await rejectContract(contractId.value!, form.value.comment)
|
||||
$modal.msgSuccess('已驳回')
|
||||
if (isBatch) {
|
||||
const results = await Promise.allSettled(
|
||||
contractIds.value!.map((id) => rejectContract(id, form.value.comment))
|
||||
)
|
||||
const successCount = results.filter((item) => item.status === 'fulfilled').length
|
||||
const failedCount = results.length - successCount
|
||||
showBatchResultMessage({
|
||||
actionLabel: '审批驳回',
|
||||
successCount,
|
||||
failedCount,
|
||||
skippedCount: skippedCount.value
|
||||
})
|
||||
shouldClose = successCount > 0
|
||||
} else {
|
||||
await rejectContract(contractId.value!, form.value.comment)
|
||||
$modal.msgSuccess('已驳回')
|
||||
}
|
||||
} else if (actionType.value === 'withdraw') {
|
||||
await withdrawContract(contractId.value!, form.value.reason)
|
||||
$modal.msgSuccess('已撤回')
|
||||
}
|
||||
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
if (shouldClose) {
|
||||
dialogVisible.value = false
|
||||
emit('success')
|
||||
}
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
@@ -350,7 +441,6 @@ const resetForm = () => {
|
||||
remark: '',
|
||||
reason: ''
|
||||
}
|
||||
submitPrefillTip.value = false
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
@@ -361,6 +451,24 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.batch-action-alert {
|
||||
margin-bottom: 12px;
|
||||
|
||||
&--success {
|
||||
:deep(.el-alert__title),
|
||||
:deep(.el-alert__icon) {
|
||||
color: var(--primary, #10b981);
|
||||
}
|
||||
}
|
||||
|
||||
&--danger {
|
||||
:deep(.el-alert__title),
|
||||
:deep(.el-alert__icon) {
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-tip-block {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
|
||||
@@ -141,16 +141,16 @@
|
||||
<el-button
|
||||
v-if="activeView === 'submitted'"
|
||||
class="btn-ghost"
|
||||
:disabled="single"
|
||||
@click="handleApprove(contractList[contractList.findIndex((item) => item.id === ids[0])])"
|
||||
:disabled="!canApproveBatch"
|
||||
@click="handleApprove()"
|
||||
>
|
||||
<el-icon><Select /></el-icon>审批通过
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="activeView === 'submitted'"
|
||||
class="btn-ghost"
|
||||
:disabled="single"
|
||||
@click="handleReject(contractList[contractList.findIndex((item) => item.id === ids[0])])"
|
||||
:disabled="!canApproveBatch"
|
||||
@click="handleReject()"
|
||||
>
|
||||
<el-icon><Close /></el-icon>审批驳回
|
||||
</el-button>
|
||||
@@ -1111,12 +1111,13 @@ const handleSubmitBatch = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (draftContracts.length < selectedContracts.length) {
|
||||
$modal.msgWarning(`选中了 ${selectedContracts.length} 条合同,其中 ${draftContracts.length} 条为草稿状态,只有草稿状态的合同可以提交审批`);
|
||||
const skippedCount = selectedContracts.length - draftContracts.length;
|
||||
if (skippedCount > 0) {
|
||||
$modal.msgWarning(`已跳过 ${skippedCount} 条非草稿合同,仅处理草稿状态单据`);
|
||||
}
|
||||
|
||||
const draftIds = draftContracts.map((item) => item.id);
|
||||
approvalDialogRef.value?.openBatch(draftIds, 'submit');
|
||||
approvalDialogRef.value?.openBatch(draftIds, 'submit', { skippedCount });
|
||||
};
|
||||
|
||||
/** 判断是否可以提交审批(至少有一条草稿状态的合同) */
|
||||
@@ -1126,32 +1127,55 @@ const canSubmit = computed(() => {
|
||||
return selectedContracts.some((item) => item.status === 'draft');
|
||||
});
|
||||
|
||||
/** 审批通过 */
|
||||
const handleApprove = (row?: ContractVO) => {
|
||||
if (!row && ids.value.length !== 1) {
|
||||
$modal.msgWarning('请选择一条数据进行审批');
|
||||
/** 判断是否可以审批(至少有一条当前用户可审批的合同) */
|
||||
const canApproveBatch = computed(() => {
|
||||
if (!selectedRows.value.length) return false;
|
||||
return selectedRows.value.some((item) => item.canApprove);
|
||||
});
|
||||
|
||||
/** 筛选可审批合同并打开审批弹窗(支持单条与批量) */
|
||||
const openApprovalForTargets = (
|
||||
row: ContractVO | undefined,
|
||||
type: 'approve' | 'reject'
|
||||
) => {
|
||||
const targets = row ? [row] : selectedRows.value;
|
||||
if (!targets.length) {
|
||||
$modal.msgWarning('请选择要审批的合同');
|
||||
return;
|
||||
}
|
||||
const targetRow = row || contractList.value[contractList.value.findIndex((item) => item.id === ids.value[0])];
|
||||
if (!targetRow) {
|
||||
$modal.msgWarning('未找到选中的数据');
|
||||
|
||||
const validTargets = targets.filter((item) => item.canApprove);
|
||||
const skippedCount = targets.length - validTargets.length;
|
||||
|
||||
if (!validTargets.length) {
|
||||
$modal.msgWarning(
|
||||
targets.length === 1
|
||||
? '您没有该合同的审批权限,或已审批过该合同'
|
||||
: '所选合同均不可审批(无权、已审或不在待审批状态)'
|
||||
);
|
||||
return;
|
||||
}
|
||||
approvalDialogRef.value?.open(targetRow.id, 'approve');
|
||||
|
||||
if (skippedCount > 0) {
|
||||
$modal.msgWarning(`已跳过 ${skippedCount} 条不可审批的合同,仅处理可审批单据`);
|
||||
}
|
||||
|
||||
const validIds = validTargets.map((item) => item.id);
|
||||
if (validIds.length === 1 && skippedCount === 0) {
|
||||
approvalDialogRef.value?.open(validIds[0], type);
|
||||
} else {
|
||||
approvalDialogRef.value?.openBatch(validIds, type, { skippedCount });
|
||||
}
|
||||
};
|
||||
|
||||
/** 审批驳回 */
|
||||
/** 审批通过(支持单条与批量) */
|
||||
const handleApprove = (row?: ContractVO) => {
|
||||
openApprovalForTargets(row, 'approve');
|
||||
};
|
||||
|
||||
/** 审批驳回(支持单条与批量) */
|
||||
const handleReject = (row?: ContractVO) => {
|
||||
if (!row && ids.value.length !== 1) {
|
||||
$modal.msgWarning('请选择一条数据进行审批');
|
||||
return;
|
||||
}
|
||||
const targetRow = row || contractList.value[contractList.value.findIndex((item) => item.id === ids.value[0])];
|
||||
if (!targetRow) {
|
||||
$modal.msgWarning('未找到选中的数据');
|
||||
return;
|
||||
}
|
||||
approvalDialogRef.value?.open(targetRow.id, 'reject');
|
||||
openApprovalForTargets(row, 'reject');
|
||||
};
|
||||
|
||||
/** 上传合同扫描件 */
|
||||
|
||||
@@ -81,11 +81,13 @@
|
||||
try {
|
||||
const res = await fetchUserOptions()
|
||||
const options = (res as any)?.data ?? res ?? []
|
||||
userList.value = (Array.isArray(options) ? options : []).map((item: any) => ({
|
||||
userId: item.value ?? item.userId ?? item.id,
|
||||
nickName: item.nickName ?? item.label ?? item.userName ?? '',
|
||||
userName: item.userNo ?? item.user_no ?? item.userName ?? ''
|
||||
}))
|
||||
userList.value = (Array.isArray(options) ? options : [])
|
||||
.map((item: any) => ({
|
||||
userId: String(item.value ?? item.userId ?? item.id ?? ''),
|
||||
nickName: item.nickName ?? item.label ?? item.userName ?? '',
|
||||
userName: item.userNo ?? item.user_no ?? item.userName ?? ''
|
||||
}))
|
||||
.filter((u) => u.userId)
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error)
|
||||
userList.value = []
|
||||
|
||||
@@ -123,13 +123,29 @@ watch(dialogVisible, (val) => {
|
||||
emit('update:visible', val)
|
||||
})
|
||||
|
||||
const DEFAULT_OPINIONS = ['审批通过', '审批驳回'] as const
|
||||
|
||||
const form = ref({
|
||||
quoteId: undefined as number | undefined,
|
||||
result: 'approved',
|
||||
opinion: '',
|
||||
opinion: '审批通过',
|
||||
nextApproverUserId: undefined as string | number | undefined
|
||||
})
|
||||
|
||||
const defaultOpinion = (result: string) => (result === 'rejected' ? '审批驳回' : '审批通过')
|
||||
|
||||
watch(
|
||||
() => form.value.result,
|
||||
(result) => {
|
||||
if (
|
||||
!form.value.opinion.trim() ||
|
||||
DEFAULT_OPINIONS.includes(form.value.opinion as (typeof DEFAULT_OPINIONS)[number])
|
||||
) {
|
||||
form.value.opinion = defaultOpinion(result)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const rules = computed<FormRules>(() => {
|
||||
if (props.approvalType === 'approve') {
|
||||
return {
|
||||
@@ -165,7 +181,7 @@ const resetForm = () => {
|
||||
form.value = {
|
||||
quoteId: props.quoteInfo.id as number | undefined,
|
||||
result: 'approved',
|
||||
opinion: '',
|
||||
opinion: defaultOpinion('approved'),
|
||||
nextApproverUserId: undefined
|
||||
}
|
||||
formRef.value?.clearValidate()
|
||||
|
||||
@@ -140,12 +140,14 @@ const loadUsers = async (nickName?: string) => {
|
||||
try {
|
||||
const res = await fetchUserOptions()
|
||||
const options = (res as any)?.data ?? res ?? []
|
||||
const mapped = (Array.isArray(options) ? options : []).map((item: any) => ({
|
||||
userId: item.value ?? item.userId ?? item.id,
|
||||
nickName: item.nickName ?? item.label ?? item.userName ?? '',
|
||||
userName: item.userNo ?? item.user_no ?? item.userName ?? '',
|
||||
deptName: item.deptName ?? ''
|
||||
}))
|
||||
const mapped = (Array.isArray(options) ? options : [])
|
||||
.map((item: any) => ({
|
||||
userId: String(item.value ?? item.userId ?? item.id ?? ''),
|
||||
nickName: item.nickName ?? item.label ?? item.userName ?? '',
|
||||
userName: item.userNo ?? item.user_no ?? item.userName ?? '',
|
||||
deptName: item.deptName ?? ''
|
||||
}))
|
||||
.filter((user) => user.userId)
|
||||
userList.value = nickName
|
||||
? mapped.filter((user) => user.nickName.includes(nickName) || user.userName.includes(nickName))
|
||||
: mapped
|
||||
|
||||
@@ -463,14 +463,6 @@
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
>
|
||||
<el-alert
|
||||
v-if="submitPrefillTip"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="已按「报价单审批人设置」预填审批人,可修改后提交"
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item prop="approverIds" class="form-col-span-2">
|
||||
<template #label>
|
||||
@@ -697,7 +689,6 @@ const submitForm = ref({
|
||||
const submitRules = ref({
|
||||
approverIds: [{ required: true, message: '请选择审批人', trigger: 'change' }]
|
||||
});
|
||||
const submitPrefillTip = ref(false);
|
||||
const submitFormRef = ref<FormInstance>();
|
||||
|
||||
// 用户列表(负责人 + 审批人候选)
|
||||
@@ -1097,11 +1088,11 @@ const loadUserList = async () => {
|
||||
const res = await fetchUserOptions();
|
||||
const options = (res as any)?.data ?? res ?? [];
|
||||
userList.value = (Array.isArray(options) ? options : []).map((item: any) => ({
|
||||
userId: item.value ?? item.userId ?? item.id,
|
||||
userId: String(item.value ?? item.userId ?? item.id ?? ''),
|
||||
nickName: item.nickName ?? item.label ?? item.userName ?? '',
|
||||
userName: item.userNo ?? item.user_no ?? item.userName ?? '',
|
||||
deptName: item.deptName ?? ''
|
||||
}));
|
||||
})).filter((u) => u.userId);
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error);
|
||||
userList.value = [];
|
||||
@@ -1719,7 +1710,6 @@ const handleSubmit = async () => {
|
||||
approverIds: prefillIds,
|
||||
remark: ''
|
||||
};
|
||||
submitPrefillTip.value = prefillIds.length > 0;
|
||||
|
||||
// 打开审批对话框
|
||||
submitDialog.value.visible = true;
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
<el-button
|
||||
v-if="activeView !== 'submitted' && activeView !== 'accepted'"
|
||||
class="btn-ghost"
|
||||
:disabled="single"
|
||||
:disabled="multiple"
|
||||
@click="handleSubmit()"
|
||||
>
|
||||
<el-icon><Document /></el-icon>提交审批
|
||||
@@ -123,7 +123,7 @@
|
||||
<el-button
|
||||
v-if="activeView === 'submitted'"
|
||||
class="btn-ghost"
|
||||
:disabled="single"
|
||||
:disabled="multiple"
|
||||
@click="handleApproval()"
|
||||
>
|
||||
<el-icon><DocumentChecked /></el-icon>审批
|
||||
@@ -406,6 +406,22 @@
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-alert
|
||||
v-if="submitTargetIds.length > 1"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="batch-action-alert batch-action-alert--success"
|
||||
:title="`将提交 ${submitTargetIds.length} 条草稿报价单(使用同一组审批人与备注)`"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="submitSkippedCount > 0"
|
||||
type="error"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="batch-action-alert batch-action-alert--danger"
|
||||
:title="`已跳过 ${submitSkippedCount} 条非草稿报价单`"
|
||||
/>
|
||||
<el-form
|
||||
ref="submitFormRef"
|
||||
:model="submitForm"
|
||||
@@ -415,14 +431,6 @@
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
>
|
||||
<el-alert
|
||||
v-if="submitPrefillTip"
|
||||
type="success"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="已按「报价单审批人设置」预填审批人,可修改后提交"
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<div class="form-grid-2">
|
||||
<el-form-item prop="approverIds" class="form-col-span-2">
|
||||
<template #label>
|
||||
@@ -567,7 +575,40 @@
|
||||
modal-class="order-module-dialog-mask"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<section v-if="currentApprovalQuote" class="approval-dialog-section">
|
||||
<el-alert
|
||||
v-if="batchApprovalTargets.length > 1"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="batch-action-alert"
|
||||
:title="`将审批 ${batchApprovalTargets.length} 条报价单(使用同一审批结果与意见)`"
|
||||
/>
|
||||
<el-alert
|
||||
v-if="approvalSkippedCount > 0"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="batch-action-alert"
|
||||
:title="`已跳过 ${approvalSkippedCount} 条无权或不在待审批状态的报价单`"
|
||||
/>
|
||||
<section
|
||||
v-if="batchApprovalTargets.length > 1"
|
||||
class="approval-dialog-section"
|
||||
>
|
||||
<h3 class="approval-section-title">待审批报价单</h3>
|
||||
<div class="batch-approval-quote-list">
|
||||
<el-tag
|
||||
v-for="item in batchApprovalTargets"
|
||||
:key="String(item.quote.id)"
|
||||
size="small"
|
||||
class="batch-approval-quote-tag"
|
||||
>
|
||||
{{ item.quote.quoteNo || item.quote.id }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-else-if="currentApprovalQuote" class="approval-dialog-section">
|
||||
<h3 class="approval-section-title">报价单信息</h3>
|
||||
<el-descriptions :column="2" class="approval-kv-descriptions">
|
||||
<el-descriptions-item label="报价单号">{{ currentApprovalQuote.quoteNo }}</el-descriptions-item>
|
||||
@@ -588,7 +629,7 @@
|
||||
</el-descriptions>
|
||||
</section>
|
||||
|
||||
<section v-if="approvalList.length > 0" class="approval-dialog-section">
|
||||
<section v-if="batchApprovalTargets.length <= 1 && approvalList.length > 0" class="approval-dialog-section">
|
||||
<div class="approval-section-head">
|
||||
<h3 class="approval-section-title">审批流程</h3>
|
||||
<el-tag size="small" type="info">
|
||||
@@ -637,8 +678,7 @@
|
||||
ref="approvalFormRef"
|
||||
:model="approvalForm"
|
||||
:rules="approvalRules"
|
||||
label-position="right"
|
||||
label-width="100px"
|
||||
label-position="top"
|
||||
class="crm-dialog-form"
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
@@ -730,7 +770,7 @@ import DictTag from '@/components/DictTag/index.vue'
|
||||
import { $modal } from '@/utils/modal'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { formatBrandForPrint } from '@/utils/crm-product-brand'
|
||||
import { ref, reactive, onMounted, nextTick, getCurrentInstance, toRefs, computed, type ComponentInternalInstance } from 'vue';
|
||||
import { ref, reactive, onMounted, nextTick, getCurrentInstance, toRefs, computed, watch, type ComponentInternalInstance } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import type { FormInstance } from 'element-plus';
|
||||
@@ -1035,16 +1075,15 @@ const submitDialog = reactive({
|
||||
visible: false,
|
||||
loading: false
|
||||
});
|
||||
const submitTargetIds = ref<(string | number)[]>([]);
|
||||
const submitSkippedCount = ref(0);
|
||||
const submitForm = ref({
|
||||
id: undefined as string | number | undefined,
|
||||
approverIds: [] as (string | number)[],
|
||||
remark: ''
|
||||
});
|
||||
const submitRules = ref({
|
||||
approverIds: [{ required: true, message: '请选择审批人', trigger: 'change' }]
|
||||
});
|
||||
const submitPrefillTip = ref(false);
|
||||
|
||||
// 用户列表(审批人候选)
|
||||
const userList = ref<UserVO[]>([]);
|
||||
|
||||
@@ -1095,7 +1134,7 @@ const approvalDialog = reactive({
|
||||
const approvalForm = ref<ApprovalForm>({
|
||||
id: undefined as string | number | undefined,
|
||||
approvalResult: 'approved',
|
||||
approvalOpinion: '',
|
||||
approvalOpinion: '审批通过',
|
||||
remark: ''
|
||||
});
|
||||
const approvalRules = ref({
|
||||
@@ -1113,8 +1152,53 @@ const approvalRules = ref({
|
||||
}
|
||||
]
|
||||
});
|
||||
const QUOTE_DEFAULT_OPINIONS = ['审批通过', '审批驳回'] as const
|
||||
watch(
|
||||
() => approvalForm.value.approvalResult,
|
||||
(result) => {
|
||||
if (
|
||||
!approvalForm.value.approvalOpinion.trim() ||
|
||||
QUOTE_DEFAULT_OPINIONS.includes(
|
||||
approvalForm.value.approvalOpinion as (typeof QUOTE_DEFAULT_OPINIONS)[number]
|
||||
)
|
||||
) {
|
||||
approvalForm.value.approvalOpinion = result === 'rejected' ? '审批驳回' : '审批通过'
|
||||
}
|
||||
}
|
||||
)
|
||||
const approvalList = ref<QuoteApprovalVO[]>([]);
|
||||
const currentApprovalQuote = ref<QuoteVO | null>(null);
|
||||
type BatchApprovalTarget = { quote: QuoteVO; approvalId: string | number };
|
||||
const batchApprovalTargets = ref<BatchApprovalTarget[]>([]);
|
||||
const approvalSkippedCount = ref(0);
|
||||
|
||||
/** 批量操作结果提示 */
|
||||
const showBatchResultMessage = (options: {
|
||||
actionLabel: string
|
||||
successCount: number
|
||||
failedCount: number
|
||||
skippedCount?: number
|
||||
}) => {
|
||||
const { actionLabel, successCount, failedCount, skippedCount = 0 } = options
|
||||
const skipText = skippedCount > 0 ? `,跳过 ${skippedCount} 条` : ''
|
||||
if (successCount === 0 && failedCount === 0) {
|
||||
$modal.msgWarning(`没有可${actionLabel}的报价单${skipText}`)
|
||||
return
|
||||
}
|
||||
if (successCount === 0) {
|
||||
$modal.msgError(`${actionLabel}失败${skipText}`)
|
||||
return
|
||||
}
|
||||
if (failedCount > 0) {
|
||||
$modal.msgWarning(`成功${actionLabel} ${successCount} 条,失败 ${failedCount} 条${skipText}`)
|
||||
return
|
||||
}
|
||||
if (successCount === 1 && skippedCount === 0) {
|
||||
$modal.msgSuccess(`${actionLabel}成功`)
|
||||
return
|
||||
}
|
||||
$modal.msgSuccess(`成功${actionLabel} ${successCount} 条${skipText}`)
|
||||
}
|
||||
|
||||
const queryFormRef = ref<FormInstance>();
|
||||
const submitFormRef = ref<FormInstance>();
|
||||
@@ -1301,12 +1385,12 @@ const handleView = (row?: QuoteVO) => {
|
||||
const handleQuoteDetailSubmit = async (quote: QuoteVO) => {
|
||||
await loadUserList();
|
||||
const prefillIds = await resolvePrefillApproverIds(userList.value);
|
||||
submitTargetIds.value = [quote.id];
|
||||
submitSkippedCount.value = 0;
|
||||
submitForm.value = {
|
||||
id: quote.id,
|
||||
approverIds: prefillIds,
|
||||
remark: ''
|
||||
};
|
||||
submitPrefillTip.value = prefillIds.length > 0;
|
||||
submitDialog.visible = true;
|
||||
};
|
||||
|
||||
@@ -1386,11 +1470,12 @@ const loadUserList = async () => {
|
||||
const res = await fetchUserOptions();
|
||||
const options = (res as any)?.data ?? res ?? [];
|
||||
userList.value = (Array.isArray(options) ? options : []).map((item: any) => ({
|
||||
userId: item.value ?? item.userId ?? item.id,
|
||||
// 与设置/预填统一为字符串,避免 el-select 因 number/string 类型不一致无法回显
|
||||
userId: String(item.value ?? item.userId ?? item.id ?? ''),
|
||||
nickName: item.nickName ?? item.label ?? item.userName ?? '',
|
||||
userName: item.userNo ?? item.user_no ?? item.userName ?? '',
|
||||
deptName: item.deptName ?? ''
|
||||
}));
|
||||
})).filter((u) => u.userId);
|
||||
} catch (error) {
|
||||
console.error('加载用户列表失败:', error);
|
||||
userList.value = [];
|
||||
@@ -1408,46 +1493,71 @@ async function resolvePrefillApproverIds(userList: { userId: string | number }[]
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交审批 */
|
||||
/** 提交审批(支持单条与批量) */
|
||||
const handleSubmit = async (row?: QuoteVO) => {
|
||||
if (!row && ids.value.length !== 1) {
|
||||
$modal.msgWarning('请选择一条数据进行提交审批');
|
||||
const targets = row ? [row] : selectedRows.value;
|
||||
if (!targets.length) {
|
||||
$modal.msgWarning('请选择要提交审批的数据');
|
||||
return;
|
||||
}
|
||||
const id = row?.id || ids.value[0];
|
||||
const quote = row || quoteList.value[quoteList.value.findIndex((item) => item.id === id)];
|
||||
if (quote && quote.status !== 'draft') {
|
||||
$modal.msgWarning('只能提交草稿状态的报价单');
|
||||
|
||||
const validTargets = targets.filter((item) => item.status === 'draft');
|
||||
const skippedCount = targets.length - validTargets.length;
|
||||
|
||||
if (!validTargets.length) {
|
||||
$modal.msgWarning('所选报价单均不是草稿状态,无法提交审批');
|
||||
return;
|
||||
}
|
||||
|
||||
if (skippedCount > 0) {
|
||||
$modal.msgWarning(`已跳过 ${skippedCount} 条非草稿报价单,仅处理草稿状态单据`);
|
||||
}
|
||||
|
||||
await loadUserList();
|
||||
const prefillIds = await resolvePrefillApproverIds(userList.value);
|
||||
submitTargetIds.value = validTargets.map((item) => item.id);
|
||||
submitSkippedCount.value = skippedCount;
|
||||
submitForm.value = {
|
||||
id: id,
|
||||
approverIds: prefillIds,
|
||||
remark: ''
|
||||
};
|
||||
submitPrefillTip.value = prefillIds.length > 0;
|
||||
submitDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 确认提交审批 */
|
||||
const confirmSubmit = async () => {
|
||||
await submitFormRef.value?.validate();
|
||||
if (!submitTargetIds.value.length) {
|
||||
$modal.msgWarning('没有可提交审批的报价单');
|
||||
return;
|
||||
}
|
||||
|
||||
submitDialog.loading = true;
|
||||
try {
|
||||
// 直接使用原始ID,不转换类型(避免大整数精度丢失)
|
||||
// 后端会正确处理字符串或数字类型的ID
|
||||
await submitQuote({
|
||||
id: submitForm.value.id!,
|
||||
approverIds: submitForm.value.approverIds,
|
||||
remark: submitForm.value.remark
|
||||
const results = await Promise.allSettled(
|
||||
submitTargetIds.value.map((id) =>
|
||||
submitQuote({
|
||||
id,
|
||||
approverIds: submitForm.value.approverIds,
|
||||
remark: submitForm.value.remark
|
||||
})
|
||||
)
|
||||
);
|
||||
const successCount = results.filter((item) => item.status === 'fulfilled').length;
|
||||
const failedCount = results.length - successCount;
|
||||
showBatchResultMessage({
|
||||
actionLabel: '提交',
|
||||
successCount,
|
||||
failedCount,
|
||||
skippedCount: submitSkippedCount.value
|
||||
});
|
||||
$modal.msgSuccess('提交成功');
|
||||
submitDialog.visible = false;
|
||||
await getList();
|
||||
|
||||
await quoteDetailRef.value?.refresh();
|
||||
if (successCount > 0) {
|
||||
submitDialog.visible = false;
|
||||
await getList();
|
||||
await quoteDetailRef.value?.refresh();
|
||||
}
|
||||
} finally {
|
||||
submitDialog.loading = false;
|
||||
}
|
||||
@@ -1551,73 +1661,104 @@ const handleToContract = async (row: QuoteVO) => {
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 审批报价单 */
|
||||
/** 审批报价单(支持单条与批量) */
|
||||
const handleApproval = async (row?: QuoteVO) => {
|
||||
if (!row && ids.value.length !== 1) {
|
||||
$modal.msgWarning('请选择一条数据进行审批');
|
||||
const targets = row ? [row] : selectedRows.value;
|
||||
if (!targets.length) {
|
||||
$modal.msgWarning('请选择要审批的数据');
|
||||
return;
|
||||
}
|
||||
const id = row?.id || ids.value[0];
|
||||
const quote = row || quoteList.value[quoteList.value.findIndex((item) => item.id === id)];
|
||||
if (!quote.canApprove || activeView.value !== 'submitted') {
|
||||
$modal.msgWarning('只能审批待审批的报价单');
|
||||
if (activeView.value !== 'submitted') {
|
||||
$modal.msgWarning('请在「待审批」视图中进行审批');
|
||||
return;
|
||||
}
|
||||
// 保存当前审批的报价单
|
||||
currentApprovalQuote.value = quote;
|
||||
|
||||
// 重置表单
|
||||
const currentUserId = resolveCurrentUserId();
|
||||
if (!currentUserId) {
|
||||
$modal.msgError('无法获取当前用户信息,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置表单(默认审批意见,可直接确认)
|
||||
approvalForm.value = {
|
||||
id: undefined,
|
||||
id: undefined as unknown as string | number,
|
||||
approvalResult: 'approved',
|
||||
approvalOpinion: '',
|
||||
approvalOpinion: '审批通过',
|
||||
remark: ''
|
||||
};
|
||||
|
||||
// 清空审批记录列表
|
||||
approvalList.value = [];
|
||||
batchApprovalTargets.value = [];
|
||||
currentApprovalQuote.value = null;
|
||||
|
||||
const candidateQuotes = targets.filter((item) => item.canApprove);
|
||||
let skippedCount = targets.length - candidateQuotes.length;
|
||||
const validTargets: BatchApprovalTarget[] = [];
|
||||
let singleApprovalList: QuoteApprovalVO[] = [];
|
||||
|
||||
// 加载审批记录
|
||||
try {
|
||||
const res = await listApproval(id);
|
||||
approvalList.value = res.data || [];
|
||||
|
||||
// 获取当前用户ID
|
||||
const currentUserId = resolveCurrentUserId();
|
||||
if (!currentUserId) {
|
||||
$modal.msgError('无法获取当前用户信息,请重新登录');
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到当前用户的待审批记录
|
||||
const pendingApproval = approvalList.value.find(
|
||||
(item) => String(item.approverId) === String(currentUserId) && item.approvalResult === 'pending'
|
||||
const loadResults = await Promise.allSettled(
|
||||
candidateQuotes.map(async (quote) => {
|
||||
const res = await listApproval(quote.id);
|
||||
return { quote, list: res.data || [] };
|
||||
})
|
||||
);
|
||||
|
||||
if (!pendingApproval) {
|
||||
// 检查用户是否已经审批过
|
||||
const approvedRecord = approvalList.value.find(
|
||||
(item) => String(item.approverId) === String(currentUserId)
|
||||
for (const result of loadResults) {
|
||||
if (result.status !== 'fulfilled') {
|
||||
skippedCount += 1;
|
||||
continue;
|
||||
}
|
||||
const { quote, list } = result.value;
|
||||
const pendingApproval = list.find(
|
||||
(item) =>
|
||||
String(item.approverId) === String(currentUserId) && item.approvalResult === 'pending'
|
||||
);
|
||||
if (approvedRecord) {
|
||||
const resultText = approvedRecord.approvalResult === 'approved' ? '已通过' : '已拒绝';
|
||||
$modal.msgWarning(`您已经审批过该报价单(${resultText}),不能重复审批`);
|
||||
} else {
|
||||
$modal.msgWarning('您没有该报价单的审批权限');
|
||||
if (!pendingApproval) {
|
||||
skippedCount += 1;
|
||||
continue;
|
||||
}
|
||||
validTargets.push({ quote, approvalId: pendingApproval.id });
|
||||
if (candidateQuotes.length === 1) {
|
||||
singleApprovalList = list;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置审批记录ID
|
||||
approvalForm.value.id = pendingApproval.id;
|
||||
|
||||
// 打开审批对话框
|
||||
approvalDialog.visible = true;
|
||||
} catch (error: any) {
|
||||
console.error('加载审批记录失败:', error);
|
||||
$modal.msgError(error?.msg || '加载审批记录失败,请稍后重试');
|
||||
approvalList.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validTargets.length) {
|
||||
if (targets.length === 1) {
|
||||
$modal.msgWarning('您没有该报价单的审批权限,或已审批过该报价单');
|
||||
} else {
|
||||
$modal.msgWarning('所选报价单均不可审批(无权、已审或不在待审批状态)');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (skippedCount > 0) {
|
||||
$modal.msgWarning(`已跳过 ${skippedCount} 条不可审批的报价单,仅处理可审批单据`);
|
||||
}
|
||||
|
||||
batchApprovalTargets.value = validTargets;
|
||||
approvalSkippedCount.value = skippedCount;
|
||||
if (validTargets.length === 1) {
|
||||
const only = validTargets[0];
|
||||
currentApprovalQuote.value = only.quote;
|
||||
approvalForm.value.id = only.approvalId;
|
||||
if (singleApprovalList.length) {
|
||||
approvalList.value = singleApprovalList;
|
||||
} else {
|
||||
try {
|
||||
const res = await listApproval(only.quote.id);
|
||||
approvalList.value = res.data || [];
|
||||
} catch {
|
||||
approvalList.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
approvalDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 确认审批 */
|
||||
@@ -1630,12 +1771,22 @@ const confirmApproval = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!batchApprovalTargets.value.length) {
|
||||
$modal.msgWarning('没有可审批的报价单');
|
||||
return;
|
||||
}
|
||||
|
||||
// 二次确认
|
||||
const resultText = approvalForm.value.approvalResult === 'approved' ? '通过' : '拒绝';
|
||||
const targetCount = batchApprovalTargets.value.length;
|
||||
const confirmMsg =
|
||||
approvalForm.value.approvalResult === 'rejected'
|
||||
? `确认要拒绝该报价单吗?拒绝后报价单将无法再次提交审批。\n拒绝理由:${approvalForm.value.approvalOpinion}`
|
||||
: `确认要通过该报价单吗?`;
|
||||
? targetCount === 1
|
||||
? `确认要拒绝该报价单吗?拒绝后报价单将无法再次提交审批。\n拒绝理由:${approvalForm.value.approvalOpinion}`
|
||||
: `确认要拒绝选中的 ${targetCount} 条报价单吗?拒绝后将无法再次提交审批。\n拒绝理由:${approvalForm.value.approvalOpinion}`
|
||||
: targetCount === 1
|
||||
? '确认要通过该报价单吗?'
|
||||
: `确认要通过选中的 ${targetCount} 条报价单吗?`;
|
||||
|
||||
try {
|
||||
await $modal.confirm(confirmMsg);
|
||||
@@ -1645,17 +1796,35 @@ const confirmApproval = async () => {
|
||||
|
||||
approvalDialog.loading = true;
|
||||
try {
|
||||
await approveQuote(approvalForm.value);
|
||||
const results = await Promise.allSettled(
|
||||
batchApprovalTargets.value.map((item) =>
|
||||
approveQuote({
|
||||
id: item.approvalId,
|
||||
approvalResult: approvalForm.value.approvalResult,
|
||||
approvalOpinion: approvalForm.value.approvalOpinion,
|
||||
remark: approvalForm.value.remark
|
||||
})
|
||||
)
|
||||
);
|
||||
const successCount = results.filter((item) => item.status === 'fulfilled').length;
|
||||
const failedCount = results.length - successCount;
|
||||
|
||||
// 显示成功消息
|
||||
const quoteNo = currentApprovalQuote.value?.quoteNo || '';
|
||||
$modal.msgSuccess(`报价单【${quoteNo}】审批${resultText}成功`);
|
||||
if (targetCount === 1 && successCount === 1 && approvalSkippedCount.value === 0) {
|
||||
const quoteNo = currentApprovalQuote.value?.quoteNo || batchApprovalTargets.value[0]?.quote.quoteNo || '';
|
||||
$modal.msgSuccess(`报价单【${quoteNo}】审批${resultText}成功`);
|
||||
} else {
|
||||
showBatchResultMessage({
|
||||
actionLabel: `审批${resultText}`,
|
||||
successCount,
|
||||
failedCount,
|
||||
skippedCount: approvalSkippedCount.value
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭对话框
|
||||
approvalDialog.visible = false;
|
||||
|
||||
// 刷新列表
|
||||
await getList();
|
||||
if (successCount > 0) {
|
||||
approvalDialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('审批失败:', error);
|
||||
$modal.msgError(error?.msg || `审批${resultText}失败,请稍后重试`);
|
||||
@@ -2739,6 +2908,34 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.batch-action-alert {
|
||||
margin-bottom: 12px;
|
||||
|
||||
&--success {
|
||||
:deep(.el-alert__title),
|
||||
:deep(.el-alert__icon) {
|
||||
color: var(--primary, #10b981);
|
||||
}
|
||||
}
|
||||
|
||||
&--danger {
|
||||
:deep(.el-alert__title),
|
||||
:deep(.el-alert__icon) {
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.batch-approval-quote-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.batch-approval-quote-tag {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.approval-kv-descriptions {
|
||||
:deep(.el-descriptions__label) {
|
||||
font-weight: 500;
|
||||
|
||||
Reference in new issue
Block a user