index.vue 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. <script lang="ts" setup>
  2. import Selector from '/src/views/reportManage/dataCenter/normalDisplay/components/Selector/index.vue';
  3. import { onMounted, reactive, ref } from 'vue';
  4. import { VxeGridInstance, VxeGridListeners, VxeGridProps, VXETable } from 'vxe-table';
  5. import {
  6. exportTaskData,
  7. getCurrencyCodeSelect,
  8. getOperationSelect,
  9. getTasks,
  10. postCreateTask,
  11. postDeleteTask,
  12. postSendMessage,
  13. postUpdateManyTask,
  14. postUpdateTask,
  15. postUpdateTaskStatus
  16. } from '/src/views/reportManage/TaskManage/api.ts';
  17. import { ComponentSize, ElMessage, FormInstance, FormRules } from 'element-plus';
  18. import { Delete, Plus } from '@element-plus/icons-vue';
  19. import { dateType, requiredFields } from '/@/views/reportManage/TaskManage/utils/enum';
  20. import { taskColumns } from '/@/views/reportManage/TaskManage/utils/columns';
  21. const selectorRef = ref(null);
  22. const message = ref('');
  23. const currencyList = ref([]);// 货币列表
  24. //表单
  25. interface taskRuleForm {
  26. number: string;
  27. name: string;
  28. country: string;
  29. brand: string;
  30. operation: string[];
  31. operater: [];
  32. currency: string;
  33. currencyCodePlatform: string;
  34. line: string;
  35. ipaddress: string;
  36. company: string;
  37. platform: string;
  38. companyEnglishName: string;
  39. address: string;
  40. juridicalPerson: string;
  41. juridicalPersonCreditCard: string;
  42. juridicalPersonCreditCardAddress: string;
  43. receivablesAccount: string;
  44. receivablesAccountCompany: string;
  45. vatNumber: string;
  46. vatCompany: string;
  47. }
  48. const formSize = ref<ComponentSize>('default');
  49. const dialogFormVisible = ref(false);
  50. const taskRuleFormRef = ref<FormInstance>();
  51. const taskRuleForm = reactive({
  52. number: '',
  53. name: '',
  54. country: '',
  55. brand: '',
  56. operation: [],
  57. operater: [],
  58. currency: '',
  59. currencyCodePlatform: '',
  60. });
  61. const resetForm = (formEl: FormInstance | undefined) => {
  62. if (!formEl) return;
  63. formEl.resetFields();
  64. };
  65. const rules = reactive<FormRules>({
  66. number: [{ required: true, message: '请输入平台编号', trigger: 'blur' },],
  67. name: [{ required: true, message: '请输入平台名称', trigger: 'blur' }],
  68. country: [{ required: true, message: '请输入国家', trigger: 'blur' }],
  69. brand: [{ required: true, message: '请输入品牌', trigger: 'blur' }],
  70. operation: [{ required: true, message: '请选择填写人', trigger: 'change' }],
  71. operater: [{ required: true, message: '请输入运营', trigger: 'change' }],
  72. currency: [{ required: true, message: '请输入回款/余额币种', trigger: 'blur' }],
  73. currencyCodePlatform: [{ required: true, message: '请输入平台货币', trigger: 'blur' }],
  74. line: [{ required: true, message: '请输入线路', trigger: 'blur' }],
  75. ipaddress: [{ required: true, message: '请输入IP地址', trigger: 'blur' }],
  76. company: [{ required: true, message: '请输入注册公司', trigger: 'blur' }],
  77. platform: [{ required: true, message: '请输入平台', trigger: 'blur' }],
  78. });
  79. // 修改填写人弹窗
  80. const userDialogFormVisible = ref(false);
  81. const updateSelect = ref(1);
  82. //表格
  83. interface RowVO {
  84. platformNumber: string;
  85. platformName: string;
  86. country: string;
  87. brandName: string;
  88. user_name: string;
  89. operater: [];
  90. currencyCode: string;
  91. currencyCodePlatform: string;
  92. child_user_number: number;
  93. user: [];
  94. line: string;
  95. ipaddress: string;
  96. company: string;
  97. platform: string;
  98. }
  99. const xGrid = ref<VxeGridInstance<RowVO>>();
  100. const originalData = ref([]);
  101. let allTasks = []; // 用于存储所有任务数据
  102. const operationList = ref([]);// 填写人列表
  103. const filter = ref({}); // 筛选条件
  104. const gridOptions = reactive<VxeGridProps<RowVO>>({
  105. border: 'inner',
  106. keepSource: true,
  107. //showOverflow: true,
  108. height: 930,
  109. loading: false,
  110. round: true,
  111. toolbarConfig: {
  112. zoom: {
  113. iconIn: 'vxe-icon-fullscreen',
  114. iconOut: 'vxe-icon-minimize'
  115. },
  116. slots: {
  117. buttons: 'toolbar_buttons',
  118. tools: 'toolbar_tools'
  119. }
  120. },
  121. rowConfig: {
  122. isHover: true,
  123. },
  124. columnConfig: {
  125. resizable: true,
  126. },
  127. pagerConfig: {
  128. enabled: true,
  129. total: 20,
  130. currentPage: 1,
  131. pageSize: 20,
  132. pageSizes: [10, 20, 30],
  133. },
  134. editConfig: {
  135. trigger: 'click',
  136. mode: 'row',
  137. showStatus: true,
  138. //autoClear: false,
  139. },
  140. checkboxConfig: {
  141. reserve: true,
  142. highlight: true,
  143. range: true,
  144. },
  145. columns: taskColumns,
  146. data: [],
  147. });
  148. const gridEvents: VxeGridListeners<RowVO> = {
  149. pageChange({ currentPage, pageSize }) {
  150. if (gridOptions.pagerConfig) {
  151. gridOptions.pagerConfig.currentPage = currentPage;
  152. gridOptions.pagerConfig.pageSize = pageSize;
  153. getTaskList();
  154. }
  155. },
  156. };
  157. const hasActiveEditRow = (row: RowVO) => {
  158. const $grid = xGrid.value;
  159. if ($grid) {
  160. return $grid.isEditByRow(row);
  161. }
  162. return false;
  163. };
  164. const editRowEvent = (row: RowVO) => {
  165. const $grid = xGrid.value;
  166. if ($grid) {
  167. // 在进入编辑状态前保存原始数据
  168. //originalDataMap.set(row.id, { ...row });
  169. //// 初始化 row.user 确保其与 row.user_name 同步
  170. if (!row.user || row.user.length === 0) {
  171. row.user = userNameToUser(row.user_name); // 转换 user_name 到 user
  172. }
  173. $grid.setEditRow(row);
  174. }
  175. };
  176. const handleEditActived = ({ row, column }) => {
  177. if (!row.user || row.user.length === 0) {
  178. row.user = userNameToUser(row.user_name); // 转换 user_name 到 user
  179. }
  180. };
  181. const handleEditClosed = ({ row, column }) => {
  182. if (column.property === 'user_name') {
  183. // 将 user 转换为 user_name 并更新 row
  184. row.user_name = userToUserName(row.user);
  185. // 强制刷新视图
  186. $grid.refreshRow(row);
  187. }
  188. };
  189. // 禁用自动退出编辑模式
  190. const disableAutoExitEdit = () => {
  191. const $grid = xGrid.value;
  192. if ($grid) {
  193. gridOptions.editConfig.autoClear = false; // 手动模式,禁止点击其他地方退出编辑模式
  194. }
  195. };
  196. // 启用自动退出编辑模式
  197. const enableAutoExitEdit = () => {
  198. const $grid = xGrid.value;
  199. if ($grid) {
  200. gridOptions.editConfig.autoClear = true; // 点击触发退出编辑模式
  201. }
  202. };
  203. // 将 user 数组转换为 user_name 字符串
  204. function userToUserName(user: string[]): string {
  205. return operationList.value
  206. .filter(op => user.includes(op.value))
  207. .map(op => op.label)
  208. .join(', ');
  209. }
  210. // 将 user_name 字符串转换为 user 数组
  211. function userNameToUser(user_name: string): string[] {
  212. return operationList.value
  213. .filter(op => user_name.includes(op.label))
  214. .map(op => op.value);
  215. }
  216. // 清除编辑状态并保存已编辑的数据
  217. const clearRowEvent = (row) => {
  218. const $grid = xGrid.value;
  219. if ($grid) {
  220. // 手动保存当前行的数据
  221. const editRecord = $grid.getEditRecord();
  222. if (editRecord) {
  223. const { row: editedRow } = editRecord;
  224. // 更新原始 row 数据,确保编辑的值被保存
  225. row.user = editedRow.user;
  226. row.user_name = userToUserName(editedRow.user);
  227. }
  228. // 清除编辑状态
  229. $grid.clearEdit();
  230. }
  231. };
  232. // 获取任务列表
  233. async function getTaskList() {
  234. try {
  235. gridOptions.loading = true;
  236. const response = await getTasks({
  237. page: gridOptions.pagerConfig.currentPage,
  238. limit: gridOptions.pagerConfig.pageSize,
  239. ...filter.value,
  240. });
  241. gridOptions.data = response.data;
  242. originalData.value = JSON.parse(JSON.stringify(response.data));
  243. gridOptions.pagerConfig.total = response.total;
  244. } catch (error) {
  245. console.error('Error fetching task data:', error);
  246. } finally {
  247. gridOptions.loading = false;
  248. }
  249. }
  250. // 筛选器变化
  251. function filteredDataChange(newList) {
  252. filter.value = newList.value;
  253. if (selectorRef.value) {
  254. if (gridOptions.pagerConfig) {
  255. gridOptions.pagerConfig.currentPage = 1;
  256. }
  257. getTaskList(newList.value);
  258. }
  259. }
  260. // 全部保存按钮
  261. async function saveEvent(row) {
  262. clearRowEvent(row);
  263. const $grid = xGrid.value;
  264. if ($grid) {
  265. try {
  266. const { updateRecords } = $grid.getRecordset();
  267. const originalDataMap = new Map(originalData.value.map(item => [item.id, item]));
  268. const updatedRecords = updateRecords.map(record => {
  269. const originalRecord = originalDataMap.get(record.id);
  270. if (!originalRecord) return null;
  271. const updatedFields = {};
  272. // 比较字段并记录变化
  273. for (const key in record) {
  274. // 针对 operater 字段的特殊处理
  275. if (key === 'operater') {
  276. // 如果 operater 是字符串,将其转换为数组
  277. let recordOperater = Array.isArray(record.operater)
  278. ? record.operater
  279. : record.operater.split(',').map(item => item.trim()); // 中文逗号分割并去除空格
  280. // 如果 originalRecord.operater 是字符串,也转换为数组
  281. let originalOperater = Array.isArray(originalRecord.operater)
  282. ? originalRecord.operater
  283. : originalRecord.operater.split(',').map(item => item.trim());
  284. // 比较两个数组,确保它们不相等时才记录变化
  285. if (JSON.stringify(recordOperater) !== JSON.stringify(originalOperater)) {
  286. updatedFields[key] = recordOperater; // 存储更新后的 operater 数组
  287. }
  288. } else {
  289. // 对于非 operater 字段,进行普通的比较
  290. if (record[key] !== originalRecord[key]) {
  291. updatedFields[key] = record[key];
  292. }
  293. }
  294. }
  295. // 如果有变化字段,返回该记录,否则跳过
  296. if (Object.keys(updatedFields).length > 0) {
  297. return {
  298. id: record.id,
  299. ...updatedFields
  300. };
  301. }
  302. return null;
  303. }).filter(record => record); // 过滤掉值为 null 的记录
  304. if (updatedRecords.length > 0) {
  305. // 调用接口提交更新
  306. await postUpdateManyTask(updatedRecords);
  307. await getTaskList();
  308. await VXETable.modal.message({
  309. content: `更新 ${ updatedRecords.length } 条`,
  310. status: 'success',
  311. });
  312. } else {
  313. await VXETable.modal.message({
  314. content: '没有检测到变化',
  315. status: 'info',
  316. });
  317. }
  318. } catch (e) {
  319. console.log('error', e);
  320. }
  321. }
  322. }
  323. // 批量修改填写人
  324. async function updateUser() {
  325. const $grid = xGrid.value;
  326. if ($grid) {
  327. const selectRecords = $grid.getCheckboxRecords(); // 获取勾选的表格行
  328. const selectedUsers = taskRuleForm.operation; // 获取选择的填写人
  329. const operationType = updateSelect.value; // 获取选择的操作类型
  330. const updateData = selectRecords.map(record => {
  331. const existingUsers = userNameToUser(record.user_name) || [];
  332. let updatedUsers;
  333. if (operationType === 1) { // 添加操作
  334. updatedUsers = Array.from(new Set([...existingUsers, ...selectedUsers]));
  335. } else if (operationType === 2) { // 删除操作
  336. updatedUsers = existingUsers.filter(user => !selectedUsers.includes(user));
  337. }
  338. return {
  339. id: record.id,
  340. user: updatedUsers, // 更新后的 user 数组
  341. };
  342. });
  343. try {
  344. // 调用接口批量保存修改的数据
  345. await postUpdateManyTask(updateData);
  346. if (operationType === 1) { // 添加操作
  347. ElMessage.success('添加成功');
  348. } else if (operationType === 2) { // 删除操作
  349. ElMessage.success('删除成功');
  350. }
  351. userDialogFormVisible.value = false; // 关闭弹窗
  352. await getTaskList(); // 重新加载表格数据
  353. } catch (error) {
  354. console.error('修改失败', error);
  355. ElMessage.error('修改失败');
  356. } finally {
  357. // 清除表单数据
  358. taskRuleForm.operation = []; // 清除选择的填写人
  359. updateSelect.value = 1; // 清除操作类型
  360. }
  361. }
  362. }
  363. function updateUserCancel() {
  364. taskRuleForm.operation = [];
  365. updateSelect.value = 1;
  366. userDialogFormVisible.value = false;
  367. }
  368. // 删除任务
  369. async function deleteTask() {
  370. const $grid = xGrid.value;
  371. if ($grid) {
  372. const selectRecords = $grid.getCheckboxRecords();
  373. const selectedIds = selectRecords.map(record => record.id);
  374. const obj = { keys: selectedIds };
  375. try {
  376. const resp = await postDeleteTask(obj);
  377. if (resp.code === 2000) {
  378. ElMessage({
  379. message: '删除成功',
  380. type: 'success',
  381. });
  382. await getTaskList();
  383. }
  384. } catch (e) {
  385. ElMessage({
  386. message: '删除失败',
  387. type: 'error',
  388. });
  389. }
  390. }
  391. }
  392. const isDeleteDisabled = computed(() => {
  393. const $grid = xGrid.value;
  394. return !$grid || $grid.getCheckboxRecords().length === 0;
  395. });
  396. const removeEvent = async () => {
  397. const $grid = xGrid.value;
  398. if ($grid) {
  399. const selectRecords = $grid.getCheckboxRecords();
  400. if (selectRecords.length > 0) {
  401. const type = await VXETable.modal.confirm('您确定要删除选中的数据?');
  402. if (type === 'confirm') {
  403. await deleteTask(selectRecords);
  404. await $grid.removeCheckboxRow();
  405. }
  406. } else {
  407. await VXETable.modal.message({ content: '请选择要删除的数据', status: 'error' });
  408. }
  409. }
  410. };
  411. // 数据校验
  412. const validateRow = (row) => {
  413. for (const { field, title } of requiredFields) {
  414. if (!row[field] || (Array.isArray(row[field]) && row[field].length === 0)) {
  415. ElMessage.error(`${ title }不能为空`);
  416. return;
  417. }
  418. }
  419. if (!currencyList.value.includes(row.currencyCode)) {
  420. ElMessage.error('回款币种格式不正确,请重新选择');
  421. return;
  422. }
  423. if (!currencyList.value.includes(row.currencyCodePlatform)) {
  424. ElMessage.error('回款/余额币种格式不正确,请重新选择');
  425. return;
  426. }
  427. return true;
  428. };
  429. async function updateRow(row) {
  430. const $grid = xGrid.value;
  431. if ($grid) {
  432. const updatedRowData = {
  433. id: row.id,
  434. platformNumber: row.platformNumber,
  435. platformName: row.platformName,
  436. country: row.country,
  437. brandName: row.brandName,
  438. user: row.user,
  439. operater: Array.isArray(row.operater) ? row.operater : row.operater.split(',').map(item => item.trim()),
  440. currencyCode: row.currencyCode,
  441. currencyCodePlatform: row.currencyCodePlatform,
  442. line: row.line,
  443. ipaddress: row.ipaddress,
  444. company: row.company,
  445. platform: row.platform,
  446. companyEnglishName: row.companyEnglishName,
  447. address: row.address,
  448. juridicalPerson: row.juridicalPerson,
  449. juridicalPersonCreditCard: row.juridicalPersonCreditCard,
  450. juridicalPersonCreditCardAddress: row.juridicalPersonCreditCardAddress,
  451. receivablesAccount: row.receivablesAccount,
  452. receivablesAccountCompany: row.receivablesAccountCompany,
  453. vatNumber: row.vatNumber,
  454. vatCompany: row.vatCompany,
  455. };
  456. try {
  457. const response = await postUpdateTask(updatedRowData);
  458. if (response.code === 2000) {
  459. ElMessage.success('更新成功');
  460. } else if (response.code == 400) {
  461. ElMessage.warning(`${ response.data.description }`);
  462. } else {
  463. ElMessage.error('更新失败');
  464. }
  465. } catch (error) {
  466. console.log('error:', error);
  467. }
  468. }
  469. }
  470. // 更新行任务保存按钮
  471. const saveRowEvent = async (row: RowVO) => {
  472. //clearRowEvent(row)
  473. const $grid = xGrid.value;
  474. if ($grid) {
  475. if (!validateRow(row)) {
  476. return;
  477. }
  478. await $grid.clearEdit();
  479. await updateRow(row);
  480. await getTaskList();
  481. //gridOptions.loading = true;
  482. //setTimeout(() => {
  483. // gridOptions.loading = false;
  484. //}, 300);
  485. }
  486. };
  487. // 更新状态
  488. async function handleStatusChange(row) {
  489. const $grid = xGrid.value;
  490. if ($grid) {
  491. const updatedData = {
  492. id: row.id,
  493. status: row.status,
  494. };
  495. // 传partial=1,可只修改状态
  496. const query = { partial: 1, };
  497. try {
  498. const response = await postUpdateTaskStatus(query, updatedData);
  499. if (response.code === 2000) {
  500. ElMessage.success('状态更新成功');
  501. } else if (response.code == 400) {
  502. ElMessage.warning(`${ response.data.description }`);
  503. } else {
  504. ElMessage.error('状态更新失败');
  505. }
  506. } catch (error) {
  507. console.log('error:', error);
  508. }
  509. }
  510. }
  511. // 创建任务
  512. async function createTask() {
  513. const body = {
  514. country: taskRuleForm.country,
  515. platformNumber: taskRuleForm.number,
  516. platformName: taskRuleForm.name,
  517. brandName: taskRuleForm.brand,
  518. currencyCode: taskRuleForm.currency,
  519. currencyCodePlatform: taskRuleForm.currencyCodePlatform,
  520. line: taskRuleForm.line,
  521. ipaddress: taskRuleForm.ipaddress,
  522. company: taskRuleForm.company,
  523. platform: taskRuleForm.platform,
  524. companyEnglishName: taskRuleForm.companyEnglishName,
  525. address: taskRuleForm.address,
  526. juridicalPerson: taskRuleForm.juridicalPerson,
  527. juridicalPersonCreditCard: taskRuleForm.juridicalPersonCreditCard,
  528. juridicalPersonCreditCardAddress: taskRuleForm.juridicalPersonCreditCardAddress,
  529. receivablesAccount: taskRuleForm.receivablesAccount,
  530. receivablesAccountCompany: taskRuleForm.receivablesAccountCompany,
  531. vatNumber: taskRuleForm.vatNumber,
  532. vatCompany: taskRuleForm.vatCompany,
  533. user: taskRuleForm.operation,
  534. operater: [taskRuleForm.operater],
  535. };
  536. try {
  537. const resp = await postCreateTask(body);
  538. if (resp.code === 2000) {
  539. dialogFormVisible.value = false;
  540. await getTaskList(); // 重新获取任务列表
  541. ElMessage({ message: '创建成功', type: 'success', });
  542. }
  543. } catch (error) {
  544. console.log('error:', error);
  545. ElMessage({ message: '创建失败', type: 'error', });
  546. }
  547. }
  548. // 提交任务确认按钮
  549. const submitForm = async (formEl) => {
  550. if (!formEl) return;
  551. await formEl.validate(async (valid, fields) => {
  552. if (valid) {
  553. const isDuplicate = allTasks.some(task => String(task.platformNumber) === String(taskRuleForm.number));
  554. if (isDuplicate) {
  555. await ElMessage({ message: '平台编号已存在,请重新输入', type: 'warning' });
  556. return;
  557. }
  558. if (!currencyList.value.includes(taskRuleForm.currency)) {
  559. await ElMessage({ message: '回款币种无效,请重新选择', type: 'warning' });
  560. return;
  561. }
  562. if (!currencyList.value.includes(taskRuleForm.currencyCodePlatform)) {
  563. await ElMessage({ message: '回款/余额币种无效,请重新选择', type: 'warning' });
  564. return;
  565. }
  566. await createTask();
  567. taskRuleFormRef.value.resetFields();
  568. }
  569. });
  570. };
  571. function handleClose(done: Function) {
  572. if (taskRuleFormRef.value) taskRuleFormRef.value.resetFields();
  573. done();
  574. }
  575. //发送通知
  576. async function sendMessage(selectedValue: string) {
  577. const body = {
  578. date_type: selectedValue,
  579. };
  580. try {
  581. const response = await postSendMessage(body);
  582. if (response.code === 2000) {
  583. ElMessage.success('发送成功');
  584. } else if (response.code == 400) {
  585. ElMessage.warning(`${ response.data.description }`);
  586. } else {
  587. ElMessage.error('发送失败');
  588. }
  589. } catch (error) {
  590. }
  591. }
  592. // 导出接口
  593. async function handleExport() {
  594. try {
  595. gridOptions.loading = true;
  596. const response = await exportTaskData(filter.value,);
  597. const url = window.URL.createObjectURL(new Blob([response.data]));
  598. const link = document.createElement('a');
  599. link.href = url;
  600. link.setAttribute('download', '店铺数据.xlsx');
  601. document.body.appendChild(link);
  602. link.click();
  603. gridOptions.loading = false;
  604. ElMessage.success('导出数据成功');
  605. } catch (error) {
  606. console.error('导出数据失败:', error);
  607. }
  608. }
  609. // 获取填写人下拉框
  610. async function fetchOperationSelect() {
  611. try {
  612. const resp = await getOperationSelect();
  613. operationList.value = resp.data.map((item: any) => {
  614. return { value: item.id, label: item.name };
  615. });
  616. } catch (e) {
  617. console.error('Failed to fetch operation select:', e);
  618. }
  619. }
  620. // 获取币种下拉框
  621. async function fetchCurrencyList() {
  622. try {
  623. const response = await getCurrencyCodeSelect(); // 替换为你的后端接口
  624. currencyList.value = response.data;
  625. } catch (error) {
  626. ElMessage.error('请求失败');
  627. }
  628. }
  629. const querySearch = (queryString, cb) => {
  630. const results = queryString
  631. ? currencyList.value.filter(currency => currency.toLowerCase().includes(queryString.toLowerCase()))
  632. : currencyList.value;
  633. cb(results);
  634. };
  635. const handleSelect = item => {
  636. taskRuleForm.currency = item;
  637. };
  638. const handleCurrencyCodePlatformSelect = item => {
  639. taskRuleForm.currencyCodePlatform = item;
  640. };
  641. function handleRowSelect(item, row) {
  642. row.currencyCode = item;
  643. }
  644. function handelRowCurrencyCodePlatformSelect(item, row) {
  645. row.currencyCodePlatform = item;
  646. }
  647. const formItems = ref([
  648. { label: '平台编号', prop: 'number', type: 'input', placeholder: '请输入平台编号' },
  649. { label: '平台名称', prop: 'name', type: 'input', placeholder: '请输入平台名称' },
  650. { label: '国家', prop: 'country', type: 'input', placeholder: '请输入国家' },
  651. { label: '品牌', prop: 'brand', type: 'input', placeholder: '请输入品牌' },
  652. {
  653. label: '录入人员',
  654. prop: 'operation',
  655. type: 'select',
  656. placeholder: '请选择录入人员',
  657. multiple: true,
  658. collapseTags: true,
  659. collapseTagsTooltip: true,
  660. options: operationList
  661. },
  662. { label: '运营', prop: 'operater', type: 'input', placeholder: '请输入运营' },
  663. {
  664. label: '平台币种',
  665. prop: 'currency',
  666. type: 'autocomplete',
  667. placeholder: '请输入回款币种',
  668. debounce: 100,
  669. fetchSuggestions: querySearch,
  670. triggerOnFocus: false,
  671. clearable: true,
  672. onSelect: handleSelect
  673. },
  674. {
  675. label: '回款/余额币种',
  676. prop: 'currencyCodePlatform',
  677. type: 'autocomplete',
  678. placeholder: '请输入回款/余额币种',
  679. debounce: 100,
  680. fetchSuggestions: querySearch,
  681. triggerOnFocus: false,
  682. clearable: true,
  683. onSelect: handleCurrencyCodePlatformSelect
  684. },
  685. { label: '平台', prop: 'platform', type: 'input', placeholder: '请输入平台' },
  686. { label: '线路', prop: 'line', type: 'input', placeholder: '请输入线路' },
  687. { label: 'IP地址', prop: 'ipaddress', type: 'input', placeholder: '请输入IP地址' },
  688. { label: '注册公司', prop: 'company', type: 'input', placeholder: '请输入注册公司' },
  689. { label: '公司英文名称', prop: 'companyEnglishName', type: 'input', placeholder: '请输入公司英文名称' },
  690. { label: '公司地址', prop: 'address', type: 'input', placeholder: '请输入公司地址' },
  691. { label: '公司法人', prop: 'juridicalPerson', type: 'input', placeholder: '请输入公司法人' },
  692. { label: '法人信用卡', prop: 'juridicalPersonCreditCard', type: 'input', placeholder: '请输入法人信用卡' },
  693. { label: '信用卡地址', prop: 'juridicalPersonCreditCardAddress', type: 'input', placeholder: '请输入信用卡地址' },
  694. { label: '收款账号', prop: 'receivablesAccount', type: 'input', placeholder: '请输入收款账号' },
  695. { label: '收款账号公司', prop: 'receivablesAccountCompany', type: 'input', placeholder: '请输入收款账号公司' },
  696. { label: 'VAT税号', prop: 'vatNumber', type: 'input', placeholder: '请输入VAT税号' },
  697. { label: 'VAT税号公司名称', prop: 'vatCompany', type: 'input', placeholder: '请输入VAT税号公司名称' },
  698. ]);
  699. // 表格样式
  700. const cellStyle = () => {
  701. return {
  702. fontSize: '12px',
  703. fontWeight: '600',
  704. };
  705. };
  706. const headerCellStyle = () => {
  707. return {
  708. fontSize: '13px',
  709. };
  710. };
  711. onMounted(() => {
  712. //getTaskList();
  713. fetchOperationSelect();
  714. fetchCurrencyList();
  715. });
  716. </script>
  717. <template>
  718. <div class="p-2.5">
  719. <el-card class="custom-card-style flex gap-1.5 justify-between" shadow="hover">
  720. <Selector ref="selectorRef" :showOperationSearch="true" @update:filteredData="filteredDataChange" />
  721. </el-card>
  722. <el-card class="my-3" shadow="hover">
  723. <vxe-grid ref="xGrid" :cell-style="cellStyle" :header-cell-style="headerCellStyle" stripe v-bind="gridOptions"
  724. v-on="gridEvents" @edit-actived="handleEditActived" @edit-closed="handleEditClosed">
  725. <template #toolbar_buttons>
  726. <el-button :icon="Plus" type="primary" @click="dialogFormVisible = true"> 添加任务</el-button>
  727. <!--<el-button plain type="success" @click="saveEvent">保存</el-button>-->
  728. <el-button :disabled="isDeleteDisabled" :icon="Delete" plain type="danger" @click="removeEvent">删除
  729. </el-button>
  730. <el-button v-if="!isDeleteDisabled" plain round type="success" @click="userDialogFormVisible =true">
  731. 修改填写人
  732. </el-button>
  733. </template>
  734. <template #toolbar_tools>
  735. <div class="pr-2.5">
  736. <el-tooltip content="保存" placement="top">
  737. <vxe-button circle icon="vxe-icon-save" @click="saveEvent"></vxe-button>
  738. </el-tooltip>
  739. <el-tooltip content="发送通知" placement="top">
  740. <el-dropdown style="padding: 0 10px;" trigger="click">
  741. <vxe-button circle icon="vxe-icon-bell"></vxe-button>
  742. <template #dropdown>
  743. <el-dropdown-menu>
  744. <el-dropdown-item v-for="info of dateType" @click="sendMessage(info.value)">{{
  745. info.label
  746. }}
  747. </el-dropdown-item>
  748. </el-dropdown-menu>
  749. </template>
  750. </el-dropdown>
  751. </el-tooltip>
  752. <el-tooltip content="下载表格" placement="top">
  753. <vxe-button circle icon="vxe-icon-download" @click="handleExport"></vxe-button>
  754. </el-tooltip>
  755. </div>
  756. </template>
  757. <template #operate="{ row }">
  758. <template v-if="hasActiveEditRow(row)">
  759. <vxe-button content="取消" type="text" @click="clearRowEvent(row)"></vxe-button>
  760. <vxe-button content="保存" status="success" type="text" @click="saveRowEvent(row)"></vxe-button>
  761. </template>
  762. <template v-else>
  763. <el-tooltip content="编辑" placement="top">
  764. <el-button icon="Edit" type="text" @click="editRowEvent(row)"></el-button>
  765. </el-tooltip>
  766. </template>
  767. </template>
  768. <template #number_edit="{ row }">
  769. <vxe-input v-model="row.platformNumber"></vxe-input>
  770. </template>
  771. <template #name_edit="{ row }">
  772. <vxe-input v-model="row.platformName"></vxe-input>
  773. </template>
  774. <template #country_edit="{ row }">
  775. <vxe-input v-model="row.country"></vxe-input>
  776. </template>
  777. <template #brand_edit="{ row }">
  778. <vxe-input v-model="row.brandName"></vxe-input>
  779. </template>
  780. <template #line_edit="{ row }">
  781. <vxe-input v-model="row.line"></vxe-input>
  782. </template>
  783. <template #ipaddress_edit="{ row }">
  784. <vxe-input v-model="row.ipaddress"></vxe-input>
  785. </template>
  786. <template #company_edit="{ row }">
  787. <vxe-input v-model="row.company"></vxe-input>
  788. </template>
  789. <template #platform_edit="{ row }">
  790. <vxe-input v-model="row.platform"></vxe-input>
  791. </template>
  792. <template #status_default="{ row }">
  793. <el-switch
  794. v-model="row.status"
  795. :active-value="1"
  796. :inactive-value="0"
  797. inline-prompt
  798. size="small"
  799. @change="handleStatusChange(row)"
  800. />
  801. </template>
  802. <template #operation_edit="{ row }">
  803. <vxe-select v-model="row.user" multiple>
  804. <vxe-option v-for="item in operationList" :key="item.value" :label="item.label"
  805. :value="item.value"></vxe-option>
  806. </vxe-select>
  807. </template>
  808. <template #operater_name_edit="{ row }">
  809. <vxe-input v-model="row.operater"></vxe-input>
  810. </template>
  811. <template #currency_edit="{ row }">
  812. <!--<vxe-input v-model="row.currencyCode"></vxe-input>-->
  813. <el-autocomplete
  814. v-model="row.currencyCode"
  815. :debounce="100"
  816. :fetch-suggestions="querySearch"
  817. :trigger-on-focus="false"
  818. clearable
  819. @blur="enableAutoExitEdit"
  820. @focus="disableAutoExitEdit"
  821. @select="item => handleRowSelect(item, row)"
  822. >
  823. <template v-slot="{ item }">
  824. <div>{{ item }}</div>
  825. </template>
  826. </el-autocomplete>
  827. </template>
  828. <template #currencyCodePlatform_edit="{ row }">
  829. <el-autocomplete
  830. v-model="row.currencyCodePlatform"
  831. :debounce="100"
  832. :fetch-suggestions="querySearch"
  833. :trigger-on-focus="false"
  834. clearable
  835. @blur="enableAutoExitEdit"
  836. @focus="disableAutoExitEdit"
  837. @select="item => handelRowCurrencyCodePlatformSelect(item,row)"
  838. >
  839. <template v-slot="{ item }">
  840. <div>{{ item }}</div>
  841. </template>
  842. </el-autocomplete>
  843. </template>
  844. <template #companyEnglishName_edit="{ row }">
  845. <vxe-input v-model="row.companyEnglishName"></vxe-input>
  846. </template>
  847. <template #address_edit="{ row }">
  848. <vxe-input v-model="row.address"></vxe-input>
  849. </template>
  850. <template #juridicalPerson_edit="{ row }">
  851. <vxe-input v-model="row.juridicalPerson"></vxe-input>
  852. </template>
  853. <template #juridicalPersonCreditCard_edit="{ row }">
  854. <vxe-input v-model="row.juridicalPersonCreditCard"></vxe-input>
  855. </template>
  856. <template #juridicalPersonCreditCardAddress_edit="{ row }">
  857. <vxe-input v-model="row.juridicalPersonCreditCardAddress"></vxe-input>
  858. </template>
  859. <template #receivablesAccount_edit="{ row }">
  860. <vxe-input v-model="row.receivablesAccount"></vxe-input>
  861. </template>
  862. <template #receivablesAccountCompany_edit="{ row }">
  863. <vxe-input v-model="row.receivablesAccountCompany"></vxe-input>
  864. </template>
  865. <template #vatNumber_edit="{ row }">
  866. <vxe-input v-model="row.vatNumber"></vxe-input>
  867. </template>
  868. <template #vatCompany_edit="{ row }">
  869. <vxe-input v-model="row.vatCompany"></vxe-input>
  870. </template>
  871. /
  872. </vxe-grid>
  873. </el-card>
  874. </div>
  875. <el-dialog v-model="dialogFormVisible" :before-close="handleClose" style="border-radius: 10px;" title="新建任务"
  876. width="500">
  877. <el-form
  878. ref="taskRuleFormRef"
  879. :model="taskRuleForm"
  880. :rules="rules"
  881. :size="formSize"
  882. class="demo-taskRuleForm"
  883. label-width="auto"
  884. status-icon
  885. style="max-width: 600px">
  886. <el-form-item
  887. v-for="(item, index) in formItems"
  888. :key="index"
  889. :label="item.label"
  890. :prop="item.prop"
  891. >
  892. <template v-if="item.type === 'input'">
  893. <el-input v-model="taskRuleForm[item.prop]" :placeholder="item.placeholder" />
  894. </template>
  895. <template v-else-if="item.type === 'select'">
  896. <el-select
  897. v-model="taskRuleForm[item.prop]"
  898. :collapse-tags="item.collapseTags"
  899. :collapse-tags-tooltip="item.collapseTagsTooltip"
  900. :multiple="item.multiple"
  901. :placeholder="item.placeholder"
  902. >
  903. <el-option
  904. v-for="option in item.options"
  905. :key="option.value"
  906. :label="option.label"
  907. :value="option.value"
  908. ></el-option>
  909. </el-select>
  910. </template>
  911. <template v-else-if="item.type === 'autocomplete'">
  912. <el-autocomplete
  913. v-model="taskRuleForm[item.prop]"
  914. :clearable="item.clearable"
  915. :debounce="item.debounce"
  916. :fetch-suggestions="item.fetchSuggestions"
  917. :placeholder="item.placeholder"
  918. :trigger-on-focus="item.triggerOnFocus"
  919. @select="item.onSelect"
  920. >
  921. <template v-slot="{ item }">
  922. <div>{{ item }}</div>
  923. </template>
  924. </el-autocomplete>
  925. </template>
  926. </el-form-item>
  927. </el-form>
  928. <template #footer>
  929. <div class="dialog-footer">
  930. <el-button @click="dialogFormVisible = false ;resetForm(taskRuleFormRef)">取消</el-button>
  931. <el-button type="primary" @click="submitForm(taskRuleFormRef)"> 确认</el-button>
  932. </div>
  933. </template>
  934. </el-dialog>
  935. <el-dialog v-model="userDialogFormVisible" :before-close="updateUserCancel" align-center
  936. style="border-radius: 10px;" title="修改填写人" width="500">
  937. <div class="mb-3">
  938. <el-radio-group v-model="updateSelect">
  939. <el-radio :label="1">添加</el-radio>
  940. <el-radio :label="2">删除</el-radio>
  941. </el-radio-group>
  942. </div>
  943. <el-form-item align-center label="录入人员:" prop="operation" width="500">
  944. <el-select v-model="taskRuleForm.operation" collapse-tags collapse-tags-tooltip multiple
  945. placeholder="请选择录入人员">
  946. <el-option v-for="item in operationList" :key="item.value" :label="item.label"
  947. :value="item.value"></el-option>
  948. </el-select>
  949. </el-form-item>
  950. <template #footer>
  951. <div class="dialog-footer">
  952. <el-button @click="updateUserCancel">取消</el-button>
  953. <el-button type="primary" @click="updateUser"> 确认</el-button>
  954. </div>
  955. </template>
  956. </el-dialog>
  957. </template>
  958. <style scoped>
  959. .custom-card-style {
  960. z-index: 999;
  961. position: sticky;
  962. /* margin-top: 15px; */
  963. margin-bottom: 5px;
  964. }
  965. .el-card {
  966. border: none;
  967. box-shadow: none;
  968. }
  969. :deep(.vxe-table--header .vxe-header--row th .vxe-cell,
  970. .vxe-table--body .vxe-body--row td .vxe-cell) {
  971. padding-left: 5px !important;
  972. padding-right: 0 !important;
  973. }
  974. </style>