edit.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <script setup lang="ts">
  2. import BasicModal from '/@/components/Modal/src/BasicModal.vue';
  3. import { useModal, useModalInner } from '/@/components/Modal';
  4. import { Steps } from 'ant-design-vue';
  5. import { ref, unref, reactive } from 'vue';
  6. import { useTable } from '/@/components/Table';
  7. import BasicForm from '/@/components/Form/src/BasicForm.vue';
  8. import { useForm } from '/@/components/Form';
  9. import { textbookColumns } from './data.config';
  10. import BasicTable from '/@/components/Table/src/BasicTable.vue';
  11. import addTextbook from './addTextbook.vue';
  12. import SelectClass from '/@/views/educational/class/components/ClassModal.vue';
  13. import { getTextbookListSubscription } from '/@/services/apis/TextbookController';
  14. import { useMessage } from '/@/hooks/web/useMessage';
  15. import historyView from './historyView.vue';
  16. import { requestMagicApi } from '/@/api/magicApi';
  17. import {
  18. getTextbookSubscriptionInfo,
  19. postTextbookTextbookSubscription,
  20. putTextbookTextbookSubscription,
  21. } from '/@/services/apis/TextbookSubscriptionController';
  22. import { cloneDeep, uniqueId } from 'lodash-es';
  23. const step = Steps.Step;
  24. const selectRow = ref<any>([]);
  25. const thisStep = ref(0);
  26. const dataInfo = ref<any>({});
  27. const selectUserList = ref<any>([]);
  28. const isUpdate = ref(true);
  29. const modelRef = ref<Recordable>({});
  30. const state = reactive<{ isSearch: boolean; sourceData: any[] }>({
  31. isSearch: false,
  32. sourceData: [],
  33. });
  34. const [addReg, { openModal: addModelOpen }] = useModal();
  35. const [historyViewReg, { openModal: historyViewOpen }] = useModal();
  36. const emits = defineEmits(['success', 'register']);
  37. const [formReg, { validate, resetFields, setFieldsValue, setProps }] = useForm({
  38. labelWidth: 120,
  39. schemas: [
  40. {
  41. label: '学期',
  42. field: 'baseSemesterId',
  43. component: 'ApiSelect',
  44. colProps: { span: 24 },
  45. componentProps: () => {
  46. return {
  47. api: requestMagicApi,
  48. getPopupContainer: () => document.body,
  49. params: {
  50. url: '/baseData/semester/option',
  51. },
  52. showSearch: true,
  53. filterOption: (input: string, option: any) => {
  54. return (
  55. option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 ||
  56. option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
  57. );
  58. },
  59. onChange: () => {
  60. handleChange();
  61. },
  62. };
  63. },
  64. required: true,
  65. },
  66. {
  67. label: '征订方式',
  68. field: 'subscriptionMethod',
  69. component: 'Select',
  70. required: true,
  71. componentProps: () => {
  72. return {
  73. options: [
  74. {
  75. label: '按班级征订',
  76. value: 1,
  77. },
  78. {
  79. label: '按教材征订',
  80. value: 2,
  81. },
  82. ],
  83. onChange: () => {
  84. handleChange();
  85. },
  86. };
  87. },
  88. colProps: { span: 24 },
  89. },
  90. {
  91. label: '备注',
  92. field: 'remark',
  93. component: 'InputTextArea',
  94. colProps: { span: 24 },
  95. },
  96. ],
  97. showActionButtonGroup: false,
  98. });
  99. const updateSourceData = (record) => {
  100. const index = state.sourceData.findIndex((item) => item.uuid === record.uuid);
  101. if (index > -1) {
  102. state.sourceData[index] = cloneDeep(record);
  103. }
  104. };
  105. const handleChangeDiscount = (record) => {
  106. if (record.discount < 1) {
  107. record.discount = 1;
  108. }
  109. if (record.discount > 10) {
  110. record.discount = 10;
  111. }
  112. record.price = Number((Number(record.sourcePrice || 0) * record.discount) / 10).toFixed(2);
  113. updateSourceData(record);
  114. };
  115. const handleChangePrice = (record) => {
  116. if (record.price !== 0) {
  117. record.discount = Number(((record.price / record.sourcePrice) * 10).toFixed(1));
  118. } else {
  119. record.discount = 0;
  120. }
  121. updateSourceData(record);
  122. };
  123. const [modalReg, { closeModal, setModalProps }] = useModalInner(async (data) => {
  124. isUpdate.value = !!data?.isUpdate;
  125. modelRef.value = { ...data.baseData };
  126. getForm().resetFields();
  127. setModalProps({ loading: true });
  128. if (unref(isUpdate)) {
  129. const resData = await getTextbookSubscriptionInfo({ id: data.baseData.id });
  130. modelRef.value = { ...resData };
  131. setFieldsValue(resData);
  132. dataInfo.value = { ...resData };
  133. resData.textbookSubscriptionItemList.forEach((item: any) => {
  134. item.studentSubscriptionNumber = item.studentNum;
  135. item.teacherSubscriptionNumber = item.teacherNum;
  136. item.courseName = item.courseSubjectIdCn;
  137. item.sourcePrice = item.pricing;
  138. item.uuid = uniqueId();
  139. });
  140. state.sourceData = resData.textbookSubscriptionItemList;
  141. setTableData(resData.textbookSubscriptionItemList);
  142. thisStep.value = 1;
  143. } else {
  144. thisStep.value = 0;
  145. state.sourceData = [];
  146. }
  147. state.isSearch = false;
  148. setProps({ disabled: unref(isUpdate) });
  149. setModalProps({ loading: false });
  150. });
  151. const handleChange = () => {
  152. setTableData([]);
  153. selectUserList.value = [];
  154. setSelectedRowKeys([]);
  155. };
  156. const handleNext = () => {
  157. validate().then((res) => {
  158. dataInfo.value = res;
  159. thisStep.value++;
  160. });
  161. };
  162. const handleClose = () => {
  163. thisStep.value = 0;
  164. resetFields();
  165. closeModal();
  166. };
  167. const [selectModalReg, { openModal: selectModalOpen }] = useModal();
  168. const [tableRef, { getForm, setTableData, getDataSource, setSelectedRowKeys }] = useTable({
  169. title: '征订列表',
  170. columns: textbookColumns,
  171. bordered: true,
  172. rowKey: 'uuid',
  173. rowSelection: {
  174. type: 'checkbox',
  175. onChange: (selectedRowKeys) => {
  176. selectRow.value = selectedRowKeys;
  177. },
  178. },
  179. pagination: false,
  180. actionColumn: {
  181. width: 170,
  182. title: '操作',
  183. dataIndex: 'action',
  184. slots: { customRender: 'action' },
  185. fixed: 'right',
  186. },
  187. resizeHeightOffset: 80,
  188. useSearchForm: true,
  189. formConfig: {
  190. labelWidth: 100,
  191. schemas: [
  192. {
  193. label: '书名',
  194. field: 'bookName',
  195. component: 'Input',
  196. colProps: { span: 8 },
  197. },
  198. {
  199. label: '书号',
  200. field: 'issn',
  201. component: 'Input',
  202. colProps: { span: 8 },
  203. },
  204. {
  205. label: '课程',
  206. field: 'courseName',
  207. component: 'Input',
  208. colProps: { span: 8 },
  209. },
  210. ],
  211. submitFunc: async () => {
  212. const values = getForm().getFieldsValue();
  213. let searchData: any[] = cloneDeep(state.sourceData);
  214. if (values.bookName || values.issn || values.courseName) {
  215. if (values.bookName) {
  216. searchData = searchData.filter((item) => item.bookName.includes(values.bookName));
  217. }
  218. if (values.issn) {
  219. searchData = searchData.filter((item) => item.issn.includes(values.issn));
  220. }
  221. if (values.courseName) {
  222. searchData = searchData.filter((item) => item.courseName.includes(values.courseName));
  223. }
  224. state.isSearch = true;
  225. } else {
  226. state.isSearch = false;
  227. }
  228. setTableData(searchData);
  229. },
  230. },
  231. });
  232. const handleAdd = () => {
  233. addModelOpen(true, {
  234. baseSemesterId: dataInfo.value.baseSemesterId,
  235. });
  236. };
  237. const handleSuccess = (e) => {
  238. const data = cloneDeep(state.sourceData);
  239. const addList: any[] = [];
  240. e.forEach((item: any) => {
  241. if (!data.find((i: any) => i.textbookId === item.id)) {
  242. item.sourcePrice = item.price;
  243. item.textbookId = item.id;
  244. item.uuid = uniqueId();
  245. item.id = '';
  246. handleChangeDiscount(item);
  247. addList.push(item);
  248. }
  249. });
  250. state.sourceData = data.concat(addList);
  251. setTableData(data.concat(addList));
  252. // redoHeight();
  253. };
  254. const handleClear = (uuid) => {
  255. const keys = uuid ? [uuid] : selectRow.value;
  256. const data = getDataSource();
  257. setTableData(data.filter((item: any) => !keys.includes(item.uuid)));
  258. state.sourceData = state.sourceData.filter((item: any) => !keys.includes(item.uuid));
  259. };
  260. const handleSelect = async (e) => {
  261. selectUserList.value = e.list;
  262. const ids = selectUserList.value.map((item: any) => item.id);
  263. const data = await getTextbookListSubscription({
  264. classIds: ids.join(','),
  265. baseSemesterId: dataInfo.value.baseSemesterId,
  266. textbookSubscriptionId: modelRef.value.id,
  267. });
  268. data.forEach((item: any) => {
  269. item.sourcePrice = item.price;
  270. item.uuid = uniqueId();
  271. handleChangeDiscount(item);
  272. });
  273. state.sourceData = data;
  274. setTableData(data);
  275. };
  276. const { createMessage } = useMessage();
  277. const handleSubmit = async () => {
  278. const dataSoruce = cloneDeep(state.sourceData);
  279. if (dataSoruce.length === 0) {
  280. createMessage.warning('至少征订一本教材');
  281. return;
  282. }
  283. dataSoruce.forEach((item: any) => {
  284. item.studentNum = item.studentSubscriptionNumber || 0;
  285. item.teacherNum = item.teacherSubscriptionNumber || 0;
  286. });
  287. const postParams = unref(modelRef);
  288. Object.assign(postParams, {
  289. remark: dataInfo.value?.remark,
  290. subscriptionMethod: dataInfo.value.subscriptionMethod,
  291. baseSemesterId: dataInfo.value.baseSemesterId,
  292. textbookSubscriptionItemList: dataSoruce,
  293. baseClassIds: dataSoruce[0].classIds,
  294. });
  295. try {
  296. if (isUpdate.value) {
  297. await putTextbookTextbookSubscription(postParams, 'none');
  298. } else {
  299. await postTextbookTextbookSubscription(postParams, 'none');
  300. }
  301. createMessage.success('新增成功');
  302. emits('success');
  303. thisStep.value = 0;
  304. await resetFields();
  305. setTableData([]);
  306. selectUserList.value = [];
  307. closeModal();
  308. } catch {
  309. createMessage.error('新增失败');
  310. }
  311. };
  312. const handleViewHistory = (id) => {
  313. historyViewOpen(true, { textbookId: id, baseSemesterId: dataInfo.value.baseSemesterId });
  314. };
  315. </script>
  316. <template>
  317. <BasicModal
  318. @cancel="handleClose"
  319. :canFullscreen="false"
  320. defaultFullscreen
  321. v-bind="$attrs"
  322. @register="modalReg"
  323. title="教材征订"
  324. >
  325. <div class="full flex flex-col">
  326. <div class="w-2/3 h-[50px]">
  327. <Steps v-model:current="thisStep">
  328. <step disabled title="基础信息" />
  329. <step disabled title="教材征订" />
  330. </Steps>
  331. </div>
  332. <div style="height: calc(100% - 50px)">
  333. <BasicForm @register="formReg" v-show="thisStep === 0" />
  334. <BasicTable @register="tableRef" v-show="thisStep === 1">
  335. <template #toolbar>
  336. <a-button
  337. v-if="dataInfo && dataInfo.subscriptionMethod == 2"
  338. type="primary"
  339. class="ml-[12px]"
  340. @click="handleAdd"
  341. >
  342. 选择教材
  343. </a-button>
  344. <a-button
  345. v-if="dataInfo && dataInfo.subscriptionMethod == 1"
  346. type="primary"
  347. class="ml-[12px]"
  348. @click="selectModalOpen(true, { baseData: { list: selectUserList } })"
  349. >
  350. 选择班级
  351. </a-button>
  352. <a-button type="primary" @click="handleClear(null)">批量移出</a-button>
  353. </template>
  354. <template #studentSubscriptionNumber="{ record }">
  355. <a-input-number
  356. :step="1"
  357. :min="0"
  358. v-model:value="record.studentSubscriptionNumber"
  359. @change="updateSourceData(record)"
  360. />
  361. </template>
  362. <template #teacherSubscriptionNumber="{ record }">
  363. <a-input-number
  364. :step="1"
  365. :min="0"
  366. v-model:value="record.teacherSubscriptionNumber"
  367. @change="updateSourceData(record)"
  368. />
  369. </template>
  370. <template #discount="{ record }">
  371. <a-input-number
  372. :step="1"
  373. :min="0"
  374. v-model:value="record.discount"
  375. @change="handleChangeDiscount(record)"
  376. />
  377. </template>
  378. <template #price="{ record }">
  379. <a-input-number
  380. :step="1"
  381. :min="0"
  382. v-model:value="record.price"
  383. @change="handleChangePrice(record)"
  384. />
  385. </template>
  386. <template #action="{ record }">
  387. <a-button type="link" @click="handleClear(record.uuid)">移出</a-button>
  388. <a-button type="link" @click="handleViewHistory(record.textbookId)">
  389. 历史征订
  390. </a-button>
  391. </template>
  392. </BasicTable>
  393. </div>
  394. </div>
  395. <addTextbook @register="addReg" @success="handleSuccess" />
  396. <SelectClass @register="selectModalReg" @success="handleSelect" />
  397. <historyView @register="historyViewReg" />
  398. <template #footer>
  399. <a-button @click="handleClose">取消</a-button>
  400. <a-button v-show="thisStep === 1" @click="thisStep = 0" class="ml-[24px]">上一步</a-button>
  401. <a-button v-show="thisStep === 0" type="primary" @click="handleNext" class="ml-[24px]">
  402. 下一步
  403. </a-button>
  404. <a-button v-show="thisStep === 1" type="primary" class="ml-[24px]" @click="handleSubmit">
  405. 提交
  406. </a-button>
  407. </template>
  408. </BasicModal>
  409. </template>
  410. <style scoped lang="less"></style>