feat(gxmu): 审核流的审核意见改为必填项
通过/拒绝审核时审核意见为空或仅空白字符将被拦截,提交前去除首尾空格,输入框限制 500 字并显示字数。
This commit is contained in:
@@ -15,7 +15,9 @@
|
||||
<a-tag>{{ moduleName || module || '-' }}</a-tag>
|
||||
<span>业务ID:</span>
|
||||
<a-tag>{{ pk ?? '-' }}</a-tag>
|
||||
<a-button size="small" :loading="loading" @click="loadData">刷新</a-button>
|
||||
<a-button size="small" :loading="loading" @click="loadData"
|
||||
>刷新</a-button
|
||||
>
|
||||
</a-space>
|
||||
|
||||
<div v-if="flowList.length" class="flow-wrap">
|
||||
@@ -23,7 +25,9 @@
|
||||
<a-step v-for="item in flowList" :key="item.id">
|
||||
<template #title>
|
||||
<div class="flow-title">
|
||||
<span>{{ item.user?.realName || item.user?.username || '未分配审核人' }}</span>
|
||||
<span>{{
|
||||
item.user?.realName || item.user?.username || '未分配审核人'
|
||||
}}</span>
|
||||
<a-tag :color="getStatusColor(item.status)">
|
||||
{{ getStatusText(item.status) }}
|
||||
</a-tag>
|
||||
@@ -31,13 +35,27 @@
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="flow-desc">
|
||||
<div>创建时间:{{ toDateString(item.createTime, 'yyyy-MM-dd HH:mm:ss') }}</div>
|
||||
<div
|
||||
>创建时间:{{
|
||||
toDateString(item.createTime, 'yyyy-MM-dd HH:mm:ss')
|
||||
}}</div
|
||||
>
|
||||
<div v-if="item.content">审核意见:{{ item.content }}</div>
|
||||
<a-space v-if="canReview(item)" style="margin-top: 12px">
|
||||
<a-button type="primary" size="small" :loading="submitting" @click="openActionModal('approve', item)">
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="submitting"
|
||||
@click="openActionModal('approve', item)"
|
||||
>
|
||||
通过
|
||||
</a-button>
|
||||
<a-button danger size="small" :loading="submitting" @click="openActionModal('reject', item)">
|
||||
<a-button
|
||||
danger
|
||||
size="small"
|
||||
:loading="submitting"
|
||||
@click="openActionModal('reject', item)"
|
||||
>
|
||||
拒绝
|
||||
</a-button>
|
||||
</a-space>
|
||||
@@ -56,13 +74,20 @@
|
||||
:confirm-loading="submitting"
|
||||
@ok="submitReview"
|
||||
>
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="审核意见(非必填)">
|
||||
<a-form
|
||||
ref="actionFormRef"
|
||||
layout="vertical"
|
||||
:model="actionForm"
|
||||
:rules="actionRules"
|
||||
>
|
||||
<a-form-item label="审核意见" name="content">
|
||||
<a-textarea
|
||||
v-model:value="actionContent"
|
||||
v-model:value="actionForm.content"
|
||||
:rows="4"
|
||||
:maxlength="500"
|
||||
show-count
|
||||
allow-clear
|
||||
placeholder="请输入审核意见"
|
||||
placeholder="请输入审核意见(必填)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -71,163 +96,187 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Empty, message } from 'ant-design-vue';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
import { listReviewList, updateReviewList } from '@/api/gxmu/reviewList';
|
||||
import type { ReviewList } from '@/api/gxmu/reviewList/model';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { Empty, message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
import { listReviewList, updateReviewList } from '@/api/gxmu/reviewList';
|
||||
import type { ReviewList } from '@/api/gxmu/reviewList/model';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
module?: string;
|
||||
moduleName?: string;
|
||||
pk?: number;
|
||||
}>();
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
module?: string;
|
||||
moduleName?: string;
|
||||
pk?: number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
(e: 'done'): void;
|
||||
}>();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const loginUserId = computed(() => Number(userStore.info?.userId || 0));
|
||||
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const flowList = ref<ReviewList[]>([]);
|
||||
const actionVisible = ref(false);
|
||||
const actionType = ref<'approve' | 'reject'>('approve');
|
||||
const actionContent = ref('');
|
||||
const actionTarget = ref<ReviewList | null>(null);
|
||||
const userStore = useUserStore();
|
||||
const loginUserId = computed(() => Number(userStore.info?.userId || 0));
|
||||
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const flowList = ref<ReviewList[]>([]);
|
||||
const actionVisible = ref(false);
|
||||
const actionType = ref<'approve' | 'reject'>('approve');
|
||||
const actionFormRef = ref<FormInstance | null>(null);
|
||||
const actionForm = reactive<{ content: string }>({ content: '' });
|
||||
const actionTarget = ref<ReviewList | null>(null);
|
||||
|
||||
const currentStepIndex = computed(() => {
|
||||
const pendingIndex = flowList.value.findIndex((item) => item.status === 0);
|
||||
if (pendingIndex >= 0) {
|
||||
return pendingIndex;
|
||||
}
|
||||
return Math.max(flowList.value.length - 1, 0);
|
||||
});
|
||||
// 审核意见必填(仅空白字符视为未填写)
|
||||
const actionRules = {
|
||||
content: [
|
||||
{
|
||||
required: true,
|
||||
validator: (_rule: unknown, value: string) => {
|
||||
const text = (value || '').trim();
|
||||
return text
|
||||
? Promise.resolve()
|
||||
: Promise.reject(new Error('请输入审核意见'));
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const getStatusText = (status?: number) => {
|
||||
if (status === 1) return '通过';
|
||||
if (status === 2) return '拒绝';
|
||||
return '待审核';
|
||||
};
|
||||
|
||||
const getStatusColor = (status?: number) => {
|
||||
if (status === 1) return 'success';
|
||||
if (status === 2) return 'error';
|
||||
return 'default';
|
||||
};
|
||||
|
||||
const canReview = (item: ReviewList) => {
|
||||
return item.status === 0 && Number(item.userId || 0) === loginUserId.value;
|
||||
};
|
||||
|
||||
const loadData = () => {
|
||||
if (!props.module || !props.pk) {
|
||||
flowList.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
listReviewList({
|
||||
module: props.module,
|
||||
pk: props.pk
|
||||
})
|
||||
.then((data) => {
|
||||
flowList.value = [...(data || [])].sort(
|
||||
(a, b) => Number(b.id || 0) - Number(a.id || 0)
|
||||
);
|
||||
})
|
||||
.catch((e) => {
|
||||
flowList.value = [];
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const openActionModal = (type: 'approve' | 'reject', item: ReviewList) => {
|
||||
if (!canReview(item)) {
|
||||
message.error('当前记录不是由你审核');
|
||||
return;
|
||||
}
|
||||
actionType.value = type;
|
||||
actionTarget.value = item;
|
||||
actionContent.value = '';
|
||||
actionVisible.value = true;
|
||||
};
|
||||
|
||||
const submitReview = () => {
|
||||
if (!actionTarget.value?.id) {
|
||||
actionVisible.value = false;
|
||||
return;
|
||||
}
|
||||
if (!canReview(actionTarget.value)) {
|
||||
actionVisible.value = false;
|
||||
message.error('当前记录不是由你审核');
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
updateReviewList({
|
||||
id: actionTarget.value.id,
|
||||
status: actionType.value === 'approve' ? 1 : 2,
|
||||
module: actionTarget.value.module,
|
||||
pk: actionTarget.value.pk,
|
||||
sortNumber: actionTarget.value.sortNumber,
|
||||
content: actionContent.value || undefined
|
||||
})
|
||||
.then((msg) => {
|
||||
message.success(msg);
|
||||
actionVisible.value = false;
|
||||
emit('done');
|
||||
loadData();
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
submitting.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
loadData();
|
||||
} else {
|
||||
flowList.value = [];
|
||||
actionVisible.value = false;
|
||||
actionTarget.value = null;
|
||||
actionContent.value = '';
|
||||
const currentStepIndex = computed(() => {
|
||||
const pendingIndex = flowList.value.findIndex((item) => item.status === 0);
|
||||
if (pendingIndex >= 0) {
|
||||
return pendingIndex;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
return Math.max(flowList.value.length - 1, 0);
|
||||
});
|
||||
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const getStatusText = (status?: number) => {
|
||||
if (status === 1) return '通过';
|
||||
if (status === 2) return '拒绝';
|
||||
return '待审核';
|
||||
};
|
||||
|
||||
const getStatusColor = (status?: number) => {
|
||||
if (status === 1) return 'success';
|
||||
if (status === 2) return 'error';
|
||||
return 'default';
|
||||
};
|
||||
|
||||
const canReview = (item: ReviewList) => {
|
||||
return item.status === 0 && Number(item.userId || 0) === loginUserId.value;
|
||||
};
|
||||
|
||||
const loadData = () => {
|
||||
if (!props.module || !props.pk) {
|
||||
flowList.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
listReviewList({
|
||||
module: props.module,
|
||||
pk: props.pk
|
||||
})
|
||||
.then((data) => {
|
||||
flowList.value = [...(data || [])].sort(
|
||||
(a, b) => Number(b.id || 0) - Number(a.id || 0)
|
||||
);
|
||||
})
|
||||
.catch((e) => {
|
||||
flowList.value = [];
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const openActionModal = (type: 'approve' | 'reject', item: ReviewList) => {
|
||||
if (!canReview(item)) {
|
||||
message.error('当前记录不是由你审核');
|
||||
return;
|
||||
}
|
||||
actionType.value = type;
|
||||
actionTarget.value = item;
|
||||
actionForm.content = '';
|
||||
actionVisible.value = true;
|
||||
};
|
||||
|
||||
const submitReview = () => {
|
||||
const target = actionTarget.value;
|
||||
if (!target?.id) {
|
||||
actionVisible.value = false;
|
||||
return;
|
||||
}
|
||||
if (!canReview(target)) {
|
||||
actionVisible.value = false;
|
||||
message.error('当前记录不是由你审核');
|
||||
return;
|
||||
}
|
||||
actionFormRef.value
|
||||
?.validate()
|
||||
.then(() => {
|
||||
submitting.value = true;
|
||||
return updateReviewList({
|
||||
id: target.id,
|
||||
status: actionType.value === 'approve' ? 1 : 2,
|
||||
module: target.module,
|
||||
pk: target.pk,
|
||||
sortNumber: target.sortNumber,
|
||||
content: actionForm.content.trim()
|
||||
})
|
||||
.then((msg) => {
|
||||
message.success(msg);
|
||||
actionVisible.value = false;
|
||||
emit('done');
|
||||
loadData();
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
submitting.value = false;
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
loadData();
|
||||
} else {
|
||||
flowList.value = [];
|
||||
actionVisible.value = false;
|
||||
actionTarget.value = null;
|
||||
actionForm.content = '';
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.flow-wrap {
|
||||
max-height: 560px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.flow-wrap {
|
||||
max-height: 560px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.flow-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.flow-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.flow-desc {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 1.8;
|
||||
}
|
||||
.flow-desc {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 1.8;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user