瀏覽代碼

驾驶仓数据手动处理

wukai 3 天之前
父節點
當前提交
7c89b1f3d7
共有 4 個文件被更改,包括 1906 次插入0 次删除
  1. 47 0
      src/api/house/input.js
  2. 687 0
      src/views/dashboard/input/cogs.vue
  3. 631 0
      src/views/dashboard/input/rev.vue
  4. 541 0
      src/views/dashboard/input/sales.vue

+ 47 - 0
src/api/house/input.js

@@ -0,0 +1,47 @@
+import request from '@/utils/request'
+
+export function getRev(query) {
+    return request({
+        url: '/api/driver/rev',
+        method: 'get',
+        params: query
+    })
+}
+
+export function saveRev(data) {
+    return request({
+        url: '/api/driver/rev',
+        method: 'post',
+        data: data
+    })
+}
+
+export function getSales() {
+    return request({
+        url: '/api/driver/sales',
+        method: 'get'
+    })
+}
+
+export function saveSales(data) {
+    return request({
+        url: '/api/driver/sales',
+        method: 'post',
+        data: data
+    })
+}
+
+export function getCogs() {
+    return request({
+        url: '/api/driver/cogs',
+        method: 'get'
+    })
+}
+
+export function saveCogs(data) {
+    return request({
+        url: '/api/driver/cogs',
+        method: 'post',
+        data: data
+    })
+}

+ 687 - 0
src/views/dashboard/input/cogs.vue

