Explorar o código

fix:单项目成本管理

liqh hai 5 meses
pai
achega
23dce56441

+ 74 - 0
src/components/Catalog/index.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="d-catalog">
+    <div class="d-header">
+      <span>{{ title }}</span>
+
+      <div class="h-right" v-if="slots.headerRight">
+        <a-space>
+          <slot name="headerRight"></slot>
+        </a-space>
+      </div>
+    </div>
+
+    <div class="d-content" v-if="slots.content">
+      <slot name="content"></slot>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { useSlots } from "vue";
+
+  const props = defineProps({
+    title: {
+      required: true,
+      default: '标题',
+    },
+  });
+
+  const slots = useSlots()
+
+</script>
+
+<style lang="scss" scoped>
+  .d-catalog {
+    width: 100%;
+    height: 100%;
+
+    .d-header {
+      font-size: 16px;
+      font-weight: 600;
+      padding: 10px 0 10px 10px;
+      border-bottom: 1px solid #e4e7ed;
+      cursor: pointer;
+      position: relative;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: 10px;
+      &::before {
+        width: 4px;
+        height: 18px;
+        background: var(--vxe-ui-font-primary-color);
+        position: absolute;
+        left: 0;
+        top: 50%;
+        transform: translateY(-50%);
+        content: '';
+        border-radius: 4px;
+      }
+
+      .h-right {
+        position: absolute;
+        right: 0;
+        bottom: 6px;
+      }
+    }
+
+    .d-content {
+      width: 100%;
+      height: 100%;
+      padding: 20px 0;
+    }
+  }
+</style>

+ 5 - 8
src/views/project/cost-manage/loan-manage/index.vue

@@ -4,7 +4,6 @@
 
 <script setup lang="jsx">
   import { ref } from 'vue';
-  import { useRouter } from 'vue-router';
   import useDict from '/@/utils/dict-util';
   await useDict.init(['MATE_DELIVERY_STATUS']);
 
@@ -41,7 +40,7 @@
               stateName = '办结';
               break;
             default:
-              stateName = 'default';
+              stateName = '无状态';
           }
           return (
             <div class='smart-table-operate'>
@@ -57,7 +56,7 @@
         width: '100px',
         ellipsis: true,
         customRender: ({ text, record, index, column }) => {
-          return <div class='smart-table-operate'>{record.applicant.actualName}</div>;
+          return <div class='smart-table-operate'>{record?.applicant?.actualName}</div>;
         },
       },
       {
@@ -101,7 +100,7 @@
         align: 'center',
         width: '60px',
         customRender: ({ text, record, index, column }) => {
-          return <div class='smart-table-operate'>{record.createBy.actualName}</div>;
+          return <div class='smart-table-operate'>{record?.createBy?.actualName}</div>;
         },
       },
       {
@@ -140,14 +139,12 @@
     // afterFetch({ data }) {},
   });
 
-  const router = useRouter();
-
+  // 查看(借款单)
   function goDetailPage(record) {
     window.open(`http://localhost:8081/#/MvcSheet/formWork?flowCode=${record.flowCode}&bizObjectID=${record.bizObjectId}`);
   }
-
+  //发起报销单
   function ToInitiateReimbursement(record) {
-    console.log('recodfdfd', record);
     window.open(`http://localhost:8081/#/MvcSheet/formWork?flowCode=${record.flowCode}&tag=START`);
   }
 </script>

+ 1 - 3
src/views/project/cost-manage/reimburse-manage/index.vue

@@ -4,7 +4,6 @@
 
 <script setup lang="jsx">
   import { ref } from 'vue';
-  import { useRouter } from 'vue-router';
   import useDict from '/@/utils/dict-util';
   await useDict.init(['MATE_DELIVERY_STATUS']);
 
@@ -118,8 +117,7 @@
     // afterFetch({ data }) {},
   });
 
-  const router = useRouter();
-
+  // 查看(费用报销单)
   function goDetailPage(record) {
     window.open(`http://localhost:8081/#/MvcSheet/formWork?flowCode=${record.flowCode}&bizObjectID=${record.bizObjectId}`);
   }

