evaluation-form.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <template>
  2. <div class="about main-background main-background-type0">
  3. <div class="nav-placeholder" />
  4. <section class="main-section large">
  5. <div class="content">
  6. <div class="title left-right">
  7. <a-button :icon="h(ArrowLeftOutlined)" @click="router.back()">返回</a-button>
  8. <h2>自查评估表</h2>
  9. <div class="w-20"></div>
  10. </div>
  11. <a-spin :spinning="loader.loading.value">
  12. <a-result
  13. v-if="!currentForm"
  14. status="info"
  15. title="您还未填写评估表"
  16. >
  17. <template #extra>
  18. <a-button type="primary" @click="createForm">去填写评估表</a-button>
  19. </template>
  20. </a-result>
  21. <div v-else>
  22. <SelfAssessmentFormDisplay
  23. ref="blockRef"
  24. :current-form="(currentForm as SelfAssessmentDetail)"
  25. :check-item-list="(checkItemList as CheckItemInfo[])"
  26. :current-form-check-items="(currentFormCheckItems as SelfAssessmentCheckItemAnswer[])"
  27. :readonly="false"
  28. />
  29. <a-divider />
  30. <a-typography-title :level="4">各级审核意见(待终审填写)</a-typography-title>
  31. <div v-for="(sec, secIdx) in externalReviewSectionTitles" :key="secIdx" class="mb-6">
  32. <a-typography-text strong>{{ sec.title }}</a-typography-text>
  33. <a-input v-model:value="sec.suggestion" :disabled="sec.disabled" placeholder="(待终审填写)" class="mt-2" />
  34. <a-space class="mt-2" wrap>
  35. <a-checkbox v-for="(label, i) in externalReviewScoreRow1" :key="`r1-${i}`" disabled :checked="false">{{ label }}</a-checkbox>
  36. </a-space>
  37. <a-space class="mt-2" wrap>
  38. <a-checkbox v-for="(label, i) in externalReviewScoreRow2" :key="`r2-${i}`" disabled :checked="false">{{ label }}</a-checkbox>
  39. </a-space>
  40. <div class="text-end text-secondary mt-2 text-sm">填写单位(盖章) 年 月 日</div>
  41. </div>
  42. <a-divider />
  43. <a-space direction="vertical" class="w-full" size="middle">
  44. <a-button type="primary" block :loading="submitLoading" @click="saveForm">保存评估表</a-button>
  45. <a-button type="primary" block :loading="submitLoading" @click="submitForm">提交审核</a-button>
  46. <a-button block :loading="submitLoading" @click="downloadForm">下载评估表 PDF</a-button>
  47. </a-space>
  48. </div>
  49. </a-spin>
  50. </div>
  51. </section>
  52. </div>
  53. </template>
  54. <script setup lang="ts">
  55. import { computed, h, onMounted, ref, watch } from 'vue';
  56. import { useRoute, useRouter } from 'vue-router';
  57. import { message, Modal } from 'ant-design-vue';
  58. import { waitTimeOut } from '@imengyu/imengyu-utils';
  59. import { RequestApiError } from '@imengyu/imengyu-utils';
  60. import { ArrowLeftOutlined } from '@ant-design/icons-vue';
  61. import { useAuthStore } from '@/stores/auth';
  62. import AssessmentContentApi, {
  63. SelfAssessmentDetail,
  64. CheckItemInfo,
  65. SelfAssessmentCheckItemAnswer,
  66. } from '@/api/collect/AssessmentContent';
  67. import SelfAssessmentFormDisplay from './components/SelfAssessmentFormDisplay.vue';
  68. import { useSimpleDataLoader } from '@/composeables/useSimpleDataLoader';
  69. import { injectAppConfiguration } from '@/api/system/useAppConfiguration.ts';
  70. function formatErr(e: unknown): string {
  71. if (e instanceof RequestApiError)
  72. return e.errorMessage;
  73. if (e instanceof Error)
  74. return e.message;
  75. return String(e);
  76. }
  77. const router = useRouter();
  78. const route = useRoute();
  79. const authStore = useAuthStore();
  80. const appConfiguration = injectAppConfiguration();
  81. const queryId = computed(() => Number(route.query.id) || 0);
  82. const queryUserId = computed(() => Number(route.query.userId) || 0);
  83. const currentForm = ref<SelfAssessmentDetail | null>(null);
  84. const currentFormCheckItems = ref([] as SelfAssessmentCheckItemAnswer[]);
  85. const checkItemList = ref([] as CheckItemInfo[]);
  86. const blockRef = ref<InstanceType<typeof SelfAssessmentFormDisplay> | null>(null);
  87. const submitLoading = ref(false);
  88. const externalReviewSectionTitles = ref([
  89. { title: '1. 项目保护单位意见', suggestion: '', disabled: false },
  90. { title: '2. 县(区)文旅部门审核意见', suggestion: '', disabled: true },
  91. { title: '3. 设区市文旅部门、省非遗中心审核意见', suggestion: '', disabled: true },
  92. ]);
  93. const externalReviewScoreRow1 = ['优秀', '合格', '不合格'] as const;
  94. const externalReviewScoreRow2 = ['丧失传承能力', '取消资格'] as const;
  95. const levelTitle = computed(() => {
  96. if (currentForm.value?.level === 23) return '国家级';
  97. if (currentForm.value?.level === 24) return '省级';
  98. if (currentForm.value?.level === 25) return '市级';
  99. return '国家级';
  100. });
  101. function loadEditorContent() {
  102. if (!currentForm.value)
  103. return;
  104. if (typeof currentForm.value.content !== 'object' || currentForm.value.content === null)
  105. currentForm.value.content = {};
  106. currentForm.value.content.title = `传承人填写${currentForm.value.year}年1月1日至${currentForm.value.year}年12月31日${levelTitle.value}非遗传承人义务履行和传承补助经费使用情况等,不超过1000字,如未履行职责请进行说明。参考提纲如下:`;
  107. for (let i = 0; i < 8; i++) {
  108. if (typeof currentForm.value.content[`item${i}`] !== 'string')
  109. currentForm.value.content[`item${i}`] = '';
  110. }
  111. }
  112. async function loadBasicInfo() {
  113. const uid = authStore.userInfo?.id ?? authStore.userId;
  114. const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
  115. const f = currentForm.value;
  116. if (!f)
  117. return;
  118. f.inheritor = basicInfo.name;
  119. f.unit = basicInfo.unit;
  120. f.ichName = basicInfo.ichName;
  121. f.mobile = basicInfo.mobile;
  122. f.level = basicInfo.level;
  123. f.idCard = basicInfo.idCard;
  124. f.address = basicInfo.address;
  125. }
  126. async function loadCheckItems() {
  127. const f = currentForm.value;
  128. if (!f)
  129. return;
  130. const { top } = await AssessmentContentApi.getCheckItems(Number(f.level));
  131. checkItemList.value = top as CheckItemInfo[];
  132. currentFormCheckItems.value = [...f.checkItems] as SelfAssessmentCheckItemAnswer[];
  133. }
  134. async function createForm() {
  135. const uid = authStore.userInfo?.id ?? authStore.userId;
  136. const detail = new SelfAssessmentDetail();
  137. detail.userId = uid;
  138. detail.year = appConfiguration.value?.collectFormYear || new Date().getFullYear();
  139. detail.checkItems = [];
  140. currentForm.value = detail;
  141. loadEditorContent();
  142. await loadBasicInfo();
  143. await loadCheckItems();
  144. }
  145. async function saveForm() {
  146. try {
  147. await blockRef.value?.validate();
  148. } catch (e: unknown) {
  149. if (e && typeof e === 'object' && 'errorFields' in (e as object))
  150. message.warning('请填写完整信息');
  151. else if (e instanceof Error)
  152. message.warning(e.message);
  153. else
  154. message.warning('请填写完整信息');
  155. return;
  156. }
  157. submitLoading.value = true;
  158. const cf = currentForm.value;
  159. if (!cf) {
  160. submitLoading.value = false;
  161. return;
  162. }
  163. cf.checkItems = currentFormCheckItems.value;
  164. try {
  165. await AssessmentContentApi.saveSelfAssessment(cf as SelfAssessmentDetail);
  166. message.success('保存评估表成功');
  167. await waitTimeOut(500);
  168. await loader.load();
  169. } catch (error) {
  170. Modal.error({
  171. title: '保存评估表失败',
  172. content: formatErr(error),
  173. });
  174. }
  175. submitLoading.value = false;
  176. }
  177. async function submitForm() {
  178. try {
  179. await blockRef.value?.validate();
  180. } catch (e: unknown) {
  181. if (e && typeof e === 'object' && 'errorFields' in (e as object))
  182. message.warning('请填写完整信息');
  183. else if (e instanceof Error)
  184. message.warning(e.message);
  185. else
  186. message.warning('请填写完整信息');
  187. return;
  188. }
  189. submitLoading.value = true;
  190. const cf = currentForm.value;
  191. if (!cf) {
  192. submitLoading.value = false;
  193. return;
  194. }
  195. cf.checkItems = currentFormCheckItems.value;
  196. try {
  197. await AssessmentContentApi.saveSelfAssessment(cf as SelfAssessmentDetail, 1);
  198. message.success('提交审核成功');
  199. await waitTimeOut(500);
  200. await loader.load();
  201. } catch (error) {
  202. Modal.error({
  203. title: '提交审核失败',
  204. content: formatErr(error),
  205. });
  206. }
  207. submitLoading.value = false;
  208. }
  209. async function downloadForm() {
  210. if (!currentForm.value?.id) {
  211. message.warning('请先保存评估表后再下载 PDF');
  212. return;
  213. }
  214. try {
  215. await AssessmentContentApi.downloadSelfAssessmentPdf(currentForm.value.id);
  216. message.success('已开始下载');
  217. } catch (error) {
  218. Modal.error({
  219. title: '下载评估表失败',
  220. content: formatErr(error),
  221. });
  222. }
  223. }
  224. const loader = useSimpleDataLoader(async () => {
  225. if (queryId.value > 0) {
  226. const detail = await AssessmentContentApi.getSelfAssessmentDetail(queryId.value, queryUserId.value || undefined);
  227. currentForm.value = detail;
  228. loadEditorContent();
  229. await loadCheckItems();
  230. return currentForm.value;
  231. }
  232. const uid = authStore.userInfo?.id ?? authStore.userId;
  233. const basicInfo = await AssessmentContentApi.getInheritorBasic(uid);
  234. if (basicInfo.checkId > 0) {
  235. const detail = await AssessmentContentApi.getSelfAssessmentDetail(basicInfo.checkId, uid);
  236. currentForm.value = detail;
  237. console.log(currentForm.value);
  238. loadEditorContent();
  239. await loadCheckItems();
  240. } else {
  241. currentForm.value = null;
  242. }
  243. return currentForm.value;
  244. }, {
  245. immediate: false,
  246. });
  247. onMounted(() => {
  248. loader.load();
  249. });
  250. watch(
  251. () => [queryId.value, queryUserId.value],
  252. () => {
  253. loader.load();
  254. },
  255. );
  256. </script>
  257. <style scoped>
  258. .total-points {
  259. font-size: 1.75rem;
  260. color: #315816;
  261. font-weight: 600;
  262. }
  263. </style>