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