+ 270 - 0
src/views/project/project-manage/single-project/components/cost-manage/cost-analysis/components/OrgStructChart/index.vue

@@ -0,0 +1,270 @@
+<template>
+  <div ref="containerRef" class="org-chart-container" />
+</template>
+
+<script setup>
+  import { ref, onMounted, onBeforeUnmount } from 'vue';
+  import { Graph, Node, Point } from '@antv/x6';
+  // 组件挂载后初始化图表
+  onMounted(() => {
+    console.log("434343");
+    
+    registerChartElements();
+    initChart();
+  });
+
+  const containerRef = ref(null);
+  let graph = null;
+
+  // 注册节点和边的类型
+  const registerChartElements = () => {
+    Graph.registerNode(
+      'org-node',
+      {
+        width: 180,
+        height: 60,
+        markup: [
+          {
+            tagName: 'rect',
+            selector: 'body',
+          },
+          {
+            tagName: 'image',
+            selector: 'avatar',
+          },
+          {
+            tagName: 'text',
+            selector: 'rank',
+          },
+          {
+            tagName: 'text',
+            selector: 'name',
+          },
+        ],
+        attrs: {
+          body: {
+            refWidth: '100%',
+            refHeight: '100%',
+            fill: '#3378FF',
+            stroke: '#3378FF',
+            strokeWidth: 1,
+            rx: 10, //角弧度
+            ry: 10, //角弧度
+            pointerEvents: 'visiblePainted',
+          },
+          avatar: {
+            width: 48,
+            height: 48,
+            refX: 8,
+            refY: 6,
+          },
+          rank: {
+            refX: '50%', // 设置为 50% 表示水平居中
+            refY: 0.2,
+            fill: '#fff',
+            fontFamily: 'Courier New',
+            fontSize: 14,
+            textAnchor: 'middle',
+          },
+          name: {
+            refX: '50%', // 设置为 50% 表示水平居中
+            refY: 0.6,
+            fill: '#fff',
+            fontFamily: 'Courier New',
+            fontSize: 16,
+            fontWeight: '800',
+            textAnchor: 'middle',
+          },
+        },
+      },
+      true
+    );
+
+    Graph.registerEdge(
+      'org-edge',
+      {
+        zIndex: -1,
+        attrs: {
+          line: {
+            fill: 'none',
+            strokeLinejoin: 'round',
+            strokeWidth: 2,
+            stroke: '#A2B1C3',
+            sourceMarker: null,
+            targetMarker: null,
+          },
+        },
+      },
+      true
+    );
+  };
+
+  // 创建节点
+  const createNode = (x, y, rank, name, image) => {
+    return graph.addNode({
+      x,
+      y,
+      shape: 'org-node',
+      attrs: {
+        avatar: {
+          opacity: 0.7,
+          // 'xlink:href': image,
+        },
+        rank: {
+          text: rank,
+          wordSpacing: '-5px',
+          letterSpacing: 0,
+        },
+        name: {
+          text: name,
+          fontSize: 16,
+          fontFamily: 'Arial',
+          letterSpacing: 0,
+        },
+      },
+      // 禁用节点拖动
+      movable: false,
+    });
+  };
+
+  // 创建连线
+  const createLink = (source, target, vertices) => {
+    return graph.addEdge({
+      vertices,
+      source: {
+        cell: source,
+      },
+      target: {
+        cell: target,
+      },
+      shape: 'org-edge',
+      // 禁用连线拖动
+      connector: {
+        name: 'normal',
+        args: {
+          style: {
+            pointerEvents: 'none',
+          },
+        },
+      },
+      // 禁用连线调整
+      router: {
+        name: 'manhattan',
+        args: {
+          padding: 10,
+        },
+      },
+      attrs: {
+        line: {
+          // 禁止选中连线
+          pointerEvents: 'none',
+        },
+      },
+    });
+  };
+
+  // 初始化图表
+  const initChart = () => {
+    if (!containerRef.value) return;
+
+    // 创建图表实例
+    graph = new Graph({
+      container: containerRef.value,
+      connecting: {
+        anchor: 'orth',
+      },
+      // 禁用鼠标滚轮缩放
+      mousewheel: {
+        enabled: true,
+      },
+      // 禁用平移
+      panning: true,
+      // 禁用框选
+      selecting: {
+        enabled: false,
+      },
+      interacting: {
+        nodeMovable: false,
+        edgeMovable: false,
+      },
+    });
+
+    // 图表数据
+    const male = 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*kUy8SrEDp6YAAAAAAAAAAAAAARQnAQ';
+    const female = 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*f6hhT75YjkIAAAAAAAAAAAAAARQnAQ';
+
+    // 创建节点
+    const bart1 = createNode(100, 70, '项目合同额(元)', '2000,00.00', male);
+    const bart = createNode(300, 70, '项目总成本(元)', '2000,00.00', male);
+    const bart2 = createNode(500, 70, '项目净利润(元)', '2000,00.00', male);
+    const homer = createNode(20, 200, '人力成本(元)', '2000,00.00', male);
+    const marge = createNode(220, 200, '差率成本(元)', '2000,00.00', female);
+    const lisa = createNode(430, 200, '销售成本(元)', '2000,00.00', female);
+    const lisa1 = createNode(630, 200, '经营成本(元)', '2000,00.00', female);
+
+    // 创建连线
+
+    createLink(bart, homer, [
+      {
+        x: 395,
+        y: 180,
+      },
+      {
+        x: 115,
+        y: 180,
+      },
+    ]);
+    createLink(bart, marge, [
+      {
+        x: 395,
+        y: 180,
+      },
+      {
+        x: 310,
+        y: 180,
+      },
+    ]);
+    createLink(bart, lisa, [
+      {
+        x: 395,
+        y: 180,
+      },
+      {
+        x: 525,
+        y: 180,
+      },
+    ]);
+    createLink(bart, lisa1, [
+      {
+        x: 395,
+        y: 180,
+      },
+      {
+        x: 585,
+        y: 180,
+      },
+    ]);
+
+    // 调整视图以适应所有节点
+    graph.zoomToFit({ padding: 20, maxScale: 1 });
+  };
+
+  // 组件卸载前清理图表
+  onBeforeUnmount(() => {
+    if (graph) {
+      graph.dispose();
+      graph = null;
+    }
+  });
+
+</script>
+
+<style scoped>
+  .org-chart-container {
+    width: 100%;
+    height: 385px;
+    border: 1px solid #e5e7eb;
+    border-radius: 6px;
+    overflow: hidden;
+  }
+</style>

