| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- <template>
- <div class="about main-background main-background-type0">
- <div class="nav-placeholder" />
- <section class="main-section large">
- <div class="content">
- <div class="title left-right gap-2">
- <a-button :icon="h(ArrowLeftOutlined)" @click="router.back()">返回</a-button>
- <h2>自查评估表</h2>
- <div class="w-20"></div>
- </div>
- <a-spin :spinning="loader.loading.value">
- <a-result
- v-if="!currentForm"
- status="info"
- title="您还未填写评估表"
- >
- <template #extra>
- <a-button type="primary" @click="createForm">去填写评估表</a-button>
- </template>
- </a-result>
- <div v-else>
- <a-alert
- v-if="authStore.isAdmin"
- type="info"
- show-icon
- class="mb-4"
- >
- <template #message>
- 本页用于管理员手动修改内容,如果您是审核者,请点击<a href="javascript:void(0)" @click="reviewerView">这里审核</a>。
- </template>
- </a-alert>
- <SelfAssessmentFormDisplay
- ref="blockRef"
- :current-form="(currentForm as SelfAssessmentDetail)"
- :check-item-list="(checkItemList as CheckItemInfo[])"
- :current-form-check-items="(currentFormCheckItems as SelfAssessmentCheckItemAnswer[])"
- :readonly="false"
- />
- <template v-if="isReviewer && currentForm">
-
- <a-image v-if="currentForm.scan" :src="currentForm.scan" class="small-preview" alt="avatar" />
- <a-upload
- class="flex flex-row items-center gap-2 mt-2"
- list-type="picture-card"
- :show-upload-list="false"
- :custom-request="(options: any) => handleScanUpload(options)"
- >
- <UploadOutlined /> 上传
- </a-upload>
- <a-divider />
- <a-button type="primary" block :loading="submitLoading" @click="reviewModalVisible = true">过会后编辑分数和意见</a-button>
- </template>
- <a-divider />
- <a-space direction="vertical" class="w-full" size="middle">
- <a-button type="primary" block :loading="submitLoading" @click="saveForm">保存评估表</a-button>
- <a-button v-if="!authStore.isAdmin && currentForm?.progress === 0" type="primary" block :loading="submitLoading" @click="submitForm">提交审核</a-button>
- <div class="flex flex-row gap-2">
- <a-button block @click="previewForm">预览评估表 PDF</a-button>
- <a-button block :loading="submitLoading" @click="downloadForm">下载评估表 PDF</a-button>
- </div>
- </a-space>
- </div>
- </a-spin>
- </div>
- </section>
- <a-modal
- title="预览评估表 PDF"
- v-model:visible="previewVisible"
- width="80%"
- :footer="null"
- >
- <iframe :src="previewUrl" class="w-full h-full" style="min-height: 60vh"></iframe>
- </a-modal>
- <a-modal
- title="过会后编辑分数和意见"
- v-model:visible="reviewModalVisible"
- :confirm-loading="reviewSubmitting"
- ok-text="提交审核"
- cancel-text="取消"
- @ok="handleReviewSubmit"
- >
- <a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
- <a-form-item label="审核角色" required>
- <a-select v-model:value="reviewForm.groupId" placeholder="请选择审核角色">
- <a-select-option v-for="opt in groupOptions" :key="opt.value" :value="Number(opt.value)">{{ opt.label }}</a-select-option>
- </a-select>
- </a-form-item>
- <a-form-item label="审核操作" required>
- <a-radio-group v-model:value="reviewForm.rejectType">
- <a-radio :value="0">通过</a-radio>
- <a-radio :value="rejectTypeForGroup">退回</a-radio>
- </a-radio-group>
- </a-form-item>
- <a-form-item v-if="reviewForm.rejectType === 0" label="评估级别">
- <a-select v-model:value="reviewForm.opinion" placeholder="请选择评估级别">
- <a-select-option v-for="opt in opinionOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</a-select-option>
- </a-select>
- </a-form-item>
- <a-form-item v-if="reviewForm.rejectType === 0" label="评分">
- <a-input-number v-model:value="reviewForm.points" :min="0" :max="100" class="w-full" placeholder="请输入评分" />
- </a-form-item>
- <a-form-item v-if="reviewForm.rejectType > 0" label="退回意见">
- <a-textarea v-model:value="reviewForm.rejectReason" :rows="3" :maxlength="500" show-count placeholder="请输入退回意见" />
- </a-form-item>
- <a-form-item label="发送短信">
- <a-switch v-model:checked="reviewSendMsg" />
- </a-form-item>
- </a-form>
- </a-modal>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, h, onMounted, ref, watch } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import { message, Modal, type UploadProps } from 'ant-design-vue';
- import { UploadOutlined } from '@ant-design/icons-vue';
- import { StringUtils, waitTimeOut } from '@imengyu/imengyu-utils';
- import { RequestApiError } from '@imengyu/imengyu-utils';
- import { ArrowLeftOutlined } from '@ant-design/icons-vue';
- import { useAuthStore } from '@/stores/auth';
- import AssessmentContentApi, {
- SelfAssessmentDetail,
- CheckItemInfo,
- SelfAssessmentCheckItemAnswer,
- type SelfAssessmentReviewOnlyPayload,
- } from '@/api/collect/AssessmentContent';
- import SelfAssessmentFormDisplay from './components/SelfAssessmentFormDisplay.vue';
- import { useSimpleDataLoader } from '@/composeables/useSimpleDataLoader';
- import { injectAppConfiguration } from '@/api/system/useAppConfiguration.ts';
- import { getFormErrorFieldsMessage } from '@/common/Form.ts';
- import { LEVEL_NATIONAL, LEVEL_PROVINCE, LEVEL_DISTRICT } from '@/api/collect/AssessmentConsts.ts';
- import { useReview } from './composeables/Review.ts';
- import { GROUP_LABELS, GROUP_TO_REVIEW_PROGRESS } from './composeables/GroupData.ts';
- import CommonContent from '@/api/CommonContent.ts';
- function formatErr(e: unknown): string {
- if (e instanceof RequestApiError)
- return e.errorMessage;
- if (e instanceof Error)
- return e.message;
- return String(e);
- }
- const router = useRouter();
- const route = useRoute();
- const authStore = useAuthStore();
- const appConfiguration = injectAppConfiguration();
- const queryId = computed(() => Number(route.query.id) || 0);
- const queryUserId = computed(() => Number(route.query.userId) || 0);
- const currentForm = ref<SelfAssessmentDetail | null>(null);
- const currentFormCheckItems = ref([] as SelfAssessmentCheckItemAnswer[]);
- const checkItemList = ref([] as CheckItemInfo[]);
- const currentProgress = computed(() => currentForm.value?.progress ?? 0);
- const { currentUserGroup, isDistrict, isEndLevel, isProtectUnit } = useReview(currentProgress);
- const isReviewer = computed(() => isDistrict.value || isEndLevel.value || isProtectUnit.value);
- const blockRef = ref<InstanceType<typeof SelfAssessmentFormDisplay> | null>(null);
- const submitLoading = ref(false);
- const levelTitle = computed(() => {
- if (currentForm.value?.level === LEVEL_NATIONAL) return '国家级';
- if (currentForm.value?.level === LEVEL_PROVINCE) return '省级';
- if (currentForm.value?.level === LEVEL_DISTRICT) return '市级';
- return '国家级';
- });
- function loadEditorContent() {
- if (!currentForm.value)
- return;
- if (typeof currentForm.value.content !== 'object' || currentForm.value.content === null)
- currentForm.value.content = {};
- currentForm.value.content.title = `传承人填写${currentForm.value.year}年1月1日至${currentForm.value.year}年12月31日${levelTitle.value}非遗传承人义务履行和传承补助经费使用情况等,不超过1000字,如未履行职责请进行说明。参考提纲如下:`;
- for (let i = 0; i < 8; i++) {
- if (typeof currentForm.value.content[`item${i}`] !== 'string')
- currentForm.value.content[`item${i}`] = '';
- }
- }
- async function loadBasicInfo() {
- const uid = authStore.userInfo?.id ?? authStore.userId;
- const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
- const f = currentForm.value;
- if (!f)
- return;
- f.inheritor = basicInfo.name;
- f.unit = basicInfo.unit;
- f.ichName = basicInfo.ichName;
- f.mobile = basicInfo.mobile;
- f.level = basicInfo.level;
- f.idCard = basicInfo.idCard;
- f.address = basicInfo.address;
-
- }
- async function loadCheckItems() {
- const f = currentForm.value;
- if (!f)
- return;
- const { top } = await AssessmentContentApi.getCheckItems(Number(f.level));
- checkItemList.value = top as CheckItemInfo[];
- currentFormCheckItems.value = [...f.checkItems] as SelfAssessmentCheckItemAnswer[];
- }
- async function createForm() {
- const uid = authStore.userInfo?.id ?? authStore.userId;
- const detail = new SelfAssessmentDetail();
- detail.userId = uid;
- detail.year = appConfiguration.value?.collectFormYear || new Date().getFullYear();
- detail.checkItems = [];
- currentForm.value = detail;
- loadEditorContent();
- await loadBasicInfo();
- await loadCheckItems();
- }
- async function saveForm() {
- const cf = currentForm.value;
- submitLoading.value = true;
- if (!cf) {
- submitLoading.value = false;
- return;
- }
- if (cf.progress > 0) {
- try {
- await blockRef.value?.validate();
- } catch (e: unknown) {
- message.warning('请填写完整信息: ' + getFormErrorFieldsMessage(e));
- submitLoading.value = false;
- return;
- }
- }
- /* if (cf.progress > 0) {
- const confirmed = await new Promise<boolean>((resolve) => {
- Modal.confirm({
- title: '提示',
- content: '您之前已提交审核,修改将导致审核撤回并需要重新审核,是否继续修改?',
- okText: '继续修改',
- cancelText: '取消',
- onOk: () => resolve(true),
- onCancel: () => resolve(false),
- });
- });
- if (!confirmed)
- return;
- } */
- cf.checkItems = currentFormCheckItems.value;
- try {
- await AssessmentContentApi.saveSelfAssessment(cf as SelfAssessmentDetail/* , 0 */);
- message.success('保存评估表成功');
- await waitTimeOut(500);
- await loader.load();
- } catch (error) {
- Modal.error({
- title: '保存评估表失败',
- content: formatErr(error),
- });
- }
- submitLoading.value = false;
- }
- async function submitForm() {
- try {
- await blockRef.value?.validate();
- } catch (e: unknown) {
- message.warning('请填写完整信息: ' + getFormErrorFieldsMessage(e));
- return;
- }
- const confirmed = await new Promise<boolean>((resolve) => {
- Modal.confirm({
- title: '提示',
- content: '您确认要提交审核吗?请确认各项信息填写无误。',
- okText: '确认提交',
- cancelText: '取消',
- onOk: () => resolve(true),
- onCancel: () => resolve(false),
- });
- });
- if (!confirmed)
- return;
- submitLoading.value = true;
- const cf = currentForm.value;
- if (!cf) {
- submitLoading.value = false;
- return;
- }
- cf.checkItems = currentFormCheckItems.value;
- try {
- await AssessmentContentApi.saveSelfAssessment(cf as SelfAssessmentDetail, 1);
- message.success('提交审核成功');
- await waitTimeOut(500);
- await loader.load();
- } catch (error) {
- Modal.error({
- title: '提交审核失败',
- content: formatErr(error),
- });
- }
- submitLoading.value = false;
- }
- const previewVisible = ref(false);
- const previewUrl = ref('');
- function revokePreviewUrl() {
- if (previewUrl.value) {
- URL.revokeObjectURL(previewUrl.value);
- previewUrl.value = '';
- }
- }
- async function previewForm() {
- if (!currentForm.value?.id) {
- message.warning('请先保存评估表后再预览 PDF');
- return;
- }
- try {
- previewVisible.value = true;
- revokePreviewUrl();
- previewUrl.value = await AssessmentContentApi.previewSelfAssessmentPdf(currentForm.value.id);
- } catch (error) {
- Modal.error({
- title: '预览评估表失败',
- content: formatErr(error),
- });
- }
- }
- watch(previewVisible, (visible) => {
- if (!visible) {
- revokePreviewUrl();
- }
- });
- async function downloadForm() {
- if (!currentForm.value?.id) {
- message.warning('请先保存评估表后再下载 PDF');
- return;
- }
- try {
- await AssessmentContentApi.downloadSelfAssessmentPdf(currentForm.value.id);
- message.success('已开始下载');
- } catch (error) {
- Modal.error({
- title: '下载评估表失败',
- content: formatErr(error),
- });
- }
- }
- function reviewerView() {
- router.push({
- name: 'CollectEvaluationFormReview',
- query: {
- id: queryId.value,
- userId: queryUserId.value,
- ...(currentForm.value?.progress != null && currentForm.value?.progress !== undefined ? { progress: String(currentForm.value?.progress) } : {}),
- },
- });
- }
- const reviewModalVisible = ref(false);
- const reviewSubmitting = ref(false);
- const reviewSendMsg = ref(false);
- const reviewForm = ref({
- groupId: Number(currentUserGroup.value?.id ?? 0),
- rejectType: 0,
- opinion: undefined as number | undefined,
- points: undefined as number | undefined,
- rejectReason: '',
- });
- const groupOptions = computed(() => {
- return Object.entries(GROUP_LABELS)
- .filter(([key]) => Number(key) > 0)
- .map(([key, label]) => ({ value: key, label }));
- });
- const opinionOptions = [
- { label: '优秀', value: 1 },
- { label: '合格', value: 2 },
- { label: '不合格', value: 3 },
- { label: '丧失传承能力', value: 4 },
- { label: '取消资格', value: 5 },
- ];
- const rejectTypeForGroup = computed(() => {
- const gid = Number(reviewForm.value.groupId);
- return GROUP_TO_REVIEW_PROGRESS[gid]?.rejectTarget || 0;
- });
- async function handleScanUpload(options: Parameters<NonNullable<UploadProps['customRequest']>>[0]) {
- const { file, onSuccess, onError } = options;
- try {
- const res = await CommonContent.uploadSmallFile(file as File, 'image', 'file');
- onSuccess?.({
- url: res.fullurl,
- name: StringUtils.path.getFileName(res.fullurl),
- });
- currentForm.value!.scan = res.fullurl;
- } catch (err) {
- onError?.(err as Error);
- message.error(err instanceof Error ? err.message : String(err));
- }
- }
- async function handleReviewSubmit() {
- const cf = currentForm.value;
- if (!cf?.id) {
- message.warning('请先保存评估表');
- return;
- }
- if (!reviewForm.value.groupId) {
- message.warning('请选择审核角色');
- return;
- }
- reviewSubmitting.value = true;
- try {
- const payload: SelfAssessmentReviewOnlyPayload = {
- checkId: cf.id,
- groupId: reviewForm.value.groupId,
- rejectType: reviewForm.value.rejectType,
- rejectReason: reviewForm.value.rejectType > 0 ? reviewForm.value.rejectReason : undefined,
- opinion: reviewForm.value.rejectType === 0 ? reviewForm.value.opinion : undefined,
- points: reviewForm.value.rejectType === 0 ? reviewForm.value.points : undefined,
- sendMsg: reviewSendMsg.value ? 1 : 0,
- };
- await AssessmentContentApi.reviewOnly(payload);
- message.success('审核提交成功');
- reviewModalVisible.value = false;
- reviewForm.value = { groupId: currentUserGroup.value?.id ?? 0, rejectType: 0, opinion: undefined, points: undefined, rejectReason: '' };
- reviewSendMsg.value = false;
- await loader.load();
- } catch (error) {
- Modal.error({
- title: '审核提交失败',
- content: formatErr(error),
- });
- }
- reviewSubmitting.value = false;
- }
- const loader = useSimpleDataLoader(async () => {
- if (queryId.value > 0) {
- const detail = await AssessmentContentApi.getSelfAssessmentDetail(queryId.value, queryUserId.value || undefined);
- currentForm.value = detail;
- loadEditorContent();
- await loadCheckItems();
- return currentForm.value;
- }
- const uid = authStore.userInfo?.id ?? authStore.userId;
- const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
- if (basicInfo.checkId > 0) {
- const detail = await AssessmentContentApi.getSelfAssessmentDetail(basicInfo.checkId, uid);
- currentForm.value = detail;
- console.log(currentForm.value);
- loadEditorContent();
- await loadCheckItems();
- } else {
- currentForm.value = null;
- }
- return currentForm.value;
- }, {
- immediate: false,
- });
- onMounted(() => {
- loader.load();
- });
- watch(
- () => [queryId.value, queryUserId.value],
- () => {
- loader.load();
- },
- );
- </script>
- <style scoped>
- .total-points {
- font-size: 1.75rem;
- color: #315816;
- font-weight: 600;
- }
- </style>
|