forecastIndex.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. import { getForecastList, addForecast, updateForecast } from '@/api/forecast'
  2. import { getCustomerList } from '@/api/common'
  3. import { getItemList } from '@/api/common'
  4. import {
  5. APPROVAL_STATUS,
  6. APPROVAL_STATUS_CONFIG,
  7. APPROVAL_STATUS_OPTIONS,
  8. getApprovalStatusLabel,
  9. getApprovalStatusType,
  10. canEdit,
  11. FORECAST_FORM_RULES,
  12. DEFAULT_FORECAST_FORM
  13. } from '@/constants/forecast'
  14. import { mapGetters } from 'vuex'
  15. import ForecastFormAvue from '@/components/forecast-form/index'
  16. /**
  17. * @typedef {import('@/api/forecast/types').ForecastRecord} ForecastRecord
  18. * @typedef {import('@/api/forecast/types').ForecastForm} ForecastForm
  19. * @typedef {import('./types').CustomerOption} CustomerOption
  20. * @typedef {import('./types').ItemOption} ItemOption
  21. * @typedef {import('./types').PageConfig} PageConfig
  22. * @typedef {import('./types').ForecastComponent} ForecastComponent
  23. * @typedef {import('@/api/forecast/types').ForecastForm} ForecastFormModel
  24. * @typedef {import('@/api/forecast/types').ForecastRecord} ForecastItem
  25. */
  26. /**
  27. * 经销商销售预测申报页面业务逻辑混入
  28. * @description 提供预测申报的增删改查、表单验证、远程搜索等功能
  29. * @this {ForecastComponent & Vue}
  30. */
  31. export default {
  32. /**
  33. * 组件注册
  34. */
  35. components: {
  36. ForecastFormAvue
  37. },
  38. data() {
  39. return {
  40. /** @type {ForecastForm} 表单数据 */
  41. form: { ...DEFAULT_FORECAST_FORM },
  42. /** @type {Record<string, any>} 查询参数 */
  43. query: {},
  44. /** @type {boolean} 表格加载状态 */
  45. loading: true,
  46. /** @type {boolean} 表单提交状态 */
  47. submitting: false,
  48. /** @type {boolean} 保存按钮加载状态 */
  49. saveLoading: false,
  50. /** @type {boolean} 添加/编辑弹窗显示状态(保留兼容性) */
  51. dialogVisible: false,
  52. /** @type {boolean} 是否为编辑模式(保留兼容性) */
  53. isEdit: false,
  54. /** @type {boolean} 表单页面显示状态 */
  55. editVisible: false,
  56. /** @type {'add'|'edit'|'view'} 表单模式:'add' | 'edit' | 'view' */
  57. editMode: 'add',
  58. /** @type {string} 表单标题 */
  59. editTitle: '',
  60. /** @type {string|number|null} 当前编辑的预测记录ID */
  61. currentForecastId: null,
  62. /** @type {{pageSize: number, currentPage: number, total: number}} 分页配置 */
  63. page: {
  64. pageSize: 10,
  65. currentPage: 1,
  66. total: 0
  67. },
  68. /** @type {Array<ForecastRecord>} 表格数据 */
  69. data: [],
  70. /** @type {Array<CustomerOption>} 客户选项 */
  71. customerOptions: [],
  72. /** @type {boolean} 客户选项加载状态 */
  73. customerLoading: false,
  74. /** @type {Array<ItemOption>} 物料选项 */
  75. itemOptions: [],
  76. /** @type {boolean} 物料选项加载状态 */
  77. itemLoading: false,
  78. /** @type {AvueCrudOption} avue表格配置 */
  79. option: {
  80. height: 'auto',
  81. calcHeight: 30,
  82. tip: false,
  83. searchShow: true,
  84. searchMenuSpan: 6,
  85. border: true,
  86. index: true,
  87. viewBtn: false,
  88. selection: false,
  89. addBtn: true,
  90. editBtn: false, // 禁用内置编辑按钮
  91. delBtn: false,
  92. excelBtn: false,
  93. columnBtn: false,
  94. refreshBtn: true,
  95. dialogClickModal: false,
  96. addBtnText: '新增预测申报',
  97. editBtnText: '编辑',
  98. // 添加这个配置来完全隐藏操作列
  99. menu: false,
  100. column: [
  101. {
  102. label: '年份',
  103. prop: 'year',
  104. type: 'year',
  105. valueFormat: 'yyyy', // 添加这行,确保提交的数据是YYYY格式
  106. search: true,
  107. searchSpan: 6,
  108. width: 100
  109. },
  110. {
  111. label: '月份',
  112. prop: 'month',
  113. type: 'select',
  114. dicData: [
  115. { label: '1月', value: 1 },
  116. { label: '2月', value: 2 },
  117. { label: '3月', value: 3 },
  118. { label: '4月', value: 4 },
  119. { label: '5月', value: 5 },
  120. { label: '6月', value: 6 },
  121. { label: '7月', value: 7 },
  122. { label: '8月', value: 8 },
  123. { label: '9月', value: 9 },
  124. { label: '10月', value: 10 },
  125. { label: '11月', value: 11 },
  126. { label: '12月', value: 12 }
  127. ],
  128. search: true,
  129. searchSpan: 6,
  130. width: 100
  131. },
  132. {
  133. label: '物料名称',
  134. prop: 'itemName',
  135. search: false,
  136. width: 200,
  137. overHidden: true
  138. },
  139. {
  140. label: '物料编码',
  141. prop: 'itemCode',
  142. search: false,
  143. width: 150,
  144. overHidden: true
  145. },
  146. {
  147. label: '规格',
  148. prop: 'specs',
  149. search: false,
  150. width: 150,
  151. overHidden: true
  152. },
  153. {
  154. label: '预测数量',
  155. prop: 'forecastQuantity',
  156. type: 'number',
  157. precision: 4,
  158. search: false,
  159. width: 120,
  160. align: 'right'
  161. },
  162. {
  163. label: '当前库存',
  164. prop: 'currentInventory',
  165. type: 'number',
  166. precision: 4,
  167. search: false,
  168. width: 120,
  169. align: 'right'
  170. },
  171. {
  172. label: '品牌名称',
  173. prop: 'brandName',
  174. search: false,
  175. width: 120,
  176. overHidden: true
  177. },
  178. {
  179. label: '审批状态',
  180. prop: 'approvalStatus',
  181. type: 'select',
  182. dicData: APPROVAL_STATUS_OPTIONS,
  183. slot: true,
  184. search: true,
  185. searchSpan: 6,
  186. width: 120
  187. },
  188. {
  189. label: '审批人',
  190. prop: 'approvedName',
  191. search: false,
  192. width: 120,
  193. overHidden: true
  194. },
  195. {
  196. label: '审批时间',
  197. prop: 'approvedTime',
  198. type: 'datetime',
  199. format: 'yyyy-MM-dd HH:mm:ss',
  200. search: false,
  201. width: 160
  202. },
  203. {
  204. label: '预测编码',
  205. prop: 'forecastCode',
  206. search: true,
  207. searchSpan: 6,
  208. width: 150,
  209. overHidden: true
  210. },
  211. {
  212. label: '客户名称',
  213. prop: 'customerName',
  214. search: true,
  215. searchSpan: 6,
  216. width: 180,
  217. overHidden: true
  218. },
  219. {
  220. label: '客户编码',
  221. prop: 'customerCode',
  222. search: false,
  223. width: 150,
  224. overHidden: true
  225. },
  226. {
  227. label: '创建时间',
  228. prop: 'createTime',
  229. type: 'datetime',
  230. format: 'yyyy-MM-dd HH:mm:ss',
  231. search: false,
  232. width: 160
  233. },
  234. // 添加自定义编辑按钮列
  235. {
  236. label: '操作',
  237. prop: 'editBtn',
  238. slot: true,
  239. width: 120,
  240. fixed: 'right'
  241. }
  242. ]
  243. },
  244. /** @type {Record<string, Array<ValidationRule>>} 表单验证规则 */
  245. formRules: FORECAST_FORM_RULES
  246. }
  247. },
  248. computed: {
  249. ...mapGetters(['permission']),
  250. /**
  251. * 权限配置
  252. * @this {Vue & ForecastComponent}
  253. * @returns {{addBtn: boolean, viewBtn: boolean, delBtn: boolean, editBtn: boolean}} 权限配置对象
  254. */
  255. permissionList() {
  256. return {
  257. // addBtn: this.vaildData(this.permission.forecast_add, false),
  258. // viewBtn: this.vaildData(this.permission.forecast_view, false),
  259. // delBtn: false, // 不提供删除功能
  260. // editBtn: this.vaildData(this.permission.forecast_edit, false)
  261. addBtn: false,
  262. viewBtn: false,
  263. delBtn: false, // 不提供删除功能
  264. editBtn: false
  265. }
  266. },
  267. /**
  268. * 弹窗标题
  269. * @this {Vue & ForecastComponent}
  270. * @returns {string} 弹窗标题文本
  271. */
  272. dialogTitle() {
  273. return this.isEdit ? '编辑预测申报' : '新增预测申报'
  274. }
  275. },
  276. /**
  277. * 组件创建时初始化
  278. * @this {Vue & ForecastComponent}
  279. */
  280. created() {
  281. this.loadCustomerOptions()
  282. this.loadItemOptions()
  283. },
  284. methods: {
  285. /**
  286. * 检查是否可以编辑
  287. * @param {ForecastRecord} row - 预测记录行数据
  288. * @returns {boolean} 是否可以编辑
  289. */
  290. canEdit(row) {
  291. return canEdit(row.approvalStatus || APPROVAL_STATUS.PENDING)
  292. },
  293. /**
  294. * 加载客户选项
  295. * @this {ForecastComponent & Vue}
  296. * @returns {Promise<void>}
  297. */
  298. async loadCustomerOptions() {
  299. try {
  300. this.customerLoading = true
  301. const res = await getCustomerList(1, 20)
  302. if (res.data && res.data.success) {
  303. this.customerOptions = res.data.data.records.map(item => ({
  304. value: parseInt(item.Customer_ID.toString()),
  305. label: item.Customer_NAME,
  306. customerId: parseInt(item.Customer_ID.toString()),
  307. customerCode: item.Customer_CODE,
  308. customerName: item.Customer_NAME
  309. }))
  310. }
  311. } catch (error) {
  312. console.error('加载客户选项失败:', error)
  313. this.$message.error('加载客户选项失败')
  314. } finally {
  315. this.customerLoading = false
  316. }
  317. },
  318. /**
  319. * 远程搜索客户
  320. * @this {ForecastComponent & Vue}
  321. * @param {string} query - 搜索关键词
  322. * @returns {Promise<void>}
  323. */
  324. async remoteSearchCustomer(query) {
  325. if (!query) {
  326. this.loadCustomerOptions()
  327. return
  328. }
  329. try {
  330. this.customerLoading = true
  331. /** @type {import('@/api/types/common').CustomerQueryParams} */
  332. const params = {
  333. customerName: query,
  334. current: 1,
  335. size: 50
  336. }
  337. const res = await getCustomerList(1, 50, params)
  338. if (res.data && res.data.success) {
  339. this.customerOptions = res.data.data.records.map(item => ({
  340. value: parseInt(item.Customer_ID.toString()),
  341. label: item.Customer_NAME,
  342. customerId: parseInt(item.Customer_ID.toString()),
  343. customerCode: item.Customer_CODE,
  344. customerName: item.Customer_NAME
  345. }))
  346. }
  347. } catch (error) {
  348. console.error('搜索客户失败:', error)
  349. } finally {
  350. this.customerLoading = false
  351. }
  352. },
  353. /**
  354. * 客户选择变化处理
  355. * @this {ForecastComponent & Vue}
  356. * @param {number} customerId - 客户ID
  357. * @returns {void}
  358. */
  359. handleCustomerChange(customerId) {
  360. const customer = this.customerOptions.find(item => item.value === customerId)
  361. if (customer) {
  362. this.form.customerId = customer.value
  363. this.form.customerCode = customer.customerCode
  364. this.form.customerName = customer.customerName
  365. }
  366. },
  367. /**
  368. * 加载物料选项
  369. * @this {ForecastComponent & Vue}
  370. * @returns {Promise<void>}
  371. */
  372. async loadItemOptions() {
  373. try {
  374. this.itemLoading = true
  375. const res = await getItemList(1, 20)
  376. if (res.data && res.data.success) {
  377. this.itemOptions = res.data.data.records.map(item => ({
  378. value: item.Item_ID,
  379. label: item.Item_Name,
  380. itemId: item.Item_ID,
  381. itemCode: item.Item_Code,
  382. itemName: item.Item_Name,
  383. specs: item.Item_PECS || ''
  384. }))
  385. }
  386. } catch (error) {
  387. console.error('加载物料选项失败:', error)
  388. this.$message.error('加载物料选项失败')
  389. } finally {
  390. this.itemLoading = false
  391. }
  392. },
  393. /**
  394. * 远程搜索物料
  395. * @this {ForecastComponent & Vue}
  396. * @param {string} query - 搜索关键词
  397. * @returns {Promise<void>}
  398. */
  399. async remoteSearchItem(query) {
  400. if (!query) {
  401. this.loadItemOptions()
  402. return
  403. }
  404. try {
  405. this.itemLoading = true
  406. const res = await getItemList(1, 50, {
  407. itemName: query
  408. })
  409. if (res.data && res.data.success) {
  410. this.itemOptions = res.data.data.records.map(item => ({
  411. value: item.Item_ID,
  412. label: item.Item_Name,
  413. itemId: item.Item_ID,
  414. itemCode: item.Item_Code,
  415. itemName: item.Item_Name,
  416. specs: item.Item_PECS || ''
  417. }))
  418. }
  419. } catch (error) {
  420. console.error('搜索物料失败:', error)
  421. } finally {
  422. this.itemLoading = false
  423. }
  424. },
  425. /**
  426. * 物料选择变化处理
  427. * @this {Vue & ForecastComponent}
  428. * @param {number} itemId - 物料ID
  429. * @returns {void}
  430. */
  431. handleItemChange(itemId) {
  432. const item = this.itemOptions.find(option => option.value === itemId)
  433. if (item) {
  434. this.form.itemId = item.value
  435. this.form.itemCode = item.itemCode
  436. this.form.itemName = item.itemName
  437. this.form.specs = item.specs
  438. }
  439. },
  440. /**
  441. * 获取审批状态标签
  442. * @param {number} status - 审批状态
  443. * @returns {string} 状态标签
  444. */
  445. getApprovalStatusLabel,
  446. /**
  447. * 获取审批状态类型
  448. * @param {number} status - 审批状态
  449. * @returns {string} 状态类型
  450. */
  451. getApprovalStatusType,
  452. /**
  453. * 检查是否可以编辑
  454. * @param {ForecastRecord|ForecastItem} row - 行数据
  455. * @returns {boolean} 是否可以编辑
  456. */
  457. canEditRow(row) {
  458. return row.approvalStatus !== undefined ? canEdit(row.approvalStatus) : false
  459. },
  460. /**
  461. * 搜索重置
  462. * @this {Vue & {query: Record<string, any>, page: {pageSize: number, currentPage: number, total: number}, onLoad: (page: any, params?: any) => Promise<void>}}
  463. * @returns {void}
  464. */
  465. searchReset() {
  466. this.query = {}
  467. this.onLoad(this.page)
  468. },
  469. /**
  470. * 搜索条件变化
  471. * @this {Vue & {query: Record<string, any>, page: {pageSize: number, currentPage: number, total: number}, onLoad: (page: any, params?: any) => Promise<void>}}
  472. * @param {Record<string, any>} params - 搜索参数
  473. * @param {() => void} done - 完成回调
  474. * @returns {void}
  475. */
  476. searchChange(params, done) {
  477. this.query = params
  478. this.page.currentPage = 1
  479. this.onLoad(this.page, params)
  480. done()
  481. },
  482. /**
  483. * 页码变化
  484. * @this {Vue & {page: {pageSize: number, currentPage: number, total: number}}}
  485. * @param {number} currentPage - 当前页码
  486. * @returns {void}
  487. */
  488. currentChange(currentPage) {
  489. this.page.currentPage = currentPage
  490. },
  491. /**
  492. * 页大小变化
  493. * @this {Vue & {page: {pageSize: number, currentPage: number, total: number}}}
  494. * @param {number} pageSize - 页大小
  495. * @returns {void}
  496. */
  497. sizeChange(pageSize) {
  498. this.page.pageSize = pageSize
  499. },
  500. /**
  501. * 刷新数据
  502. * @this {Vue & {page: {pageSize: number, currentPage: number, total: number}, query: Record<string, any>, onLoad: (page: any, params?: any) => Promise<void>}}
  503. * @returns {void}
  504. */
  505. refreshChange() {
  506. this.onLoad(this.page, this.query)
  507. },
  508. /**
  509. * 加载数据
  510. * @this {Vue & {loading: boolean, page: {pageSize: number, currentPage: number, total: number}, query: Record<string, any>, data: Array<ForecastRecord>, $message: any}}
  511. * @param {{currentPage: number, pageSize: number}} page - 分页参数
  512. * @param {Record<string, any>} params - 查询参数
  513. * @returns {Promise<void>}
  514. */
  515. async onLoad(page, params = {}) {
  516. this.loading = true
  517. try {
  518. const res = await getForecastList(page.currentPage, page.pageSize, {
  519. ...params,
  520. ...this.query
  521. })
  522. if (res.data && res.data.success) {
  523. const data = res.data.data
  524. this.page.total = data.total
  525. this.data = data.records
  526. } else {
  527. this.$message.error(res.data?.msg || '加载数据失败')
  528. this.data = []
  529. this.page.total = 0
  530. }
  531. } catch (error) {
  532. console.error('加载数据失败:', error)
  533. this.$message.error('加载数据失败,请稍后重试')
  534. this.data = []
  535. this.page.total = 0
  536. } finally {
  537. this.loading = false
  538. }
  539. },
  540. /**
  541. * 新增按钮点击
  542. * @this {Vue & {isEdit: boolean, currentForecastId: string|number|null, dialogVisible: boolean}}
  543. * @returns {void}
  544. */
  545. rowAdd() {
  546. this.isEdit = false
  547. this.currentForecastId = null
  548. this.dialogVisible = true
  549. },
  550. /**
  551. * 新增预测申报(新版本)
  552. * @this {Vue & {editMode: string, editTitle: string, currentForecastId: string|number|null, editVisible: boolean, form: ForecastFormModel, isEdit: boolean, dialogVisible: boolean, generateForecastCode: () => void}}
  553. * @description 显示新增预测申报表单页面
  554. * @returns {void}
  555. */
  556. handleAdd() {
  557. console.log('新增预测申报')
  558. this.editMode = 'add'
  559. this.editTitle = '新增预测申报'
  560. this.currentForecastId = null
  561. this.editVisible = true
  562. // 重置表单数据
  563. this.form = { ...DEFAULT_FORECAST_FORM }
  564. // 生成预测编码
  565. this.generateForecastCode()
  566. // 保持兼容性
  567. this.isEdit = false
  568. this.dialogVisible = true
  569. },
  570. /**
  571. * 编辑按钮点击
  572. * @this {Vue & {isEdit: boolean, currentForecastId: string|number|null, dialogVisible: boolean, $message: any, canEditRow: (row: ForecastRecord) => boolean}}
  573. * @param {ForecastRecord} row - 行数据
  574. * @param {number} index - 行索引
  575. * @returns {void}
  576. */
  577. rowEdit(row, index) {
  578. if (!this.canEditRow(row)) {
  579. this.$message.warning('已审批或已拒绝的记录不能修改')
  580. return
  581. }
  582. this.isEdit = true
  583. this.currentForecastId = row.id
  584. this.dialogVisible = true
  585. },
  586. /**
  587. * 编辑按钮点击(新版本)
  588. * @this {Vue & {editMode: string, editTitle: string, currentForecastId: string|number|null, editVisible: boolean, $message: any, canEditRow: (row: ForecastItem) => boolean, form: ForecastFormModel, isEdit: boolean, dialogVisible: boolean}}
  589. * @param {ForecastItem} row - 行数据
  590. * @param {number} index - 行索引
  591. * @description 显示编辑预测申报表单页面
  592. * @returns {void}
  593. */
  594. handleEdit(row, index) {
  595. if (!this.canEditRow(row)) {
  596. this.$message.warning('已审批或已拒绝的记录不能修改')
  597. return
  598. }
  599. this.editMode = 'edit'
  600. this.editTitle = '编辑预测申报'
  601. this.currentForecastId = row.id
  602. this.editVisible = true
  603. // 设置表单数据
  604. this.form = { ...row }
  605. // 保持兼容性
  606. this.isEdit = true
  607. this.dialogVisible = true
  608. },
  609. /**
  610. * 返回列表
  611. * @this {Vue & {editVisible: boolean, editMode: string, editTitle: string, currentForecastId: string|number|null, form: ForecastFormModel, dialogVisible: boolean, isEdit: boolean}}
  612. * @description 关闭表单页面,返回列表页面
  613. * @returns {void}
  614. */
  615. handleBackToList() {
  616. this.editVisible = false
  617. this.editMode = 'add'
  618. this.editTitle = ''
  619. this.currentForecastId = null
  620. this.form = { ...DEFAULT_FORECAST_FORM }
  621. // 保持兼容性
  622. this.dialogVisible = false
  623. this.isEdit = false
  624. },
  625. /**
  626. * 处理保存按钮点击
  627. * @this {Vue & {saveLoading: boolean, $refs: any}}
  628. * @returns {void}
  629. */
  630. handleSave() {
  631. if (this.$refs.forecastForm) {
  632. this.saveLoading = true
  633. this.$refs.forecastForm.handleSubmit()
  634. }
  635. },
  636. /**
  637. * 处理表单提交成功
  638. * @this {Vue & {saveLoading: boolean, editMode: string, $message: any, handleBackToList: () => void, refreshChange: () => void}}
  639. * @param {{msg?: string}} data - 提交成功的数据
  640. * @returns {void}
  641. */
  642. handleFormSubmit(data) {
  643. console.log('表单提交成功', data)
  644. console.log('当前编辑模式:', this.editMode)
  645. this.saveLoading = false
  646. // 保存当前编辑模式,因为 handleBackToList 会重置它
  647. const currentEditMode = this.editMode
  648. // 关闭表单页面,返回列表
  649. this.handleBackToList()
  650. // 刷新列表数据
  651. this.refreshChange()
  652. // 显示成功消息 - 使用API返回的msg字段
  653. const successMsg = data?.msg || (currentEditMode === 'add' ? '新增成功' : '修改成功')
  654. console.log(data)
  655. this.$message.success(successMsg)
  656. },
  657. /**
  658. * 处理表单关闭
  659. * @this {Vue & {saveLoading: boolean, handleBackToList: () => void}}
  660. * @returns {void}
  661. */
  662. handleFormClose() {
  663. console.log('表单关闭')
  664. this.saveLoading = false
  665. // 返回列表页面
  666. this.handleBackToList()
  667. },
  668. /**
  669. * 表单提交(兼容旧版本)
  670. * @returns {Promise<void>}
  671. */
  672. async rowSave() {
  673. // 此方法保留用于兼容 avue-crud 的事件绑定
  674. // 实际表单提交逻辑已移至 forecast-form 组件内部
  675. console.warn('rowSave 方法已废弃,请使用组件化表单')
  676. },
  677. /**
  678. * 取消表单(兼容旧版本)
  679. * @this {Vue & {dialogVisible: boolean}}
  680. * @returns {void}
  681. */
  682. rowCancel() {
  683. // 此方法保留用于兼容 avue-crud 的事件绑定
  684. this.dialogVisible = false
  685. },
  686. /**
  687. * 生成预测编码
  688. * @this {Vue & {form: ForecastFormModel}}
  689. * @returns {void}
  690. */
  691. generateForecastCode() {
  692. const now = new Date()
  693. const year = now.getFullYear()
  694. const month = String(now.getMonth() + 1).padStart(2, '0')
  695. const timestamp = now.getTime().toString().slice(-6)
  696. this.form.forecastCode = `FC-${year}-${month}-${timestamp}`
  697. }
  698. }
  699. }