@@ -0,0 +1,687 @@
+<template>
+  <div class="data-entry-container">
+    <h1>COGS数据录入工具</h1>
+
+    <!-- 当前成本结构 -->
+    <div class="form-section">
+      <h2>当前成本结构</h2>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>成本项</th>
+          <th>金额(元/米)</th>
+          <th>占比(%)</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr>
+          <td>{{ formData.curr.n1 }}</td>
+          <td>
+            <input type="number" v-model="formData.curr.v1" placeholder="请输入金额">
+          </td>
+          <td>
+            <input type="number" v-model="formData.curr.p1" placeholder="请输入占比">
+          </td>
+        </tr>
+        <tr>
+          <td>{{ formData.curr.n2 }}</td>
+          <td>
+            <input type="number" v-model="formData.curr.v2" placeholder="请输入金额">
+          </td>
+          <td>
+            <input type="number" v-model="formData.curr.p2" placeholder="请输入占比">
+          </td>
+        </tr>
+        <tr>
+          <td>{{ formData.curr.n3 }}</td>
+          <td>
+            <input type="number" v-model="formData.curr.v3" placeholder="请输入金额">
+          </td>
+          <td>
+            <input type="number" v-model="formData.curr.p3" placeholder="请输入占比">
+          </td>
+        </tr>
+        <tr>
+          <td>{{ formData.curr.n4 }}</td>
+          <td>
+            <input type="number" v-model="formData.curr.v4" placeholder="请输入金额">
+          </td>
+          <td>
+            <input type="number" v-model="formData.curr.p4" placeholder="请输入占比">
+          </td>
+        </tr>
+        <tr>
+          <td>{{ formData.curr.n5 }}</td>
+          <td>
+            <input type="number" v-model="formData.curr.v5" placeholder="请输入金额">
+          </td>
+          <td>
+            <input type="number" v-model="formData.curr.p5" placeholder="请输入占比">
+          </td>
+        </tr>
+        <tr>
+          <td>{{ formData.curr.n6 }}</td>
+          <td>
+            <input type="number" v-model="formData.curr.v6" placeholder="请输入金额">
+          </td>
+          <td>
+            <input type="number" v-model="formData.curr.p6" placeholder="请输入占比">
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 历史成本数据 -->
+    <div class="form-section">
+      <h2>单位成本历史数据</h2>
+      <button @click="regenerateHistoricalData" class="add-btn">重新生成过去12个月</button>
+      <table class="data-table">
+        <thead>
+          <tr>
+            <th>月份</th>
+            <th v-for="(key, index) in ['n1', 'n2', 'n3', 'n4', 'n5', 'n6']" :key="index">{{ formData.curr[key] }}</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(item, index) in formData.his.n1" :key="index">
+            <td>
+              <input type="text" v-model="formData.his.n1[index].month" placeholder="请输入月份">
+            </td>
+            <td v-for="(key, idx) in ['n1', 'n2', 'n3', 'n4', 'n5', 'n6']" :key="idx">
+              <input type="number" v-model="formData.his[key][index].value" placeholder="请输入值">
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 成本总趋势 -->
+    <div class="form-section">
+      <h2>成本总趋势</h2>
+      <button @click="regenerateHistoricalData" class="add-btn">重新生成过去12个月</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>月份</th>
+          <th>总成本(元/米)</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.trend" :key="index">
+          <td>
+            <input type="text" v-model="item.month" placeholder="请输入月份">
+          </td>
+          <td>
+            <input type="number" v-model="item.value" placeholder="请输入值">
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 生产效率数据 -->
+    <div class="form-section">
+      <h2>人均单位成本</h2>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th v-for="(item, index) in formData.prod" :key="index">{{ item.name }}</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr>
+          <td v-for="(item, index) in formData.prod" :key="index">
+            <input type="number" v-model="item.value" placeholder="请输入值">
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 成本明细数据 -->
+    <div class="form-section">
+      <h2>成本明细数据</h2>
+      <button @click="addDetailItem" class="add-btn">添加成本项</button>
+      <div class="table-container">
+        <table class="data-table detail-table">
+          <thead>
+          <tr>
+            <th>成本项</th>
+            <th v-for="(process, index) in processes" :key="index">{{ process }}</th>
+            <th>操作</th>
+          </tr>
+          </thead>
+          <tbody>
+          <tr v-for="(item, index) in formData.detail" :key="index">
+            <td>
+              <input v-model="item.item" placeholder="请输入成本项">
+            </td>
+            <td v-for="(value, valueIndex) in item.values" :key="valueIndex">
+              <input type="number" v-model="item.values[valueIndex]" placeholder="请输入值">
+            </td>
+            <td>
+              <button @click="removeDetailItem(index)" class="remove-btn">删除</button>
+            </td>
+          </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+
+    <div class="actions">
+      <button @click="submitForm" class="primary">保存</button>
+    </div>
+
+    <div v-if="jsonOutput" class="json-output">
+      <h3>生成的JSON数据</h3>
+      <pre>{{ jsonOutput }}</pre>
+      <button @click="copyToClipboard">复制到剪贴板</button>
+    </div>
+  </div>
+</template>
+
+<script>
+import {onMounted, reactive, ref} from 'vue';
+import {getCogs, saveCogs} from "@/api/house/input";
+
+export default {
+  name: "CogsInput",
+  setup() {
+    const formData = reactive({
+      curr: {
+        total: "4.32",
+        n1: "人工工资+社保+福利",
+        v1: 0.76,
+        p1: 14.7,
+        n2: "电+汽",
+        v2: 1.3,
+        p2: 26.7,
+        n3: "运营+管理",
+        v3: 0.33,
+        p3: 11.3,
+        n4: "机物料+修理+制版材料+染料+助剂+辅料+水",
+        v4: 1.63,
+        p4: 33.3,
+        n5: "折旧",
+        v5: 0.48,
+        p5: 13.3,
+        n6: "其他",
+        v6: 0.08,
+        p6: 0.67
+      },
+      his: {
+        n1: [],
+        n2: [],
+        n3: [],
+        n4: [],
+        n5: [],
+        n6: []
+      },
+      trend: [],
+      prod: [],
+      detail: []
+    });
+
+    const jsonOutput = ref('');
+    const processes = ref(["加弹", "经编", "前整", "印染", "后整", "成品"]);
+
+    // 页面加载时获取数据
+    const fetchData = async () => {
+      try {
+        const response = await getCogs();
+        if (response && response.data) {
+          // 用获取到的数据填充表单
+          Object.assign(formData, response.data);
+        } else {
+          console.warn('接口返回数据为空或格式不正确');
+        }
+      } catch (error) {
+        console.error('获取数据失败:', error);
+        // 保留默认数据,确保页面不为空白
+      }
+    };
+
+    // 组件挂载时调用获取数据的方法
+    onMounted(() => {
+      fetchData();
+    });
+
+    const submitForm = async () => {
+      try {
+        await saveCogs(formData);
+        alert('表单提交成功');
+      } catch (error) {
+        console.error('提交失败:', error);
+        alert('提交失败');
+      }
+    };
+    const generatePastMonths = () => {
+      const months = [];
+      const now = new Date();
+      for (let i = 11; i >= 0; i--) {
+        const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
+        const month = (date.getMonth() + 1).toString().padStart(2, '0');
+        const year = date.getFullYear();
+        months.push(`${year}-${month}`);
+      }
+      return months;
+    };
+
+    // 添加重新生成历史数据的方法
+    const regenerateHistoricalData = () => {
+      const newMonths = generatePastMonths();
+      
+      // 更新每个成本项的月份数据
+      const costKeys = ['n1', 'n2', 'n3', 'n4', 'n5', 'n6'];
+      costKeys.forEach(key => {
+        if (formData.his[key] && Array.isArray(formData.his[key])) {
+          formData.his[key] = formData.his[key].map((item, index) => ({
+            ...item,
+            month: newMonths[index] || item.month
+          }));
+        }
+      });
+      
+      // 更新总趋势数据的月份
+      if (formData.trend && Array.isArray(formData.trend)) {
+        formData.trend = formData.trend.map((item, index) => ({
+          ...item,
+          month: newMonths[index] || item.month
+        }));
+      }
+    };
+
+
+    const addDetailItem = () => {
+      formData.detail.push({
+        item: "",
+        values: Array(processes.value.length).fill(0)
+      });
+    };
+
+    const removeDetailItem = (index) => {
+      formData.detail.splice(index, 1);
+    };
+
+    const generateJson = () => {
+      jsonOutput.value = JSON.stringify(formData, null, 2);
+    };
+
+    const copyToClipboard = () => {
+      navigator.clipboard.writeText(jsonOutput.value)
+          .then(() => alert('已复制到剪贴板'))
+          .catch(err => console.error('复制失败:', err));
+    };
+
+    return {
+      formData,
+      jsonOutput,
+      processes,
+      submitForm,
+      addDetailItem,
+      removeDetailItem,
+      generateJson,
+      copyToClipboard,
+      regenerateHistoricalData
+    };
+  }
+};
+</script>
+
+<style scoped>
+/* 基础布局样式 */
+.data-entry-container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 20px;
+  background-color: #ffffff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+}
+
+/* 标题样式 */
+h1 {
+  text-align: center;
+  margin-bottom: 24px;
+  color: #333;
+  font-size: 24px;
+}
+
+h2 {
+  margin: 20px 0 10px 0;
+  color: #333;
+}
+
+h3 {
+  margin: 15px 0 10px 0;
+  color: #555;
+  font-size: 16px;
+}
+
+/* 表单区域样式 */
+.form-section {
+  margin-bottom: 24px;
+  background: #f8f9fa;
+  padding: 20px;
+  border-radius: 6px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.form-section h2 {
+  margin-bottom: 16px;
+  color: #333;
+  font-size: 18px;
+  border-bottom: 1px solid #e0e0e0;
+  padding-bottom: 8px;
+}
+
+.form-subsection {
+  margin: 20px 0;
+  padding: 15px;
+  background: #ffffff;
+  border-radius: 4px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+}
+
+/* 响应式布局 */
+@media (max-width: 768px) {
+  .form-section {
+    padding: 15px;
+  }
+
+  .form-subsection {
+    padding: 10px;
+  }
+
+  .data-table td input {
+    font-size: 14px;
+    padding: 4px 8px;
+  }
+}
+
+/* 悬停效果 */
+.form-subsection:hover {
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  transition: box-shadow 0.3s ease-in-out;
+}
+
+.form-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+  gap: 16px;
+}
+
+.form-group {
+  display: flex;
+  flex-direction: column;
+}
+
+.form-group label {
+  margin-bottom: 6px;
+  font-weight: 500;
+  color: #555;
+}
+
+.form-group input {
+  padding: 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  transition: border-color 0.3s;
+}
+
+.form-group input:focus {
+  outline: none;
+  border-color: #409eff;
+}
+
+.add-btn {
+  background-color: #409eff;
+  color: white;
+  border: none;
+  padding: 8px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+  margin-bottom: 12px;
+  transition: background-color 0.3s;
+}
+
+.add-btn:hover {
+  background-color: #337ecc;
+}
+
+.data-table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-bottom: 12px;
+  background-color: white;
+  border-radius: 4px;
+  overflow: hidden;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.data-table th,
+.data-table td {
+  padding: 12px;
+  border: 1px solid #ebeef5;
+  text-align: left;
+}
+
+.data-table th {
+  background-color: #f5f7fa;
+  font-weight: 500;
+  color: #606266;
+}
+
+.data-table td input {
+  width: 100%;
+  padding: 6px 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.remove-btn {
+  background-color: #f56c6c;
+  color: white;
+  border: none;
+  padding: 6px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.remove-btn:hover {
+  background-color: #e45656;
+}
+
+.actions {
+  display: flex;
+  justify-content: center;
+  gap: 16px;
+  margin-top: 24px;
+}
+
+.actions button {
+  padding: 10px 24px;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+  transition: all 0.3s;
+}
+
+.actions button:not(.primary) {
+  background-color: #f4f4f5;
+  color: #606266;
+}
+
+.actions button:not(.primary):hover {
+  background-color: #e9e9eb;
+}
+
+.actions button.primary {
+  background-color: #67c23a;
+  color: white;
+}
+
+.actions button.primary:hover {
+  background-color: #5daf34;
+}
+
+.json-output {
+  margin-top: 24px;
+  background-color: #f5f7fa;
+  padding: 16px;
+  border-radius: 4px;
+}
+
+.json-output h3 {
+  margin-top: 0;
+  color: #333;
+}
+
+.json-output pre {
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  background-color: #fff;
+  padding: 12px;
+  border-radius: 4px;
+  max-height: 300px;
+  overflow-y: auto;
+}
+
+.table-container {
+  overflow-x: auto;
+}
+
+.detail-table th,
+.detail-table td {
+  white-space: nowrap;
+}
+
+/* 按钮组样式 */
+.actions {
+  display: flex;
+  justify-content: center;
+  gap: 16px;
+  margin-top: 24px;
+}
+
+.actions button {
+  padding: 10px 24px;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+  transition: all 0.3s;
+}
+
+.actions button:not(.primary) {
+  background-color: #f4f4f5;
+  color: #606266;
+}
+
+.actions button:not(.primary):hover {
+  background-color: #e9e9eb;
+}
+
+.actions button.primary {
+  background-color: #67c23a;
+  color: white;
+}
+
+.actions button.primary:hover {
+  background-color: #5daf34;
+}
+
+/* JSON输出区域样式 */
+.json-output {
+  margin-top: 24px;
+  background-color: #f5f7fa;
+  padding: 16px;
+  border-radius: 4px;
+}
+
+.json-output h3 {
+  margin-top: 0;
+  color: #333;
+}
+
+.json-output pre {
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  background-color: #fff;
+  padding: 12px;
+  border-radius: 4px;
+  max-height: 300px;
+  overflow-y: auto;
+}
+
+/* 表格容器 */
+.table-container {
+  overflow-x: auto;
+}
+
+/* 表格样式 */
+.data-table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-bottom: 12px;
+  background-color: white;
+  border-radius: 4px;
+  overflow: hidden;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.data-table th,
+.data-table td {
+  padding: 12px;
+  border: 1px solid #ebeef5;
+  text-align: left;
+}
+
+.data-table th {
+  background-color: #f5f7fa;
+  font-weight: 500;
+  color: #606266;
+}
+
+.data-table td input {
+  width: 100%;
+  padding: 6px 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+/* 按钮样式 */
+.add-btn {
+  background-color: #409eff;
+  color: white;
+  border: none;
+  padding: 8px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+  margin-bottom: 12px;
+  transition: background-color 0.3s;
+}
+
+.add-btn:hover {
+  background-color: #337ecc;
+}
+
+.remove-btn {
+  background-color: #f56c6c;
+  color: white;
+  border: none;
+  padding: 6px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.remove-btn:hover {
+  background-color: #e45656;
+}
+</style>

+ 631 - 0
src/views/dashboard/input/rev.vue

@@ -0,0 +1,631 @@
+<template>
+  <div class="data-entry-container">
+    <h1>全球营收数据录入工具</h1>
+
+    <div class="form-section">
+      <h2>基础财务指标</h2>
+      <div class="form-grid">
+        <div class="form-group">
+          <label>年度总营收(万元)</label>
+          <input type="number" v-model="formData.rev">
+        </div>
+        <div class="form-group">
+          <label>年度总成本(万元)</label>
+          <input type="number" v-model="formData.cogs">
+        </div>
+        <div class="form-group">
+          <label>年度总利润(万元)</label>
+          <input type="number" v-model="formData.gp">
+        </div>
+        <div class="form-group">
+          <label>年度毛利率(%)</label>
+          <input type="number" v-model="formData.gpm">
+        </div>
+        <div class="form-group">
+          <label>总资产(万元)</label>
+          <input type="number" v-model="formData.assets">
+        </div>
+        <div class="form-group">
+          <label>业务覆盖国家数量</label>
+          <input type="number" v-model="formData.country">
+        </div>
+        <div class="form-group">
+          <label>年度订单总量</label>
+          <input type="number" v-model="formData.order">
+        </div>
+      </div>
+    </div>
+
+    <!-- 营收构成明细 -->
+    <div class="form-section">
+      <h2>营收构成明细</h2>
+      <button @click="addRevenueItem" class="add-btn">添加收入类型</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>收入类型</th>
+          <th>金额(万元)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.revList" :key="index">
+          <td>
+            <input v-model="item.type" placeholder="请输入收入类型">
+          </td>
+          <td>
+            <input type="number" v-model="item.value" placeholder="请输入金额">
+          </td>
+          <td>
+            <button @click="removeRevenueItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+
+    <!-- 成本构成明细 -->
+    <div class="form-section">
+      <h2>成本构成明细</h2>
+      <button @click="addCostItem" class="add-btn">添加成本类型</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>成本类型</th>
+          <th>金额(万元)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.cogsList" :key="index">
+          <td>
+            <input v-model="item.type" placeholder="请输入成本类型">
+          </td>
+          <td>
+            <input type="number" v-model="item.value" placeholder="请输入金额">
+          </td>
+          <td>
+            <button @click="removeCostItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 各国业务数据 -->
+    <div class="form-section">
+      <h2>各国业务数据</h2>
+      <button @click="addCountryItem" class="add-btn">添加国家数据</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>国家名称</th>
+          <th>订单数量</th>
+          <th>收入(万元)</th>
+          <th>利润(万元)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.orderList" :key="index">
+          <td>
+            <el-select
+                v-model="item.country"
+                filterable
+                allow-create
+                default-first-option
+                placeholder="请选择国家"
+                clearable
+                style="width: 100%">
+              <el-option
+                  v-for="country in countryOptions"
+                  :key="country.value"
+                  :label="country.label"
+                  :value="country.value">
+              </el-option>
+            </el-select>
+          </td>
+          <td>
+            <input type="number" v-model="item.num" placeholder="请输入订单数量">
+          </td>
+          <td>
+            <input type="number" v-model="item.rev" placeholder="请输入收入">
+          </td>
+          <td>
+            <input type="number" v-model="item.gp" placeholder="请输入利润">
+          </td>
+          <td>
+            <button @click="removeCountryItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 产品线利润数据 -->
+    <div class="form-section">
+      <h2>产品线利润数据</h2>
+      <button @click="addProductItem" class="add-btn">添加产品数据</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>产品类型</th>
+          <th>利润金额(万元)</th>
+          <th>毛利率(%)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.gpList" :key="index">
+          <td>
+            <input v-model="item.type" placeholder="请输入产品类型">
+          </td>
+          <td>
+            <input type="number" v-model="item.value" placeholder="请输入利润金额">
+          </td>
+          <td>
+            <input type="number" v-model="item.gmp" placeholder="请输入毛利率">
+          </td>
+          <td>
+            <button @click="removeProductItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 月度经营数据 -->
+    <div class="form-section">
+      <h2>月度经营数据</h2>
+      <button @click="regenerateTradeList" class="add-btn">重新生成过去12个月</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>月份</th>
+          <th>收入(万元)</th>
+          <th>成本(万元)</th>
+          <th>利润(万元)</th>
+          <th>利润率(%)</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.tradeList" :key="index">
+          <td>
+            <input v-model="item.month" placeholder="请输入月份">
+          </td>
+          <td>
+            <input type="number" v-model="item.revenue" placeholder="请输入收入">
+          </td>
+          <td>
+            <input type="number" v-model="item.cost" placeholder="请输入成本">
+          </td>
+          <td>
+            <input type="number" v-model="item.profit" placeholder="请输入利润">
+          </td>
+          <td>
+            <input type="number" v-model="item.profitRate" placeholder="请输入利润率">
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 月度预测数据 -->
+    <div class="form-section">
+      <h2>月度预测数据</h2>
+      <button @click="regenerateForecastList" class="add-btn">重新生成未来12个月</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>月份</th>
+          <th>预测收入(万元)</th>
+          <th>预测利润率(%)</th>
+          <th>预测资产(万元)</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.forecastList" :key="index">
+          <td>
+            <input v-model="item.month" placeholder="请输入月份">
+          </td>
+          <td>
+            <input type="number" v-model="item.revenue" placeholder="请输入预测收入">
+          </td>
+          <td>
+            <input type="number" v-model="item.profitRate" placeholder="请输入预测利润率">
+          </td>
+          <td>
+            <input type="number" v-model="item.assets" placeholder="请输入预测资产">
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+
+    <div class="actions">
+      <button @click="submitForm" class="primary">保存</button>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import {onMounted, reactive, ref} from 'vue';
+import {getRev, saveRev} from "@/api/house/input";
+import {ElOption, ElSelect} from 'element-plus';
+
+export default {
+  name: "RevInput",
+  components: {
+    ElSelect,
+    ElOption
+  },
+  setup() {
+    // 常见国家列表
+    const countryOptions = [
+      {label: '中国', value: '中国'},
+      {label: '美国', value: '美国'},
+      {label: '英国', value: '英国'},
+      {label: '德国', value: '德国'},
+      {label: '法国', value: '法国'},
+      {label: '日本', value: '日本'},
+      {label: '韩国', value: '韩国'},
+      {label: '澳大利亚', value: '澳大利亚'},
+      {label: '巴西', value: '巴西'},
+      {label: '印度', value: '印度'},
+      {label: '俄罗斯', value: '俄罗斯'},
+      {label: '加拿大', value: '加拿大'},
+      {label: '意大利', value: '意大利'},
+      {label: '西班牙', value: '西班牙'},
+      {label: '荷兰', value: '荷兰'},
+      {label: '瑞典', value: '瑞典'},
+      {label: '瑞士', value: '瑞士'},
+      {label: '新加坡', value: '新加坡'},
+      {label: '马来西亚', value: '马来西亚'},
+      {label: '泰国', value: '泰国'},
+      {label: '越南', value: '越南'},
+      {label: '墨西哥', value: '墨西哥'},
+      {label: '阿根廷', value: '阿根廷'},
+      {label: '南非', value: '南非'},
+      {label: '埃及', value: '埃及'},
+      {label: '土耳其', value: '土耳其'},
+      {label: '沙特阿拉伯', value: '沙特阿拉伯'},
+      {label: '阿联酋', value: '阿联酋'}
+    ];
+
+    // 添加生成过去12个月月份的方法
+    const generatePastMonths = () => {
+      const months = [];
+      const now = new Date();
+      for (let i = 11; i >= 0; i--) {
+        const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
+        const month = (date.getMonth() + 1).toString().padStart(2, '0');
+        const year = date.getFullYear();
+        months.push(`${year}-${month}`);
+      }
+      return months;
+    };
+
+    // 添加生成未来12个月月份的方法
+    const generateFutureMonths = () => {
+      const months = [];
+      const now = new Date();
+      for (let i = 0; i < 12; i++) {
+        const date = new Date(now.getFullYear(), now.getMonth() + i + 1, 1);
+        const month = (date.getMonth() + 1).toString().padStart(2, '0');
+        const year = date.getFullYear();
+        months.push(`${year}-${month}`);
+      }
+      return months;
+    };
+
+    // 添加重新生成过去12个月数据的方法
+    const regenerateTradeList = () => {
+      formData.tradeList = formData.tradeList.map((item, index) => ({
+        ...item,
+        month: generatePastMonths()[index] || item.month
+      }));
+    };
+
+    // 添加重新生成未来12个月数据的方法
+    const regenerateForecastList = () => {
+      const newMonths = generateFutureMonths();
+      formData.forecastList = formData.forecastList.map((item, index) => ({
+        ...item,
+        month: newMonths[index] || item.month
+      }));
+    };
+
+    const formData = reactive({});
+
+    const jsonOutput = ref('');
+
+    // 页面加载时获取数据
+    const fetchData = async () => {
+      try {
+        const response = await getRev();
+        if (response && response.data) {
+          // 用获取到的数据填充表单
+          Object.assign(formData, response.data);
+        } else {
+          console.warn('接口返回数据为空或格式不正确');
+          // 如果没有获取到数据,则使用自动生成的月份数据
+          formData.tradeList = generatePastMonths().map(month => ({
+            month,
+            revenue: 0,
+            cost: 0,
+            profit: 0,
+            profitRate: 0
+          }));
+          formData.forecastList = generateFutureMonths().map(month => ({
+            month,
+            revenue: 0,
+            profitRate: 0,
+            assets: 0
+          }));
+        }
+      } catch (error) {
+        console.error('获取数据失败:', error);
+        // 保留默认数据,确保页面不为空白
+        formData.tradeList = generatePastMonths().map(month => ({
+          month,
+          revenue: 0,
+          cost: 0,
+          profit: 0,
+          profitRate: 0
+        }));
+        formData.forecastList = generateFutureMonths().map(month => ({
+          month,
+          revenue: 0,
+          profitRate: 0,
+          assets: 0
+        }));
+      }
+    };
+
+    // 组件挂载时调用获取数据的方法
+    onMounted(() => {
+      fetchData();
+    });
+
+    const addRevenueItem = () => {
+      formData.revList.push({type: "", value: 0});
+    };
+
+    const removeRevenueItem = (index) => {
+      formData.revList.splice(index, 1);
+    };
+
+    const addCostItem = () => {
+      formData.cogsList.push({type: "", value: 0});
+    };
+
+    const removeCostItem = (index) => {
+      formData.cogsList.splice(index, 1);
+    };
+
+    const addCountryItem = () => {
+      formData.orderList.push({country: "", num: 0, rev: 0, gp: 0});
+    };
+
+    const removeCountryItem = (index) => {
+      formData.orderList.splice(index, 1);
+    };
+
+    const addProductItem = () => {
+      formData.gpList.push({type: "", value: 0, gmp: 0});
+    };
+
+    const removeProductItem = (index) => {
+      formData.gpList.splice(index, 1);
+    };
+
+    const submitForm = async () => {
+      try {
+        // 调用 saveRev 提交表单数据
+        await saveRev(formData);
+        console.log('表单提交成功');
+      } catch (error) {
+        console.error('提交失败:', error);
+      }
+    };
+    return {
+      formData,
+      countryOptions,
+      jsonOutput,
+      addRevenueItem,
+      removeRevenueItem,
+      addCostItem,
+      removeCostItem,
+      addCountryItem,
+      removeCountryItem,
+      addProductItem,
+      removeProductItem,
+      regenerateTradeList,
+      regenerateForecastList,
+      submitForm
+    };
+  }
+};
+</script>
+
+<style scoped>
+.data-entry-container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 20px;
+  background-color: #ffffff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+}
+
+h1 {
+  text-align: center;
+  margin-bottom: 24px;
+  color: #333;
+  font-size: 24px;
+}
+
+.form-section {
+  margin-bottom: 24px;
+  background: #f8f9fa;
+  padding: 20px;
+  border-radius: 6px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.form-section h2 {
+  margin-bottom: 16px;
+  color: #333;
+  font-size: 18px;
+  border-bottom: 1px solid #e0e0e0;
+  padding-bottom: 8px;
+}
+
+.form-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+  gap: 16px;
+}
+
+.form-group {
+  display: flex;
+  flex-direction: column;
+}
+
+.form-group label {
+  margin-bottom: 6px;
+  font-weight: 500;
+  color: #555;
+}
+
+.form-group input {
+  padding: 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  transition: border-color 0.3s;
+}
+
+.form-group input:focus {
+  outline: none;
+  border-color: #409eff;
+}
+
+.add-btn {
+  background-color: #409eff;
+  color: white;
+  border: none;
+  padding: 8px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+  margin-bottom: 12px;
+  transition: background-color 0.3s;
+}
+
+.add-btn:hover {
+  background-color: #337ecc;
+}
+
+.data-table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-bottom: 12px;
+  background-color: white;
+  border-radius: 4px;
+  overflow: hidden;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.data-table th,
+.data-table td {
+  padding: 12px;
+  border: 1px solid #ebeef5;
+  text-align: left;
+}
+
+.data-table th {
+  background-color: #f5f7fa;
+  font-weight: 500;
+  color: #606266;
+}
+
+.data-table td input {
+  width: 100%;
+  padding: 6px 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.remove-btn {
+  background-color: #f56c6c;
+  color: white;
+  border: none;
+  padding: 6px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.remove-btn:hover {
+  background-color: #e45656;
+}
+
+.actions {
+  display: flex;
+  justify-content: center;
+  gap: 16px;
+  margin-top: 24px;
+}
+
+.actions button {
+  padding: 10px 24px;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+  transition: all 0.3s;
+}
+
+.actions button:not(.primary) {
+  background-color: #f4f4f5;
+  color: #606266;
+}
+
+.actions button:not(.primary):hover {
+  background-color: #e9e9eb;
+}
+
+.actions button.primary {
+  background-color: #67c23a;
+  color: white;
+}
+
+.actions button.primary:hover {
+  background-color: #5daf34;
+}
+
+.json-output {
+  margin-top: 24px;
+  background-color: #f5f7fa;
+  padding: 16px;
+  border-radius: 4px;
+}
+
+.json-output h3 {
+  margin-top: 0;
+  color: #333;
+}
+
+.json-output pre {
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  background-color: #fff;
+  padding: 12px;
+  border-radius: 4px;
+  max-height: 300px;
+  overflow-y: auto;
+}
+</style>

+ 541 - 0
src/views/dashboard/input/sales.vue

@@ -0,0 +1,541 @@
+<template>
+  <div class="data-entry-container">
+    <h1>全球销售数据录入工具</h1>
+
+    <!-- 月度销售数据 -->
+    <div class="form-section">
+      <h2>月度销售数据</h2>
+      <button @click="addSalesItem" class="add-btn">重新生成过去12个月</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>月份</th>
+          <th>销售收入(万元)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.sales" :key="index">
+          <td>
+            <input v-model="item.month" placeholder="请输入月份">
+          </td>
+          <td>
+            <input type="number" v-model="item.revenue" placeholder="请输入销售收入">
+          </td>
+          <td>
+            <button @click="removeSalesItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 渠道销售排名 -->
+    <div class="form-section">
+      <h2>渠道销售排名</h2>
+      <button @click="addChannelItem" class="add-btn">添加渠道数据</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>渠道</th>
+          <th>销售额(万元)</th>
+          <th>占比(%)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.channel" :key="index">
+          <td>
+            <input v-model="item.channel" placeholder="请输入渠道名称">
+          </td>
+          <td>
+            <input type="number" v-model="item.amount" placeholder="请输入销售额">
+          </td>
+          <td>
+            <input type="number" v-model="item.percentage" placeholder="请输入占比">
+          </td>
+          <td>
+            <button @click="removeChannelItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 花色销售排名 -->
+    <div class="form-section">
+      <h2>花色销售排名</h2>
+      <button @click="addPatternItem" class="add-btn">添加花色数据</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>花色</th>
+          <th>销售额(万元)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.pattern" :key="index">
+          <td>
+            <input v-model="item.pattern" placeholder="请输入花色名称">
+          </td>
+          <td>
+            <input type="number" v-model="item.amount" placeholder="请输入销售额">
+          </td>
+          <td>
+            <button @click="removePatternItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <!-- 销售类型区域数据 -->
+    <div class="form-section">
+      <h2>各销售类型区域数据</h2>
+      <button @click="addTypeItem" class="add-btn">添加销售类型</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>销售类型</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, typeIndex) in formData.type" :key="typeIndex">
+          <td>
+            <input v-model="item.salesType" placeholder="请输入销售类型">
+          </td>
+          <td>
+            <button @click="removeTypeItem(typeIndex)" class="remove-btn">删除类型</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+
+      <!-- 区域数据详情 -->
+      <div v-for="(item, typeIndex) in formData.type" :key="typeIndex" class="form-subsection">
+        <h3>{{ item.salesType }} - 区域数据</h3>
+        <button @click="addRegionItem(typeIndex)" class="add-btn">添加区域数据</button>
+        <table class="data-table">
+          <thead>
+          <tr>
+            <th>区域</th>
+            <th>销售额(万元)</th>
+            <th>操作</th>
+          </tr>
+          </thead>
+          <tbody>
+          <tr v-for="(region, regionIndex) in item.regions" :key="regionIndex">
+            <td>
+              <input v-model="region.region" placeholder="请输入区域名称">
+            </td>
+            <td>
+              <input type="number" v-model="region.amount" placeholder="请输入销售额">
+            </td>
+            <td>
+              <button @click="removeRegionItem(typeIndex, regionIndex)" class="remove-btn">删除</button>
+            </td>
+          </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+
+    <!-- 规格销售占比 -->
+    <div class="form-section">
+      <h2>规格销售占比</h2>
+      <button @click="addSpecItem" class="add-btn">添加规格数据</button>
+      <table class="data-table">
+        <thead>
+        <tr>
+          <th>规格</th>
+          <th>销售额(万元)</th>
+          <th>占比(%)</th>
+          <th>操作</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr v-for="(item, index) in formData.bom" :key="index">
+          <td>
+            <input v-model="item.bom" placeholder="请输入规格">
+          </td>
+          <td>
+            <input type="number" v-model="item.amount" placeholder="请输入销售额">
+          </td>
+          <td>
+            <input type="number" v-model="item.percentage" placeholder="请输入占比">
+          </td>
+          <td>
+            <button @click="removeSpecItem(index)" class="remove-btn">删除</button>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div class="actions">
+      <button @click="submitForm" class="primary">保存</button>
+    </div>
+
+    <div v-if="jsonOutput" class="json-output">
+      <h3>生成的JSON数据</h3>
+      <pre>{{ jsonOutput }}</pre>
+      <button @click="copyToClipboard">复制到剪贴板</button>
+    </div>
+  </div>
+</template>
+
+<script>
+import {onMounted, reactive, ref} from 'vue';
+import {getSales, saveSales} from "@/api/house/input";
+
+export default {
+  name: "SalesInput",
+  setup() {
+    const formData = reactive({});
+
+    const jsonOutput = ref('');
+
+    // 页面加载时获取数据
+    const fetchData = async () => {
+      try {
+        const response = await getSales();
+        if (response && response.data) {
+          // 用获取到的数据填充表单
+          formData.sales = response.data.sales || [];
+          formData.channel = response.data.channel || [];
+          formData.pattern = response.data.pattern || [];
+          formData.type = response.data.type || [];
+          formData.bom = response.data.bom || [];
+        } else {
+          console.warn('接口返回数据为空或格式不正确');
+        }
+      } catch (error) {
+        console.error('获取数据失败:', error);
+        // 保留默认数据,确保页面不为空白
+      }
+    };
+
+    // 组件挂载时调用获取数据的方法
+    onMounted(() => {
+      fetchData();
+    });
+    const submitForm = async () => {
+      try {
+        // 调用 saveRev 提交表单数据
+        await saveSales(formData);
+        console.log('表单提交成功');
+      } catch (error) {
+        console.error('提交失败:', error);
+      }
+    };
+    const addSalesItem = () => {
+      formData.sales = formData.sales.map((item, index) => ({
+        ...item,
+        month: generatePastMonths()[index] || item.month
+      }));
+    };
+    const generatePastMonths = () => {
+      const months = [];
+      const now = new Date();
+      for (let i = 11; i >= 0; i--) {
+        const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
+        const month = (date.getMonth() + 1).toString().padStart(2, '0');
+        const year = date.getFullYear();
+        months.push(`${year}-${month}`);
+      }
+      return months;
+    };
+    const removeSalesItem = (index) => {
+      formData.sales.splice(index, 1);
+    };
+
+    const addChannelItem = () => {
+      formData.channel.push({channel: "", amount: 0, percentage: 0});
+    };
+
+    const removeChannelItem = (index) => {
+      formData.channel.splice(index, 1);
+    };
+
+    const addPatternItem = () => {
+      formData.pattern.push({pattern: "", amount: 0});
+    };
+
+    const removePatternItem = (index) => {
+      formData.pattern.splice(index, 1);
+    };
+
+    const addTypeItem = () => {
+      formData.type.push({salesType: "", regions: []});
+    };
+
+    const removeTypeItem = (typeIndex) => {
+      formData.type.splice(typeIndex, 1);
+    };
+
+    const addRegionItem = (typeIndex) => {
+      formData.type[typeIndex].regions.push({region: "", amount: 0});
+    };
+
+    const removeRegionItem = (typeIndex, regionIndex) => {
+      formData.type[typeIndex].regions.splice(regionIndex, 1);
+    };
+
+    const addSpecItem = () => {
+      formData.bom.push({bom: "", amount: 0, percentage: 0});
+    };
+
+    const removeSpecItem = (index) => {
+      formData.bom.splice(index, 1);
+    };
+
+    const resetForm = () => {
+      if (confirm('确定要重置所有数据吗?')) {
+        formData.sales = [];
+        formData.channel = [];
+        formData.pattern = [];
+        formData.type = [];
+        formData.bom = [];
+        jsonOutput.value = '';
+      }
+    };
+
+    const generateJson = () => {
+      const result = {
+        sales: formData.sales,
+        channel: formData.channel,
+        pattern: formData.pattern,
+        type: formData.type,
+        bom: formData.bom
+      };
+      jsonOutput.value = JSON.stringify(result, null, 2);
+    };
+
+    const copyToClipboard = () => {
+      navigator.clipboard.writeText(jsonOutput.value)
+          .then(() => alert('已复制到剪贴板'))
+          .catch(err => console.error('复制失败:', err));
+    };
+
+    return {
+      formData,
+      jsonOutput,
+      addSalesItem,
+      removeSalesItem,
+      addChannelItem,
+      removeChannelItem,
+      addPatternItem,
+      removePatternItem,
+      addTypeItem,
+      removeTypeItem,
+      addRegionItem,
+      removeRegionItem,
+      addSpecItem,
+      removeSpecItem,
+      resetForm,
+      generateJson,
+      copyToClipboard,
+      submitForm
+    };
+  }
+};
+</script>
+
+<style scoped>
+.data-entry-container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 20px;
+  background-color: #ffffff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+}
+
+h1 {
+  text-align: center;
+  margin-bottom: 24px;
+  color: #333;
+  font-size: 24px;
+}
+
+h2 {
+  margin: 20px 0 10px 0;
+  color: #333;
+}
+
+h3 {
+  margin: 15px 0 10px 0;
+  color: #555;
+  font-size: 16px;
+}
+
+.form-section {
+  margin-bottom: 24px;
+  background: #f8f9fa;
+  padding: 20px;
+  border-radius: 6px;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.form-section h2 {
+  margin-bottom: 16px;
+  color: #333;
+  font-size: 18px;
+  border-bottom: 1px solid #e0e0e0;
+  padding-bottom: 8px;
+}
+
+.form-subsection {
+  margin: 20px 0;
+  padding: 15px;
+  background: #ffffff;
+  border-radius: 4px;
+}
+
+.form-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+  gap: 16px;
+}
+
+.form-group {
+  display: flex;
+  flex-direction: column;
+}
+
+.form-group label {
+  margin-bottom: 6px;
+  font-weight: 500;
+  color: #555;
+}
+
+.form-group input {
+  padding: 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  transition: border-color 0.3s;
+}
+
+.form-group input:focus {
+  outline: none;
+  border-color: #409eff;
+}
+
+.add-btn {
+  background-color: #409eff;
+  color: white;
+  border: none;
+  padding: 8px 16px;
+  border-radius: 4px;
+  cursor: pointer;
+  margin-bottom: 12px;
+  transition: background-color 0.3s;
+}
+
+.add-btn:hover {
+  background-color: #337ecc;
+}
+
+.data-table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-bottom: 12px;
+  background-color: white;
+  border-radius: 4px;
+  overflow: hidden;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+}
+
+.data-table th,
+.data-table td {
+  padding: 12px;
+  border: 1px solid #ebeef5;
+  text-align: left;
+}
+
+.data-table th {
+  background-color: #f5f7fa;
+  font-weight: 500;
+  color: #606266;
+}
+
+.data-table td input {
+  width: 100%;
+  padding: 6px 10px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.remove-btn {
+  background-color: #f56c6c;
+  color: white;
+  border: none;
+  padding: 6px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.remove-btn:hover {
+  background-color: #e45656;
+}
+
+.actions {
+  display: flex;
+  justify-content: center;
+  gap: 16px;
+  margin-top: 24px;
+}
+
+.actions button {
+  padding: 10px 24px;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+  transition: all 0.3s;
+}
+
+.actions button:not(.primary) {
+  background-color: #f4f4f5;
+  color: #606266;
+}
+
+.actions button:not(.primary):hover {
+  background-color: #e9e9eb;
+}
+
+.actions button.primary {
+  background-color: #67c23a;
+  color: white;
+}
+
+.actions button.primary:hover {
+  background-color: #5daf34;
+}
+
+.json-output {
+  margin-top: 24px;
+  background-color: #f5f7fa;
+  padding: 16px;
+  border-radius: 4px;
+}
+
+.json-output h3 {
+  margin-top: 0;
+  color: #333;
+}
+
+.json-output pre {
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  background-color: #fff;
+  padding: 12px;
+  border-radius: 4px;
+  max-height: 300px;
+  overflow-y: auto;
+}
+</style>