evaluation-form.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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.md">
  15. <DynamicForm
  16. ref="form1Ref"
  17. :model="currentForm"
  18. :options="formOptions"
  19. />
  20. <Button @click="navTo('/pages/test/test')">test</Button>
  21. <H3>自查项目选择</H3>
  22. <FlexCol v-if="checkItemList && checkItemList.length > 0" gap="gap.md">
  23. <FlexCol v-for="(item, index) in checkItemList" :key="item.id" gap="gap.md">
  24. <Text fontConfig="subTitleText" :text="`${index + 1}. ${item.name}`" />
  25. <FlexCol v-if="item.checkType == 2" gap="gap.sm">
  26. <FlexRow v-for="child in item.children" :key="child.id" justify="space-between">
  27. <CheckBox
  28. :text="`${child.name} (${child.points}分)`"
  29. :modelValue="hasCheckedItem(child.id)"
  30. @update:modelValue="setCheckedItem(item as CheckItemInfo, child as CheckItemInfo, $event)"
  31. />
  32. <Stepper
  33. v-if="hasCheckedItem(child.id)"
  34. :min="0"
  35. :max="20"
  36. :step="1"
  37. :modelValue="getCheckedItemCount(child.id) ?? 0"
  38. @update:modelValue="setCheckedItem(item as CheckItemInfo, child as CheckItemInfo, $event)"
  39. />
  40. <view v-else></view>
  41. </FlexRow>
  42. </FlexCol>
  43. <FlexCol v-else gap="gap.sm">
  44. <CheckBox
  45. v-for="child in item.children" :key="child.id"
  46. :text="`${child.name} (${child.points}分)`"
  47. :modelValue="hasCheckedItem(child.id)"
  48. @update:modelValue="setCheckedItem(item as CheckItemInfo, child as CheckItemInfo, $event)"
  49. />
  50. </FlexCol>
  51. </FlexCol>
  52. </FlexCol>
  53. <DynamicForm
  54. ref="form3Ref"
  55. :model="currentForm"
  56. :options="formOptionsEnd"
  57. />
  58. <FlexRow align="center" justify="space-between">
  59. <H3>自评总分</H3>
  60. <Text fontSize="44rpx" fontFamily="HUNdin1451" :text="`${totalPoints}分`" />
  61. </FlexRow>
  62. <Button type="primary" block :loading="submitLoading" @click="saveForm">保存评估表</Button>
  63. <Button :loading="submitLoading" @click="downloadForm">下载评估表PDF</Button>
  64. </FlexCol>
  65. </template>
  66. </SimplePageContentLoader>
  67. <XBarSpace />
  68. </FlexCol>
  69. </CommonRoot>
  70. </template>
  71. <script setup lang="ts">
  72. import { computed, ref } from 'vue';
  73. import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
  74. import { useAuthStore } from '@/store/auth';
  75. import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
  76. import { ArrayUtils, assertNotNull, formatError, waitTimeOut } from '@imengyu/imengyu-utils';
  77. import { toast, alert } from '@/components/dialog/CommonRoot';
  78. import AssessmentContentApi, {
  79. SelfAssessmentDetail,
  80. CheckItemInfo,
  81. SelfAssessmentCheckItemAnswer,
  82. } from '@/api/collect/AssessmentContent';
  83. import CommonRoot from '@/components/dialog/CommonRoot.vue';
  84. import Button from '@/components/basic/Button.vue';
  85. import Result from '@/components/feedback/Result.vue';
  86. import FlexCol from '@/components/layout/FlexCol.vue';
  87. import Height from '@/components/layout/space/Height.vue';
  88. import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
  89. import DynamicForm from '@/components/dynamic/DynamicForm.vue';
  90. import H3 from '@/components/typography/H3.vue';
  91. import FlexRow from '@/components/layout/FlexRow.vue';
  92. import Text from '@/components/basic/Text.vue';
  93. import XBarSpace from '@/components/layout/space/XBarSpace.vue';
  94. import CheckBox from '@/components/form/CheckBox.vue';
  95. import Stepper from '@/components/form/Stepper.vue';
  96. import type { IDynamicFormOptions, IDynamicFormRef } from '@/components/dynamic';
  97. import type { RadioValueProps } from '@/components/dynamic/wrappers/RadioValue';
  98. import type { FieldProps } from '@/components/form/Field.vue';
  99. import type { SignatureFieldProps } from '@/components/form/SignatureField.vue';
  100. import { useImageSimpleUploadCo } from '@/common/components/upload/ImageUploadCo';
  101. import { navTo } from '@/components/utils/PageAction';
  102. let loaded = false;
  103. const { querys } = useLoadQuerys({
  104. id: 0,
  105. }, () => {
  106. if (loaded)
  107. return;
  108. loaded = true;
  109. loader.load();
  110. });
  111. const currentForm = ref<SelfAssessmentDetail | null>(null);
  112. const currentFormCheckItems = ref<SelfAssessmentCheckItemAnswer[]>([]);
  113. const authStore = useAuthStore();
  114. const form1Ref = ref<IDynamicFormRef | null>(null);
  115. const form3Ref = ref<IDynamicFormRef | null>(null);
  116. const formOptions = ref<IDynamicFormOptions>({
  117. formAdditionaProps: {
  118. labelFlex: 4,
  119. inputFlex: 8,
  120. },
  121. formItems: [
  122. {
  123. type: 'flat-group',
  124. label: '传承人自查评估',
  125. name: 'selfAssessmentGroup',
  126. childrenColProps: { span: 24 },
  127. children: [
  128. {
  129. label: '传承人名称',
  130. name: 'inheritor',
  131. type: 'text',
  132. additionalProps: { placeholder: '请输入传承人名称' },
  133. },
  134. {
  135. label: '项目保护单位',
  136. name: 'unit',
  137. type: 'text',
  138. additionalProps: { placeholder: '请输入项目保护单位' },
  139. },
  140. {
  141. label: '项目名称',
  142. name: 'ichName',
  143. type: 'text',
  144. additionalProps: { placeholder: '请输入项目名称' },
  145. },
  146. {
  147. label: '联系电话',
  148. name: 'mobile',
  149. type: 'text',
  150. additionalProps: { placeholder: '请输入联系电话' },
  151. },
  152. {
  153. label: '身份证号',
  154. name: 'idCard',
  155. type: 'text',
  156. additionalProps: { placeholder: '请输入身份证号' },
  157. },
  158. {
  159. label: '级别',
  160. name: 'level',
  161. type: 'select-id',
  162. additionalProps: {
  163. placeholder: '请选择级别',
  164. loadData: async () => [
  165. { text: '国家级', value: 23 },
  166. { text: '省级', value: 24 },
  167. { text: '市级', value: 25 },
  168. ],
  169. },
  170. },
  171. {
  172. label: '家庭住址',
  173. name: 'address',
  174. type: 'text',
  175. additionalProps: { placeholder: '请输入家庭住址' },
  176. },
  177. {
  178. label: '自评报告',
  179. name: 'content',
  180. type: 'richtext',
  181. additionalProps: { placeholder: '请填写自评报告' },
  182. },
  183. ],
  184. },
  185. ],
  186. formRules: {
  187. inheritor: [{ required: true, message: '请输入传承人名称' }],
  188. unit: [{ required: true, message: '请输入项目保护单位' }],
  189. ichName: [{ required: true, message: '请输入项目名称' }],
  190. mobile: [{ required: true, message: '请输入联系电话' }],
  191. idCard: [{ required: true, message: '请输入身份证号' }],
  192. level: [{ required: true, message: '请选择级别' }],
  193. address: [{ required: true, message: '请输入家庭住址' }],
  194. content: [{ required: true, message: '请填写自评报告' }],
  195. self: [{ required: true, message: '请选择自我评估' }],
  196. },
  197. });
  198. const formOptionsEnd = ref<IDynamicFormOptions>({
  199. formAdditionaProps: {
  200. labelPosition: 'top',
  201. },
  202. formItems: [
  203. {
  204. type: 'flat-group',
  205. label: '传承人自查评估',
  206. name: 'selfAssessmentGroup',
  207. childrenColProps: { span: 24 },
  208. children: [
  209. {
  210. label: '其他相关情况(扣分内容)',
  211. name: 'deductContent',
  212. type: 'text',
  213. additionalProps: {
  214. showWordLimit: true,
  215. maxlength: 260,
  216. placeholder: '请输入其他相关情况(扣分内容)',
  217. } as FieldProps,
  218. },
  219. {
  220. label: '其他相关情况(扣分分值)',
  221. name: 'deductPoints',
  222. type: 'number',
  223. additionalProps: {
  224. placeholder: '请输入其他相关情况(扣分分值)',
  225. min: 0,
  226. max: 100,
  227. },
  228. },
  229. {
  230. label: '自我评估',
  231. name: 'self',
  232. type: 'radio-value',
  233. additionalProps: {
  234. options: [
  235. { text: '优秀', value: 1 },
  236. { text: '合格', value: 2 },
  237. { text: '不合格', value: 3 },
  238. { text: '丧失传承能力', value: 4 },
  239. { text: '取消资格', value: 5 },
  240. ],
  241. vertical: true,
  242. } as RadioValueProps,
  243. },
  244. {
  245. label: '传承人签名',
  246. name: 'sign',
  247. type: 'sign',
  248. formProps: {
  249. showRightArrow: true,
  250. },
  251. additionalProps: {
  252. upload: useImageSimpleUploadCo(),
  253. } as SignatureFieldProps,
  254. }
  255. ],
  256. },
  257. ],
  258. formRules: {
  259. self: [{ required: true, message: '请选择自我评估' }],
  260. sign: [{ required: true, message: '请传承人签名' }],
  261. },
  262. });
  263. const checkItemList = ref<CheckItemInfo[]>([]);
  264. const totalPoints = computed(() => {
  265. if (!currentForm.value)
  266. return 0;
  267. return currentFormCheckItems.value.reduce((acc, item) => acc + (item.count * item.points), 0);
  268. });
  269. async function loadBasicInfo() {
  270. const basicInfo = await AssessmentContentApi.getInheritorBasic(authStore.userInfo?.id);
  271. assertNotNull(currentForm.value, 'currentForm is null');
  272. currentForm.value.inheritor = basicInfo.name;
  273. currentForm.value.unit = basicInfo.unit;
  274. currentForm.value.ichName = basicInfo.ichName;
  275. currentForm.value.mobile = basicInfo.mobile;
  276. currentForm.value.level = basicInfo.level;
  277. currentForm.value.idCard = basicInfo.idCard;
  278. currentForm.value.address = basicInfo.address;
  279. }
  280. async function loadCheckItems() {
  281. assertNotNull(currentForm.value, 'currentForm is null');
  282. checkItemList.value = await AssessmentContentApi.getCheckItems(Number(currentForm.value.level));
  283. currentFormCheckItems.value = currentForm.value.checkItems.concat();
  284. }
  285. function hasCheckedItem(id: number) {
  286. return currentFormCheckItems.value.some(item => item.id === id);
  287. }
  288. function getCheckedItemCount(id: number) {
  289. console.log('getCheckedItemCount', id);
  290. return currentFormCheckItems.value.find(item => item.id === id)?.count;
  291. }
  292. function setCheckedItem(checkItem: CheckItemInfo, childItem: CheckItemInfo, count: number|boolean) {
  293. if (!currentForm.value)
  294. return;
  295. if (typeof count === 'boolean') {
  296. count = count ? 1 : 0;
  297. }
  298. console.log('setCheckedItem', childItem.id, count);
  299. let item = currentFormCheckItems.value.find(item => item.id === childItem.id);
  300. if (!item) {
  301. item = new SelfAssessmentCheckItemAnswer();
  302. currentFormCheckItems.value.push(item);
  303. }
  304. if (item.count === count)
  305. return;
  306. item.id = childItem.id;
  307. item.points = childItem.points;
  308. item.count = count;
  309. switch (checkItem.checkType) {
  310. case 1: {
  311. /** 单选,清除其他选项 */
  312. const allChildren = checkItem.children.map(child => child.id);
  313. currentFormCheckItems.value.forEach(item => {
  314. if (allChildren.includes(item.id) && item.id !== childItem.id)
  315. item.count = 0;
  316. });
  317. break;
  318. }
  319. }
  320. if (item.count === 0)
  321. ArrayUtils.remove(currentFormCheckItems.value, item);
  322. }
  323. const submitLoading = ref(false);
  324. async function createForm() {
  325. const detail = new SelfAssessmentDetail();
  326. detail.userId = authStore.userInfo!.id;
  327. detail.year = new Date().getFullYear();
  328. detail.checkItems = [];
  329. currentForm.value = detail;
  330. await loadBasicInfo();
  331. await loadCheckItems();
  332. }
  333. async function saveForm() {
  334. const detail = currentForm.value;
  335. try {
  336. await form1Ref.value?.validate();
  337. await form3Ref.value?.validate();
  338. } catch (error) {
  339. toast('请填写完整信息');
  340. return;
  341. }
  342. submitLoading.value = true;
  343. currentForm.value!.checkItems = currentFormCheckItems.value;
  344. try {
  345. assertNotNull(detail, 'currentForm is null');
  346. await AssessmentContentApi.saveSelfAssessment(detail as SelfAssessmentDetail);
  347. toast('保存评估表成功');
  348. } catch (error) {
  349. alert({
  350. title: '保存评估表失败',
  351. content: formatError(error),
  352. });
  353. }
  354. submitLoading.value = false;
  355. }
  356. async function downloadForm() {
  357. try {
  358. assertNotNull(currentForm.value, 'currentForm is null');
  359. throw new Error('没这个接口');
  360. } catch (error) {
  361. alert({
  362. title: '下载评估表失败',
  363. content: formatError(error),
  364. });
  365. }
  366. }
  367. const loader = useSimpleDataLoader(async () => {
  368. await waitTimeOut(1000);
  369. console.log('load');
  370. if (querys.value.id > 0) {
  371. const detail = await AssessmentContentApi.getSelfAssessmentDetail(querys.value.id);
  372. currentForm.value = detail;
  373. await loadCheckItems();
  374. return;
  375. }
  376. const list = await AssessmentContentApi.getSelfAssessmentList({
  377. userId: authStore.userInfo?.id,
  378. year: new Date().getFullYear(),
  379. });
  380. if (list.data.length > 0) {
  381. const detail = await AssessmentContentApi.getSelfAssessmentDetail(list.data[0].id);
  382. currentForm.value = detail;
  383. await loadCheckItems();
  384. } else {
  385. currentForm.value = null;
  386. createForm();
  387. }
  388. return currentForm.value;
  389. }, false);
  390. </script>