Browse Source

refactor<数据录入>

xinyan 5 tháng trước cách đây
mục cha
commit
fb3e122e17

+ 390 - 0
src/views/reportManage/dataCenter/normalDisplay/components/DataEntryTable/components/CreateDialog.vue

@@ -0,0 +1,390 @@
+<script lang="ts" setup>/**
+ * @Name: CreateDialog.vue
+ * @Description: 数据录入-创建
+ * @Author: xinyan
+ */
+import { computed, onMounted, reactive, ref } from 'vue';
+import { FormInstance, FormRules } from 'element-plus';
+import { dayColumns, monthColumns, weekColumns } from '/src/views/reportManage/dataCenter/utils/columns';
+import { flattenColumns, validateNumber } from '/src/views/reportManage/dataCenter/utils/tools';
+import { postCreateDayData, postCreateMonthData, postCreateWeekData } from '/src/views/reportManage/dataCenter/api';
+import TipDialog from '/src/views/reportManage/dataCenter/normalDisplay/components/DataEntryTable/components/TipDialog.vue';
+
+
+const props = defineProps({
+  currentRow: Object,
+  dateType: String,
+  ySalesData: Object,
+  currentDate: Object,
+});
+const { currentRow, dateType, ySalesData, currentDate } = props;
+
+const createOpen = defineModel({ default: false });
+const emit = defineEmits(['refresh']);
+
+const openTip = ref(false);
+const dialogMessage = ref('');
+const isSubmitting = ref(false);
+const isNormal = ref(false);
+
+interface taskDataForm {
+  sales_original: number,
+  ad_sales_original: number,
+  ad_cost_original: number,
+  total_sales_current_monthly_original: number;
+  impression: number;
+  ad_click: number;
+  ad_order: number;
+  money_by_amazon: number;
+  money_by_other: number;
+  session: number;
+  order: number;
+  availableSalesDay: number;
+  intransitInventory: number;
+  overseasStorage: number;
+  refundRate: number;
+}
+
+const taskDataFormRef = ref<FormInstance>();
+const taskDataForm = reactive({
+  sales_original: null,
+  ad_sales_original: null,
+  ad_cost_original: null,
+  total_sales_current_monthly_original: null,
+  impression: null,
+  ad_click: null,
+  ad_order: null,
+  session: null,
+  order: null,
+  availableSalesDay: null,
+  intransitInventory: null,
+  overseasStorage: null,
+  refundRate: null,
+  money_by_amazon: null,
+  money_by_other: null,
+});
+
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
+};
+const rules = reactive<FormRules>({
+  sales_original: [
+    { required: true, message: '请输入销售额', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },
+  ],
+  ad_sales_original: [
+    { required: true, message: '请输入广告销售额', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },
+  ],
+  ad_cost_original: [
+    { required: true, message: '请输入广告花费', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },
+  ],
+  total_sales_current_monthly_original: [
+    { required: true, message: '请输入当月销售额', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },
+  ],
+  impression: [{ required: true, message: '请输入广告展示量', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  ad_click: [{ required: true, message: '请输入广告点击量', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  ad_order: [{ required: true, message: '请输入广告订单量', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  money_by_amazon: [{ required: true, message: '请输入截止目前Amazon回款金额', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  money_by_other: [{ required: true, message: '请输入截止目前Other回款金额', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  session: [{ required: true, message: '请输入会话数', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  order: [{ required: true, message: '请输入订单', trigger: 'blur' }],
+  availableSalesDay: [{ required: true, message: '请输入可用销量天数', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  intransitInventory: [{ required: true, message: '请输入在途库存', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  overseasStorage: [{ required: true, message: '请输入海外仓库存', trigger: 'blur' },
+    { validator: validateNumber, trigger: 'blur' },],
+  refundRate: [{ required: true, message: '请输入最近90天平台退货率', trigger: 'blur' }],
+});
+
+const flatDayColumns = ref(flattenColumns(dayColumns.value));
+const flatWeekColumns = ref(flattenColumns(weekColumns.value));
+const flatMonthColumns = ref(flattenColumns(monthColumns.value));
+
+const filteredDayColumns = computed(() => {
+  const flat = flatDayColumns.value;
+  return flat.filter(item => !['平台编号', '平台名称', '国家', '品牌', '操作', '运营', '销售额', '广告销售额', '广告花费', '平台币种'].includes(item.title));
+});
+
+const filteredWeekColumns = computed(() => {
+  const flat = flatWeekColumns.value;
+  return flat.filter(item => !['平台编号', '平台名称', '国家', '品牌', '操作', '运营', '销售额', '广告销售额', '广告花费', '当月累计销售额', '平台币种', '回款/余额币种'].includes(item.title));
+});
+
+const filteredMonthColumns = computed(() => {
+  const flat = flatMonthColumns.value;
+  return flat.filter(item => !['平台编号', '平台名称', '国家', '品牌', '操作', '运营', '销售额', '广告销售额', '广告花费', '平台币种'].includes(item.title));
+});
+
+//表单规则
+function createDayDataRules() {
+  if (dateType === 'day') {
+    rules.ad_sales_original[0].required = false;
+    rules.ad_cost_original[0].required = false;
+  }
+  if (dateType === 'month') {
+    rules.ad_sales_original[0].required = false;
+    rules.ad_cost_original[0].required = false;
+    rules.impression[0].required = false;
+    rules.ad_click[0].required = false;
+    rules.ad_order[0].required = false;
+  }
+}
+
+//创建日数据
+async function createDayData() {
+  const body = {
+    sales_original: taskDataForm.sales_original,
+    ad_sales_original: taskDataForm.ad_sales_original,
+    ad_cost_original: taskDataForm.ad_cost_original,
+    data_datetime: currentDate.day_start_date,
+    task: currentRow.task,
+  };
+  if (!currentRow.id) {
+    try {
+      const resp = await postCreateDayData(body);
+      if (resp.code === 2000) {
+        createOpen.value = false;
+        emit('refresh');
+        taskDataFormRef.value.resetFields();
+        await VXETable.modal.message({ content: '创建成功', status: 'success' });
+      }
+    } catch (e) {
+      await VXETable.modal.message({ content: '创建失败', status: 'error' });
+    }
+  } else {
+    createOpen.value = false;
+    await VXETable.modal.message({ content: '此日期对应数据已存在', status: 'error' });
+  }
+}
+
+//创建周数据
+async function createWeekData() {
+  const body = {
+    sales_original: taskDataForm.sales_original,
+    ad_sales_original: taskDataForm.ad_sales_original,
+    ad_cost_original: taskDataForm.ad_cost_original,
+    total_sales_current_monthly_original: taskDataForm.total_sales_current_monthly_original,
+    impression: taskDataForm.impression,
+    ad_click: taskDataForm.ad_click,
+    ad_order: taskDataForm.ad_order,
+    money_by_amazon: taskDataForm.money_by_amazon,
+    money_by_other: taskDataForm.money_by_other,
+    session: taskDataForm.session,
+    order: taskDataForm.order,
+    availableSalesDay: taskDataForm.availableSalesDay,
+    intransitInventory: taskDataForm.intransitInventory,
+    overseasStorage: taskDataForm.overseasStorage,
+    refundRate: taskDataForm.refundRate,
+    sales_start_time: currentDate.week_start_date,
+    sales_end_time: currentDate.week_end_date,
+    ad_start_time: currentDate.ad_start_time,
+    ad_end_time: currentDate.ad_end_time,
+    task: currentRow.task,
+  };
+  if (!currentRow.id) {
+    try {
+      const resp = await postCreateWeekData(body);
+      if (resp.code === 2000) {
+        createOpen.value = false;
+        emit('refresh');
+        taskDataFormRef.value.resetFields();
+        await VXETable.modal.message({ content: '创建成功', status: 'success' });
+      }
+    } catch (e) {
+      await VXETable.modal.message({ content: '创建失败', status: 'error' });
+    }
+  } else {
+    createOpen.value = false;
+    await VXETable.modal.message({ content: '此日期对应数据已存在', status: 'error' });
+  }
+}
+
+//创建月数据
+async function createMonthData() {
+  const body = {
+    sales_original: taskDataForm.sales_original,
+    ad_sales_original: taskDataForm.ad_sales_original,
+    ad_cost_original: taskDataForm.ad_cost_original,
+    impression: taskDataForm.impression,
+    ad_click: taskDataForm.ad_click,
+    ad_order: taskDataForm.ad_order,
+    data_start_time: currentDate.month_start_date,
+    data_end_time: currentDate.month_end_date,
+    task: currentRow.task,
+  };
+  if (!currentRow.id) {
+    try {
+      const resp = await postCreateMonthData(body);
+      if (resp.code === 2000) {
+        createOpen.value = false;
+        emit('refresh');
+        taskDataFormRef.value.resetFields();
+        await VXETable.modal.message({ content: '创建成功', status: 'success' });
+      }
+    } catch (e) {
+      await VXETable.modal.message({ content: '创建失败', status: 'error' });
+    }
+  } else {
+    createOpen.value = false;
+    await VXETable.modal.message({ content: '此日期对应数据已存在', status: 'error' });
+  }
+}
+
+//创建任务
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl || isSubmitting.value) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      const fieldsToWarn = validateForm();
+      isNormal.value = validateForm2();
+      if (fieldsToWarn.length > 0 || isNormal.value) {
+        openTip.value = true;
+        dialogMessage.value = `${ fieldsToWarn.join(', ') }`;
+        return;
+      } else {
+        isSubmitting.value = true;
+        try {
+          await submit(formEl);
+        } finally {
+          isSubmitting.value = false;
+        }
+      }
+    }
+  });
+};
+
+const submit = async () => {
+  openTip.value = false;
+  if (dateType === 'day') {
+    // const fieldsToValidate = ['sales_original'];
+    // if (!validateNumericFields(taskDataForm, fieldsToValidate)) return; // 验证字段
+    await createDayData();
+  }
+  if (dateType === 'week') {
+    // const fieldsToValidate = ['sales_original', 'ad_sales_original', 'ad_cost_original', 'total_sales_current_monthly_original', 'impression', 'ad_click', 'ad_order', 'money_by_amazon', 'money_by_other', 'session', 'order', 'availableSalesDay', 'intransitInventory', 'overseasStorage', 'refundRate'];
+    // if (!validateNumericFields(taskDataForm, fieldsToValidate)) return; // 验证字段
+    await createWeekData();
+  }
+  if (dateType === 'month') {
+    // const fieldsToValidate = ['sales_original'];
+    // if (!validateNumericFields(taskDataForm, fieldsToValidate)) return; // 验证字段
+    await createMonthData();
+  }
+};
+
+function handleClose(done: Function) {
+  if (taskDataFormRef.value) taskDataFormRef.value.resetFields();
+  done();
+}
+
+// 验证与前一天偏移值
+const validateForm = () => {
+  const fieldsToWarn = [];
+  const fieldsToValidate = ['sales_original', 'total_sales_current_monthly_original'];
+  if (ySalesData) {
+    for (const key of fieldsToValidate) {
+      const newValue = taskDataForm[key];
+      const yDayValue = ySalesData[key];
+      const column = flatWeekColumns.value.find(col => col.field === key);
+      const title = column ? column.title : key;
+
+      if (newValue != null && yDayValue !== null && yDayValue !== undefined) {
+        const diffPercentage = Math.abs((newValue - yDayValue) / yDayValue) * 100;
+        if (diffPercentage > 50) {
+          fieldsToWarn.push(title);
+        }
+      }
+    }
+  }
+  return fieldsToWarn;
+};
+
+// 验证ROI
+const validateForm2 = () => {
+  const adSalesValue = taskDataForm['ad_sales_original'];
+  const adCostValue = taskDataForm['ad_cost_original'];
+
+  if (adSalesValue != null && adCostValue != null && adCostValue !== 0) {
+    const ratio = (adSalesValue - adCostValue) / adCostValue;
+    return ratio < 0;
+  }
+};
+
+onMounted(() => {
+  createDayDataRules();
+});
+</script>
+
+<template>
+  <el-dialog v-model="createOpen" :before-close="handleClose" :close-on-click-modal="false"
+             :close-on-press-escape="false"
+             style="border-radius: 10px;"
+             width="600">
+    <template #title>
+      <span class="text-xl">数据录入 - 创建</span>
+      <div class="mt-2" style="display: flex; align-items: center;color: darkgray">
+        <div style="margin-right: 8px;">
+          平台编号:
+          <span class="italic pl-1 pr-2">{{ currentRow.platformNumber }}</span>
+        </div>
+        <div>
+          平台名称:
+          <span class="italic pl-1">{{ currentRow.platformName }}</span>
+        </div>
+      </div>
+    </template>
+    <el-form
+        ref="taskDataFormRef"
+        :model="taskDataForm"
+        :rules="rules"
+        label-position="top"
+        label-width="auto"
+        status-icon
+        style="max-width: 600px;">
+      <div v-if="dateType === 'day'">
+        <el-form-item v-for="item in filteredDayColumns" :key="item.field" :label="item.title" :prop="item.field">
+          <el-input v-model="taskDataForm[item.field]" :placeholder="`请输入${item.title}`" />
+        </el-form-item>
+      </div>
+      <div v-if="dateType === 'week'" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px">
+        <el-form-item v-for="item in filteredWeekColumns" :key="item.field" :label="item.title" :prop="item.field">
+          <el-input v-model="taskDataForm[item.field]" style="width: 100%" />
+        </el-form-item>
+      </div>
+      <div v-if="dateType === 'month'">
+        <el-form-item v-for="item in filteredMonthColumns" :key="item.field" :label="item.title" :prop="item.field">
+          <el-input v-model="taskDataForm[item.field]" :placeholder="`请输入${item.title}`" />
+        </el-form-item>
+      </div>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="createOpen = false ;resetForm(taskDataFormRef)">取消</el-button>
+        <el-button
+            :disabled="isSubmitting"
+            type="primary"
+            @click="submitForm(taskDataFormRef)"
+        >
+          {{ isSubmitting ? '提交中...' : '确认' }}
+        </el-button>
+      </div>
+    </template>
+  </el-dialog>
+  <TipDialog v-if="openTip" v-model="openTip" :message="dialogMessage" :isNormal="isNormal"
+             @submit="submit()"></TipDialog>
+</template>
+
+<style scoped>
+
+</style>

+ 74 - 0
src/views/reportManage/dataCenter/normalDisplay/components/DataEntryTable/components/TipDialog.vue

@@ -0,0 +1,74 @@
+<script setup lang="ts">
+/**
+ * @Name: TipDialog.vue
+ * @Description: 数据错误提示弹窗
+ * @Author: xinyan
+ */
+
+import { Warning } from '@element-plus/icons-vue';
+
+const props = defineProps({
+  message: Object,
+  isNormal: {
+    type: Boolean,
+    default: false,
+  },
+  isROI: {
+    type: Boolean,
+    default: false,
+  }
+});
+
+const { message ,isNormal, isROI} = props;
+const openTip = defineModel({ default: false });
+
+const emit = defineEmits(['submit', 'update']);
+
+
+function submit() {
+  emit('submit')
+}
+
+function update() {
+  emit('update')
+}
+
+</script>
+
+<template>
+  <el-dialog
+      v-model="openTip"
+      align-center
+      style="border-radius: 10px;"
+      title="重要提示"
+      width="30%"
+  >
+    <template #title>
+      <el-button link style="font-size: 18px" type="warning">
+        <el-icon size="22px">
+          <warning />
+        </el-icon>
+        重要提示
+      </el-button>
+    </template>
+    <div v-if="message">
+      <span>您输入的 </span>
+      <span style="color: #f1a055;">{{ message }}</span>
+      <span> 相较于上次填入的数据偏离值超过 50% ,是否仍要提交?</span>
+    </div>
+    <div v-if="isNormal ||isROI" class="pt-2">
+      <span>ROI < 0,请确认数据填入是否正确!</span>
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="openTip = false">返回修改</el-button>
+        <el-button type="primary" @click="isROI? update() : submit()">继续提交</el-button>
+      </span>
+    </template>
+  </el-dialog>
+
+</template>
+
+<style scoped>
+
+</style>

+ 37 - 433
src/views/reportManage/dataCenter/normalDisplay/components/TableDataEntry.vue → src/views/reportManage/dataCenter/normalDisplay/components/DataEntryTable/index.vue

@@ -1,4 +1,9 @@
 <script lang="ts" setup>
+/**
+ * @Name: DataEntryTable.vue
+ * @Description: 数据录入
+ * @Author: xinyan
+ */
 import { computed, onMounted, reactive, ref } from 'vue';
 import { VxeGridInstance, VXETable } from 'vxe-table';
 import dayjs from 'dayjs';
@@ -11,18 +16,16 @@ import {
   getMonthTaskData,
   getWeekBeforeData,
   getWeekTaskData,
-  postCreateDayData,
-  postCreateMonthData,
-  postCreateWeekData,
   postUpdateDayData,
   postUpdateMonthData,
   postUpdateWeekData
 } from '/src/views/reportManage/dataCenter/api';
-import { dayColumns, monthColumns, weekColumns } from '../../utils/columns';
-import { ComponentSize, ElMessage, FormInstance, FormRules } from 'element-plus';
 import enLocale from 'element-plus/es/locale/lang/en';
 import Selector from '/@/views/reportManage/dataCenter/normalDisplay/components/Selector/index.vue';
-import { Warning } from '@element-plus/icons-vue';
+import CreateDialog from '/src/views/reportManage/dataCenter/normalDisplay/components/DataEntryTable/components/CreateDialog.vue';
+import { validateNumericFields } from '/@/views/reportManage/dataCenter/utils/tools';
+import TipDialog from '/src/views/reportManage/dataCenter/normalDisplay/components/DataEntryTable/components/TipDialog.vue';
+import { dayColumns, monthColumns, weekColumns } from '/@/views/reportManage/dataCenter/utils/columns';
 
 
 dayjs.extend(isoWeek);
@@ -141,85 +144,20 @@ function setDefaultDate() {
   }
 }
 
-//表单
-interface taskDataForm {
-  sales_original: number,
-  ad_sales_original: number,
-  ad_cost_original: number,
-
-  total_sales_current_monthly_original: number;
-  impression: number;
-  ad_click: number;
-  ad_order: number;
-  money_by_amazon: number;
-  money_by_other: number;
-  session: number;
-  order: number;
-  availableSalesDay: number;
-  intransitInventory: number;
-  overseasStorage: number;
-  refundRate: number;
-}
+const currentDate = ref({});
 
-const formSize = ref<ComponentSize>('default');
-const isSubmitting = ref(false);
-const dayFormVisible = ref(false);
-const dialogVisible = ref(false);
-const dialogMessage = ref('');
+const createOpen = ref(false);
+const openTip = ref(false);
 const isROI = ref(false);
-const taskDataFormRef = ref<FormInstance>();
-const taskDataForm = reactive({
-  sales_original: null,
-  ad_sales_original: null,
-  ad_cost_original: null,
-  total_sales_current_monthly_original: null,
-  impression: null,
-  ad_click: null,
-  ad_order: null,
-  session: null,
-  order: null,
-  availableSalesDay: null,
-  intransitInventory: null,
-  overseasStorage: null,
-  refundRate: null,
-  money_by_amazon: null,
-  money_by_other: null,
-});
-
-const resetForm = (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
-};
-const rules = reactive<FormRules>({
-  sales_original: [{ required: true, message: '请输入销售额', trigger: 'blur' }],
-  ad_sales_original: [{ required: true, message: '请输入广告销售额', trigger: 'blur' }],
-  ad_cost_original: [{ required: true, message: '请输入广告花费', trigger: 'blur' }],
-  total_sales_current_monthly_original: [{ required: true, message: '请输入当月销售额', trigger: 'blur' }],
-  impression: [{ required: true, message: '请输入广告展示量', trigger: 'blur' }],
-  ad_click: [{ required: true, message: '请输入广告点击量', trigger: 'blur' }],
-  ad_order: [{ required: true, message: '请输入广告订单量', trigger: 'blur' }],
-  money_by_amazon: [{ required: true, message: '请输入截止目前Amazon回款金额', trigger: 'blur' }],
-  money_by_other: [{ required: true, message: '请输入截止目前Other回款金额', trigger: 'blur' }],
-  session: [{ required: true, message: '请输入会话数', trigger: 'blur' }],
-  order: [{ required: true, message: '请输入订单', trigger: 'blur' }],
-  availableSalesDay: [{ required: true, message: '请输入可用销量天数', trigger: 'blur' }],
-  intransitInventory: [{ required: true, message: '请输入在途库存', trigger: 'blur' }],
-  overseasStorage: [{ required: true, message: '请输入海外仓库存', trigger: 'blur' }],
-  refundRate: [{ required: true, message: '请输入最近90天平台退货率', trigger: 'blur' }],
-});
-
-const flatDayColumns = ref(flattenColumns(dayColumns.value));
-const flatWeekColumns = ref(flattenColumns(weekColumns.value));
-const flatMonthColumns = ref(flattenColumns(monthColumns.value));
 
 const ySalesData = ref({
   sales_original: '',
   total_sales_current_monthly_original: '',
 });
+
 //表格
-let taskId = 0;
-let currentId = 0;
-const currentRow = ref('');
+const currentRow = ref({});
+let dateParams;
 
 const dayData = [];
 const weekData = [];
@@ -352,8 +290,6 @@ function updateDataChange(newId) {
 }
 
 // 获取表格数据
-let dateParams;
-
 async function fetchTaskData(apiFunction) {
   try {
     gridOptions.loading = true;
@@ -404,204 +340,18 @@ function fetchCurrentTaskData() {
     };
     fetchMonthTaskData();
   }
-}
-
-const editEvent = async (row: RowVO) => {
-  taskId = row.task;
-  currentId = row.id;
-  currentRow.value = row;
-  Object.assign(taskDataForm, row);
-  await currentSalesTip();
-  dayFormVisible.value = true;
-};
-
-// 校验数据
-const validateNumericFields = (fields: Record<string, any>, fieldsToValidate: string[]): boolean => {
-  const numericFields = ['sales_original', 'total_sales_current_monthly_original', 'ad_sales_original', 'ad_cost_original', 'money_by_amazon', 'money_by_other', 'refundRate'];
-  const integerFields = ['impression', 'ad_click', 'ad_order', 'session', 'order', 'availableSalesDay', 'intransitInventory', 'overseasStorage'];
-
-  for (const field of fieldsToValidate) {
-    const value = fields[field];
-    const column = flatWeekColumns.value.find(col => col.field === field);
-    const title = column ? column.title : field;
-
-    if (numericFields.includes(field)) {
-      if (value === null || value === undefined || value === '') {
-        ElMessage.warning(`${ title } 不能为空`);
-        return false;
-      }
-      if (isNaN(Number(value))) {
-        ElMessage.warning(`${ title } 必须为数字`);
-        return false;
-      }
-    } else if (integerFields.includes(field)) {
-      if (value === null || value === undefined || value === '') {
-        ElMessage.warning(`${ title } 不能为空`);
-        return false;
-      }
-      if (!Number.isInteger(Number(value)) || isNaN(Number(value))) {
-        ElMessage.warning(`${ title } 必须为整数`);
-        return false;
-      }
-    }
-  }
-  return true;
-};
-
-//表单规则
-function createDayDataRules() {
-  if (dateType === 'day') {
-    rules.ad_sales_original[0].required = false;
-    rules.ad_cost_original[0].required = false;
-  }
-  if (dateType === 'month') {
-    rules.ad_sales_original[0].required = false;
-    rules.ad_cost_original[0].required = false;
-    rules.impression[0].required = false;
-    rules.ad_click[0].required = false;
-    rules.ad_order[0].required = false;
-  }
-}
-
-//创建日数据
-async function createDayData() {
-  const body = {
-    sales_original: taskDataForm.sales_original,
-    ad_sales_original: taskDataForm.ad_sales_original,
-    ad_cost_original: taskDataForm.ad_cost_original,
-    data_datetime: dailySalesTime.value,
-    task: taskId,
-  };
-  if (!currentId) {
-    try {
-      const resp = await postCreateDayData(body);
-      if (resp.code === 2000) {
-        dayFormVisible.value = false;
-        await fetchDayTaskData();
-        taskDataFormRef.value.resetFields();
-        await VXETable.modal.message({ content: '创建成功', status: 'success' });
-      }
-    } catch (e) {
-      await VXETable.modal.message({ content: '创建失败', status: 'error' });
-    }
-  } else {
-    dayFormVisible.value = false;
-    await VXETable.modal.message({ content: '此日期对应数据已存在', status: 'error' });
-  }
-}
-
-//创建周数据
-async function createWeekData() {
-  const body = {
-    sales_original: taskDataForm.sales_original,
-    ad_sales_original: taskDataForm.ad_sales_original,
-    ad_cost_original: taskDataForm.ad_cost_original,
-    total_sales_current_monthly_original: taskDataForm.total_sales_current_monthly_original,
-    impression: taskDataForm.impression,
-    ad_click: taskDataForm.ad_click,
-    ad_order: taskDataForm.ad_order,
-    money_by_amazon: taskDataForm.money_by_amazon,
-    money_by_other: taskDataForm.money_by_other,
-    session: taskDataForm.session,
-    order: taskDataForm.order,
-    availableSalesDay: taskDataForm.availableSalesDay,
-    intransitInventory: taskDataForm.intransitInventory,
-    overseasStorage: taskDataForm.overseasStorage,
-    refundRate: taskDataForm.refundRate,
-    sales_start_time: entryStartDate.value,
-    sales_end_time: entryEndDate.value,
+  currentDate.value = {
+    ...dateParams,
     ad_start_time: adStartDate.value,
     ad_end_time: adEndDate.value,
-    task: taskId,
-  };
-  if (!currentId) {
-    try {
-      const resp = await postCreateWeekData(body);
-      if (resp.code === 2000) {
-        dayFormVisible.value = false;
-        await fetchWeekTaskData();
-        taskDataFormRef.value.resetFields();
-        await VXETable.modal.message({ content: '创建成功', status: 'success' });
-      }
-    } catch (e) {
-      await VXETable.modal.message({ content: '创建失败', status: 'error' });
-    }
-  } else {
-    dayFormVisible.value = false;
-    await VXETable.modal.message({ content: '此日期对应数据已存在', status: 'error' });
-  }
-}
-
-//创建月数据
-async function createMonthData() {
-  const body = {
-    sales_original: taskDataForm.sales_original,
-    ad_sales_original: taskDataForm.ad_sales_original,
-    ad_cost_original: taskDataForm.ad_cost_original,
-    impression: taskDataForm.impression,
-    ad_click: taskDataForm.ad_click,
-    ad_order: taskDataForm.ad_order,
-    data_start_time: startDate.value,
-    data_end_time: endDate.value,
-    task: taskId,
   };
-  if (!currentId) {
-    try {
-      const resp = await postCreateMonthData(body);
-      if (resp.code === 2000) {
-        dayFormVisible.value = false;
-        await fetchMonthTaskData();
-        taskDataFormRef.value.resetFields();
-        await VXETable.modal.message({ content: '创建成功', status: 'success' });
-      }
-    } catch (e) {
-      await VXETable.modal.message({ content: '创建失败', status: 'error' });
-    }
-  } else {
-    dayFormVisible.value = false;
-    await VXETable.modal.message({ content: '此日期对应数据已存在', status: 'error' });
-  }
 }
 
-//创建任务
-const submitForm = async (formEl: FormInstance | undefined) => {
-  const fieldsToWarn = validateForm();
-  if (fieldsToWarn.length > 0 || validateForm2()) {
-    dialogVisible.value = true;
-    dialogMessage.value = `${ fieldsToWarn.join(', ') }`;
-    return;
-  } else {
-    if (!formEl || isSubmitting.value) return; // 防止重复提交
-    await formEl.validate(async (valid, fields) => {
-      if (valid) {
-        isSubmitting.value = true; // 表单开始提交,禁用按钮
-        try {
-          await submit(formEl);
-        } finally {
-          isSubmitting.value = false; // 无论成功或失败,恢复按钮
-        }
-      }
-    });
-  }
-};
-
-const submit = async () => {
-  dialogVisible.value = false;
-  if (dateType === 'day') {
-    const fieldsToValidate = ['sales_original'];
-    if (!validateNumericFields(taskDataForm, fieldsToValidate)) return; // 验证字段
-    await createDayData();
-  }
-  if (dateType === 'week') {
-    const fieldsToValidate = ['sales_original', 'ad_sales_original', 'ad_cost_original', 'total_sales_current_monthly_original', 'impression', 'ad_click', 'ad_order', 'money_by_amazon', 'money_by_other', 'session', 'order', 'availableSalesDay', 'intransitInventory', 'overseasStorage', 'refundRate'];
-    if (!validateNumericFields(taskDataForm, fieldsToValidate)) return; // 验证字段
-    await createWeekData();
-  }
-  if (dateType === 'month') {
-    const fieldsToValidate = ['sales_original'];
-    if (!validateNumericFields(taskDataForm, fieldsToValidate)) return; // 验证字段
-    await createMonthData();
-  }
+const createEvent = async (row: RowVO) => {
+  currentRow.value = row;
+  // Object.assign(taskDataForm, row);
+  await currentSalesTip();
+  createOpen.value = true;
 };
 
 // 更新日数据
@@ -709,18 +459,18 @@ async function updateMonthData(row: RowVO) {
   }
 }
 
-const editRowEvent = async (row: any) => {
+const saveRow = async (row: any) => {
   currentRow.value = row;
-  if (validateForm3(row)) {
-    isROI.value = true;
-    dialogVisible.value = true;
+  isROI.value = validateForm3(row);
+  if (isROI.value) {
+    openTip.value = true;
     return;
   }
-  await updateRowEvent(row);
+  await updateRow(row);
 };
 
-const updateRowEvent = async (row: RowVO) => {
-  dialogVisible.value = false;
+const updateRow = async (row: RowVO) => {
+  openTip.value = false;
   const $grid = xGrid.value;
   if ($grid) {
     if (dateType === 'day') {
@@ -739,7 +489,7 @@ const updateRowEvent = async (row: RowVO) => {
     activeEditRow.value = false;
     await $grid.clearEdit();
   }
-}
+};
 
 // 日数据提示
 async function salesTip(apiFunction, dateTypeKey) {
@@ -749,7 +499,7 @@ async function salesTip(apiFunction, dateTypeKey) {
     month: yMonthDay.value,
   };
   const resp = await apiFunction({
-    task: taskId,
+    task: currentRow.value.task,
     [`${ dateTypeKey }_end_date`]: dateMap[dateTypeKey],
   });
   if (dateType === 'week') {
@@ -790,38 +540,6 @@ const currentGridOptions = computed(() => {
   };
 });
 
-function flattenColumns(columns) {
-  let result = [];
-  columns.forEach(column => {
-    if (column.children && column.children.length > 0) {
-      result = result.concat(flattenColumns(column.children));
-    } else {
-      result.push(column);
-    }
-  });
-  return result;
-}
-
-const filteredDayColumns = computed(() => {
-  const flat = flatDayColumns.value;
-  return flat.filter(item => !['平台编号', '平台名称', '国家', '品牌', '操作', '运营', '销售额', '广告销售额', '广告花费', '平台币种'].includes(item.title));
-});
-
-const filteredWeekColumns = computed(() => {
-  const flat = flatWeekColumns.value;
-  return flat.filter(item => !['平台编号', '平台名称', '国家', '品牌', '操作', '运营', '销售额', '广告销售额', '广告花费', '当月累计销售额', '平台币种', '回款/余额币种'].includes(item.title));
-});
-
-const filteredMonthColumns = computed(() => {
-  const flat = flatMonthColumns.value;
-  return flat.filter(item => !['平台编号', '平台名称', '国家', '品牌', '操作', '运营', '销售额', '广告销售额', '广告花费', '平台币种'].includes(item.title));
-});
-
-function handleClose(done: Function) {
-  if (taskDataFormRef.value) taskDataFormRef.value.resetFields();
-  done();
-}
-
 const cellStyle = () => {
   return {
     fontSize: '13px',
@@ -835,37 +553,6 @@ const headerCellStyle = () => {
   };
 };
 
-const validateForm = () => {
-  const fieldsToWarn = [];
-  const fieldsToValidate = ['sales_original', 'total_sales_current_monthly_original'];
-  if (ySalesData.value) {
-    for (const key of fieldsToValidate) {
-      const newValue = taskDataForm[key];
-      const yDayValue = ySalesData.value[key];
-      const column = flatWeekColumns.value.find(col => col.field === key);
-      const title = column ? column.title : key;
-
-      if (newValue != null && yDayValue !== null && yDayValue !== undefined) {
-        const diffPercentage = Math.abs((newValue - yDayValue) / yDayValue) * 100;
-        if (diffPercentage > 50) {
-          fieldsToWarn.push(title);
-        }
-      }
-    }
-  }
-  return fieldsToWarn;
-};
-
-const validateForm2 = () => {
-  const adSalesValue = taskDataForm['ad_sales_original'];
-  const adCostValue = taskDataForm['ad_cost_original'];
-
-  if (adSalesValue != null && adCostValue != null && adCostValue !== 0) {
-    const ratio = (adSalesValue - adCostValue) / adCostValue;
-    return ratio < 0;
-  }
-};
-
 const validateForm3 = (row: RowVO) => {
   const adSalesValue = row.ad_sales_original;
   const adCostValue = row.ad_cost_original;
@@ -878,8 +565,6 @@ const validateForm3 = (row: RowVO) => {
 
 onMounted(() => {
   setDefaultDate();
-  //fetchCurrentTaskData();
-  createDayDataRules();
 });
 </script>
 
@@ -954,15 +639,15 @@ onMounted(() => {
         <template #operate="{ row }">
           <template v-if="hasActiveEditRow(row)">
             <el-button link size="small" @click="clearRowEvent(row)">取消</el-button>
-            <el-button link size="small" type="warning" @click="editRowEvent(row)">保存</el-button>
+            <el-button link size="small" type="warning" @click="saveRow(row)">保存</el-button>
           </template>
           <template v-else>
-            <el-button :disabled="!row.id||activeEditRow"  link size="small" type="success" @click="handelEditRow(row)">
+            <el-button :disabled="!row.id||activeEditRow" link size="small" type="success" @click="handelEditRow(row)">
               修改
             </el-button>
           </template>
           <el-button v-if="!hasActiveEditRow(row)" :disabled="row.id||activeEditRow" link size="small" type="primary"
-                     @click="editEvent(row)">创建
+                     @click="createEvent(row)">创建
           </el-button>
         </template>
         <template #total_header="{ row }">
@@ -1024,90 +709,9 @@ onMounted(() => {
       </vxe-grid>
     </div>
   </el-card>
-  <el-dialog v-model="dayFormVisible" :before-close="handleClose" :title="`创建任务 : ${currentRow.platformNumber} / ${currentRow.platformName}`"
-             style="border-radius: 10px;"
-             width="600">
-    <template #title>
-      <span class="text-xl">创建任务</span>
-      <div class="mt-2" style="display: flex; align-items: center;color: darkgray">
-        <div style="margin-right: 8px;">
-          平台编号:
-          <span class="italic pl-1 pr-2">{{ currentRow.platformNumber }}</span>
-        </div>
-        <div>
-          平台名称:
-          <span class="italic pl-1">{{ currentRow.platformName }}</span>
-        </div>
-      </div>
-    </template>
-    <el-form
-        ref="taskDataFormRef"
-        :model="taskDataForm"
-        :rules="rules"
-        :size="formSize"
-        label-position="top"
-        label-width="auto"
-        status-icon
-        style="max-width: 600px;">
-      <div v-if="dateType === 'day'">
-        <el-form-item v-for="item in filteredDayColumns" :key="item.field" :label="item.title" :prop="item.field">
-          <el-input v-model="taskDataForm[item.field]" :placeholder="`请输入${item.title}`" />
-        </el-form-item>
-      </div>
-      <div v-if="dateType === 'week'" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 5px">
-        <el-form-item v-for="item in filteredWeekColumns" :key="item.field" :label="item.title" :prop="item.field">
-          <el-input v-model="taskDataForm[item.field]" style="width: 100%" />
-        </el-form-item>
-      </div>
-      <div v-if="dateType === 'month'">
-        <el-form-item v-for="item in filteredMonthColumns" :key="item.field" :label="item.title" :prop="item.field">
-          <el-input v-model="taskDataForm[item.field]" :placeholder="`请输入${item.title}`" />
-        </el-form-item>
-      </div>
-    </el-form>
-    <template #footer>
-      <div class="dialog-footer">
-        <el-button @click="dayFormVisible = false ;resetForm(taskDataFormRef)">取消</el-button>
-        <el-button
-            :disabled="isSubmitting"
-            type="primary"
-            @click="submitForm(taskDataFormRef)"
-        >
-          {{ isSubmitting ? '提交中...' : '确认' }}
-        </el-button>
-      </div>
-    </template>
-  </el-dialog>
-  <el-dialog
-      v-model="dialogVisible"
-      align-center
-      style="border-radius: 10px;"
-      title="重要提示"
-      width="30%"
-  >
-    <template #title>
-      <el-button link style="font-size: 18px" type="warning">
-        <el-icon size="22px">
-          <warning />
-        </el-icon>
-        重要提示
-      </el-button>
-    </template>
-    <div v-if="dialogMessage">
-      <span>您输入的 </span>
-      <span style="color: #f1a055;">{{ dialogMessage }}</span>
-      <span> 相较于上次填入的数据偏离值超过 50% ,是否仍要提交?</span>
-    </div>
-    <div v-if="validateForm2()||isROI" class="pt-2">
-      <span>ROI < 0,请确认数据填入是否正确!</span>
-    </div>
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">返回修改</el-button>
-        <el-button type="primary" @click="isROI? updateRowEvent(currentRow) : submit()">继续提交</el-button>
-      </span>
-    </template>
-  </el-dialog>
+  <CreateDialog v-if="createOpen" v-model="createOpen" :currentDate="currentDate" :currentRow="currentRow"
+                :dateType="dateType" :ySalesData="ySalesData" @refresh="fetchCurrentTaskData"></CreateDialog>
+  <TipDialog v-if="openTip" v-model="openTip" :isROI="isROI" @update="updateRow(currentRow)"></TipDialog>
 </template>
 
 <style lang="scss" scoped>

+ 12 - 0
src/views/reportManage/dataCenter/normalDisplay/components/PlanningSales.vue

@@ -442,4 +442,16 @@ onMounted(() => {
   white-space: nowrap;
   margin-right: 2px;
 }
+
+.el-card {
+  border: none;
+  box-shadow: none;
+}
+
+.el-card:hover {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
+  0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  border-color: #eee;
+  transition: all 0.2s ease-in-out;
+}
 </style>

+ 1 - 1
src/views/reportManage/dataCenter/normalDisplay/components/TableDataDisplay.vue

@@ -341,7 +341,7 @@ function clearSort() {
 
 const handleImport = () => {
   const url = router.resolve({
-    name: 'TableDataEntry',
+    name: 'DataEntry',
     query: {
       dateType: dateType.value,
     }

+ 71 - 6
src/views/reportManage/dataCenter/utils/tools.ts

@@ -1,11 +1,14 @@
-import dayjs, { Dayjs } from 'dayjs';
-import { unref } from 'vue';
+import { ref, unref } from 'vue';
 import XEUtils from 'xe-utils';
+import { ElMessage } from 'element-plus';
+import { weekColumns } from '/@/views/reportManage/dataCenter/utils/columns';
+import { weekMetricsEnum } from '/@/views/reportManage/dataCenter/utils/enum';
+
 
 export function buildChartOpt(option: any, metrics: any[]) {
   const tmp: any = {};
   const opt: any = {
-    legend: {selected: {}},
+    legend: { selected: {} },
     yAxis: [],
     series: [],
   };
@@ -19,11 +22,11 @@ export function buildChartOpt(option: any, metrics: any[]) {
       opt.series.push({
         id: info.id,
         name: metricInfo.label,
-        encode: {y: metricInfo.metric},
+        encode: { y: metricInfo.metric },
       });
-      opt.yAxis.push({id: info.id, name: metricInfo.label, show: true});
+      opt.yAxis.push({ id: info.id, name: metricInfo.label, show: true });
     } else {
-      opt.yAxis.push({id: info.id, show: false});
+      opt.yAxis.push({ id: info.id, show: false });
     }
   }
   for (const label of Object.keys(option.legend.selected)) {
@@ -79,3 +82,65 @@ export function monthlyQueryParams(body: any) {
   }
   return date;
 }
+
+export function flattenColumns(columns) {
+  let result = [];
+  columns.forEach(column => {
+    if (column.children && column.children.length > 0) {
+      result = result.concat(flattenColumns(column.children));
+    } else {
+      result.push(column);
+    }
+  });
+  return result;
+}
+
+export function validateNumericFields(fields: Record<string, any>, fieldsToValidate: string[]): boolean {
+  const numericFields = ['sales_original', 'total_sales_current_monthly_original', 'ad_sales_original', 'ad_cost_original', 'money_by_amazon', 'money_by_other', 'refundRate'];
+  const integerFields = ['impression', 'ad_click', 'ad_order', 'session', 'order', 'availableSalesDay', 'intransitInventory', 'overseasStorage'];
+
+  for (const field of fieldsToValidate) {
+    const value = fields[field];
+    const flatWeekColumns = ref(flattenColumns(weekColumns.value));
+    const column = flatWeekColumns.value.find(col => col.field === field);
+    const title = column ? column.title : field;
+
+    if (numericFields.includes(field)) {
+      if (value === null || value === undefined || value === '') {
+        ElMessage.warning(`${ title } 不能为空`);
+        return false;
+      }
+      if (isNaN(Number(value))) {
+        ElMessage.warning(`${ title } 必须为数字`);
+        return false;
+      }
+    } else if (integerFields.includes(field)) {
+      if (value === null || value === undefined || value === '') {
+        ElMessage.warning(`${ title } 不能为空`);
+        return false;
+      }
+      if (!Number.isInteger(Number(value)) || isNaN(Number(value))) {
+        ElMessage.warning(`${ title } 必须为整数`);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+export function validateNumber(rule, value) {
+  if (value !== '' && isNaN(value)) {
+    const fieldName = rule.field; // 获取当前字段名称
+    const flatWeekColumns = ref(flattenColumns(weekColumns.value));
+    const column = flatWeekColumns.value.find(col => col.field === fieldName);
+    const title = column ? column.title : field;
+
+    if (title) {
+      return new Error(`${title} 必须为数字`);
+    } else {
+      return new Error('输入值必须为数字'); // 如果无法找到对应的 label,使用通用错误信息
+    }
+  }
+  return true; // 输入有效
+}
+