evaluation-form.vue 13 KB

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