要实现我的日程,首先需要做一个日历页面,可以进行日、天、周、月的切换,其次在日历上展示各种日程,也可以直接在日历上边进行日程的添加操作等处理,本次直接借助第三方日程组件进行处理。以下是我的日程的前端代码
<template> <el-card class="aui-card--fill" shadow="never"> <div> <el-row :gutter="10"> <el-col :span="0"> <ul id="list-group-item"> </ul> </el-col> <el-col :span="24"> <FullCalendar ref="fullCalendar" :options="calendarOptions"/> </el-col> </el-row> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getEventList"></add-or-update> <view-info v-if="viewInfoVisible" ref="viewInfo" @refreshDataList="getEventList"></view-info> </div> </el-card></template><script>import FullCalendar from '@fullcalendar/vue'import dayGridPlugin from '@fullcalendar/daygrid'import timeGridPlugin from '@fullcalendar/timegrid'import interactionPlugin, { Draggable } from '@fullcalendar/interaction'import listPlugin from '@fullcalendar/list'import AddOrUpdate from './schedule-add-or-update'import ViewInfo from './schedule-view-info'import * as moment from 'moment'export default { data () { return { addOrUpdateVisible: false, // 新增/更新,弹窗visible状态 viewInfoVisible: false, calendarOptions: { plugins: [ dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin // needed for dateClick ], headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,list' }, // 设置按钮中文 buttonText: { today: '今天', dayGridMonth: '月', timeGridWeek: '周', timeGridDay: '日', list: '列表' }, customButtons: { prev: { // this overrides the prev button text: 'PREV', click: () => { this.prevMethod() }, }, next: { // this overrides the next button text: 'PREV', click: () => { this.nextMethod() }, }, today: { text: '今天', click: () => { this.todayMethod() }, }, }, eventTextColor: '#000000', firstDay: '1', // 设置一周开始的第一天 locale: 'zh', // 设置语言 initialView: 'dayGridMonth', // 初始视图 editable: false, // 确定是否可以修改日历上的事件。 selectable: true, // 允许用户通过单击和拖动来突出显示多天或多个时间段。 selectMirror: false, // 是否在用户拖动时绘制“占位符”事件。 dayMaxEvents: true, weekends: true, // 是否在任何日历视图中包括周六/周日列。 dropAccept: '.list-group-item', drop: this.drop, select: this.openAddView, eventClick: this.openUpdateView, eventAdd: this.eventAdd, events: [] } } }, components: { AddOrUpdate, ViewInfo, FullCalendar }, mounted () { var containerEl = document.getElementById('list-group-item') // 初始化外部事件 // eslint-disable-next-line no-new new Draggable(containerEl, { itemSelector: '.list-group-item' }) // 获取历史数据 this.getEventList() }, methods: { /* 打开添加页面 */ openAddView (selectInfo) { this.addOrUpdateVisible = true this.$nextTick(() => { this.$refs.addOrUpdate.init() }) }, /* 打开更新页面 */ openUpdateView (selectInfo) { this.addOrUpdateVisible = true this.$nextTick(() => { this.$refs.addOrUpdate.dataForm.id = selectInfo.event.id this.$refs.addOrUpdate.init() }) }, /** 获日程 */ getEventList () { // 获取当前视图日历的范围 const time = this.$refs.fullCalendar.getApi().currentDataManager.state.dateProfile.renderRange this.$http.get(`/oaschedule/schedule/list`, { params: { startDate: moment(time.start).format('YYYY-MM-DD HH:mm:ss'), // 视图开始时间 endDate: moment(time.end).format('YYYY-MM-DD HH:mm:ss')// 视图结束时间 } }).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } const data = res.data const arr = [] data.forEach(item => { const obj = { id: item.id, title: item.title, start: item.start, end: item.end, allDay: true, backgroundColor: item.backgroundColor } arr.push(obj) }) this.calendarOptions.events = arr }).catch(() => { }) }, /** 上个月 */ prevMethod () { this.$refs.fullCalendar.getApi().prev() // 更新上个月视图 this.getEventList() // 调用获取视图活动数据方法 }, /** 下个月 */ nextMethod () { this.$refs.fullCalendar.getApi().next() // 更新下个月视图 this.getEventList() // 调用获取视图活动数据方法 }, /** 今天 */ todayMethod () { this.$refs.fullCalendar.getApi().today() // 更新今天视图 this.getEventList() // 调用获取视图活动数据方法 }, }}</script><style lang="scss" scoped>.fc { max-width: 1200px; margin: 0 auto;}/*周六周日背景颜色*/.fc-day-sat{ background: darkred; color:#0000FF;}.fc-day-sun{ background: darkred; color:#FF0000 ;}.el-flex-row { flex-wrap: wrap}.demo-app-main { flex-grow: 1; padding: 3em;}</style>
页面效果如下:
从以上代码可以看出,使用了Element UI库的常用组件,包括el-card、el-row、el-col等。其中,el-card是一个卡片组件,el-form是一个表单组件,el-input是一个输入框组件,el-row和el-col是用来布局的组件。 还使用了FullCalendar第三方日历组件,使用FullCalendar来创建日历、事件、视图等内容。同时,你也可以了解有关日历、事件和视图的所有属性和方法的详细信息。 methods定义了openAddView、openUpdateView、getEventList、prevMethod、nextMethod、todayMethod等方法。openAddView方法用于打开添加日程页面,openUpdateView方法用于打开修改页面,getEventList方法用于获取日程数据,prevMethod、nextMethod、todayMethod这三个方法用于日期切换时更新数据的操作。以上方法皆通过发送HTTP请求并根据返回结果进行相应的提示和操作。 点击日历框时,弹出新增文件的dialog,代码如下:
<template> <el-dialog :visible.sync="visible" :title="!dataForm.id ? $t('add') : $t('update')" :close-on-click-modal="false" :close-on-press-escape="false"> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmitHandle()" :label-width="$i18n.locale === 'en-US' ? '120px' : '120px'"> <el-form-item label="名称" prop="scheduleTitle"> <el-input v-model="dataForm.scheduleTitle" placeholder="名称" maxlength="32"></el-input> </el-form-item> <el-form-item label="内容" prop="scheduleContent"> <el-input v-model="dataForm.scheduleContent" placeholder="内容" maxlength="100"></el-input> </el-form-item> <el-form-item label="重要程度" prop="scheduleLevel"> <my-select v-model="dataForm.scheduleLevel" dict-type="ScheduleLevelEnum" placeholder="重要程度"></my-select> </el-form-item> <el-form-item label="状态" prop="status"> <my-select v-model="dataForm.status" dict-type="status" placeholder="状态" clearable></my-select> </el-form-item> <el-form-item label="开始时间" prop="startDate"> <el-date-picker v-model="dataForm.startDate" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间"></el-date-picker> </el-form-item> <el-form-item label="结束时间" prop="endDate"> <el-date-picker v-model="dataForm.endDate" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="结束时间"></el-date-picker> </el-form-item> <el-form-item label="备注" prop="remarks"> <el-input type="textarea" v-model="dataForm.remarks" placeholder="备注" maxlength="200"></el-input> </el-form-item> </el-form> <template slot="footer"> <el-button v-if="dataForm.id" icon="el-icon-delete" @click="delSchedule">删除</el-button> <el-button @click="visible = false" icon="el-icon-close">{{ $t('cancel') }}</el-button> <el-button type="primary" @click="dataFormSubmitHandle()" icon="el-icon-check">{{ $t('confirm') }}</el-button> </template> </el-dialog></template><script>import debounce from 'lodash/debounce'export default { data () { return { visible: false, dataForm: { id: '', scheduleTitle: '', scheduleContent: '', scheduleLevel: '', status: '', userId: '', startDate: '', endDate: '', remarks: '', creator: '', createDate: '', updater: '', updateDate: '' } } }, computed: { dataRule () { return { scheduleTitle: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], scheduleContent: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], scheduleLevel: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], status: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], startDate: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], endDate: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ] } } }, methods: { init () { this.visible = true this.$nextTick(() => { this.$refs['dataForm'].resetFields() if (this.dataForm.id) { this.getInfo() } }) }, // 获取信息 getInfo () { this.$http.get(`/oaschedule/schedule/${this.dataForm.id}`).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.dataForm = { ...this.dataForm, ...res.data } }).catch(() => {}) }, // 表单提交 dataFormSubmitHandle: debounce(function () { this.$refs['dataForm'].validate((valid) => { if (!valid) { return false } this.$http[!this.dataForm.id ? 'post' : 'put']('/oaschedule/schedule/', this.dataForm).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.$message({ message: this.$t('prompt.success'), type: 'success', duration: 500, onClose: () => { this.visible = false this.$emit('refreshDataList') } }) }).catch(() => {}) }) }, 1000, { 'leading': true, 'trailing': false }), delSchedule () { this.$http.delete('/oaschedule/schedule/' + this.dataForm.id).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.$message({ message: this.$t('prompt.success'), type: 'success', duration: 500, onClose: () => { this.visible = false this.$emit('refreshDataList') } }) }).catch(() => { }) } }}</script><style lang="scss" scoped></style>
页面效果如下:
以上代码的主要逻辑是为了实现form表单的添加校验功能。以下是部分代码解释: 是一个对话框组件,用于展示文件上传的弹窗。:visible.sync="visible"表示通过visible属性控制对话框的显示和隐藏,:title属性根据dataForm.id的值来确定对话框的标题是"添加"还是"更新",:close-on-click-modal="false"和:close-on-press-escape="false"表示点击对话框外部和按下ESC键时不关闭对话框。
methods中的方法解释: init方法用于初始化对话框,设置对话框的显示状态,并重置表单的字段。 getInfo方法用于获取文件的相关信息(在修改时),发送HTTP请求并根据返回结果进行相应的操作。 delSchedule方法在用户日程的删除功能,发送HTTP请求并根据返回结果进行相应的提示。 dataFormSubmitHandle方法用于表单的提交,先进行表单的校验,然后发送HTTP请求进行数据的添加或更新。
要实现资产入库,首先需要做一个资产入库的列表记录,之后还需要新增、详情等页面的支持。以下是资产入库的前端代码
<template> <el-card shadow="never" class="aui-card--fill"> <div class="mod-oaassets__assetswarehousing}"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form-item> <my-select v-model="dataForm.receiveStatus" dict-type="status" placeholder="验收状态" clearable></my-select> </el-form-item> <el-form-item> <my-select-user ref="mySelectUser" v-model="dataForm.receiveUserId" placeholder="验收人"></my-select-user> </el-form-item> <el-form-item> <el-date-picker v-model="createDateRange" type="datetimerange" clearable :change="changeCreateDateRange()" range-separator="至" value-format="yyyy-MM-dd HH:mm:ss" format="yyyy-MM-dd HH:mm:ss" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker> </el-form-item> <el-form-item> <el-button @click="getDataList()" icon="el-icon-search">{{ $t('query') }}</el-button> </el-form-item> <el-form-item> <el-button type="info" @click="exportHandle()" icon="el-icon-download">{{ $t('export') }}</el-button> </el-form-item> <el-form-item> <el-button v-if="$hasPermission('oaassets:assetswarehousing:save')" type="primary" @click="addOrUpdateHandle()" icon="el-icon-plus">{{ $t('add') }} </el-button> </el-form-item> <el-form-item> <el-button v-if="$hasPermission('oaassets:assetswarehousing:delete')" type="danger" @click="deleteHandle()" icon="el-icon-delete">{{ $t('deleteBatch') }} </el-button> </el-form-item> </el-form> <el-table stripe @sort-change="dataListSortChangeHandle" v-loading="dataListLoading" :data="dataList" border @selection-change="dataListSelectionChangeHandle" style="width: 100%;"> <el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column> <el-table-column prop="buyDocNumber" label="申购单号" header-align="center" align="center" min-width="160" show-overflow-tooltip></el-table-column> <el-table-column prop="receiveStatus" label="验收状态" header-align="center" align="center" min-width="160" show-overflow-tooltip> <template slot-scope="scope"> <el-tag effect="dark" type="info"> {{ $getDictLabel('AssetsWarehousingEnums', scope.row.receiveStatus) }} </el-tag> </template> </el-table-column> <el-table-column prop="receiveDate" label="验收日期" header-align="center" align="center" min-width="160" show-overflow-tooltip></el-table-column> <el-table-column prop="receiveUserId" label="验收人" header-align="center" align="center" min-width="160" show-overflow-tooltip></el-table-column> <el-table-column prop="remarks" label="备注" header-align="center" align="center" min-width="160" show-overflow-tooltip></el-table-column> <el-table-column align="createDate" header-align="center" label="申请时间" min-width="160" prop="createDate" show-overflow-tooltip></el-table-column> <el-table-column :label="$t('handle')" fixed="right" header-align="center" align="center" width="180"> <template slot-scope="scope"> <el-button v-if="$hasPermission('oaassets:assetswarehousing:info')" type="text" size="small" @click="viewInfoHandle(scope.row.id)" icon="el-icon-view">{{ $t('info') }} </el-button> <el-button v-if="$hasPermission('oaassets:assetswarehousing:update') && scope.row.receiveStatus ===0" type="text" size="small" @click="addOrUpdateHandle(scope.row.id)" icon="el-icon-edit"> {{ $t('update') }} </el-button> <el-button v-if="$hasPermission('oaassets:assetswarehousing:delete') && scope.row.receiveStatus ===0" type="text" size="small" @click="deleteHandle(scope.row.id)" icon="el-icon-delete"> {{ $t('delete') }} </el-button> <el-button v-if="scope.row.receiveStatus ===0" icon="el-icon-delete" size="small" type="text" @click="submitApprovalHandle(scope.row.id)">提交 </el-button> <el-button v-if="scope.row.receiveStatus ===1" icon="el-icon-delete" size="small" type="text" @click="approvalHandle(scope.row.id)">审批 </el-button> </template> </el-table-column> </el-table> <el-pagination :current-page="page" :page-sizes="[10, 20, 50, 100]" :page-size="limit" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="pageSizeChangeHandle" @current-change="pageCurrentChangeHandle"> </el-pagination> <!-- 弹窗, 新增 / 修改 --> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update> <view-info v-if="viewInfoVisible" ref="viewInfo" @refreshDataList="getDataList"></view-info> </div> </el-card></template><script>import mixinViewModule from '@/mixins/view-module'import AddOrUpdate from './assetswarehousing-add-or-update'import ViewInfo from './assetswarehousing-view-info'import dateUtil from '@/utils/dateUtil'export default { mixins: [mixinViewModule], data () { return { mixinViewModuleOptions: { getDataListURL: '/oaassets/assetswarehousing/page', getDataListIsPage: true, createdIsNeed: false, exportURL: '/oaassets/assetswarehousing/export', deleteURL: '/oaassets/assetswarehousing', deleteIsBatch: true }, dataForm: { receiveStatus: '', receiveUserId: '', createDateBegin: '', createDateEnd: '' }, createDateRange: dateUtil.getDefaultDateRangeStringArray('nearlyMonth', 'YYYY-MM-DD 00:00:00') } }, components: { AddOrUpdate, ViewInfo }, mounted () { this.createDateRange = dateUtil.getDefaultDateRangeStringArray('nearlyMonth', 'YYYY-MM-DD 00:00:00') this.changeCreateDateRange() this.getDataList() }, methods: { changeCreateDateRange () { if (this.createDateRange && this.createDateRange.length !== 0) { this.dataForm.createDateBegin = this.createDateRange[0] this.dataForm.createDateEnd = this.createDateRange[1] } else { this.dataForm.createDateBegin = null this.dataForm.createDateEnd = null } }, submitApprovalHandle (id) { this.$http.get(`/oaassets/assetswarehousing/submitApprovalHandle/` + id).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.getDataList() }).catch(() => { }) }, approvalHandle (id) { this.$http.get(`/oaassets/assetswarehousing/approvalHandle/` + id).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.getDataList() }).catch(() => { }) } }}</script><style lang="scss" scoped></style>
页面效果如下: 从以上代码可以看出,使用了Element UI库的常用组件,包括el-card、el-form、el-input、el-row、el-col、el-button、el-table、el-table-column和el-pagination等。其中,el-card是一个卡片组件,el-form是一个表单组件,el-input是一个输入框组件,el-row和el-col是用来布局的组件,el-button是一个按钮组件,el-table是一个表格组件,el-table-column是表格的列组件,el-pagination是一个分页组件。 是一个表单组件,用于展示文件搜索的表单。@keyup.enter.native="getDataList()"表示在按下回车键时触发相关搜索的方法。 和是用于布局的组件,:span属性表示占据的列数,:gutter属性表示列之间的间隔。 用于展示文件列表。:data属性绑定了dataList属性,表示表格的数据来源。 中,使用了组件定义了表格的列。prop属性表示列对应的数据字段,label属性表示列的标题。 methods定义了两个方法submitApprovalHandle和approvalHandle。submitApprovalHandle方法用于触发表单的提交启动流程操作,发送HTTP请求并根据返回结果进行相应的提示和操作。approvalHandle方法用于流程的审批操作。 点击新增按钮时,弹出新增文件的dialog,代码如下:
<template> <el-dialog :close-on-click-modal="false" :close-on-press-escape="false" :title="!dataForm.id ? $t('add') : $t('update')" :visible.sync="visible"> <el-form ref="dataForm" :label-width="$i18n.locale === 'en-US' ? '120px' : '120px'" :model="dataForm" :rules="dataRule" @keyup.enter.native="dataFormSubmitHandle()"> <el-form-item label="申购单号" prop="buyDocNumber"> <el-input v-model="dataForm.buyDocNumber" placeholder="申购单号" @blur="getAssetsListInfo(dataForm.buyDocNumber)"></el-input> </el-form-item> <el-form-item label="资产" prop="assetsList"> <template> <el-table :data="dataForm.assetsList" border style="width: 98%"> <el-table-column label="资产名称" prop="assetsName"> </el-table-column> <el-table-column label="编号" prop="assetsCode"> </el-table-column> <el-table-column label="规格" prop="assetsNorm"> </el-table-column> <el-table-column label="入库数量" prop="num"> <template slot-scope="scope"> <el-input-number v-model="scope.row.num" min="1" placeholder="入库数量" size="small"></el-input-number> </template> </el-table-column> </el-table> </template> </el-form-item> <el-form-item label="验收日期" prop="receiveDate"> <el-date-picker v-model="dataForm.receiveDate" placeholder="验收日期" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker> </el-form-item> <el-form-item label="备注" prop="remarks"> <el-input type="textarea" v-model="dataForm.remarks" maxlength="200" placeholder="备注"></el-input> </el-form-item> </el-form> <template slot="footer"> <el-button icon="el-icon-close" @click="visible = false">{{ $t('cancel') }}</el-button> <el-button icon="el-icon-check" type="primary" @click="dataFormSubmitHandle()">{{ $t('confirm') }}</el-button> <el-button icon="el-icon-check" type="primary" @click="submitHandle()">提交</el-button> </template> </el-dialog></template><script>import debounce from 'lodash/debounce'export default { data () { return { visible: false, dataForm: { id: '', buyDocNumber: '', receiveStatus: '', receiveDate: '', receiveUserId: '', remarks: '', creator: '', createDate: '', updater: '', updateDate: '', assetsList: [] } } }, computed: { dataRule () { return { receiveStatus: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], receiveDate: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], assetsList: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], receiveUserId: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], buyDocNumber: [ { required: true, message: this.$t('validate.required'), trigger: 'blur' } ], } } }, methods: { init () { this.visible = true this.$nextTick(() => { this.$refs['dataForm'].resetFields() if (this.dataForm.id) { this.getInfo() } }) }, getAssetsListInfo (buyDocNumber) { if (buyDocNumber === null || buyDocNumber === undefined || buyDocNumber === ''){ return } this.$http.get(`/oaassets/assetsbuy/getByDocNumber/` + buyDocNumber).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } if (res.data === null || res.data.assetsList === null || res.data.assetsList === undefined || res.data.assetsList.length === 0) { return this.$message.error('未查询到采购数据,请检查采购单号是否正确!') } this.dataForm.assetsList = res.data.assetsList }).catch(() => { }) }, // 获取信息 getInfo () { this.$http.get(`/oaassets/assetswarehousing/${this.dataForm.id}`).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.dataForm = { ...this.dataForm, ...res.data } }).catch(() => { }) }, // 表单提交 dataFormSubmitHandle: debounce(function () { this.$refs['dataForm'].validate((valid) => { if (!valid) { return false } this.$http[!this.dataForm.id ? 'post' : 'put']('/oaassets/assetswarehousing/', this.dataForm).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.$message({ message: this.$t('prompt.success'), type: 'success', duration: 500, onClose: () => { this.visible = false this.$emit('refreshDataList') } }) }).catch(() => { }) }) }, 1000, { 'leading': true, 'trailing': false }), submitHandle(){ this.dataForm.receiveStatus = 1; this.dataFormSubmitHandle(); } }}</script><style lang="scss" scoped></style>
页面效果如下: 是一个对话框组件,用于展示文件上传的弹窗。:visible.sync="visible"表示通过visible属性控制对话框的显示和隐藏,:title属性根据dataForm.id的值来确定对话框的标题是"添加"还是"更新",:close-on-click-modal="false"和:close-on-press-escape="false"表示点击对话框外部和按下ESC键时不关闭对话框。 是一个表单组件,用于表单输入。 methods定义了init、getAssetsListInfo、getInfo、dataFormSubmitHandle等放。 init方法用于初始化对话框,设置对话框的显示状态。 getAssetsListInfo用于获取关联的资产申购单,发送HTTP请求并根据返回结果进行相应的操作。 getInfo方法用于编辑是获取当前表单的信息,发送HTTP请求并根据返回结果进行相应的操作。 dataFormSubmitHandle方法是表单提交的处理函数,使用了debounce函数来防止抖动。this.http.post方法向服务器发送登录请求,根据服务器返回的结果进行处理,如果登录成功则将token保存到Cookies中,并跳转到首页,否则重新获取验证码并显示错误信息。
要实现报销统计,首先需要考虑统计的方式,可以按时间、部门、类型来进行统计,本次直接借助第三方图表组件echars进行处理。以下是报销统计的前端代码
<template> <div class="mod-oareimburse__reimbursestatistics}"> <el-card class="box-card" shadow="hover"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="searchData()"> <el-form-item> <el-date-picker v-model="defDate" type="datetimerange" @change="changeDate" range-separator="至" value-format="yyyy-MM-dd" format="yyyy-MM-dd" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker> </el-form-item> <el-form-item> <el-button @click="searchData()" icon="el-icon-search">{{ $t('query') }}</el-button> </el-form-item> </el-form> </el-card> <el-row :gutter="24" type="flex" class="el-flex-row"> <el-col :span="24"> <el-card class="box-card" shadow="hover"> <div class="custHead"> <span>按时间</span> </div> <div id="chart1" style="height: 240px;"></div> </el-card> </el-col> </el-row> <el-row :gutter="24" type="flex" class="el-flex-row"> <el-col :span="12"> <el-card class="box-card" shadow="hover"> <div class="custHead"> <span>按部门</span> </div> <div id="chart2" style="height: 240px;"></div> </el-card> </el-col> <el-col :span="12"> <el-card class="box-card" shadow="hover"> <div class="custHead"> <span>按类型</span> </div> <div id="chart3" style="height: 240px;"></div> </el-card> </el-col> </el-row> </div></template><script> import mixinViewModule from '@/mixins/view-module' import dateUtil from '@/utils/dateUtil' export default { mixins: [mixinViewModule], created() { const start =dateUtil.getNowDate(1,"YYYY-MM-DD")//获取当前的日期 this.defDate[0] = start; const end =dateUtil.getNowDate(0,"YYYY-MM-DD")//获取当前的日期 this.defDate[1] = end; this.dataForm.startDate = start this.dataForm.endDate = end //将转换完的正确格式的结束时间赋值到timeCycle数组的第二项 Promise.all([]).then(() => { this.searchData() }) }, data() { return { mixinViewModuleOptions: { createdIsNeed: false }, defDate:['2022-06-02', '2022-07-02'], dataForm: { startDate: '', endDate: '' } } }, methods: { changeDate:function (){ this.dataForm.startDate = this.defDate[0] this.dataForm.endDate = this.defDate[1] }, searchData: function () { this.getChart1List() this.getChart2List() this.getChart3List() }, getChart1List: function () { let myChart = this.$echarts.init(document.getElementById('chart1')) myChart.clear() let option = [{ color: this.baseColor, title: { text: '' }, toolbox: { show: true, right: 7, iconStyle: { normal: { borderColor: this.darkSetting ? '#ccc' : '#333' } }, feature: { restore: { show: true, title: ' ' }, saveAsImage: { show: true, title: ' ' } } }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { left: '30', right: '30', top: '70', bottom: '10', containLabel: true }, legend: { data: [] }, xAxis: { type: 'category', splitLine: { show: false }, axisLine: { lineStyle: { color: this.darkSetting ? '#6499fb' : '#333' } }, data: [] }, yAxis: [ { type: 'value', position: 'left', splitLine: { show: false }, axisLine: { lineStyle: { color: this.darkSetting ? '#6499fb' : '#333' } }, minInterval: 1, name: '金额' } ], series: [ { type: 'line', name: '金额', // areaStyle: {}, data: [] }] }] this.$http.get('/oareimburse/reimbursestatistics/chart1/list', { params: { ...this.dataForm } }).then(({data: res}) => { if (res.code !== 0) { return this.$message.error(res.msg) } if (res.data) { let xData = [] let v_totalAmount = [] res.data.forEach(item => { xData.push(item.date) v_totalAmount.push(item.totalAmount) }) option[0].xAxis.data = xData option[0].series[0].data = v_totalAmount myChart.setOption(option[0]) } }).catch(() => { }) }, getChart2List: function () { let that = this let myChart = this.$echarts.init(document.getElementById('chart2')) let option = [{ color: this.baseColor, tooltip: { trigger: 'item', position: 'inside', formatter: function (params) { let res = '' res += params.data.name res += '<br/>' + params.data.value return res } }, grid: { left: '5', right: '5', top: '5', bottom: '5', containLabel: true }, legend: { type: 'scroll', height: 220, orient: 'vertical', left: 10, data: [] }, toolbox: { show: true, right: 7, iconStyle: { normal: { borderColor: this.darkSetting ? '#ccc' : '#333' } }, feature: { restore: { show: true, color: this.darkSetting ? '#ccc' : '#333', title: ' ' }, saveAsImage: { show: true, title: ' ' } } }, series: [{ name: '', type: 'pie', breadcrumb: {show: false}, visibleMin: 1, roam: false, nodeClick: false, data: [] }] }] this.$http.get('/oareimburse/reimbursestatistics/chart2/list', { params: { ...this.dataForm } }).then(({data: res}) => { if (res.code !== 0) { return this.$message.error(res.msg) } if (res.data) { let legendData = [] let itemData = [] res.data.forEach(item => { let chartData = {} legendData.push({name: item.deptName}) chartData.name = item.deptName chartData.value = item.totalAmount itemData.push(chartData) }) option[0].legend.data = legendData option[0].series[0].data = itemData myChart.setOption(option[0]) } }).catch(() => { }) }, getChart3List: function () { let that = this let myChart = this.$echarts.init(document.getElementById('chart3')) let option = [{ color: this.baseColor, tooltip: { trigger: 'item', position: 'inside', formatter: function (params) { let res = '' res += params.data.name res += '<br/>' + params.data.value return res } }, grid: { left: '5', right: '5', top: '5', bottom: '5', containLabel: true }, legend: { type: 'scroll', height: 220, orient: 'vertical', left: 10, data: [] }, toolbox: { show: true, right: 7, iconStyle: { normal: { borderColor: this.darkSetting ? '#ccc' : '#333' } }, feature: { restore: { show: true, color: this.darkSetting ? '#ccc' : '#333', title: ' ' }, saveAsImage: { show: true, title: ' ' } } }, series: [{ name: '', type: 'pie', breadcrumb: {show: false}, visibleMin: 1, roam: false, nodeClick: false, data: [] }] }] this.$http.get('/oareimburse/reimbursestatistics/chart3/list', { params: { ...this.dataForm } }).then(({data: res}) => { if (res.code !== 0) { return this.$message.error(res.msg) } if (res.data) { let legendData = [] let itemData = [] res.data.forEach(item => { let chartData = {} legendData.push({name: item.typeName}) chartData.name = item.typeName chartData.value = item.reimburseAmount itemData.push(chartData) }) option[0].legend.data = legendData option[0].series[0].data = itemData myChart.setOption(option[0]) } }).catch(() => { }) }, } } </script> <style lang="scss" scoped> .el-flex-row { flex-wrap: wrap } </style>
页面效果如下: 从以上代码可以看出,使用了Element UI库的常用组件,包括el-card、el-form、el-row、el-col、el-date-picker等。其中,el-card是一个卡片组件,el-form是一个表单组件,el-date-picker是一个日期输入框组件,el-row和el-col是用来布局的组件。 还使用了echars第三方t图表组件,使用echars来创建曲线图、饼状图等图表内容。同时,你也可以了解其他图表的用法和详细信息。 methods定义了changeDate、searchData、getChart1List、getChart2List、getChart3List等方法。changeDate方法用改变查询时间,searchData用于刷新图表,getChart1List方法用于图表1的创建和数据组装,getChart2List方法用于图表2的创建和数据组装,getChart3List方法用于图表3的创建和数据组装。以上方法皆通过发送HTTP请求并根据返回结果进行相应的提示和操作。
要实现文档管理,首先要考虑以一个什么样的效果呈现,可以复杂,也可以简单的用树形+列表实现,在项目当中为了更美观好用,我们选择借助第三方组件进行处理。以下是文档管理的前端代码
<template> <div id="app"> <wlExplorer ref="wl-explorer-cpt" :header-dropdown="headerHandle" :upload-options="uploadOptions" :columns="file_table_columns" :all-path="all_folder_list" :is-folder-fn="isFolderFn" :folderType="rource_type" :data="file_table_data" :props="explorer_prop" :uploadUrl="uploadUrl" size="small" @handleFolder="handleFolder" @upload="fileUpload" @uploadSuccess="uploadSuccess" @download="download" @search="fileSearch" @del="fileDel" @move="moveUpdate" > <fadeIn v-show="fade.folder"> <h3 class="edit-header"> <p>{{folder_form.Id?'编辑':'新增'}}文件夹</p> </h3> <el-scrollbar class="scroll"> <el-form size="medium" ref="folder_form" label-position="top" :model="folder_form" :rules="folder_rules" class="folder_form rule-form" @keyup.enter.native="submitFolderFrom('folder_form')" > <el-form-item label="文件路径" prop="ParentId"> <WlTreeSelect class="u-full" nodeKey="Id" placeholder="请选择文件路径" :props="tree_select_prop" :data="tree_folder_list" v-model="folder_form.ParentId" ></WlTreeSelect> </el-form-item> <el-form-item label="文件夹名称" prop="Name"> <el-input v-model="folder_form.Name" placeholder="请输入文件夹名称"></el-input> </el-form-item> <el-form-item label="备注说明" prop="Describe"> <el-input :rows="3" type="textarea" v-model="folder_form.Describe" placeholder="请输入备注说明" ></el-input> </el-form-item> </el-form> </el-scrollbar> <div class="submit-btn-box"> <submit-btn @btn="submitFolderFrom('folder_form')" :status="load.folder"></submit-btn> <el-button size="medium" @click="fade.folder = false">取消</el-button> </div> </fadeIn> </wlExplorer> </div></template><script>import Cookies from 'js-cookie'import fadeIn from "@/components/document/fade-in"; // 导入文件管理器import submitBtn from "@/components/document/submit-btn"; // 导入防抖提交组件import { closeOtherLayout, arrayToTree } from "@/utils";export default { inject: ['refresh'], name: "app", components: { fadeIn, submitBtn }, data() { const _GB = 1024 * 1024; // const vm = this; return { uploadUrl:"", load: { folder: false, }, // loading管理 fade: { folder: false, }, // 弹出类视图管理 headerHandle: [], // 头部按钮更多操作-自定义权限 file_table_columns: [ { label: "名称", prop: "name", minWidth: 120, }, { label: "修改日期", align: "center", width: 120, formatter(row) { return row.createDate; }, }, { label: "创建日期", align: "center", width: 120, formatter(row) { return row.createDate; }, }, { label: "描述", minWidth: 100, formatter(row) { return row.remarks || "-"; }, }, ], // 自定义表格列 file_table_data: [], // 表格数据 all_folder_list: [], // 所有文件夹列表 tree_folder_list: [], // 树形文件夹列表 type: { folder: 1, img: 2, video: 3, other: 4, }, // 文件类型 rource_type: { self: 1, // 自建 }, // 数据来源类型 explorer_prop: { name: "name", match: "name", splic: true, suffix: "suffix", pathId: "id", pathPid: "pid", pathName: "name", pathChildren: "children", // String 路径数据 children字段 pathConnector: "\\", // String 路径父子数据拼接连接符,默认为'\' pathParents: "parentIds", // String 路径数据所有直系祖先节点自增长identityId逗号拼接 pathIdentityId: "id", // String 路径数据自增长id }, // 文件管理器配置项 path: {}, // 路径数据 folder_form: { ParentId: "", Name: "", preview: [], handle: [], Describe: "", }, // 文件夹表单 folder_rules: { ParentId: [ { required: true, message: "请选择文件路径", trigger: "blur" }, ], Name: [ { required: true, message: "请填写文件夹名称", trigger: "blur" }, ], }, // 文件夹表单验证 child_act_saved: {}, // 存储子组件数据,用于编辑更新 tree_select_prop: { label: "name", children: "children", }, // 树形下拉框配置项 uploadOptions: { aa: 1212, }, // 上传文件附加参数 }; }, methods: { /** * @name 上传文件提交操作 */ fileUpload(file, cb) { this.uploadOptions.pid = 0 if (file.pathId != '' && file.pathId != undefined){ this.uploadOptions.pid = file.pathId; } cb(); }, /** * @name 上传文件回调 */ uploadSuccess(res){ res.data.pid = this.uploadOptions.pid this.saveDoc(res.data) }, saveDoc(fileData){ this.$http.post(`/oadocument/document/saveDoc`, fileData).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.refresh() }).catch(() => {}) }, download(data, func) { if (Number(data[0].type) === this.type.folder){ this.$message.error("暂不支持文件夹下载") return } window.open(data[0].ossList[0].url) }, /** * 根据关键词搜索文件 * file: Object 文件属性 * update: Boolean 数据是否需要更新(不需要表示已存在) */ fileSearch(file, update) { if (update) { this.path = file; this.getFileList(); } }, // 获取文件列表 getFileList() { this.$http.get(`/oadocument/document/list`,{ params: this.path }).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.file_table_data = res.data || []; }).catch(() => {}) }, /** * 编辑文件夹 * act:Object 当前选中文件夹 * type:String 操作类型 add添加edit编辑 * file:Object 当前路径信息 */ handleFolder(act, type, file) { this.path = file; this.fade.folder = true; if (type === "add") { this.$refs["folder_form"].resetFields(); this.folder_form.Id = ""; this.folder_form.ParentId = file.id; return; } this.child_act_saved = act; this.folder_form = { ...act }; }, // 提交文件夹表单 submitFolderFrom(formName) { this.$refs[formName].validate((valid) => { if (valid) { this.load.folder = true; this.$http.post(`/oadocument/document/saveOrUpdateFolder`,this.folder_form).then(({ data: res }) => { this.load.folder = false; if (res.code !== 0) { return this.$message.error(res.msg) } this.$message.success(this.$t('prompt.success')) this.fade.folder = false; this.refresh() }).catch(() => {}) } else { return false; } }); }, // 删除文件 fileDel(data) { let cannot_del_data = []; // 收集不可删除数据 let normal_data_file = []; // 收集可删除文件 let normal_data_folder = []; // 收集可删除文件夹 data.forEach((i) => { i.RourceType !== this.rource_type.self ? cannot_del_data.push(i) // 不可删除数据 : i.Type === this.type.folder ? normal_data_folder.push(i.Id) // 可删除文件夹 : normal_data_file.push(i.Id); // 可删除文件 }); // 不可删除数据进行提示 if (cannot_del_data.length > 0) { let _msg = '<p class="title">以下文件或文件夹不可删除,已自动过滤</p>'; cannot_del_data.forEach((i) => { _msg += `<p class="msg">${i.Name}</p>`; }); this.$message({ dangerouslyUseHTMLString: true, showClose: true, message: _msg, type: "warning", customClass: "mulit-msg", }); } if (normal_data_folder.length === 0 && normal_data_file.length === 0) return; // 可删除数据正常删除 let _data = { FolderIds: normal_data_folder, FolderFileIds: normal_data_file, }; }, // 获取所有文件夹 getAllFolders() { this.$http.get(`/oadocument/document/getFolderList`).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.all_folder_list = res.data || []; let _list = [...this.all_folder_list]; let options = { id: this.explorer_prop.pathId, pid: this.explorer_prop.pathPid, children: "children", }; let root_node = { children: [], id: '0', name: '根目录', pid: '-1', remarks:'测试', type: 1 }; _list.push(root_node) this.tree_folder_list = arrayToTree(_list,options) console.log(this.tree_folder_list) }).catch(() => {}) }, // 判断是否文件夹函数 isFolderFn(row) { return Number(row.type) === this.type.folder; }, moveUpdate(to, data, load){ data[0].pid=to.id this.$http.post(`/oadocument/document/moveUpdate`,data[0]).then(({ data: res }) => { if (res.code !== 0) { return this.$message.error(res.msg) } this.$message.success(this.$t('prompt.success')) this.refresh() }).catch(() => {}) } }, created() { this.closeOtherLayout = closeOtherLayout; this.getAllFolders(); this.getFileList(); this.uploadUrl = `${window.SITE_CONFIG['apiURL']}/sys/oss/upload?token=${Cookies.get('token')}` },};</script><style>#app { font-family: "Avenir", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; position: absolute; top: 0; left: 0; padding: 25px; width: 100%; height: 100%; background: #f7f7f7; box-sizing: border-box;}</style>
页面效果如下: 从以上代码可以看出,除了使用了Element UI库的常用组件外,还是用了一个第三方的组件wlExplorer,该组件是一个基于Vue和ElementUi的文件管理器插件,提供类似某云盘操作台的功能。 methods定义了fileUpload、uploadSuccess、download、fileSearch、getFileList、handleFolder、submitFolderFrom、fileDel、getAllFolders、isFolderFn等方法。fileUpload方法用于上传文件提交操作,uploadSuccess方法用于上传文件回调,download方法用于文件额下载,fileSearch方法用于根据关键词搜索文件的操作,getFileList方法用于初始化页面获取文件列表的操作,handleFolder方法用于编辑文件夹的操作,submitFolderFrom方法用于提交文件夹表单的操作,fileDel方法用于删除文件的操作,getAllFolders方法用于获取所有文件夹的操作,fileUpload方法用于上传文件提交操作,isFolderFn方法用于判断是否文件夹函数,fileUpload方法用于上传文件提交操作。以上方法皆通过发送HTTP请求并根据返回结果进行相应的提示和操作。
扫码关注不迷路!!!
郑州升龙商业广场B座25层
service@iqiqiqi.cn
联系电话:400-8049-474
联系电话:187-0363-0315
联系电话:199-3777-5101