evaluation-form.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <template>
  2. <CommonRoot>
  3. <FlexCol padding="space.lg">
  4. <SimplePageContentLoader :loader="loader">
  5. <template v-if="loader.isFinished.value">
  6. <Result
  7. v-if="!currentForm"
  8. status="info"
  9. title="您还未填写评估表"
  10. >
  11. <Height :height="30" />
  12. <Button type="primary" @click="createForm">去填写评估表</Button>
  13. </Result>
  14. <FlexCol v-else gap="gap.lg">
  15. <SelfAssessmentFormDisplay
  16. ref="blockRef"
  17. :current-form="(currentForm as SelfAssessmentDetail)"
  18. :form-options="formOptions"
  19. :check-item-list="(checkItemList as CheckItemInfo[])"
  20. :current-form-check-items="(currentFormCheckItems as SelfAssessmentCheckItemAnswer[])"
  21. :readonly="false"
  22. />
  23. <Divider />
  24. <FlexCol gap="gap.lg">
  25. <FlexCol v-for="(title, secIdx) in externalReviewSectionTitles" :key="secIdx" gap="gap.sm">
  26. <Text bold :text="title.title" />
  27. <Field v-model="title.suggestion" :disabled="title.disabled" placeholder="(待终审填写)" />
  28. <FlexRow wrap align="center" gap="gap.md">
  29. <CheckBox
  30. v-for="(label, i) in externalReviewScoreRow1"
  31. :key="`${secIdx}-r1-${i}`"
  32. disabled
  33. :model-value="false"
  34. :text="label"
  35. :check-size="28"
  36. />
  37. </FlexRow>
  38. <FlexRow wrap align="center" gap="gap.md">
  39. <CheckBox
  40. v-for="(label, i) in externalReviewScoreRow2"
  41. :key="`${secIdx}-r2-${i}`"
  42. disabled
  43. :model-value="false"
  44. :text="label"
  45. :check-size="28"
  46. />
  47. </FlexRow>
  48. <FlexCol align="flex-end">
  49. <Text color="text.second" text="填写单位(盖章)" />
  50. <Text color="text.second" text="年 月 日" />
  51. </FlexCol>
  52. </FlexCol>
  53. </FlexCol>
  54. <Divider />
  55. <H3>佐证资料上传</H3>
  56. <Result v-if="!currentForm?.id" status="info" title="请先保存评估表后再上传佐证资料" />
  57. <Uploader
  58. v-else
  59. ref="uploaderRef"
  60. :upload="assessmentAnnexUpload"
  61. :max-upload-count="100"
  62. :max-file-size="20 * 1024 * 1024"
  63. :group-type="true"
  64. chooseType="file"
  65. list-type="list"
  66. />
  67. <Height :height="30" />
  68. <Divider />
  69. <Button type="primary" block :loading="submitLoading" @click="saveForm">保存评估表</Button>
  70. <Button :loading="submitLoading" @click="downloadForm">下载评估表PDF</Button>
  71. </FlexCol>
  72. </template>
  73. </SimplePageContentLoader>
  74. <XBarSpace />
  75. </FlexCol>
  76. </CommonRoot>
  77. </template>
  78. <script setup lang="ts">
  79. import { computed, ref } from 'vue';
  80. import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
  81. import { useAuthStore } from '@/store/auth';
  82. import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
  83. import { useAliOssUploadCo } from '@/common/components/upload/AliOssUploadCo';
  84. import { assertNotNull, formatError, waitTimeOut } from '@imengyu/imengyu-utils';
  85. import { toast, alert } from '@/components/dialog/CommonRoot';
  86. import AssessmentContentApi, {
  87. SelfAssessmentDetail,
  88. CheckItemInfo,
  89. SelfAssessmentCheckItemAnswer,
  90. getCheckAnnexType,
  91. } from '@/api/collect/AssessmentContent';
  92. import CommonRoot from '@/components/dialog/CommonRoot.vue';
  93. import Button from '@/components/basic/Button.vue';
  94. import Result from '@/components/feedback/Result.vue';
  95. import FlexCol from '@/components/layout/FlexCol.vue';
  96. import Height from '@/components/layout/space/Height.vue';
  97. import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
  98. import H3 from '@/components/typography/H3.vue';
  99. import FlexRow from '@/components/layout/FlexRow.vue';
  100. import Text from '@/components/basic/Text.vue';
  101. import XBarSpace from '@/components/layout/space/XBarSpace.vue';
  102. import Uploader, { type UploaderInstance } from '@/components/form/Uploader.vue';
  103. import { getMimeType } from '@/common/components/upload/mimes';
  104. import Divider from '@/components/display/Divider.vue';
  105. import { stringUrlToUploaderItem } from '@/components/form/Uploader';
  106. import CheckBox from '@/components/form/CheckBox.vue';
  107. import Field from '@/components/form/Field.vue';
  108. import SelfAssessmentFormDisplay from './components/SelfAssessmentFormDisplay.vue';
  109. import { buildSelfAssessmentFormOptions } from './evaluationFormOptions';
  110. import { useImageSimpleUploadCo } from '@/common/components/upload/ImageUploadCo';
  111. /** 评估表下方展示用:各级审核意见(不接数据、禁用) */
  112. const externalReviewSectionTitles = ref([
  113. { title: '1. 项目保护单位意见', suggestion: '', disabled: false },
  114. { title: '2. 县(区)文旅部门审核意见', suggestion: '', disabled: true },
  115. { title: '3. 设区市文旅部门、省非遗中心审核意见', suggestion: '', disabled: true },
  116. ]);
  117. const externalReviewScoreRow1 = ['优秀', '合格', '不合格'] as const;
  118. const externalReviewScoreRow2 = ['丧失传承能力', '取消资格'] as const;
  119. let loaded = false;
  120. const { querys } = useLoadQuerys({
  121. id: 0,
  122. userId: 0,
  123. }, () => {
  124. if (loaded)
  125. return;
  126. loaded = true;
  127. loader.load();
  128. });
  129. const currentForm = ref<SelfAssessmentDetail | null>(null);
  130. const currentFormCheckItems = ref<SelfAssessmentCheckItemAnswer[]>([]);
  131. const authStore = useAuthStore();
  132. const assessmentAnnexUpload = useAliOssUploadCo('assessment/annex', async (res, item) => {
  133. assertNotNull(currentForm.value, 'currentForm is null');
  134. const mimetype = getMimeType(item.filePath);
  135. await AssessmentContentApi.saveAnnex({
  136. name: item.name,
  137. formId: currentForm.value.id,
  138. url: res,
  139. type: getCheckAnnexType(mimetype),
  140. mimetype: mimetype,
  141. fileSize: item.size
  142. ? Math.max(1, Math.ceil(item.size / 1024))
  143. : undefined,
  144. });
  145. });
  146. const currentYear = new Date().getFullYear();
  147. const uploaderRef = ref<UploaderInstance | null>(null);
  148. const blockRef = ref<InstanceType<typeof SelfAssessmentFormDisplay> | null>(null);
  149. const signUploadCo = useImageSimpleUploadCo();
  150. const formOptions = buildSelfAssessmentFormOptions(signUploadCo);
  151. const checkItemList = ref<CheckItemInfo[]>([]);
  152. const levelTitle = computed(() => {
  153. if (currentForm.value?.level === 23) return '国家级';
  154. if (currentForm.value?.level === 24) return '省级';
  155. if (currentForm.value?.level === 25) return '市级';
  156. return '国家级';
  157. });
  158. function loadEditorContent() {
  159. if (!currentForm.value)
  160. return;
  161. if (typeof currentForm.value.content !== 'object' || currentForm.value.content === null) {
  162. currentForm.value!.content = {};
  163. }
  164. currentForm.value!.content.title = `传承人填写${currentYear}年1月1日至${currentYear}年12月31日${levelTitle.value}非遗传承人义务履行和传承补助经费使用情况等,不超过1000字,如未履行职责请进行说明。参考提纲如下:`;
  165. for (let i = 0; i < 8; i++) {
  166. if (typeof currentForm.value.content[`item${i}`] !== 'string') {
  167. currentForm.value.content[`item${i}`] = '';
  168. }
  169. }
  170. }
  171. async function loadBasicInfo() {
  172. const basicInfo = await AssessmentContentApi.getInheritorBasic(authStore.userInfo?.id);
  173. assertNotNull(currentForm.value, 'currentForm is null');
  174. currentForm.value.inheritor = basicInfo.name;
  175. currentForm.value.unit = basicInfo.unit;
  176. currentForm.value.ichName = basicInfo.ichName;
  177. currentForm.value.mobile = basicInfo.mobile;
  178. currentForm.value.level = basicInfo.level;
  179. currentForm.value.idCard = basicInfo.idCard;
  180. currentForm.value.address = basicInfo.address;
  181. }
  182. async function loadCheckItems() {
  183. assertNotNull(currentForm.value, 'currentForm is null');
  184. const { top } = await AssessmentContentApi.getCheckItems(Number(currentForm.value.level));
  185. checkItemList.value = top;
  186. currentFormCheckItems.value = currentForm.value.checkItems.concat();
  187. }
  188. async function loadAnnexList() {
  189. assertNotNull(currentForm.value, 'currentForm is null');
  190. console.log('awardTime', currentForm.value.awardTime);
  191. const annexList = await AssessmentContentApi.getAnnexList(currentForm.value.id);
  192. setTimeout(() => {
  193. if (uploaderRef.value) {
  194. uploaderRef.value.setList(annexList.data.map((item) => stringUrlToUploaderItem(item.url, item.name)));
  195. }
  196. }, 1000);
  197. }
  198. const submitLoading = ref(false);
  199. async function createForm() {
  200. const detail = new SelfAssessmentDetail();
  201. detail.userId = authStore.userInfo!.id;
  202. detail.year = new Date().getFullYear();
  203. detail.checkItems = [];
  204. currentForm.value = detail;
  205. loadEditorContent();
  206. await loadBasicInfo();
  207. await loadCheckItems();
  208. }
  209. async function saveForm() {
  210. const detail = currentForm.value;
  211. try {
  212. await blockRef.value?.validate();
  213. } catch (error) {
  214. toast('请填写完整信息');
  215. return;
  216. }
  217. submitLoading.value = true;
  218. currentForm.value!.checkItems = currentFormCheckItems.value;
  219. try {
  220. assertNotNull(detail, 'currentForm is null');
  221. await AssessmentContentApi.saveSelfAssessment(detail as SelfAssessmentDetail);
  222. toast('保存评估表成功');
  223. await waitTimeOut(1000);
  224. await loader.reload();
  225. } catch (error) {
  226. alert({
  227. title: '保存评估表失败',
  228. content: formatError(error),
  229. });
  230. }
  231. submitLoading.value = false;
  232. }
  233. async function downloadForm() {
  234. if (!currentForm.value?.id) {
  235. toast('请先保存评估表后再下载PDF');
  236. return;
  237. }
  238. try {
  239. assertNotNull(currentForm.value, 'currentForm is null');
  240. const pdfPath = await AssessmentContentApi.downloadSelfAssessmentPdf(currentForm.value.id);
  241. uni.openDocument({
  242. filePath: pdfPath,
  243. fileType: 'pdf',
  244. showMenu: true,
  245. });
  246. } catch (error) {
  247. alert({
  248. title: '下载评估表失败',
  249. content: formatError(error),
  250. });
  251. }
  252. }
  253. const loader = useSimpleDataLoader(async () => {
  254. await waitTimeOut(1000);
  255. if (querys.value.id > 0) {
  256. const detail = await AssessmentContentApi.getSelfAssessmentDetail(querys.value.id, querys.value.userId);
  257. currentForm.value = detail;
  258. loadEditorContent();
  259. await loadCheckItems();
  260. await loadAnnexList();
  261. return;
  262. }
  263. const basicInfo = await AssessmentContentApi.getInheritorBasic(authStore.userInfo?.id);
  264. if (basicInfo.checkId > 0) {
  265. const detail = await AssessmentContentApi.getSelfAssessmentDetail(
  266. basicInfo.checkId,
  267. authStore.userInfo?.id
  268. );
  269. currentForm.value = detail;
  270. loadEditorContent();
  271. await loadAnnexList();
  272. await loadCheckItems();
  273. } else {
  274. currentForm.value = null;
  275. }
  276. return currentForm.value;
  277. }, false);
  278. </script>