| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- <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>
|