Преглед изворни кода

项目管理菜单、项目列表开发、公司经营预算修改

ZaiZai пре 2 година
родитељ
комит
fc55d0887e

+ 2 - 0
src/components/index.js

@@ -1,8 +1,10 @@
 import HcDropdown from './dropdown/index.vue'
 import HcGradientCard from './gradient-card/index.vue'
+import FormItemUpload from './upload/FormItemUpload.vue'
 
 //注册全局组件
 export const setupComponents = (App) => {
     App.component('HcDropdown', HcDropdown)
     App.component('HcGradientCard', HcGradientCard)
+    App.component('FormItemUpload', FormItemUpload)
 }

+ 215 - 0
src/components/upload/FormItemUpload.vue

@@ -0,0 +1,215 @@
+<template>
+    <div class="form-item-dashed hover flex" @click="importModalClick">
+        <div v-if="uploadValue" class="flex-1 truncate">{{ fileNameValue }}</div>
+        <div v-else class="flex-1">点此上传文件</div>
+        <div v-if="uploadValue" class="text-hover" @click.stop="previewClick">预览文件</div>
+    </div>
+
+    <!--上传-->
+    <HcDialog :loading="uploadDisabled" :show="importModal" saveText="确认上传" title="上传文件" widths="38rem"
+              @close="importModalClose" @save="importModalYesClick">
+        <el-upload ref="uploadRef" :accept="accept" :action="api + action" :auto-upload="false"
+                   :before-upload="beforeUpload" :data="uploadData" :disabled="uploadDisabled"
+                   :headers="getTokenHeader()" :limit="1"
+                   :on-change="uploadChange"
+                   :on-error="uploadError" :on-exceed="uploadExceed" :on-progress="uploadprogress"
+                   :on-success="uploadSuccess" :show-file-list="false" class="hc-upload-border approach" drag>
+            <div v-loading="uploadDisabled" class="hc-upload-loading upload-file-info" element-loading-text="上传中...">
+                <template v-if="uploadFileInfo?.name">
+                    <HcIcon class="upload-file-icon" name="file-text"/>
+                    <div class="upload-file-name">{{ uploadFileInfo?.name }}</div>
+                </template>
+                <template v-else>
+                    <HcIcon class="upload-icon" name="upload-cloud"/>
+                    <div class="el-upload__text">拖动文件到这里 或 <em>点击这里选择文件</em> 并上传</div>
+                </template>
+            </div>
+            <template #tip>
+                <div class="el-upload__tip">允许格式:{{ formatTip }}, 文件大小 小于 {{ size }}MB</div>
+            </template>
+        </el-upload>
+    </HcDialog>
+</template>
+
+<script setup>
+import {ref, watch, onMounted} from "vue";
+import {getTokenHeader} from '~src/api/request/header';
+import {isFileSize} from "js-fast-way"
+import {genFileId} from "element-plus";
+
+const props = defineProps({
+    modelValue: {
+        type: String,
+        default: ''
+    },
+    datas: {
+        type: Object,
+        default: () => ({})
+    },
+    action: {
+        type: String,
+        default: "upload-file"
+    },
+    accept: {
+        type: String,
+        default: "image/png,image/jpg,image/jpeg,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,application/pdf,.doc,.docx,application/msword"
+    },
+    size: {
+        type: Number,
+        default: 20
+    },
+    formatTip: {
+        type: String,
+        default: "png/jpg/jpeg/excel/pdf/doc/docx"
+    },
+})
+
+//变量
+const uploadRef = ref(null)
+const uploadData = ref(props.datas)
+const uploadFileInfo = ref({})
+const uploadDisabled = ref(false)
+const uploadValue = ref(props.modelValue)
+
+const api = '/api/blade-resource/oss/endpoint/';
+
+//监听
+watch(() => [
+    props.datas,
+    props.modelValue
+], ([datas, val]) => {
+    uploadData.value = datas
+    uploadValue.value = val
+    getFileName(val)
+})
+
+onMounted(() => {
+    getFileName(props.modelValue)
+})
+
+//事件
+const emit = defineEmits(['progress', 'change', 'update:modelValue'])
+
+//上传弹窗
+const importModal = ref(false)
+const importModalClick = () => {
+    importModal.value = true
+}
+
+//确认上传
+const importModalYesClick = () => {
+    uploadRef.value?.submit()
+}
+
+//关闭上传
+const importModalClose = () => {
+    uploadRef.value?.clearFiles()
+    importModal.value = false
+}
+
+//获取文件名
+const fileNameValue = ref('')
+const getFileName = (url) => {
+    if (url) {
+        let num = url.lastIndexOf('/') + 1
+        fileNameValue.value = url.substring(num)
+    } else {
+        fileNameValue.value = ''
+    }
+}
+
+
+//上传前
+const beforeUpload = async (file) => {
+    if (isFileSize(file?.size, props.size)) {
+        return true;
+    } else {
+        window?.$message?.warning('文件大小, 不能过' + props.size + 'M!');
+        return false;
+    }
+}
+
+//超出限制时
+const uploadExceed = (files) => {
+    uploadRef.value?.clearFiles()
+    const file = files[0]
+    file.uid = genFileId()
+    uploadRef.value?.handleStart(file)
+}
+
+//上传中
+const uploadprogress = () => {
+    uploadDisabled.value = true
+    emit('progress', true)
+}
+
+//上传完成
+const uploadSuccess = ({code, data}) => {
+    uploadDisabled.value = false
+    emit('progress', false)
+    // const pdfUrl = data?.pdfUrl ?? ''
+    const pdfUrl = data
+    if (code === 200 && pdfUrl) {
+        uploadValue.value = pdfUrl
+        window?.$message?.success('上传成功');
+        importModal.value = false
+        getFileName(pdfUrl)
+        //事件
+        emit('update:modelValue', pdfUrl)
+        emit('change', pdfUrl)
+    } else {
+        window?.$message?.error('上传失败');
+    }
+}
+
+//上传失败
+const uploadError = () => {
+    uploadDisabled.value = false
+    emit('progress', false)
+    window?.$message?.error('上传失败');
+}
+
+//文件改变时
+const uploadChange = (file) => {
+    uploadFileInfo.value = file
+}
+
+//预览文件
+const previewClick = () => {
+    const pdfUrl = uploadValue.value ?? ''
+    if (pdfUrl) window.open(pdfUrl, '_blank')
+}
+</script>
+
+<style lang="scss">
+.hc-upload-border.approach {
+    .el-upload-dragger {
+        padding: 24px;
+    }
+    .hc-upload-loading.upload-file-info {
+        .hc-icon-i {
+            font-size: 40px;
+        }
+        .upload-icon {
+            color: var(--el-text-color-placeholder);
+        }
+        .upload-file-icon {
+            color: var(--el-color-primary-light-5);
+        }
+        .el-upload__text {
+            margin-top: 10px;
+        }
+        .upload-file-name {
+            margin-top: 10px;
+            font-size: 14px;
+            text-align: center;
+            color: var(--el-color-primary);
+        }
+    }
+    .el-upload__tip {
+        font-size: 14px;
+        margin-top: 16px;
+        color: var(--el-text-color-placeholder);
+    }
+}
+</style>

