index.vue 35 KB

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