+ 51 - 0
src/views/project/project-manage/single-project/components/cost-manage/cost-analysis/index.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="link-company">
+    <div class="link-flow">
+      <bs-catalog title="成本分析">
+        <template #content>
+          <div class="link-company-content">
+            <org-struct-chart ref="orgStructChartRef" />
+          </div>
+        </template>
+      </bs-catalog>
+    </div>
+  </div>
+</template>
+<script setup>
+  import { onMounted } from 'vue';
+  import OrgStructChart from './components/OrgStructChart/index.vue';
+  import BsCatalog from '/@/components/Catalog/index.vue';
+  
+    
+    onMounted(() => {
+      console.log('CostAnalysis component is mounted');
+    });
+</script>
+
+<style scoped lang="scss">
+  .link-company {
+    width: 100%;
+    display: flex;
+    gap: 30px;
+    .link-flow {
+      border-radius: 8px;
+      flex: 1;
+      .link-company-content {
+        width: 100%;
+        position: relative;
+        .func-btns {
+          position: absolute;
+          display: flex;
+          flex-direction: column;
+          gap: 10px;
+          right: 10px;
+          top: 10px;
+          z-index: 10;
+        }
+      }
+    }
+    .link-list {
+      width: 670px;
+    }
+  }
+</style>

+ 138 - 0
src/views/project/project-manage/single-project/components/cost-manage/echar.vue