+ 23 - 1
src/router/menus.js

@@ -26,6 +26,28 @@ export default [
             },
         ]
     },
+    {
+        source: 'home-3',
+        name: '项目管理',
+        code: 'project',
+        children: [
+            {
+                source: 'home-3',
+                name: '项目列表',
+                code: 'project-index',
+            },
+            {
+                source: 'home-3',
+                name: '项目报销',
+                code: 'project-reimburse',
+            },
+            {
+                source: 'home-3',
+                name: '项目预算',
+                code: 'project-budget',
+            },
+        ]
+    },
     {
         source: 'home-3',
         name: '个人中心',
@@ -68,7 +90,7 @@ export default [
                 name: '组织架构管理',
                 code: 'system-organization',
             },
-            
+
         ]
     },
 ]

+ 40 - 1
src/router/modules/base.js

@@ -52,7 +52,7 @@ export default [
             {
                 path: '/home/budget-form',
                 name: 'home-budget-form',
-                meta: {title: '新增/编辑公司经营预算'},
+                meta: {title: '公司经营预算信息'},
                 component: () => import('~src/views/home/budget-form.vue')
             },
             {
@@ -63,6 +63,45 @@ export default [
             }
         ],
     },
+    {
+        path: '/project',
+        name: 'project',
+        redirect: '/project/index',
+        meta: {title: '项目管理'},
+        component: Layout,
+        children: [
+            {
+                path: '/project/index',
+                name: 'project-index',
+                meta: {title: '项目列表'},
+                component: () => import('~src/views/project/index.vue')
+            },
+            {
+                path: '/project/list-info',
+                name: 'project-list-info',
+                meta: {title: '项目信息'},
+                component: () => import('~src/views/project/list-info.vue')
+            },
+            {
+                path: '/project/reimburse',
+                name: 'project-reimburse',
+                meta: {title: '项目报销'},
+                component: () => import('~src/views/project/reimburse.vue')
+            },
+            {
+                path: '/project/budget',
+                name: 'project-budget',
+                meta: {title: '项目预算'},
+                component: () => import('~src/views/project/budget.vue')
+            },
+            {
+                path: '/project/budget-form',
+                name: 'project-budget-form',
+                meta: {title: '项目预算信息'},
+                component: () => import('~src/views/project/budget-form.vue')
+            },
+        ],
+    },
     {
         path: '/user',
         name: 'user',

+ 5 - 1
src/router/modules/token.js

@@ -8,11 +8,15 @@ export default [
     'home-budget',
     'home-budget-form',
     'home-budget-chart',
+    'project-index',
+    'project-list-info',
+    'project-reimburse',
+    'project-budget',
+    'project-budget-form',
     'user-index',
     'user-config',
     'system-user',
     'system-menu',
     'system-parameter',
     'system-organization',
-
 ]

+ 19 - 3
src/views/home/budget-form.vue

@@ -1,7 +1,16 @@
 <template>
     <HcCard scrollbar>
-        <template #header>
+        <HcCardItem>
             <div class="hac-budget-form-card-header">
+                <el-form ref="formRef" inline :model="formModel" :rules="formRules">
+                    <el-form-item label="预算名称" prop="name" class="w-1/2">
+                        <el-input v-model="formModel.name" placeholder="请输入预算名称" />
+                    </el-form-item>
+                    <el-form-item label="预算起止时间" prop="time" class="ml-6">
+                        <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate"/>
+                    </el-form-item>
+                </el-form>
+
                 <el-form ref="formRef" inline :model="formModel" :rules="formRules">
                     <el-form-item label="合同" prop="key1">
                         <el-tooltip content="年度合同指标">
@@ -36,9 +45,9 @@
                     </el-form-item>
                 </el-form>
             </div>
-        </template>
+        </HcCardItem>
 
-        <HcCardItem>
+        <HcCardItem class="mt-4">
             <template #header>
                 <span class="text-black text-sm">* 人员工资预算详情:</span>
             </template>
@@ -218,6 +227,13 @@ const formRules = {
     },
 }
 
