cogs.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. <template>
  2. <div class="data-entry-container">
  3. <h1>COGS数据录入工具</h1>
  4. <!-- 当前成本结构 -->
  5. <div class="form-section">
  6. <h2>当前成本结构</h2>
  7. <table class="data-table">
  8. <thead>
  9. <tr>
  10. <th>成本项</th>
  11. <th>金额(元/米)</th>
  12. <th>占比(%)</th>
  13. </tr>
  14. </thead>
  15. <tbody>
  16. <tr>
  17. <td>{{ formData.curr.n1 }}</td>
  18. <td>
  19. <input type="number" v-model="formData.curr.v1" placeholder="请输入金额">
  20. </td>
  21. <td>
  22. <input type="number" v-model="formData.curr.p1" placeholder="请输入占比">
  23. </td>
  24. </tr>
  25. <tr>
  26. <td>{{ formData.curr.n2 }}</td>
  27. <td>
  28. <input type="number" v-model="formData.curr.v2" placeholder="请输入金额">
  29. </td>
  30. <td>
  31. <input type="number" v-model="formData.curr.p2" placeholder="请输入占比">
  32. </td>
  33. </tr>
  34. <tr>
  35. <td>{{ formData.curr.n3 }}</td>
  36. <td>
  37. <input type="number" v-model="formData.curr.v3" placeholder="请输入金额">
  38. </td>
  39. <td>
  40. <input type="number" v-model="formData.curr.p3" placeholder="请输入占比">
  41. </td>
  42. </tr>
  43. <tr>
  44. <td>{{ formData.curr.n4 }}</td>
  45. <td>
  46. <input type="number" v-model="formData.curr.v4" placeholder="请输入金额">
  47. </td>
  48. <td>
  49. <input type="number" v-model="formData.curr.p4" placeholder="请输入占比">
  50. </td>
  51. </tr>
  52. <tr>
  53. <td>{{ formData.curr.n5 }}</td>
  54. <td>
  55. <input type="number" v-model="formData.curr.v5" placeholder="请输入金额">
  56. </td>
  57. <td>
  58. <input type="number" v-model="formData.curr.p5" placeholder="请输入占比">
  59. </td>
  60. </tr>
  61. <tr>
  62. <td>{{ formData.curr.n6 }}</td>
  63. <td>
  64. <input type="number" v-model="formData.curr.v6" placeholder="请输入金额">
  65. </td>
  66. <td>
  67. <input type="number" v-model="formData.curr.p6" placeholder="请输入占比">
  68. </td>
  69. </tr>
  70. </tbody>
  71. </table>
  72. </div>
  73. <!-- 历史成本数据 -->
  74. <div class="form-section">
  75. <h2>单位成本历史数据</h2>
  76. <button @click="regenerateHistoricalData" class="add-btn">重新生成过去12个月</button>
  77. <table class="data-table">
  78. <thead>
  79. <tr>
  80. <th>月份</th>
  81. <th v-for="(key, index) in ['n1', 'n2', 'n3', 'n4', 'n5', 'n6']" :key="index">{{ formData.curr[key] }}</th>
  82. </tr>
  83. </thead>
  84. <tbody>
  85. <tr v-for="(item, index) in formData.his.n1" :key="index">
  86. <td>
  87. <input type="text" v-model="formData.his.n1[index].month" placeholder="请输入月份">
  88. </td>
  89. <td v-for="(key, idx) in ['n1', 'n2', 'n3', 'n4', 'n5', 'n6']" :key="idx">
  90. <input type="number" v-model="formData.his[key][index].value" placeholder="请输入值">
  91. </td>
  92. </tr>
  93. </tbody>
  94. </table>
  95. </div>
  96. <!-- 成本总趋势 -->
  97. <div class="form-section">
  98. <h2>成本总趋势</h2>
  99. <button @click="regenerateHistoricalData" class="add-btn">重新生成过去12个月</button>
  100. <table class="data-table">
  101. <thead>
  102. <tr>
  103. <th>月份</th>
  104. <th>总成本(元/米)</th>
  105. </tr>
  106. </thead>
  107. <tbody>
  108. <tr v-for="(item, index) in formData.trend" :key="index">
  109. <td>
  110. <input type="text" v-model="item.month" placeholder="请输入月份">
  111. </td>
  112. <td>
  113. <input type="number" v-model="item.value" placeholder="请输入值">
  114. </td>
  115. </tr>
  116. </tbody>
  117. </table>
  118. </div>
  119. <!-- 生产效率数据 -->
  120. <div class="form-section">
  121. <h2>人均单位成本</h2>
  122. <table class="data-table">
  123. <thead>
  124. <tr>
  125. <th v-for="(item, index) in formData.prod" :key="index">{{ item.name }}</th>
  126. </tr>
  127. </thead>
  128. <tbody>
  129. <tr>
  130. <td v-for="(item, index) in formData.prod" :key="index">
  131. <input type="number" v-model="item.value" placeholder="请输入值">
  132. </td>
  133. </tr>
  134. </tbody>
  135. </table>
  136. </div>
  137. <!-- 成本明细数据 -->
  138. <div class="form-section">
  139. <h2>成本明细数据</h2>
  140. <button @click="addDetailItem" class="add-btn">添加成本项</button>
  141. <div class="table-container">
  142. <table class="data-table detail-table">
  143. <thead>
  144. <tr>
  145. <th>成本项</th>
  146. <th v-for="(process, index) in processes" :key="index">{{ process }}</th>
  147. <th>操作</th>
  148. </tr>
  149. </thead>
  150. <tbody>
  151. <tr v-for="(item, index) in formData.detail" :key="index">
  152. <td>
  153. <input v-model="item.item" placeholder="请输入成本项">
  154. </td>
  155. <td v-for="(value, valueIndex) in item.values" :key="valueIndex">
  156. <input type="number" v-model="item.values[valueIndex]" placeholder="请输入值">
  157. </td>
  158. <td>
  159. <button @click="removeDetailItem(index)" class="remove-btn">删除</button>
  160. </td>
  161. </tr>
  162. </tbody>
  163. </table>
  164. </div>
  165. </div>
  166. <div class="actions">
  167. <button @click="submitForm" class="primary">保存</button>
  168. </div>
  169. <div v-if="jsonOutput" class="json-output">
  170. <h3>生成的JSON数据</h3>
  171. <pre>{{ jsonOutput }}</pre>
  172. <button @click="copyToClipboard">复制到剪贴板</button>
  173. </div>
  174. </div>
  175. </template>
  176. <script>
  177. import {onMounted, reactive, ref} from 'vue';
  178. import {getCogs, saveCogs} from "@/api/house/input";
  179. export default {
  180. name: "CogsInput",
  181. setup() {
  182. const formData = reactive({
  183. curr: {
  184. total: "4.32",
  185. n1: "人工工资+社保+福利",
  186. v1: 0.76,
  187. p1: 14.7,
  188. n2: "电+汽",
  189. v2: 1.3,
  190. p2: 26.7,
  191. n3: "运营+管理",
  192. v3: 0.33,
  193. p3: 11.3,
  194. n4: "机物料+修理+制版材料+染料+助剂+辅料+水",
  195. v4: 1.63,
  196. p4: 33.3,
  197. n5: "折旧",
  198. v5: 0.48,
  199. p5: 13.3,
  200. n6: "其他",
  201. v6: 0.08,
  202. p6: 0.67
  203. },
  204. his: {
  205. n1: [],
  206. n2: [],
  207. n3: [],
  208. n4: [],
  209. n5: [],
  210. n6: []
  211. },
  212. trend: [],
  213. prod: [],
  214. detail: []
  215. });
  216. const jsonOutput = ref('');
  217. const processes = ref(["加弹", "经编", "前整", "印染", "后整", "成品"]);
  218. // 页面加载时获取数据
  219. const fetchData = async () => {
  220. try {
  221. const response = await getCogs();
  222. if (response && response.data) {
  223. // 用获取到的数据填充表单
  224. Object.assign(formData, response.data);
  225. } else {
  226. console.warn('接口返回数据为空或格式不正确');
  227. }
  228. } catch (error) {
  229. console.error('获取数据失败:', error);
  230. // 保留默认数据,确保页面不为空白
  231. }
  232. };
  233. // 组件挂载时调用获取数据的方法
  234. onMounted(() => {
  235. fetchData();
  236. });
  237. const submitForm = async () => {
  238. try {
  239. await saveCogs(formData);
  240. alert('表单提交成功');
  241. } catch (error) {
  242. console.error('提交失败:', error);
  243. alert('提交失败');
  244. }
  245. };
  246. const generatePastMonths = () => {
  247. const months = [];
  248. const now = new Date();
  249. for (let i = 11; i >= 0; i--) {
  250. const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
  251. const month = (date.getMonth() + 1).toString().padStart(2, '0');
  252. const year = date.getFullYear();
  253. months.push(`${year}-${month}`);
  254. }
  255. return months;
  256. };
  257. // 添加重新生成历史数据的方法
  258. const regenerateHistoricalData = () => {
  259. const newMonths = generatePastMonths();
  260. // 更新每个成本项的月份数据
  261. const costKeys = ['n1', 'n2', 'n3', 'n4', 'n5', 'n6'];
  262. costKeys.forEach(key => {
  263. if (formData.his[key] && Array.isArray(formData.his[key])) {
  264. formData.his[key] = formData.his[key].map((item, index) => ({
  265. ...item,
  266. month: newMonths[index] || item.month
  267. }));
  268. }
  269. });
  270. // 更新总趋势数据的月份
  271. if (formData.trend && Array.isArray(formData.trend)) {
  272. formData.trend = formData.trend.map((item, index) => ({
  273. ...item,
  274. month: newMonths[index] || item.month
  275. }));
  276. }
  277. };
  278. const addDetailItem = () => {
  279. formData.detail.push({
  280. item: "",
  281. values: Array(processes.value.length).fill(0)
  282. });
  283. };
  284. const removeDetailItem = (index) => {
  285. formData.detail.splice(index, 1);
  286. };
  287. const generateJson = () => {
  288. jsonOutput.value = JSON.stringify(formData, null, 2);
  289. };
  290. const copyToClipboard = () => {
  291. navigator.clipboard.writeText(jsonOutput.value)
  292. .then(() => alert('已复制到剪贴板'))
  293. .catch(err => console.error('复制失败:', err));
  294. };
  295. return {
  296. formData,
  297. jsonOutput,
  298. processes,
  299. submitForm,
  300. addDetailItem,
  301. removeDetailItem,
  302. generateJson,
  303. copyToClipboard,
  304. regenerateHistoricalData
  305. };
  306. }
  307. };
  308. </script>
  309. <style scoped>
  310. /* 基础布局样式 */
  311. .data-entry-container {
  312. max-width: 1200px;
  313. margin: 0 auto;
  314. padding: 20px;
  315. background-color: #ffffff;
  316. border-radius: 8px;
  317. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  318. }
  319. /* 标题样式 */
  320. h1 {
  321. text-align: center;
  322. margin-bottom: 24px;
  323. color: #333;
  324. font-size: 24px;
  325. }
  326. h2 {
  327. margin: 20px 0 10px 0;
  328. color: #333;
  329. }
  330. h3 {
  331. margin: 15px 0 10px 0;
  332. color: #555;
  333. font-size: 16px;
  334. }
  335. /* 表单区域样式 */
  336. .form-section {
  337. margin-bottom: 24px;
  338. background: #f8f9fa;
  339. padding: 20px;
  340. border-radius: 6px;
  341. box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
  342. }
  343. .form-section h2 {
  344. margin-bottom: 16px;
  345. color: #333;
  346. font-size: 18px;
  347. border-bottom: 1px solid #e0e0e0;
  348. padding-bottom: 8px;
  349. }
  350. .form-subsection {
  351. margin: 20px 0;
  352. padding: 15px;
  353. background: #ffffff;
  354. border-radius: 4px;
  355. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  356. }
  357. /* 响应式布局 */
  358. @media (max-width: 768px) {
  359. .form-section {
  360. padding: 15px;
  361. }
  362. .form-subsection {
  363. padding: 10px;
  364. }
  365. .data-table td input {
  366. font-size: 14px;
  367. padding: 4px 8px;
  368. }
  369. }
  370. /* 悬停效果 */
  371. .form-subsection:hover {
  372. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  373. transition: box-shadow 0.3s ease-in-out;
  374. }
  375. .form-grid {
  376. display: grid;
  377. grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  378. gap: 16px;
  379. }
  380. .form-group {
  381. display: flex;
  382. flex-direction: column;
  383. }
  384. .form-group label {
  385. margin-bottom: 6px;
  386. font-weight: 500;
  387. color: #555;
  388. }
  389. .form-group input {
  390. padding: 10px;
  391. border: 1px solid #dcdfe6;
  392. border-radius: 4px;
  393. transition: border-color 0.3s;
  394. }
  395. .form-group input:focus {
  396. outline: none;
  397. border-color: #409eff;
  398. }
  399. .add-btn {
  400. background-color: #409eff;
  401. color: white;
  402. border: none;
  403. padding: 8px 16px;
  404. border-radius: 4px;
  405. cursor: pointer;
  406. margin-bottom: 12px;
  407. transition: background-color 0.3s;
  408. }
  409. .add-btn:hover {
  410. background-color: #337ecc;
  411. }
  412. .data-table {
  413. width: 100%;
  414. border-collapse: collapse;
  415. margin-bottom: 12px;
  416. background-color: white;
  417. border-radius: 4px;
  418. overflow: hidden;
  419. box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
  420. }
  421. .data-table th,
  422. .data-table td {
  423. padding: 12px;
  424. border: 1px solid #ebeef5;
  425. text-align: left;
  426. }
  427. .data-table th {
  428. background-color: #f5f7fa;
  429. font-weight: 500;
  430. color: #606266;
  431. }
  432. .data-table td input {
  433. width: 100%;
  434. padding: 6px 10px;
  435. border: 1px solid #dcdfe6;
  436. border-radius: 4px;
  437. box-sizing: border-box;
  438. }
  439. .remove-btn {
  440. background-color: #f56c6c;
  441. color: white;
  442. border: none;
  443. padding: 6px 12px;
  444. border-radius: 4px;
  445. cursor: pointer;
  446. transition: background-color 0.3s;
  447. }
  448. .remove-btn:hover {
  449. background-color: #e45656;
  450. }
  451. .actions {
  452. display: flex;
  453. justify-content: center;
  454. gap: 16px;
  455. margin-top: 24px;
  456. }
  457. .actions button {
  458. padding: 10px 24px;
  459. border: none;
  460. border-radius: 4px;
  461. cursor: pointer;
  462. font-size: 14px;
  463. transition: all 0.3s;
  464. }
  465. .actions button:not(.primary) {
  466. background-color: #f4f4f5;
  467. color: #606266;
  468. }
  469. .actions button:not(.primary):hover {
  470. background-color: #e9e9eb;
  471. }
  472. .actions button.primary {
  473. background-color: #67c23a;
  474. color: white;
  475. }
  476. .actions button.primary:hover {
  477. background-color: #5daf34;
  478. }
  479. .json-output {
  480. margin-top: 24px;
  481. background-color: #f5f7fa;
  482. padding: 16px;
  483. border-radius: 4px;
  484. }
  485. .json-output h3 {
  486. margin-top: 0;
  487. color: #333;
  488. }
  489. .json-output pre {
  490. white-space: pre-wrap;
  491. word-wrap: break-word;
  492. background-color: #fff;
  493. padding: 12px;
  494. border-radius: 4px;
  495. max-height: 300px;
  496. overflow-y: auto;
  497. }
  498. .table-container {
  499. overflow-x: auto;
  500. }
  501. .detail-table th,
  502. .detail-table td {
  503. white-space: nowrap;
  504. }
  505. /* 按钮组样式 */
  506. .actions {
  507. display: flex;
  508. justify-content: center;
  509. gap: 16px;
  510. margin-top: 24px;
  511. }
  512. .actions button {
  513. padding: 10px 24px;
  514. border: none;
  515. border-radius: 4px;
  516. cursor: pointer;
  517. font-size: 14px;
  518. transition: all 0.3s;
  519. }
  520. .actions button:not(.primary) {
  521. background-color: #f4f4f5;
  522. color: #606266;
  523. }
  524. .actions button:not(.primary):hover {
  525. background-color: #e9e9eb;
  526. }
  527. .actions button.primary {
  528. background-color: #67c23a;
  529. color: white;
  530. }
  531. .actions button.primary:hover {
  532. background-color: #5daf34;
  533. }
  534. /* JSON输出区域样式 */
  535. .json-output {
  536. margin-top: 24px;
  537. background-color: #f5f7fa;
  538. padding: 16px;
  539. border-radius: 4px;
  540. }
  541. .json-output h3 {
  542. margin-top: 0;
  543. color: #333;
  544. }
  545. .json-output pre {
  546. white-space: pre-wrap;
  547. word-wrap: break-word;
  548. background-color: #fff;
  549. padding: 12px;
  550. border-radius: 4px;
  551. max-height: 300px;
  552. overflow-y: auto;
  553. }
  554. /* 表格容器 */
  555. .table-container {
  556. overflow-x: auto;
  557. }
  558. /* 表格样式 */
  559. .data-table {
  560. width: 100%;
  561. border-collapse: collapse;
  562. margin-bottom: 12px;
  563. background-color: white;
  564. border-radius: 4px;
  565. overflow: hidden;
  566. box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
  567. }
  568. .data-table th,
  569. .data-table td {
  570. padding: 12px;
  571. border: 1px solid #ebeef5;
  572. text-align: left;
  573. }
  574. .data-table th {
  575. background-color: #f5f7fa;
  576. font-weight: 500;
  577. color: #606266;
  578. }
  579. .data-table td input {
  580. width: 100%;
  581. padding: 6px 10px;
  582. border: 1px solid #dcdfe6;
  583. border-radius: 4px;
  584. box-sizing: border-box;
  585. }
  586. /* 按钮样式 */
  587. .add-btn {
  588. background-color: #409eff;
  589. color: white;
  590. border: none;
  591. padding: 8px 16px;
  592. border-radius: 4px;
  593. cursor: pointer;
  594. margin-bottom: 12px;
  595. transition: background-color 0.3s;
  596. }
  597. .add-btn:hover {
  598. background-color: #337ecc;
  599. }
  600. .remove-btn {
  601. background-color: #f56c6c;
  602. color: white;
  603. border: none;
  604. padding: 6px 12px;
  605. border-radius: 4px;
  606. cursor: pointer;
  607. transition: background-color 0.3s;
  608. }
  609. .remove-btn:hover {
  610. background-color: #e45656;
  611. }
  612. </style>