@@ -0,0 +1,138 @@
+<template>
+  <div class="chart-container">
+    <div class="chart" ref="mainChart"></div>
+    <div class="chart" ref="subChart" :style="{ display: subChartVisible ? 'block' : 'none' }"></div>
+  </div>
+</template>
+
+<script>
+  import * as echarts from 'echarts';
+
+  export default {
+    name: 'PieChart',
+    data() {
+      return {
+        mainChartData: [
+          { name: 'A', value: 30, title: '人力成本' },
+          { name: 'B', value: 20, title: '销售成本' },
+          { name: 'C', value: 50, title: '运营成本' },
+        ],
+        subChartData: {
+          A: [
+            { name: 'A1', value: 10 , title: '人力成本'},
+            { name: 'A2', value: 20, title: '人力成本' },
+          ],
+          B: [
+            { name: 'B1', value: 10 , title: '人力成本'},
+            { name: 'B2', value: 10, title: '人力成本' },
+          ],
+          C: [
+            { name: 'C1', value: 20 , title: '人力成本'},
+            { name: 'C2', value: 30 , title: '人力成本'},
+          ],
+        },
+        subChartVisible: false, // 控制细分饼状图是否显示
+      };
+    },
+    mounted() {
+      this.initMainChart();
+    },
+    methods: {
+      initMainChart() {
+        const mainChart = echarts.init(this.$refs.mainChart);
+        mainChart.setOption({
+          tooltip: {
+            trigger: 'item',
+            formatter: function (params) {
+              const { name, title } = params.data; // 解构出 name 和 title
+              return `${title} (${params.percent}%)`;
+            },
+          },
+          series: [
+            {
+              name: '主饼状图',
+              type: 'pie',
+              radius: ['40%', '70%'],
+              data: this.mainChartData,
+              label: {
+                position: 'center',
+                show: false,
+              },
+              emphasis: {
+                label: {
+                  show: true,
+                  fontSize: 20,
+                  fontWeight: 'bold',
+                  formatter: function (params) {
+                    const { name, title } = params.data; // 解构出 name 和 title
+                    return `${title} (${params.percent}%)`;
+                  },
+                },
+                itemStyle: {
+                  shadowBlur: 10,
+                  shadowOffsetX: 0,
+                  shadowColor: 'rgba(0, 0, 0, 0.5)',
+                },
+              },
+            },
+          ],
+        });
+
+        mainChart.on('click', (params) => {
+          this.updateSubChart(params.name);
+        });
+      },
+      updateSubChart(selectedName) {
+        this.subChartVisible = true; // 显示细分饼状图
+        const subChart = echarts.init(this.$refs.subChart);
+        const subData = this.subChartData[selectedName];
+        subChart.setOption({
+          tooltip: {
+            trigger: 'item',
+          },
+          series: [
+            {
+              name: '细分饼状图',
+              type: 'pie',
+              radius: ['40%', '70%'],
+              data: subData,
+              label: {
+                position: 'center',
+                show: false,
+              },
+              emphasis: {
+                label: {
+                  show: true,
+                  fontSize: 20,
+                  fontWeight: 'bold',
+                  formatter: function (params) {
+                    const { name, title } = params.data; // 解构出 name 和 title
+                    return `${title} (${params.percent}%)`;
+                  },
+                },
+                itemStyle: {
+                  shadowBlur: 10,
+                  shadowOffsetX: 0,
+                  shadowColor: 'rgba(0, 0, 0, 0.5)',
+                },
+              },
+            },
+          ],
+        });
+      },
+    },
+  };
+</script>
+
+<style scoped>
+  .chart-container {
+    display: flex;
+    gap:10%
+    /* justify-content: space-between; */
+  }
+  .chart {
+    width: 350px;
+    height: 350px;
+
+  }
+</style>

+ 200 - 0
src/views/project/project-manage/single-project/components/cost-manage/index.vue