+//日期时间被选择
+const betweenTime = ref(null)
+const betweenTimeUpdate = ({arr, query}) => {
+    betweenTime.value = arr
+    //formModel.value.betweenTime = query
+}
+
 //工资预算表格
 const tableColumn = [
     {key: 'key1', name: '部门', align: 'center'},

+ 2 - 1
src/views/home/budget.vue

@@ -57,6 +57,7 @@ const pageChange = ({current, size}) => {
 const tableLoading = ref(false)
 const tableColumn = [
     {key: 'name', name: '名称'},
+    {key: 'key6', name: '起止时间', width: '220', align: 'center'},
     {key: 'key1', name: '总经营预算', width: '140', align: 'center'},
     {key: 'key2', name: '年度合同额指标', width: '140', align: 'center'},
     {key: 'key3', name: '年度利润指标', width: '140', align: 'center'},
@@ -65,7 +66,7 @@ const tableColumn = [
     {key: 'action', name: '操作', width: '130', align: 'center'},
 ]
 const tableData = ref([
-    {id: 1, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 1, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx', key6: '2022-07-01 ~ 2022-04-01'},
     {id: 2, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
     {id: 3, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
     {id: 4, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},

+ 1 - 1
src/views/home/components/TaskTable.vue

@@ -28,7 +28,7 @@
         </template>
         <HcTable indexName="编号" :column="tableColumn" :datas="tableData" :loading="tableLoading">
             <template #taskName="{row}">
-                <span class="text-hover" @click="rowNameClick(row)">{{row.taskName}}</span>
+                <span class="text-blue text-hover" @click="rowNameClick(row)">{{row.taskName}}</span>
             </template>
             <template #auditStatus="{row}">
                 <span class="text-green" v-if="row.auditStatus === '1'">已审核</span>

+ 289 - 0
src/views/project/budget-form.vue

@@ -0,0 +1,289 @@
+<template>
+    <HcCard scrollbar>
+        <template #header>
+            <div class="hac-budget-form-card-header">
+                <el-form ref="formRef" inline :model="formModel" :rules="formRules">
+                    <el-form-item label="合同" prop="key1">
+                        <el-tooltip content="年度合同指标">
+                            <el-input v-model="formModel.key1" placeholder="年度合同指标" />
+                        </el-tooltip>
+                    </el-form-item>
+                    <el-form-item label="利润" prop="key2">
+                        <el-tooltip content="年度利润指标">
+                            <el-input v-model="formModel.key2" placeholder="年度利润指标" />
+                        </el-tooltip>
+                    </el-form-item>
+                    <el-form-item label="预算" prop="key3">
+                        <el-tooltip content="预算总额">
+                            <el-input v-model="formModel.key3" placeholder="预算总额" disabled>
+                                <template #append>元</template>
+                            </el-input>
+                        </el-tooltip>
+                    </el-form-item>
+                    <el-form-item label="工资" prop="key4">
+                        <el-tooltip content="人员工资预算详情">
+                            <el-input v-model="formModel.key4" placeholder="人员工资预算详情" disabled>
+                                <template #append>元</template>
+                            </el-input>
+                        </el-tooltip>
+                    </el-form-item>
+                    <el-form-item label="综合" prop="key5">
+                        <el-tooltip content="综合管理预算详情">
+                            <el-input v-model="formModel.key5" placeholder="综合管理预算详情" disabled>
+                                <template #append>元</template>
+                            </el-input>
+                        </el-tooltip>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </template>
+
+        <HcCardItem>
+            <template #header>
+                <span class="text-black text-sm">* 人员工资预算详情:</span>
+            </template>
+            <template #extra>
+                <el-button type="success" @click="addTableData">
+                    <HcIcon name="add-circle"/>
+                    <span>新增</span>
+                </el-button>
+            </template>
+            <div class="h-72">
+                <HcTable :isIndex="false" :column="tableColumn" :datas="tableData">
+                    <template #key1="{row,index}">
+                        <el-select v-model="row.key1" v-if="row.isEditMode">
+                            <el-option label="管理中心" value="管理中心"/>
+                            <el-option label="人事行政资源部" value="人事行政资源部"/>
+                            <el-option label="研发部" value="研发部"/>
+                            <el-option label="技术服务部" value="技术服务部"/>
+                        </el-select>
+                        <div v-else>{{row.key1}}</div>
+                    </template>
+                    <template #key2="{row,index}">
+                        <el-select v-model="row.key2" v-if="row.isEditMode">
+                            <el-option label="总经理" value="总经理"/>
+                            <el-option label="人事行政" value="人事行政"/>
+                            <el-option label="产品经理" value="产品经理"/>
+                            <el-option label="软件实施" value="软件实施"/>
+                            <el-option label="业务实施" value="业务实施"/>
+                            <el-option label="技术经理" value="技术经理"/>
+                            <el-option label="JAVA开发" value="JAVA开发"/>
+                            <el-option label="前端开发" value="前端开发"/>
+                            <el-option label="测试" value="测试"/>
+                        </el-select>
+                        <div v-else>{{row.key2}}</div>
+                    </template>
+                    <template #key3="{row,index}">
+                        <el-input v-model="row.key3" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key3}}</div>
+                    </template>
+                    <template #key4="{row,index}">
+                        <el-input v-model="row.key4" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key4}}</div>
+                    </template>
+                    <template #key5="{row,index}">
+                        <el-input v-model="row.key5" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key5}}</div>
+                    </template>
+                    <template #key6="{row,index}">
+                        <el-select v-model="row.key6" v-if="row.isEditMode">
+                            <el-option label="在职" value="在职"/>
+                            <el-option label="计划招聘" value="计划招聘"/>
+                        </el-select>
+                        <div v-else>{{row.key6}}</div>
+                    </template>
+                    <template #key7="{row,index}">
+                        <el-input v-model="row.key7" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key7}}</div>
+                    </template>
+                    <template #action="{row,index}">
+                        <el-button size="small" type="primary" @click="row.isEditMode = false" v-if="row.isEditMode">
+                            <HcIcon name="check"/>
+                            <span>完成</span>
+                        </el-button>
+                        <el-button size="small" type="primary" @click="row.isEditMode = true" v-else>
+                            <HcIcon name="edit"/>
+                            <span>编辑</span>
+                        </el-button>
+                        <el-button size="small" type="danger" @click="delTableData(row,index)">
+                            <HcIcon name="delete-bin"/>
+                            <span>删除</span>
+                        </el-button>
+                    </template>
+                </HcTable>
+            </div>
+        </HcCardItem>
+
+        <HcCardItem class="mt-4">
+            <template #header>
+                <span class="text-black text-sm">* 综合管理预算详情:</span>
+            </template>
+            <template #extra>
+                <el-button type="success" @click="addTableData1">
+                    <HcIcon name="add-circle"/>
+                    <span>新增</span>
+                </el-button>
+            </template>
+            <div class="h-72">
+                <HcTable :isIndex="false" :column="tableColumn1" :datas="tableData1">
+                    <template #key1="{row,index}">
+                        <el-select v-model="row.key1" v-if="row.isEditMode">
+                            <el-option label="办公费" value="办公费"/>
+                            <el-option label="团建费" value="团建费"/>
+                            <el-option label="水电费" value="水电费"/>
+                            <el-option label="房租物业" value="房租物业"/>
+                            <el-option label="运杂费" value="运杂费"/>
+                            <el-option label="后勤费" value="后勤费"/>
+                        </el-select>
+                        <div v-else>{{row.key1}}</div>
+                    </template>
+                    <template #key2="{row,index}">
+                        <el-input v-model="row.key2" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key2}}</div>
+                    </template>
+                    <template #key3="{row,index}">
+                        <el-input v-model="row.key3" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key3}}</div>
+                    </template>
+                    <template #key4="{row,index}">
+                        <el-input v-model="row.key4" v-if="row.isEditMode"/>
+                        <div v-else>{{row.key4}}</div>
+                    </template>
+                    <template #action="{row,index}">
+                        <el-button size="small" type="primary" @click="row.isEditMode = false" v-if="row.isEditMode">
+                            <HcIcon name="check"/>
+                            <span>完成</span>
+                        </el-button>
+                        <el-button size="small" type="primary" @click="row.isEditMode = true" v-else>
+                            <HcIcon name="edit"/>
+                            <span>编辑</span>
+                        </el-button>
+                        <el-button size="small" type="danger" @click="delTableData1(row,index)">
+                            <HcIcon name="delete-bin"/>
+                            <span>删除</span>
+                        </el-button>
+                    </template>
+                </HcTable>
+            </div>
+        </HcCardItem>
+    </HcCard>
+</template>
+
+<script setup>
+import {onActivated, ref} from "vue";
+import {useRouter, useRoute} from 'vue-router'
+import {delMessage} from "~uti/tools";
+
+//初始变量
+const router = useRouter()
+const useRoutes = useRoute()
+const dataId = ref(useRoutes?.query?.id ?? '')
+
+//缓存页面被激活时
+onActivated(() => {
+    dataId.value = useRoutes?.query?.id ?? ''
+    console.log(useRoutes.query)
+})
+
+//顶部表单数据
+const formRef = ref(null)
+const formModel = ref({
+    key1: '', key2: '', key3: '', key4: '', key5: '',
+})
+const formRules = {
+    key1: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入年度合同指标"
+    },
+    key2: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入年度利润指标"
+    },
+    key3: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入预算总额"
+    },
+    key4: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入人员工资预算"
+    },
+    key5: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入综合管理预算"
+    },
+}
+
+//工资预算表格
+const tableColumn = [
+    {key: 'key1', name: '部门', align: 'center'},
+    {key: 'key2', name: '岗位', align: 'center'},
+    {key: 'key3', name: '工资支出', align: 'center'},
+    {key: 'key4', name: '五险一金支出',  align: 'center'},
+    {key: 'key5', name: '人员数量',  align: 'center'},
+    {key: 'key6', name: '状态', align: 'center'},
+    {key: 'key7', name: '工期(月)', align: 'center'},
+    {key: 'action', name: '操作', width: '160', align: 'center'},
+]
+const tableData = ref([
+    {id: 1, key1: '管理中心', key2: '产品经理', key3: '22', key4: '33', key5: '44', key6: '在职', key7: '22', isEditMode: false},
+])
+
+//新增工资预算表格
+const addTableData = () => {
+    tableData.value.push({isEditMode: true})
+}
+
+//删除工资预算表格
+const delTableData = (row,index) => {
+    delMessage(() => {
+        if (!row.id)  {
+            tableData.value.splice(index, 1)
+        }
+    })
+}
+
+
+//综合管理费用表格
+const tableColumn1 = [
+    {key: 'key1', name: '支出类目', align: 'center'},
+    {key: 'key2', name: '每月费用', align: 'center'},
+    {key: 'key3', name: '预算期数(月)', align: 'center'},
+    {key: 'key4', name: '测算备注',  align: 'center'},
+    {key: 'action', name: '操作', width: '160', align: 'center'},
+]
+const tableData1 = ref([
+    {id: 1, key1: '办公费', key2: '1', key3: '22', key4: '33', isEditMode: false},
+])
+
+//新增工资预算表格
+const addTableData1 = () => {
+    tableData1.value.push({isEditMode: true})
+}
+
+//删除工资预算表格
+const delTableData1 = (row,index) => {
+    delMessage(() => {
+        if (!row.id)  {
+            tableData1.value.splice(index, 1)
+        }
+    })
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>
+
+<style lang="scss">
+.hac-budget-form-card-header {
+    .el-form--inline .el-form-item {
+        margin-right: 16px;
+        margin-bottom: 16px;
+    }
+}
+</style>

+ 110 - 0
src/views/project/budget.vue

@@ -0,0 +1,110 @@
+<template>
+    <HcCard>
+        <template #header>
+            <div class="w-36">
+                <el-select v-model="searchForm.annual" block clearable placeholder="选择年度" size="large">
+                    <el-option v-for="item in annuals" :label="item.name" :value="item.key"/>
+                </el-select>
+            </div>
+        </template>
+        <template #extra>
+            <el-button size="large" color="#7728F5" hc-btn @click="toBudgetChartClick">
+                <HcIcon name="folder-chart"/>
+                <span>报表查看</span>
+            </el-button>
+            <el-button size="large" type="primary" hc-btn @click="addRowClick">
+                <HcIcon name="add"/>
+                <span>新增经营预算</span>
+            </el-button>
+        </template>
+
+        <HcTable :column="tableColumn" :datas="tableData" :loading="tableLoading">
+            <template #action="{row,index}">
+                <el-button plain size="small" type="primary" @click="editRowClick(row)">编辑</el-button>
+                <el-button plain size="small" type="danger">删除</el-button>
+            </template>
+        </HcTable>
+
+        <template #action>
+            <HcPages :pages="searchForm" @change="pageChange"/>
+        </template>
+    </HcCard>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import {useRouter} from 'vue-router'
+
+const router = useRouter()
+
+//年度数据
+const annuals = ref([
+    {name: '2023年', key: '2023'},
+    {name: '2022年', key: '2022'},
+    {name: '2021年', key: '2021'}
+])
+
+//搜索表单
+const searchForm = ref({annual: null, current: 1, size: 20, total: 0})
+//分页被点击
+const pageChange = ({current, size}) => {
+    searchForm.value.current = current
+    searchForm.value.size = size
+    getTableData()
+}
+
+//获取数据
+const tableLoading = ref(false)
+const tableColumn = [
+    {key: 'name', name: '名称'},
+    {key: 'key1', name: '总经营预算', width: '140', align: 'center'},
+    {key: 'key2', name: '年度合同额指标', width: '140', align: 'center'},
+    {key: 'key3', name: '年度利润指标', width: '140', align: 'center'},
+    {key: 'key4', name: '工资支出', width: '140', align: 'center'},
+    {key: 'key5', name: '其它管理支出', width: '140', align: 'center'},
+    {key: 'action', name: '操作', width: '130', align: 'center'},
+]
+const tableData = ref([
+    {id: 1, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 2, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 3, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 4, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 5, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 6, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+])
+
+const getTableData = () => {
+
+}
+
+
+//编辑预算
+const editRowClick = (row) => {
+    router.push({
+        name: 'home-budget-form', query: {
+            id: row.id
+        }
+    })
+}
+
+//新增预算
+const addRowClick = () => {
+    router.push({
+        name: 'home-budget-form'
+    })
+}
+
+const toBudgetChartClick = () => {
+    router.push({
+        name: 'home-budget-chart'
+    })
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>
+
+<style lang="scss">
+
+</style>

+ 196 - 0
src/views/project/index.vue

@@ -0,0 +1,196 @@
+<template>
+    <HcCard>
+        <template #header>
+            <div class="w-36">
+                <el-select v-model="searchForm.projectType" block clearable placeholder="项目类型" size="large">
+                    <el-option v-for="item in projectType" :label="item.name" :value="item.key"/>
+                </el-select>
+            </div>
+            <div class="w-40 ml-2">
+                <el-select v-model="searchForm.contractType" block clearable placeholder="合同类型" size="large">
+                    <el-option v-for="item in contractType" :label="item.name" :value="item.key"/>
+                </el-select>
+            </div>
+            <div class="w-48 ml-2">
+                <el-select v-model="searchForm.projectProcess" block clearable placeholder="项目进程" size="large">
+                    <el-option v-for="item in projectProcess" :label="item.name" :value="item.key"/>
+                </el-select>
+            </div>
+            <div class="w-36 ml-2">
+                <el-select v-model="searchForm.date" block clearable placeholder="选择时间" size="large">
+                    <el-option v-for="item in annuals" :label="item.name" :value="item.key"/>
+                </el-select>
+            </div>
+            <div class="w-40 ml-2">
+                <el-input v-model="searchForm.queryValue" clearable placeholder="请输入关键词" @keyup="keyUpEvent" size="large"/>
+            </div>
+            <div class="ml-4">
+                <el-button type="primary" @click="searchClick" size="large">
+                    <HcIcon name="search-2"/>
+                    <span>搜索</span>
+                </el-button>
+            </div>
+            <div class="ml-2">
+                <el-button size="large" @click="resetClick">
+                    <HcIcon name="close-circle"/>
+                    <span>重置</span>
+                </el-button>
+            </div>
+        </template>
+        <template #extra>
+            <el-button size="large" type="primary" hc-btn @click="addRowClick">
+                <HcIcon name="add"/>
+                <span>新增项目信息</span>
+            </el-button>
+        </template>
+
+        <HcTable :isIndex="false" :column="tableColumn" :datas="tableData" :loading="tableLoading">
+            <template #key1="{row}">
+                <span class="text-blue text-hover" @click="rowClick(row)">{{row.key1}}</span>
+            </template>
+            <template #action="{row,index}">
+                <el-button plain size="small" type="primary" @click="editRowClick(row)">编辑</el-button>
+                <el-button plain size="small" type="danger">删除</el-button>
+            </template>
+        </HcTable>
+
+        <template #action>
+            <HcPages :pages="searchForm" @change="pageChange"/>
+        </template>
+    </HcCard>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import {useRouter} from 'vue-router'
+
+const router = useRouter()
+
+//项目类型
+const projectType = ref([
+    {name: '二级路', key: '二级路'},
+    {name: '国道', key: '国道'},
+    {name: '水利水电', key: '水利水电'},
+    {name: '市政', key: '市政'},
+])
+
+//合同类型
+const contractType = ref([
+    {name: '咨询服务+软件', key: '1'},
+    {name: '软件', key: '2'},
+    {name: '咨询服务', key: '3'},
+    {name: '后期服务+软件', key: '4'},
+    {name: '数字化扫描+软件', key: '5'},
+])
+
+//项目进程
+const projectProcess = ref([
+    {name: '商机-演示沟通', key: '1'},
+    {name: '商机-成本核算及报价', key: '2'},
+    {name: '合同-服务范围洽谈', key: '3'},
+    {name: '产品-研发', key: '4'},
+    {name: '产品-配置', key: '5'},
+    {name: '产品-测试', key: '6'},
+    {name: '产品-交付', key: '7'},
+    {name: '实施-系统培训', key: '8'},
+    {name: '实施-现场服务', key: '9'},
+    {name: '实施-合同回款', key: '10'},
+    {name: '项目验收', key: '11'},
+])
+
+//年度数据
+const annuals = ref([
+    {name: '2023年5月', key: '2023-05'},
+    {name: '2022年4月', key: '2022-04'},
+    {name: '2021年3月', key: '2021-03'}
+])
+
+//搜索表单
+const searchForm = ref({
+    projectType: null, contractType: null, projectProcess: null, date: null, queryValue: '',
+    current: 1, size: 20, total: 0
+})
+
+//搜索框回车
+const keyUpEvent = (event) => {
+    if (event.key === "Enter") {
+        searchForm.value.current = 1;
+        getTableData()
+    }
+}
+
+//搜索
+const searchClick = () => {
+    searchForm.value.current = 1;
+    getTableData()
+}
+
+//重置搜索表单
+const resetClick = () => {
+    searchForm.value = {current: 1, size: 20, total: 0}
+}
+
+
+//分页被点击
+const pageChange = ({current, size}) => {
+    searchForm.value.current = current
+    searchForm.value.size = size
+    getTableData()
+}
+
+//获取数据
+const tableLoading = ref(false)
+const tableColumn = [
+    {key: 'key', name: '编号', width: '90', align: 'center'},
+    {key: 'key1', name: '项目名称'},
+    {key: 'key2', name: '项目进程', width: '160', align: 'center'},
+    {key: 'key3', name: '项目开始日期', width: '160', align: 'center'},
+    {key: 'key4', name: '项目类型', width: '140', align: 'center'},
+    {key: 'key5', name: '合同类型', width: '140', align: 'center'},
+    {key: 'key6', name: '合同额(W)', width: '100', align: 'center'},
+    {key: 'key8', name: '负责人', width: '100', align: 'center'},
+    {key: 'key7', name: '合同起止日期', width: '220', align: 'center'},
+    {key: 'action', name: '操作', width: '130', align: 'center'},
+]
+const tableData = ref([
+    {id: 1, key: 'GS-01', key1: '焦作至唐河高速公路方城至唐河段', key2: '研发-配置', key3: '2022-07-01', key4: '高速公路', key5: '软件合同', key6: '40', key8: '张三', key7: '2022-07-01 ~ 2022-04-01'},
+    {id: 2, key: 'GS-01', key1: '焦作至唐河高速公路方城至唐河段', key2: '研发-配置', key3: '2022-07-01', key4: '高速公路', key5: '软件合同', key6: '40', key7: '2022-07-01 ~ 2022-04-01'},
+    {id: 3, key: 'GS-01', key1: '焦作至唐河高速公路方城至唐河段', key2: '研发-配置', key3: '2022-07-01', key4: '高速公路', key5: '软件合同', key6: '40', key7: '2022-07-01 ~ 2022-04-01'},
+])
+
+const getTableData = () => {
+
+}
+
+//查看
+const rowClick = (row) => {
+    router.push({
+        name: 'project-list-info',
+        query: {
+            id: row.id,
+            type: 'view'
+        }
+    })
+}
+
+//编辑预算
+const editRowClick = (row) => {
+    router.push({
+        name: 'project-list-info',
+        query: {
+            id: row.id,
+            type: 'edit'
+        }
+    })
+}
+
+//新增预算
+const addRowClick = () => {
+    router.push({
+        name: 'project-list-info',
+        query: {
+            type: 'add'
+        }
+    })
+}
+</script>

+ 276 - 0
src/views/project/list-info.vue

@@ -0,0 +1,276 @@
+<template>
+    <HcCard actionUi="text-center">
+        <div class="hac-card-project-box">
+            <div class="left-box">
+                <el-scrollbar>
+                    <el-form ref="formRef" :model="formModel" :rules="formRules" label-width="auto" size="large" :disabled="dataType === 'view'">
+                        <div class="project-form-top">
+                            <HcCardItem title="基础信息">
+                                <el-form-item label="项目名称:" prop="key">
+                                    <el-input v-model="formModel.key"/>
+                                </el-form-item>
+                                <el-form-item label="项目类型:" prop="key1">
+                                    <el-select v-model="formModel.key1" block clearable placeholder="项目类型" size="large">
+                                        <el-option v-for="item in projectType" :label="item.name" :value="item.key"/>
+                                    </el-select>
+                                </el-form-item>
+                                <el-form-item label="开始时间:" prop="key2">
+                                    <el-date-picker class="block" v-model="formModel.key2" format="YYYY-MM-DD" type="date" value-format="YYYY-MM-DD"/>
+                                </el-form-item>
+                                <el-form-item label="项目进程:" prop="key3">
+                                    <el-select v-model="formModel.key3" block disabled="disabled" placeholder="项目进程" size="large">
+                                        <el-option v-for="item in projectProcess" :label="item.name" :value="item.key"/>
+                                    </el-select>
+                                </el-form-item>
+                            </HcCardItem>
+                        </div>
+                        <div class="project-form-bottom">
+                            <HcCardItem title="合同信息">
+                                <el-form-item label="负责人:" prop="key8">
+                                    <el-input v-model="formModel.key8"/>
+                                </el-form-item>
+                                <el-form-item label="起止日期:">
+                                    <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate"/>
+                                </el-form-item>
+                                <el-form-item label="合同类型:">
+                                    <el-select v-model="formModel.key5" block clearable placeholder="合同类型" size="large">
+                                        <el-option v-for="item in contractType" :label="item.name" :value="item.key"/>
+                                    </el-select>
+                                </el-form-item>
+                                <el-form-item label="合同额:">
+                                    <el-input v-model="formModel.key6"/>
+                                </el-form-item>
+                                <el-form-item label="已回款:">
+                                    <el-input v-model="formModel.key7"/>
+                                </el-form-item>
+                                <el-form-item label="上传合同:" v-if="dataType !== 'view'">
+                                    <FormItemUpload v-model="formModel.key9"/>
+                                </el-form-item>
+                                <el-form-item label="上传合同:" v-else>
+                                    <div class="form-item-div">xxxxx.pdf</div>
+                                </el-form-item>
+                            </HcCardItem>
+                        </div>
+                    </el-form>
+                </el-scrollbar>
+            </div>
+            <div class="right-box">
+                <el-scrollbar>
+                    <el-timeline class="p-1">
+                        <el-timeline-item v-for="(item, index) in timeLineData" :key="index" :type="item.type">
+                            <div class="hac-time-line-box">
+                                <div class="hac-time-line-title-box">
+                                    <div class="title">{{item.title}}</div>
+                                    <div class="state">{{item.state}}</div>
+                                </div>
+                                <div class="hac-time-line-time">
+                                    <span class="time">{{item.time}}</span>
+                                    <span class="icon text-blue text-hover" v-if="dataType !== 'view'" @click="item.editTime = true">
+                                        <HcIcon name="edit-2"/>
+                                    </span>
+                                </div>
+                                <div class="hac-time-line-time picker" v-if="item.editTime">
+                                    <div class="picker-box">
+                                        <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate"/>
+                                    </div>
+                                    <div class="icon-box text-blue text-hover" @click="item.editTime = false">
+                                        <HcIcon name="check"/>
+                                    </div>
+                                </div>
+                            </div>
+                        </el-timeline-item>
+                    </el-timeline>
+                </el-scrollbar>
+            </div>
+        </div>
+        <template #action>
+            <el-button size="large" type="info" hc-btn @click="goBackClick">
+                <HcIcon name="arrow-go-back"/>
+                <span v-if="dataType !== 'view'">取消并返回</span>
+                <span v-else>返回</span>
+            </el-button>
+            <el-button size="large" type="primary" hc-btn @click="doubleClick" v-if="dataType !== 'view'">
+                <HcIcon name="check-double"/>
+                <span>提交保存</span>
+            </el-button>
+        </template>
+    </HcCard>
+</template>
+
+<script setup>
+import {ref, onActivated} from "vue";
+import {useRoute, useRouter} from 'vue-router'
+
+const router = useRouter()
+const useRoutes = useRoute()
+
+//初始变量
+const dataType = ref(useRoutes?.query?.type ?? 'view')
+const dataId = ref(useRoutes?.query?.id ?? '')
+
+//缓存页面被激活时
+onActivated(() => {
+    dataType.value = useRoutes?.query?.type ?? 'view'
+    dataId.value = useRoutes?.query?.id ?? ''
+})
+
+//项目类型
+const projectType = ref([
+    {name: '二级路', key: '二级路'},
+    {name: '国道', key: '国道'},
+    {name: '水利水电', key: '水利水电'},
+    {name: '市政', key: '市政'},
+])
+
+//合同类型
+const contractType = ref([
+    {name: '咨询服务+软件', key: '1'},
+    {name: '软件', key: '2'},
+    {name: '咨询服务', key: '3'},
+    {name: '后期服务+软件', key: '4'},
+    {name: '数字化扫描+软件', key: '5'},
+])
+
+//项目进程
+const projectProcess = ref([
+    {name: '商机-演示沟通', key: '1'},
+    {name: '商机-成本核算及报价', key: '2'},
+    {name: '合同-服务范围洽谈', key: '3'},
+    {name: '产品-研发', key: '4'},
+    {name: '产品-配置', key: '5'},
+    {name: '产品-测试', key: '6'},
+    {name: '产品-交付', key: '7'},
+    {name: '实施-系统培训', key: '8'},
+    {name: '实施-现场服务', key: '9'},
+    {name: '实施-合同回款', key: '10'},
+    {name: '项目验收', key: '11'},
+])
+
+//顶部表单数据
+const formRef = ref(null)
+const formModel = ref({
+    key1: '', key2: '', key3: '', key4: '', key5: '',
+})
+const formRules = {
+    key: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入项目名称"
+    },
+    key1: {
+        required: true,
+        trigger: 'blur',
+        message: "请选择项目类型"
+    },
+    key2: {
+        required: true,
+        trigger: 'blur',
+        message: "请选择项目开始时间"
+    },
+    key3: {
+        required: true,
+        trigger: 'blur',
+        message: "项目进程异常"
+    },
+    key8: {
+        required: true,
+        trigger: 'blur',
+        message: "请输入负责人"
+    },
+}
+
+
+//日期时间被选择
+const betweenTime = ref(null)
+const betweenTimeUpdate = ({arr, query}) => {
+    betweenTime.value = arr
+    //formModel.value.betweenTime = query
+}
+
+
+//时间线数据
+const timeLineData = ref([
+    {title: '商机-沟通演示', state: '已闭环', time: '2023-02-23~2023-03-14', type: 'success'},
+    {title: '商机-成本核算及报价', state: '已闭环', time: '2023-02-23~2023-03-14'},
+    {title: '合同-服务范围洽谈', state: '进行中', time: '2023-02-23~2023-03-14', type: 'primary'},
+    {title: '产品-研发', state: '未开始', time: ''},
+    {title: '产品-配置', state: '未开始', time: ''},
+    {title: '产品-测试', state: '未开始', time: ''},
+    {title: '产品-交付', state: '未开始', time: ''},
+    {title: '实施-系统培训', state: '未开始', time: ''},
+    {title: '实施-现场服务', state: '未开始', time: ''},
+    {title: '实施-合同回款', state: '未开始', time: ''},
+    {title: '劳务合同', state: '未开始', time: ''},
+    {title: '交竣工验收', state: '未开始', time: ''},
+])
+
+//返回
+const goBackClick = () => {
+    router.back()
+}
+
+//提交保存
+const doubleClick = () => {
+
+}
+</script>
+
+<style lang="scss" scoped>
+.hac-card-project-box {
+    position: relative;
+    height: 100%;
+    display: flex;
+    .left-box, .right-box {
+        flex: 1;
+        position: relative;
+        height: 100%;
+    }
+    .left-box {
+        .project-form-top {
+            margin-bottom: 24px;
+            padding-right: 24px;
+        }
+        .project-form-bottom {
+            padding-top: 24px;
+            padding-right: 24px;
+            border-top: 1px solid #e9e9e9;
+        }
+    }
+    .right-box {
+        padding-left: 24px;
+        border-left: 1px solid #e9e9e9;
+        .hac-time-line-box {
+            position: relative;
+            .hac-time-line-title-box {
+                position: relative;
+                display: flex;
+                align-items: center;
+                font-size: 16px;
+                color: black;
+                margin-bottom: 12px;
+                justify-content: space-between;
+            }
+            .hac-time-line-time {
+                position: relative;
+                display: flex;
+                align-items: center;
+                .time {
+                    color: gray;
+                    margin-right: 8px;
+                }
+                .icon {
+                    font-size: 18px;
+                }
+                &.picker .icon-box {
+                    font-size: 20px;
+                    margin-left: 10px;
+                }
+            }
+        }
+    }
+}
+</style>
+
+<style lang="scss">
+
+</style>

+ 110 - 0
src/views/project/reimburse.vue

@@ -0,0 +1,110 @@
+<template>
+    <HcCard>
+        <template #header>
+            <div class="w-36">
+                <el-select v-model="searchForm.annual" block clearable placeholder="选择年度" size="large">
+                    <el-option v-for="item in annuals" :label="item.name" :value="item.key"/>
+                </el-select>
+            </div>
+        </template>
+        <template #extra>
+            <el-button size="large" color="#7728F5" hc-btn @click="toBudgetChartClick">
+                <HcIcon name="folder-chart"/>
+                <span>报表查看</span>
+            </el-button>
+            <el-button size="large" type="primary" hc-btn @click="addRowClick">
+                <HcIcon name="add"/>
+                <span>新增经营预算</span>
+            </el-button>
+        </template>
+
+        <HcTable :column="tableColumn" :datas="tableData" :loading="tableLoading">
+            <template #action="{row,index}">
+                <el-button plain size="small" type="primary" @click="editRowClick(row)">编辑</el-button>
+                <el-button plain size="small" type="danger">删除</el-button>
+            </template>
+        </HcTable>
+
+        <template #action>
+            <HcPages :pages="searchForm" @change="pageChange"/>
+        </template>
+    </HcCard>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import {useRouter} from 'vue-router'
+
+const router = useRouter()
+
+//年度数据
+const annuals = ref([
+    {name: '2023年', key: '2023'},
+    {name: '2022年', key: '2022'},
+    {name: '2021年', key: '2021'}
+])
+
+//搜索表单
+const searchForm = ref({annual: null, current: 1, size: 20, total: 0})
+//分页被点击
+const pageChange = ({current, size}) => {
+    searchForm.value.current = current
+    searchForm.value.size = size
+    getTableData()
+}
+
+//获取数据
+const tableLoading = ref(false)
+const tableColumn = [
+    {key: 'name', name: '名称'},
+    {key: 'key1', name: '总经营预算', width: '140', align: 'center'},
+    {key: 'key2', name: '年度合同额指标', width: '140', align: 'center'},
+    {key: 'key3', name: '年度利润指标', width: '140', align: 'center'},
+    {key: 'key4', name: '工资支出', width: '140', align: 'center'},
+    {key: 'key5', name: '其它管理支出', width: '140', align: 'center'},
+    {key: 'action', name: '操作', width: '130', align: 'center'},
+]
+const tableData = ref([
+    {id: 1, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 2, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 3, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 4, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 5, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+    {id: 6, name: '2023年人力支出预算表', key1: 'xxxx', key2: '236323', key3: '65632', key4: '35654', key5: 'xxx'},
+])
+
+const getTableData = () => {
+
+}
+
+
+//编辑预算
+const editRowClick = (row) => {
+    router.push({
+        name: 'home-budget-form', query: {
+            id: row.id
+        }
+    })
+}
+
+//新增预算
+const addRowClick = () => {
+    router.push({
+        name: 'home-budget-form'
+    })
+}
+
+const toBudgetChartClick = () => {
+    router.push({
+        name: 'home-budget-chart'
+    })
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>
+
+<style lang="scss">
+
+</style>