index.vue 35 KB

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