planInfo.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <script lang="ts" setup>
  2. /**
  3. * @Name: PlanningSales.vue
  4. * @Description: 计划销售额录入
  5. * @Author: xinyan
  6. */
  7. import {
  8. exportPlanSalesData,
  9. getPlanSalesData,
  10. postCreatePlanSalesData,
  11. postUpdatePlanSalesData
  12. } from '/src/views/reportManage/dataCenter/api';
  13. import { planColumns } from '/src/views/reportManage/TaskManage/utils/columns';
  14. import { reactive, ref } from 'vue';
  15. import { VxeGridInstance } from 'vxe-table';
  16. import dayjs from 'dayjs';
  17. import Selector from '/src/views/reportManage/dataCenter/normalDisplay/components/Selector/index.vue';
  18. import { ElMessage, FormInstance, FormRules } from 'element-plus';
  19. import { dateType } from '/src/views/reportManage/TaskManage/utils/enum';
  20. import { exportTaskData, getPlanList } from '/src/views/reportManage/TaskManage/api';
  21. // 年份选择器
  22. const currentYear = ref('');
  23. // 筛选查询
  24. const selectorRef = ref(null);
  25. const taskIds = ref({});
  26. // 表单数据
  27. const dialogVisible = ref(false);
  28. const formRef = ref<FormInstance>();
  29. const formData = reactive({
  30. january: null,
  31. february: null,
  32. march: null,
  33. april: null,
  34. may: null,
  35. june: null,
  36. july: null,
  37. august: null,
  38. september: null,
  39. october: null,
  40. november: null,
  41. december: null
  42. });
  43. const rules = reactive<FormRules>({
  44. january: [{ required: true, message: '请输入1月的计划销售额', trigger: 'blur' }],
  45. february: [{ required: true, message: '请输入2月的计划销售额', trigger: 'blur' }],
  46. march: [{ required: true, message: '请输入3月的计划销售额', trigger: 'blur' }],
  47. april: [{ required: true, message: '请输入4月的计划销售额', trigger: 'blur' }],
  48. may: [{ required: true, message: '请输入5月的计划销售额', trigger: 'blur' }],
  49. june: [{ required: true, message: '请输入6月的计划销售额', trigger: 'blur' }],
  50. july: [{ required: true, message: '请输入7月的计划销售额', trigger: 'blur' }],
  51. august: [{ required: true, message: '请输入8月的计划销售额', trigger: 'blur' }],
  52. september: [{ required: true, message: '请输入9月的计划销售额', trigger: 'blur' }],
  53. october: [{ required: true, message: '请输入10月的计划销售额', trigger: 'blur' }],
  54. november: [{ required: true, message: '请输入11月的计划销售额', trigger: 'blur' }],
  55. december: [{ required: true, message: '请输入12月的计划销售额', trigger: 'blur' }]
  56. });
  57. // 表单项配置,用于动态生成输入框
  58. const formItems = [
  59. { field: 'january', label: '1月' },
  60. { field: 'february', label: '2月' },
  61. { field: 'march', label: '3月' },
  62. { field: 'april', label: '4月' },
  63. { field: 'may', label: '5月' },
  64. { field: 'june', label: '6月' },
  65. { field: 'july', label: '7月' },
  66. { field: 'august', label: '8月' },
  67. { field: 'september', label: '9月' },
  68. { field: 'october', label: '10月' },
  69. { field: 'november', label: '11月' },
  70. { field: 'december', label: '12月' }
  71. ];
  72. // 表格
  73. const currentRow = ref('');
  74. const months = [
  75. '1月', '2月', '3月', '4月', '5月',
  76. '6月', '7月', '8月', '9月', '10月',
  77. '11月', '12月'
  78. ];
  79. const originalData = ref([]);
  80. const xGrid = ref<VxeGridInstance>();
  81. const activeEditRow = ref(null);
  82. const gridOptions = reactive({
  83. border: 'inner',
  84. // showOverflow: true,
  85. height: 750,
  86. loading: false,
  87. round: true,
  88. editConfig: {
  89. trigger: 'manual',
  90. mode: 'row',
  91. showStatus: true,
  92. autoClear: false,
  93. },
  94. columnConfig: {
  95. resizable: true,
  96. },
  97. toolbarConfig: {
  98. custom: true,
  99. zoom: {
  100. iconIn: 'vxe-icon-fullscreen',
  101. iconOut: 'vxe-icon-minimize'
  102. },
  103. slots: {
  104. // buttons: 'toolbar_buttons',
  105. tools: 'toolbar_tools'
  106. }
  107. },
  108. pagerConfig: {
  109. enabled: true,
  110. total: 20,
  111. currentPage: 1,
  112. pageSize: 20,
  113. pageSizes: [10, 20, 30],
  114. },
  115. columns: planColumns,
  116. data: [],
  117. });
  118. function setDefaultDate() {
  119. currentYear.value = dayjs().format('YYYY');
  120. }
  121. const disabledDate = (time: Date) => {
  122. const currentYear = new Date().getFullYear();
  123. const selectedYear = time.getFullYear();
  124. return selectedYear > currentYear + 1;
  125. };
  126. async function create(row) {
  127. currentRow.value = row;
  128. dialogVisible.value = true;
  129. }
  130. const resetForm = (formEl: FormInstance | undefined) => {
  131. if (!formEl) return;
  132. formEl.resetFields();
  133. };
  134. async function handleSave() {
  135. const monthMap = {
  136. january: 1,
  137. february: 2,
  138. march: 3,
  139. april: 4,
  140. may: 5,
  141. june: 6,
  142. july: 7,
  143. august: 8,
  144. september: 9,
  145. october: 10,
  146. november: 11,
  147. december: 12
  148. };
  149. const monthPlanSales = Object.keys(formData).map((month) => ({
  150. month: monthMap[month], // 使用映射表获取对应的月份数字
  151. planSales: parseFloat(formData[month]) || 0 // 计划销售额转换为数字
  152. }));
  153. const body = {
  154. year_date: currentYear.value,
  155. task: currentRow.value.task || 0,
  156. month_plan_sales: monthPlanSales,
  157. };
  158. try {
  159. await postCreatePlanSalesData(body);
  160. dialogVisible.value = false;
  161. await getList();
  162. } catch (error) {
  163. console.error('提交数据时出错:', error);
  164. }
  165. }
  166. const submitForm = async (formEl: FormInstance | undefined) => {
  167. if (!formEl) return;
  168. await formEl.validate((valid, fields) => {
  169. if (valid) {
  170. handleSave();
  171. formEl.resetFields();
  172. ElMessage.success('创建成功');
  173. } else {
  174. ElMessage.error('添加失败,请检查填写数据');
  175. }
  176. });
  177. };
  178. const gridEvents = {
  179. pageChange({ currentPage, pageSize }) {
  180. if (gridOptions.pagerConfig) {
  181. gridOptions.pagerConfig.currentPage = currentPage;
  182. gridOptions.pagerConfig.pageSize = pageSize;
  183. }
  184. getList();
  185. }
  186. };
  187. const isMonthIdExist = (row) => {
  188. return months.some(month => row[month] && row[month].id);
  189. };
  190. const hasActiveEditRow = (row) => {
  191. const $grid = xGrid.value;
  192. if ($grid) {
  193. return $grid.isEditByRow(row);
  194. }
  195. return false;
  196. };
  197. const clearRowEvent = (row) => {
  198. activeEditRow.value = false;
  199. const $grid = xGrid.value;
  200. if ($grid) {
  201. $grid.clearEdit();
  202. }
  203. };
  204. // 进入编辑模式
  205. const handelEditRow = (row) => {
  206. activeEditRow.value = true;
  207. const $grid = xGrid.value;
  208. if ($grid) {
  209. for (const month in row) {
  210. if (row[month] && row[month].id) {
  211. originalData.value = JSON.parse(JSON.stringify(row[month]));
  212. }
  213. }
  214. $grid.setEditRow(row);
  215. }
  216. };
  217. async function saveRow(row) {
  218. const $grid = xGrid.value;
  219. if ($grid) {
  220. const updatedData = [];
  221. for (const month in row) {
  222. if (row[month] && row[month].id) {
  223. const planSales = parseFloat(row[month].planSales);
  224. if (!isNaN(planSales)) {
  225. updatedData.push({
  226. id: row[month].id,
  227. planSales: planSales
  228. });
  229. }
  230. }
  231. }
  232. try {
  233. await postUpdatePlanSalesData(updatedData);
  234. activeEditRow.value = false;
  235. await $grid.clearEdit();
  236. await getList();
  237. ElMessage.success('更新成功');
  238. } catch (error) {
  239. ElMessage.error('更新失败,请检查填写数据');
  240. }
  241. }
  242. }
  243. async function getList() {
  244. try {
  245. gridOptions.loading = true;
  246. const response = await getPlanList({
  247. page: gridOptions.pagerConfig.currentPage,
  248. limit: gridOptions.pagerConfig.pageSize,
  249. year_date: currentYear.value,
  250. task_ids: taskIds.value,
  251. });
  252. gridOptions.data = response.data;
  253. gridOptions.pagerConfig.total = response.total;
  254. } catch (error) {
  255. console.error('Error fetching task data:', error);
  256. } finally {
  257. gridOptions.loading = false;
  258. }
  259. }
  260. function updateDataChange(newId) {
  261. if (selectorRef.value) {
  262. if (gridOptions.pagerConfig) {
  263. gridOptions.pagerConfig.currentPage = 1;
  264. }
  265. taskIds.value = newId.value;
  266. getList();
  267. }
  268. }
  269. async function handleExport() {
  270. try {
  271. gridOptions.loading = true;
  272. const response = await exportPlanSalesData({
  273. year_date: currentYear.value,
  274. task_ids: taskIds.value,
  275. });
  276. const url = window.URL.createObjectURL(new Blob([response.data]));
  277. const link = document.createElement('a');
  278. link.href = url;
  279. link.setAttribute('download', '计划销售额数据.xlsx');
  280. document.body.appendChild(link);
  281. link.click();
  282. gridOptions.loading = false;
  283. ElMessage.success('导出数据成功');
  284. } catch (error) {
  285. console.error('导出数据失败:', error);
  286. }
  287. }
  288. watch(currentYear, (newVal, oldVal) => {
  289. if (newVal !== oldVal) {
  290. currentYear.value = dayjs(newVal).format('YYYY');
  291. }
  292. });
  293. const cellStyle = () => {
  294. return {
  295. fontSize: '13px',
  296. fontWeight: '500',
  297. };
  298. };
  299. const headerCellStyle = () => {
  300. return {
  301. fontSize: '13px',
  302. };
  303. };
  304. onMounted(() => {
  305. setDefaultDate();
  306. // getList();
  307. });
  308. </script>
  309. <template>
  310. <el-card class=" my-3 mx-8 p-0">
  311. <div class="flex gap-1.5 justify-between mx-2 items-center">
  312. <Selector ref="selectorRef" @update:updateData="updateDataChange" />
  313. <div class="demo-date-picker">
  314. <div class="block">
  315. <span class="demonstration">年份:</span>
  316. <el-date-picker
  317. v-model="currentYear"
  318. :disabled-date="disabledDate"
  319. placeholder="Pick a year"
  320. style="width: 150px"
  321. type="year"
  322. @change="getList"
  323. />
  324. </div>
  325. </div>
  326. </div>
  327. </el-card>
  328. <el-card class="mx-8">
  329. <vxe-grid ref="xGrid" :cell-style="cellStyle" :header-cell-style="headerCellStyle" v-bind="gridOptions"
  330. v-on="gridEvents">
  331. <template #toolbar_tools>
  332. <div class="pr-2.5">
  333. <el-tooltip content="下载表格" placement="top">
  334. <vxe-button circle icon="vxe-icon-download" @click="handleExport"></vxe-button>
  335. </el-tooltip>
  336. </div>
  337. </template>
  338. <template #operate="{ row }">
  339. <template v-if="hasActiveEditRow(row)">
  340. <el-button link size="small" @click="clearRowEvent(row)">取消</el-button>
  341. <el-button link size="small" type="warning" @click="saveRow(row)">保存</el-button>
  342. </template>
  343. <template v-else>
  344. <el-button :disabled="!isMonthIdExist(row)||activeEditRow" link size="small" type="success"
  345. @click="handelEditRow(row)">
  346. 修改
  347. </el-button>
  348. </template>
  349. <el-button v-if="!hasActiveEditRow(row)" :disabled="isMonthIdExist(row)||activeEditRow" link size="small"
  350. type="primary"
  351. @click="create(row)">创建
  352. </el-button>
  353. </template>
  354. <template v-for="col in planColumns" #[`${col.slots?.edit}`]="{ row }">
  355. <el-input v-model="row[col.field].planSales" />
  356. </template>
  357. </vxe-grid>
  358. </el-card>
  359. <el-dialog v-model="dialogVisible" :before-close="handleClose" style="border-radius: 10px;"
  360. title="创建计划销售额" width="600">
  361. <template #title>
  362. <span class="text-xl">创建计划销售额</span>
  363. <div class="mt-2" style="display: flex; align-items: center;color: darkgray">
  364. <div style="margin-right: 8px;">
  365. 平台编号:
  366. <span class="italic pl-1 pr-2">{{ currentRow.platformNumber }}</span>
  367. </div>
  368. <div>
  369. 平台名称:
  370. <span class="italic pl-1">{{ currentRow.platformName }}</span>
  371. </div>
  372. </div>
  373. </template>
  374. <el-form
  375. ref="formRef"
  376. :model="formData"
  377. :rules="rules"
  378. label-position="top"
  379. label-width="auto"
  380. status-icon
  381. >
  382. <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px">
  383. <el-form-item v-for="item in formItems" :label="`${item.label}计划销售额`" :prop="item.field">
  384. <el-input v-model="formData[item.field]"></el-input>
  385. </el-form-item>
  386. </div>
  387. </el-form>
  388. <template #footer>
  389. <div class="dialog-footer">
  390. <el-button @click="dayFormVisible = false ;resetForm(formRef)">取消</el-button>
  391. <el-button type="primary" @click="submitForm(formRef)">确定</el-button>
  392. </div>
  393. </template>
  394. </el-dialog>
  395. </template>
  396. <style scoped>
  397. .demo-date-picker {
  398. display: flex;
  399. flex-direction: row-reverse;
  400. }
  401. .block {
  402. display: flex;
  403. align-items: center;
  404. flex-wrap: nowrap;
  405. overflow: hidden;
  406. /* width: 100%; */
  407. }
  408. .demo-date-picker .demonstration {
  409. color: var(--el-text-color-secondary);
  410. font-size: 14px;
  411. white-space: nowrap;
  412. margin-right: 2px;
  413. }
  414. </style>