index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <template>
  2. <div class="main_container">
  3. <div class="page_item end_item">
  4. <div class="page_search">
  5. <div class="search_content">
  6. <div class="content_left">
  7. <SearchBlock :searchList="blockSearchList" @searchChange="HandleSearchChange"></SearchBlock>
  8. </div>
  9. <div class="content_right">
  10. <el-button class="button_refresh" @click="OpenBatchImportDialog">批量导入</el-button>
  11. <el-button class="button_background" @click="OpenSchoolFormDialog()">新增学校</el-button>
  12. </div>
  13. </div>
  14. </div>
  15. <FormSearchGroup :searchList="formSearchList" @searchChange="HandleSearchChange"></FormSearchGroup>
  16. <Table :tableColumns="publicSeasTableColumns" :tableData="tableData"
  17. :currentPage="currentPage" :pageSize="pageSize" :totalNum="totalNum">
  18. <template #num="{row,currentIndex}">
  19. {{ currentIndex + pageSize * (currentPage - 1) }}
  20. </template>
  21. <template #operation="{row}">
  22. <div class="table_row_option">
  23. <span class="button_editor" @click="ClaimCustomer(row)">认领</span>
  24. <span class="button_editor" @click="OpenSchoolFormDialog(row)">编辑</span>
  25. <span class="button_editor" @click="GoToDetail(row)">详情</span>
  26. <span class="el_button_delete" @click="OpenDeleteDialog(row)">删除</span>
  27. </div>
  28. </template>
  29. </Table>
  30. </div>
  31. <!-- 新增/修改 学校弹窗 -->
  32. <SchoolFormDialog
  33. :schoolTitle="schoolTitle"
  34. v-model:visible="schoolVisible"
  35. v-model:editFormDialogSubmitLoading="editFormDialogSubmitLoading"
  36. :defaultFormData="schoolDefaultFormData"
  37. :dynamicFormItmOpt="dynamicFormItmOpt"
  38. @confirm="HandleSchoolDialogConfirm">
  39. </SchoolFormDialog>
  40. <!-- 批量导入 弹窗 -->
  41. <FormDialog class="batch_import_form" width="548px"
  42. title="批量导入" :visible="batchImportVisible"
  43. :submitLoading="batchImportSubmitLoading"
  44. :defaultFormData="defaultImportFormData"
  45. :formItemList="batchImportFormItemList"
  46. @fileUpload="HandleFileUplaod"
  47. @close="CloseBatchImportDialog"
  48. @confirm="HandleBatchImportComfirm">
  49. <template #templateFile>
  50. <el-button class="button_refresh" @click="HandleDownloadTemplate">下载模板</el-button>
  51. </template>
  52. <template #tableTemplate>
  53. <Table :hidePagination="true" :tableColumns="templateTableColumns" :tableData="[{},{}]" />
  54. <div style="font-size: 12px;">用于分析同一份试卷内不同试卷类型的得分、排名等情况。可下载模块进行填写后导入。</div>
  55. </template>
  56. </FormDialog>
  57. <!-- 删除弹窗 -->
  58. <Dialog :visible="deleteDialogVisible"
  59. :submitLoading="deleteDialogSubmitLoading"
  60. @close="CloseDeleteDialog"
  61. @confirm="HandleDeleteDialogConfirm">
  62. </Dialog>
  63. </div>
  64. </template>
  65. <script setup lang="ts">
  66. import { computed, onMounted, ref } from 'vue';
  67. import { useRouter } from 'vue-router';
  68. import {ElMessage} from 'element-plus'
  69. import FormSearchGroup from '@/baseComponents/FormSearchGroup.vue';
  70. import {formSearchList} from './formSearchGroup';
  71. import SearchBlock from '@/baseComponents/SearchBlock.vue';
  72. import Table from '@/baseComponents/Table.vue';
  73. import {publicSeasTableColumns,templateTableColumns} from './table'
  74. import SchoolFormDialog from '@/components/SchoolFormDialog/index.vue'
  75. import FormDialog from '@/baseComponents/FormDialog.vue';
  76. import {batchImportFormItemList} from './formDialog'
  77. import {HandleExportExcel} from '@/utils/exportExcelData'
  78. import { getHeaderSearchParamsApi,
  79. getHighSeasCustomerListApi,
  80. addHighSeasCustomerApi,
  81. importHighSeasCustomerApi,
  82. updateHighSeasCustomerApi,
  83. deleteHighSeasCustomerApi,
  84. claimHighSeasCustomerApi,
  85. } from '@/api/highSeasCustomer'
  86. import Dialog from '@/baseComponents/Dialog.vue';
  87. const router = useRouter()
  88. /** 1. 头部筛选 有关操作 */
  89. const searchParams = ref({
  90. stage:'all',
  91. province:'all',
  92. area:'all',
  93. queryStr:'',
  94. })
  95. // 获取头部筛选数据
  96. const stageOptions = ref([])
  97. const regionOptions = ref([])
  98. const blockSearchList = computed(()=>{
  99. let areaOptions = []
  100. if(searchParams.value.province !== 'all'){
  101. areaOptions = regionOptions.value.find(item=>item.value === searchParams.value.province)?.subData || []
  102. }else{
  103. areaOptions = regionOptions.value.map(item=>{
  104. return item.subData || []
  105. }).flat()
  106. }
  107. return [
  108. {
  109. label: '学段',
  110. key: 'stage',
  111. type: 'single',
  112. defaultValue: searchParams.value.stage,
  113. options: [
  114. { label: '全部', value: 'all' },
  115. ...(stageOptions.value),
  116. ],
  117. },
  118. {
  119. label: '省份',
  120. key: 'province',
  121. type: 'single',
  122. options: [
  123. { label: '全部', value: 'all' },
  124. ...(regionOptions.value),
  125. ],
  126. defaultValue: searchParams.value.province,
  127. },
  128. {
  129. label: '地区',
  130. key: 'area',
  131. type: 'single',
  132. options: [
  133. { label: '全部', value: 'all' },
  134. ...(areaOptions),
  135. ],
  136. defaultValue: searchParams.value.area,
  137. }
  138. ]
  139. });
  140. // 获取头部筛选数据
  141. const GetHeaderSearchParams = async ()=>{
  142. let res = await getHeaderSearchParamsApi()
  143. const {regionData,stageData} = res.data || {}
  144. // 学段筛选选项
  145. stageOptions.value = stageData?.map(item=>{
  146. return{
  147. label:item.name,
  148. value:JSON.stringify({
  149. label:item.name,
  150. value:item.id,
  151. }),
  152. }
  153. })?.slice(1) || []
  154. // 省份筛选项
  155. regionOptions.value = regionData?.map(item=>{
  156. return{
  157. label:item.name,
  158. value:JSON.stringify({
  159. label:item.name,
  160. value:item.id
  161. }),
  162. subData:item.subData?.map(sub=>{
  163. return {
  164. label:sub.name,
  165. value:JSON.stringify({
  166. label:sub.name,
  167. value:sub.id
  168. }),
  169. }
  170. }).slice(1) || [],
  171. }
  172. })?.slice(1) || []
  173. }
  174. /** 头部筛选 变更 */
  175. const HandleSearchChange = async (params:any,key:string)=>{
  176. searchParams.value = {
  177. ...searchParams.value,
  178. ...params
  179. }
  180. if(key === 'province'){
  181. searchParams.value.area = 'all'
  182. }
  183. await GetTableData()
  184. }
  185. /** 2. 表格数据 有关操作 */
  186. const pageSize = ref(20)
  187. const currentPage = ref(1)
  188. const totalNum = ref(0)
  189. // 表格数据
  190. const tableData = ref([])
  191. const GetTableData = async ()=>{
  192. let params = {
  193. queryStr: searchParams.value.queryStr,
  194. stageId: searchParams.value.stage === 'all' ? '' : JSON.parse(searchParams.value.stage || '{}')?.value,
  195. provinceId: searchParams.value.province === 'all' ? '' : JSON.parse(searchParams.value.province || '{}')?.value,
  196. cityId: searchParams.value.area === 'all' ? '' : JSON.parse(searchParams.value.area || '{}')?.value,
  197. pageNum: currentPage.value,
  198. pageSize: pageSize.value
  199. }
  200. let res = await getHighSeasCustomerListApi(params)
  201. tableData.value = res?.data?.records || []
  202. totalNum.value = res.data.total || 0
  203. }
  204. /**
  205. * 3. 新增/修改 学校弹窗有关数据
  206. */
  207. const schoolVisible = ref(false)
  208. const schoolTitle = ref('')
  209. const schoolDefaultFormData = ref({})
  210. const editFormDialogSubmitLoading = ref(false)
  211. // 动态变更的表单下拉项
  212. const dynamicFormItmOpt = computed(()=>{
  213. return [
  214. {
  215. key: 'schoolStage',
  216. selectList:stageOptions.value,
  217. },
  218. {
  219. key: 'provinceArea',
  220. unionSelectList: [
  221. {
  222. key: 'province',
  223. selectList:regionOptions.value,
  224. },
  225. ]
  226. },
  227. ]
  228. })
  229. const OpenSchoolFormDialog = (row?:any)=>{
  230. if(row){
  231. schoolTitle.value = '编辑学校'
  232. schoolDefaultFormData.value = {
  233. ...row,
  234. schoolStage:JSON.stringify({
  235. label:row.stageName,
  236. value:row.stageId
  237. }),
  238. provinceArea:{
  239. province:JSON.stringify({
  240. label:row.provinceName,
  241. value:row.provinceId
  242. }),
  243. district:JSON.stringify({
  244. label:row.cityName,
  245. value:row.cityId
  246. }),
  247. },
  248. }
  249. }else{
  250. schoolTitle.value = '新增学校'
  251. }
  252. schoolVisible.value = true
  253. }
  254. const CloseSchoolFormDialog = ()=>{
  255. editFormDialogSubmitLoading.value = false
  256. schoolVisible.value = false
  257. }
  258. const HandleSchoolDialogConfirm = async (params:any)=>{
  259. editFormDialogSubmitLoading.value = true
  260. const {formData,formRef} = params
  261. console.log('表单的提交信息',formData)
  262. let transferParams = {
  263. id: formData.id,
  264. name: formData.name,
  265. stageId: JSON.parse(formData.schoolStage || '{}')?.value,
  266. stageName: JSON.parse(formData.schoolStage || '{}')?.label,
  267. provinceId: JSON.parse(formData.provinceArea.province || '{}')?.value,
  268. provinceName: JSON.parse(formData.provinceArea.province || '{}')?.label,
  269. cityId: JSON.parse(formData.provinceArea.district || '{}')?.value,
  270. cityName: JSON.parse(formData.provinceArea.district || '{}')?.label,
  271. address: formData.address,
  272. }
  273. let res = undefined
  274. try{
  275. if(formData.id){
  276. // 修改
  277. res = await updateHighSeasCustomerApi(transferParams)
  278. }else{
  279. // 新增
  280. res = await addHighSeasCustomerApi(transferParams)
  281. }
  282. }catch{}
  283. if(res?.code == 200){
  284. schoolDefaultFormData.value = {}
  285. CloseSchoolFormDialog()
  286. await GetTableData()
  287. }else{
  288. editFormDialogSubmitLoading.value = false
  289. }
  290. }
  291. /**
  292. * 4. 批量导入弹窗有关数据
  293. */
  294. const batchImportVisible = ref(false)
  295. const batchImportSubmitLoading = ref(false)
  296. const defaultImportFormData = ref({})
  297. // 打开弹窗
  298. const OpenBatchImportDialog = ()=>{
  299. batchImportVisible.value = true
  300. }
  301. // 关闭弹窗
  302. const CloseBatchImportDialog = ()=>{
  303. batchImportSubmitLoading.value = false
  304. batchImportVisible.value = false
  305. }
  306. // 导入确认
  307. const HandleBatchImportComfirm = async (params:any)=>{
  308. const {formData,formRef} = params
  309. // console.log('表单信息',formData)
  310. if(!formData?.file?.url){
  311. ElMessage.warning('请先上传文件')
  312. return
  313. }
  314. batchImportSubmitLoading.value = true
  315. let fileFormData = new FormData()
  316. fileFormData.append('file',formData.file.url)
  317. let res = await importHighSeasCustomerApi(fileFormData)
  318. if(res?.code == 200){
  319. ElMessage.success(res?.msg)
  320. CloseBatchImportDialog()
  321. await GetTableData()
  322. }else{
  323. batchImportSubmitLoading.value = false
  324. }
  325. }
  326. // 下载模板
  327. const HandleDownloadTemplate = async ()=>{
  328. await HandleExportExcel(templateTableColumns.value,[],'新增学校_批量导入模板')
  329. }
  330. // 获取上传文件
  331. const HandleFileUplaod = (params)=>{
  332. const {key,file} = params
  333. console.log('file',file)
  334. defaultImportFormData.value.file = {
  335. url:file.raw,
  336. name:file.name
  337. }
  338. }
  339. /**
  340. * 5. 删除弹窗 有关数据
  341. */
  342. const deleteDialogVisible = ref(false)
  343. const deleteDialogSubmitLoading = ref(false)
  344. const currentDeleteRow = ref()
  345. const OpenDeleteDialog = (row)=>{
  346. deleteDialogVisible.value = true
  347. currentDeleteRow.value = row
  348. }
  349. const CloseDeleteDialog = ()=>{
  350. deleteDialogVisible.value = false
  351. deleteDialogSubmitLoading.value = false
  352. }
  353. // 删除确认
  354. const HandleDeleteDialogConfirm = async ()=>{
  355. deleteDialogSubmitLoading.value = true
  356. let res = await deleteHighSeasCustomerApi(currentDeleteRow.value.id)
  357. if(res?.code == 200){
  358. ElMessage.success(res?.msg)
  359. CloseDeleteDialog()
  360. await GetTableData()
  361. }else{
  362. deleteDialogSubmitLoading.value = false
  363. }
  364. }
  365. /**
  366. * 6.认领有关接口调用
  367. */
  368. const ClaimCustomer = async (row)=>{
  369. let res = await claimHighSeasCustomerApi(row.id)
  370. if(res?.code == 200){
  371. ElMessage.success(res?.msg)
  372. await GetTableData()
  373. }
  374. }
  375. /** 7.跳转详情 */
  376. const GoToDetail = (row)=>{
  377. router.push(`highSeasCustomerDetail?id=${row.id}`)
  378. }
  379. onMounted( async()=>{
  380. await GetHeaderSearchParams()
  381. await GetTableData()
  382. })
  383. </script>
  384. <style lang="scss" scoped>
  385. .end_item{
  386. gap:16px;
  387. .search_content{
  388. align-items: flex-start;
  389. }
  390. }
  391. .batch_import_form{
  392. :deep(.el-dialog__body){
  393. min-height: 345px !important;
  394. }
  395. }
  396. </style>