index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. <script setup lang="ts">
  2. import Selector from '/src/views/reportManage/dataCenter/normalDisplay/components/Selector/index.vue';
  3. import { ref, reactive, onMounted, nextTick, watch } from 'vue';
  4. import { VXETable, VxeGridInstance, VxeGridProps, VxeGridListeners } from 'vxe-table';
  5. import {
  6. getTasks,
  7. postCreateTask,
  8. getOperationSelect,
  9. postUpdateTask,
  10. postDeleteTask
  11. } from '/src/views/reportManage/TaskManage/api.ts';
  12. import { ComponentSize, ElMessage, FormInstance, FormRules } from 'element-plus';
  13. //表单
  14. interface taskRuleForm {
  15. number: string;
  16. name: string;
  17. country: string;
  18. brand: string;
  19. operation: string[];
  20. currency: string;
  21. }
  22. const formSize = ref<ComponentSize>('default');
  23. const dialogFormVisible = ref(false);
  24. const taskRuleFormRef = ref<FormInstance>();
  25. const taskRuleForm = reactive({
  26. number: '',
  27. name: '',
  28. country: '',
  29. brand: '',
  30. operation: '',
  31. currency: '',
  32. });
  33. const resetForm = (formEl: FormInstance | undefined) => {
  34. if (!formEl) return;
  35. formEl.resetFields();
  36. };
  37. const rules = reactive<FormRules>({
  38. number: [{required: true, message: '请输入平台编号', trigger: 'blur'},],
  39. name: [{required: true, message: '请输入平台名称', trigger: 'blur'}],
  40. country: [{required: true, message: '请输入国家', trigger: 'blur'}],
  41. brand: [{required: true, message: '请输入品牌', trigger: 'blur'}],
  42. operation: [{required: true, message: '请选择运营', trigger: 'change'}],
  43. currency: [{required: true, message: '请输入货币', trigger: 'blur'}],
  44. });
  45. //表格
  46. let page = 1;
  47. let limit = 10;
  48. interface RowVO {
  49. platformNumber: string;
  50. platformName: string;
  51. country: string;
  52. brandName: string;
  53. user_name: string;
  54. currencyCode: string;
  55. child_user_number: number;
  56. user: [];
  57. }
  58. const xGrid = ref<VxeGridInstance<RowVO>>();
  59. const gridOptions = reactive<VxeGridProps<RowVO>>({
  60. border: false,
  61. keepSource: true,
  62. showOverflow: true,
  63. height: 1000,
  64. loading: false,
  65. round: true,
  66. toolbarConfig: {
  67. slots: {
  68. buttons: 'toolbar_buttons',
  69. },
  70. },
  71. rowConfig: {
  72. isHover: true,
  73. },
  74. columnConfig: {
  75. resizable: true,
  76. },
  77. pagerConfig: {
  78. enabled: true,
  79. total: 20,
  80. currentPage: 1,
  81. pageSize: 20,
  82. pageSizes: [10, 20, 30],
  83. },
  84. editConfig: {
  85. trigger: 'click',
  86. mode: 'row',
  87. showStatus: true,
  88. },
  89. checkboxConfig: {
  90. reserve: true,
  91. highlight: true,
  92. range: true,
  93. },
  94. columns: [
  95. {type: 'checkbox', width: 50},
  96. // {type: 'seq', title: 'ID', width: 120},
  97. {
  98. field: 'platformNumber',
  99. title: '平台编号',
  100. editRender: {autofocus: '.vxe-input--inner'},
  101. slots: {edit: 'number_edit'},
  102. sortable: true,
  103. },
  104. {
  105. field: 'platformName',
  106. title: '平台名称',
  107. editRender: {autofocus: '.vxe-input--inner'},
  108. slots: {edit: 'name_edit'}
  109. },
  110. {field: 'country', title: '国家', editRender: {autofocus: '.vxe-input--inner'}, slots: {edit: 'country_edit'}},
  111. {field: 'brandName', title: '品牌', editRender: {}, slots: {edit: 'brand_edit'}},
  112. {field: 'user_name', title: '运营', editRender: {}, slots: {edit: 'operation_edit'}},
  113. {field: 'currencyCode', title: '汇款币种', editRender: {}, slots: {edit: 'currency_edit'}},
  114. {field: 'child_user_number', title: '录入人员数', editRender: {}, slots: {edit: 'quantity_edit'}},
  115. {title: '操作', width: 300, slots: {default: 'operate'}},
  116. ],
  117. data: [],
  118. });
  119. const operationList = ref([]);
  120. const quantityList = ref([]);
  121. // 分页
  122. const gridEvents: VxeGridListeners<RowVO> = {
  123. pageChange({currentPage, pageSize}) {
  124. // console.log(currentPage, pageSize)
  125. if (gridOptions.pagerConfig) {
  126. gridOptions.pagerConfig.currentPage = currentPage;
  127. gridOptions.pagerConfig.pageSize = pageSize;
  128. getTaskList();
  129. }
  130. },
  131. };
  132. async function getTaskList(filters = {}) {
  133. try {
  134. gridOptions.loading = true;
  135. const response = await getTasks({
  136. page: gridOptions.pagerConfig.currentPage,
  137. limit: gridOptions.pagerConfig.pageSize,
  138. ...filters,
  139. });
  140. gridOptions.data = response.data;
  141. gridOptions.pagerConfig.total = response.total;
  142. }catch (error) {
  143. console.error('Error fetching task data:', error);
  144. } finally {
  145. gridOptions.loading = false;
  146. }
  147. }
  148. const selectorRef = ref(null);
  149. function filteredDataChange(newList) {
  150. // console.log('newList:', newList.value);
  151. if (selectorRef.value) {
  152. getTaskList(newList.value);
  153. // gridOptions.data = newList.value;
  154. }
  155. }
  156. /**
  157. * 判断当前编辑行
  158. * @param {RowVO} row 当前行
  159. * @return {boolean}
  160. */
  161. const hasActiveEditRow = (row: RowVO) => {
  162. const $grid = xGrid.value;
  163. if ($grid) {
  164. return $grid.isEditByRow(row);
  165. }
  166. return false;
  167. };
  168. const editRowEvent = (row: RowVO) => {
  169. const $grid = xGrid.value;
  170. if ($grid) {
  171. $grid.setEditRow(row);
  172. }
  173. };
  174. //清除编辑状态
  175. const clearRowEvent = () => {
  176. const $grid = xGrid.value;
  177. if ($grid) {
  178. $grid.clearEdit();
  179. }
  180. };
  181. async function deleteTask() {
  182. const $grid = xGrid.value;
  183. if ($grid) {
  184. const selectRecords = $grid.getCheckboxRecords();
  185. const selectedIds = selectRecords.map(record => record.id);
  186. // console.log(selectedIds);
  187. const obj = {keys: selectedIds};
  188. try {
  189. const resp = await postDeleteTask(obj);
  190. if (resp.code === 2000) {
  191. ElMessage({
  192. message: '删除成功',
  193. type: 'success',
  194. });
  195. await getTaskList();
  196. }
  197. } catch (e) {
  198. ElMessage({
  199. message: '删除失败',
  200. type: 'error',
  201. });
  202. }
  203. }
  204. }
  205. // 删除选中行
  206. const removeEvent = async () => {
  207. const $grid = xGrid.value;
  208. if ($grid) {
  209. const selectRecords = $grid.getCheckboxRecords();
  210. if (selectRecords.length > 0) {
  211. const type = await VXETable.modal.confirm('您确定要删除选中的数据?');
  212. if (type === 'confirm') {
  213. await deleteTask(selectRecords);
  214. await $grid.removeCheckboxRow();
  215. }
  216. } else {
  217. await VXETable.modal.message({content: '请选择要删除的数据', status: 'error'});
  218. }
  219. }
  220. };
  221. async function updateRow(row: RowVO) {
  222. const $grid = xGrid.value;
  223. const transUser = ref('');
  224. operationList.value.find((item) => {
  225. if (item.label === row.user_name) {
  226. transUser.value = item.value;
  227. } else if (row.user_name === item.value) {
  228. row.user_name = item.label;
  229. transUser.value = item.value;
  230. }
  231. });
  232. if ($grid) {
  233. const updatedRowData = {
  234. id: row.id,
  235. platformNumber: row.platformNumber,
  236. platformName: row.platformName,
  237. country: row.country,
  238. brandName: row.brandName,
  239. user: [transUser.value],
  240. currencyCode: row.currencyCode,
  241. };
  242. // console.log(updatedRowData);
  243. try {
  244. const response = await postUpdateTask(updatedRowData);
  245. console.log(response);
  246. if (response.code === 2000) {
  247. ElMessage.success('更新成功');
  248. } else if (response.code == 400) {
  249. ElMessage.warning(`${response.data.description}`);
  250. } else {
  251. ElMessage.error('更新失败');
  252. }
  253. } catch (error) {
  254. console.log('error:', error);
  255. }
  256. }
  257. }
  258. // 保存表格行数据
  259. const saveRowEvent = async (row: RowVO) => {
  260. const $grid = xGrid.value;
  261. if ($grid) {
  262. await $grid.clearEdit();
  263. await updateRow(row);
  264. gridOptions.loading = true;
  265. setTimeout(() => {
  266. gridOptions.loading = false;
  267. }, 300);
  268. }
  269. };
  270. // 保存全部数据
  271. const saveEvent = async () => {
  272. const $grid = xGrid.value;
  273. if ($grid) {
  274. const {insertRecords, removeRecords, updateRecords} = $grid.getRecordset();
  275. await VXETable.modal.message({
  276. content: `新增 ${insertRecords.length} 条,删除 ${removeRecords.length} 条,更新 ${updateRecords.length} 条`,
  277. status: 'success',
  278. });
  279. }
  280. };
  281. async function createTask() {
  282. const body = {
  283. country: taskRuleForm.country,
  284. platformNumber: taskRuleForm.number,
  285. platformName: taskRuleForm.name,
  286. brandName: taskRuleForm.brand,
  287. currencyCode: taskRuleForm.currency,
  288. user: [taskRuleForm.operation],
  289. };
  290. try {
  291. const resp = await postCreateTask(body);
  292. } catch (error) {
  293. console.error('error:', error);
  294. await VXETable.modal.message({content: '创建失败,请重试', status: 'error'});
  295. }
  296. }
  297. // 新建任务
  298. const submitEvent = async () => {
  299. // 创建一个新的行对象,用于保存表单数据
  300. const newRow: RowVO = {
  301. platformNumber: taskRuleForm.number,
  302. platformName: taskRuleForm.name,
  303. country: taskRuleForm.country,
  304. brandName: taskRuleForm.brand,
  305. user_name: taskRuleForm.operation,
  306. currencyCode: taskRuleForm.currency,
  307. child_user_number: '',
  308. };
  309. // 将新行添加到表格数据中
  310. gridOptions.data.push(newRow);
  311. try {
  312. await createTask();
  313. dialogFormVisible.value = false;
  314. //获取更新表格数据
  315. await getTaskList();
  316. gridOptions.loading = true;
  317. setTimeout(() => {
  318. gridOptions.loading = false;
  319. }, 300);
  320. } catch (error) {
  321. console.error('Failed to save task:', error);
  322. }
  323. };
  324. // 提交表单
  325. const submitForm = async (formEl: FormInstance | undefined) => {
  326. if (!formEl) return;
  327. await formEl.validate(async (valid, fields) => {
  328. if (valid) {
  329. await submitEvent();
  330. await nextTick();
  331. taskRuleFormRef.value.resetFields();
  332. await VXETable.modal.message({content: '创建成功', status: 'success'});
  333. } else {
  334. // console.log('error submit!', fields);
  335. }
  336. });
  337. };
  338. async function fetchOperationSelect() {
  339. try {
  340. const resp = await getOperationSelect();
  341. operationList.value = resp.data.map((item: any) => {
  342. return {value: item.id, label: item.name};
  343. });
  344. } catch (e) {
  345. console.error('Failed to fetch operation select:', e);
  346. }
  347. }
  348. onMounted(() => {
  349. getTaskList();
  350. fetchOperationSelect();
  351. });
  352. </script>
  353. <template>
  354. <el-card class="custom-card-style flex gap-1.5 justify-between my-1.5 mx-2">
  355. <Selector ref="selectorRef" @update:filteredData="filteredDataChange" />
  356. </el-card>
  357. <el-card class="mx-2">
  358. <div style="position: relative">
  359. <vxe-grid ref="xGrid" v-bind="gridOptions" v-on="gridEvents">
  360. <template #toolbar_buttons>
  361. <vxe-button icon="vxe-icon-add" plain @click="dialogFormVisible = true"> 添加任务</vxe-button>
  362. <vxe-button icon="vxe-icon-delete" @click="removeEvent">删除</vxe-button>
  363. <vxe-button icon="vxe-icon-save" @click="saveEvent">保存</vxe-button>
  364. </template>
  365. <template #operate="{ row }">
  366. <template v-if="hasActiveEditRow(row)">
  367. <vxe-button content="取消" @click="clearRowEvent"></vxe-button>
  368. <vxe-button status="primary" content="保存" @click="saveRowEvent(row)"></vxe-button>
  369. </template>
  370. <template v-else>
  371. <vxe-button content="编辑" @click="editRowEvent(row)"></vxe-button>
  372. </template>
  373. </template>
  374. <template #number_edit="{ row }">
  375. <vxe-input v-model="row.platformNumber"></vxe-input>
  376. </template>
  377. <template #name_edit="{ row }">
  378. <vxe-input v-model="row.platformName"></vxe-input>
  379. </template>
  380. <template #country_edit="{ row }">
  381. <vxe-input v-model="row.country"></vxe-input>
  382. </template>
  383. <template #brand_edit="{ row }">
  384. <vxe-input v-model="row.brandName"></vxe-input>
  385. </template>
  386. <template #operation_edit="{ row }">
  387. <vxe-select v-model="row.user_name" transfer>
  388. <vxe-option v-for="item in operationList" :key="item.value" :value="item.value"
  389. :label="item.label"></vxe-option>
  390. </vxe-select>
  391. </template>
  392. <template #currency_edit="{ row }">
  393. <vxe-input v-model="row.currencyCode"></vxe-input>
  394. </template>
  395. <template #quantity_edit="{ row }">
  396. <vxe-select v-model="row.child_user_number" transfer>
  397. <vxe-option v-for="item in quantityList" :key="item.value" :value="item.value"
  398. :label="item.label"></vxe-option>
  399. </vxe-select>
  400. </template>
  401. </vxe-grid>
  402. </div>
  403. </el-card>
  404. <el-dialog v-model="dialogFormVisible" title="新建任务" width="500">
  405. <el-form
  406. ref="taskRuleFormRef"
  407. style="max-width: 600px"
  408. :model="taskRuleForm"
  409. :rules="rules"
  410. label-width="auto"
  411. class="demo-taskRuleForm"
  412. :size="formSize"
  413. status-icon>
  414. <el-form-item label="平台编号" prop="number">
  415. <el-input v-model="taskRuleForm.number" placeholder="请输入平台编号" />
  416. </el-form-item>
  417. <el-form-item label="平台名称" prop="name">
  418. <el-input v-model="taskRuleForm.name" placeholder="请输入平台名称" />
  419. </el-form-item>
  420. <el-form-item label="国家" prop="country">
  421. <el-input v-model="taskRuleForm.country" placeholder="请输入国家" />
  422. </el-form-item>
  423. <el-form-item label="品牌" prop="brand">
  424. <el-input v-model="taskRuleForm.brand" placeholder="请输入品牌" />
  425. </el-form-item>
  426. <el-form-item label="运营" prop="operation">
  427. <el-select v-model="taskRuleForm.operation" placeholder="请选择运营">
  428. <el-option v-for="item in operationList" :key="item.value" :value="item.value"
  429. :label="item.label"></el-option>
  430. </el-select>
  431. </el-form-item>
  432. <el-form-item label="回款币种" prop="currency">
  433. <el-input v-model="taskRuleForm.currency" placeholder="请输入回款币种" />
  434. </el-form-item>
  435. </el-form>
  436. <template #footer>
  437. <div class="dialog-footer">
  438. <el-button @click="dialogFormVisible = false ;resetForm(taskRuleFormRef)">取消</el-button>
  439. <el-button type="primary" @click="submitForm(taskRuleFormRef)"> 确认</el-button>
  440. </div>
  441. </template>
  442. </el-dialog>
  443. </template>
  444. <style scoped>
  445. .custom-card-style {
  446. z-index: 999;
  447. position: sticky;
  448. top: 0;
  449. }
  450. </style>