@@ -0,0 +1,200 @@
+<template>
+  <div class="dashboard">
+    <div class="dashboard-card" v-for="item in dashboardItems" :key="item.title">
+      <span class="dashboard-card__title">{{ item.title }}</span>
+      <div class="dashboard-card__value">
+        <span class="dashboard-card__number">{{ item.value }}</span>
+        <span class="dashboard-card__unit">{{ item.unit }}</span>
+      </div>
+    </div>
+  </div>
+  <div style="background: white; margin-top: 10px; padding: 10px">
+    <a-button type="primary" style="margin: 10px 0 10px 0" @click="open = true"> 成本分析 </a-button>
+    <a-table :columns="columns" :data-source="tableData" bordered :row-class-name="() => 'custom-row'" :scroll="{ y: 400 }">
+      <template #money="{ text, record, index }">
+        <div class="editable-cell">
+          <div v-if="isEditable(record.age1) && editableData[index]" class="editable-cell-input-wrapper">
+            <a-input-number v-model:value="editableData[index].phone1" :min="0" style="width: 100px" @pressEnter="save(index)" />
+            <check-outlined class="editable-cell-icon-check" @click="save(index)" />
+          </div>
+          <div v-else class="editable-cell-text-wrapper">
+            {{ text}}
+            <edit-outlined v-if="isEditable(record.age1)" class="editable-cell-icon" @click="edit(index)" />
+          </div>
+        </div>
+      </template>
+    </a-table>
+    <a-drawer v-model:open="open" width="1300" placement="right" title="成本分析">
+      <CostAnalysis />
+      <Echar />
+    </a-drawer>
+  </div>
+</template>
+
+<script setup>
+  import { ref, reactive } from 'vue';
+  import { cloneDeep } from 'lodash-es';
+  import { CheckOutlined, EditOutlined } from '@ant-design/icons-vue';
+  import CostAnalysis from './cost-analysis/index.vue';
+  import Echar from './echar.vue';
+
+  const dashboardItems = [
+    { title: '任务总数', value: 12, unit: '个' },
+    { title: '预计人天', value: 12, unit: '人天' },
+    { title: '累计发生人天', value: 12, unit: '天' },
+    { title: '超预计人天数', value: 12, unit: '天' },
+    { title: '超期任务数', value: 12, unit: '个' },
+  ];
+
+  const open = ref(false);
+
+  const tableData = ref([
+    { key: '1', name: '人力成本', age1: '人力薪资', tel: '0571-22098909', phone1: 3000, phone: 5000 },
+    { key: '2', name: '人力成本', age1: '人员补贴', tel: '0571-22098333', phone1: 1000, phone: 5000 },
+    { key: '3', name: '人力成本', age1: '福利费', tel: '0575-22098909', phone1: 2000, phone: 5000 },
+    { key: '4', name: '人力成本', age1: '劳务费', tel: '0575-22098909', phone1: 2000, phone: 5000 },
+    { key: '5', name: '差旅成本', age1: '交通费', tel: '0575-22098909', phone1: 2000, phone: 2000 },
+    { key: '6', name: '差旅成本', age1: '住宿费', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+    { key: '7', name: '销售成本', age1: '营销商务费用', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+    { key: '8', name: '经营成本', age1: '物料/设备采买', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+    { key: '9', name: '经营成本', age1: '房租', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+    { key: '10', name: '经营成本', age1: '管理费', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+    { key: '11', name: '经营成本', age1: '公司运营成本(分摊)', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+    { key: '12', name: '经营成本', age1: '税费', tel: '0575-22098909', phone1: 1000, phone: 2000 },
+  ]);
+
+  const editableData = reactive({});
+
+  const isEditable = (costItem) => {
+    return ['人力薪资', '人员补贴', '人员绩效', '劳务费', '公司运营成本(分摊)'].includes(costItem);
+  };
+
+  const edit = (index) => {
+    if (isEditable(tableData.value[index].age1)) {
+      editableData[index] = cloneDeep(tableData.value[index]);
+    }
+  };
+
+  const save = (index) => {
+    if (isEditable(tableData.value[index].age1)) {
+      Object.assign(tableData.value[index], editableData[index]);
+      delete editableData[index];
+    }
+  };
+
+  /* 根据 index 返回 rowSpan(合并单元格用) */
+  const getRowSpan = (_, index) => {
+    if (index === 0) return { rowSpan: 4 };
+    if (index === 1 || index === 2 || index === 3) return { rowSpan: 0 };
+    if (index === 4) return { rowSpan: 2 };
+    if (index === 5) return { rowSpan: 0 };
+    if (index === 7) return { rowSpan: 3 };
+    if (index === 8 || index === 9 || index === 10) return { rowSpan: 0 };
+    return {};
+  };
+
+  const columns = [
+    { title: '成本分类', dataIndex: 'name', align: 'center', customCell: getRowSpan },
+    { title: '成本项', dataIndex: 'age1', align: 'center' },
+    { title: '成本预算(元)', dataIndex: 'phone1', align: 'center' },
+    {
+      title: '实际成本(元)',
+      dataIndex: 'phone1',
+      align: 'center',
+      slots: { customRender: 'money' },
+    },
+    { title: '实际成本(元)', dataIndex: 'phone', align: 'center', customCell: getRowSpan },
+    { title: '成本偏差额(元)', dataIndex: 'age1', align: 'center' },
+    { title: '总预算成本(元)', dataIndex: 'age1', align: 'center' },
+    { title: '实际总成本(元)', dataIndex: 'age1', align: 'center' },
+    { title: '实际总成本偏差(元)', dataIndex: 'age1', align: 'center' },
+  ];
+</script>
+
+<style scoped lang="less">
+  .dashboard {
+    display: flex;
+    gap: 70px;
+    align-items: center;
+    justify-content: center;
+    padding: 10px;
+    background: white;
+  }
+  .dashboard-card {
+    width: 180px;
+    height: 100px;
+    background: #f5f8ff;
+    border: 1px solid #d3dfff;
+    border-radius: 10px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+  }
+  .dashboard-card__title {
+    font-size: 14px;
+    color: #666;
+  }
+  .dashboard-card__value {
+    margin-top: 10px;
+  }
+  .dashboard-card__number {
+    font-weight: bold;
+    color: #318586;
+    font-size: 20px;
+    margin-right: 8px;
+  }
+  .dashboard-card__unit {
+    font-size: 14px;
+    color: #666;
+  }
+
+  .custom-row td {
+    padding: 16px !important;
+  }
+
+  .editable-cell {
+  position: relative;
+
+  .editable-cell-input-wrapper,
+  .editable-cell-text-wrapper {
+    padding-right: 24px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  .editable-cell-text-wrapper {
+    padding: 5px 24px 5px 5px;
+  }
+
+  .editable-cell-icon,
+  .editable-cell-icon-check {
+    position: absolute;
+    right: 0;
+    width: 20px;
+    cursor: pointer;
+  }
+
+  .editable-cell-icon {
+    margin-top: 4px;
+    display: none;
+  }
+
+  .editable-cell-icon-check {
+    line-height: 28px;
+  }
+
+  .editable-cell-icon:hover,
+  .editable-cell-icon-check:hover {
+    color: #108ee9;
+  }
+
+  .editable-add-btn {
+    margin-bottom: 8px;
+  }
+}
+.editable-cell:hover .editable-cell-icon {
+  display: inline-block;
+}
+</style>

+ 2 - 3
src/views/project/project-manage/single-project/index.vue

@@ -43,6 +43,7 @@
   import ProgressInfo from './components/progress-info/index.vue';
   import FileManage from './components/file-manage/index.vue';
   import FollowUp from './components/follow-up/index.vue';
+  import CostManage from './components/cost-manage/index.vue';
 
   onMounted(() => {
     reduceRatio();
@@ -72,7 +73,7 @@
     { comValue: shallowRef(ProgressManage), comName: '任务列表' },
     { comValue: shallowRef(FileManage), comName: '文件管理' },
     { comValue: shallowRef(FollowUp), comName: '项目跟进' },
-    { comValue: shallowRef(FollowUp), comName: '成本管理' },
+    { comValue: shallowRef(CostManage), comName: '成本管理' },
     { comValue: shallowRef(FinancialManage), comName: '财务管理' },
   ]);
 
@@ -87,9 +88,7 @@
     deliveryStatus: [],
   });
   const router = useRouter();
-  const query = router.currentRoute.value.query;
   const SessionStorageProjectId = sessionStorage.getItem('SessionStorageProjectId');
-
   async function getProjectInfo() {
     const res = await projectApi.getProjectInfo(SessionStorageProjectId);
     projectInfoTemp.value = res.data;