对一年前端工作以及项目的总结
刚入职时此项目已经建好
同时是第一次写csdn。
PC端
pc端主要用到的技术为vue.js+elementUI框架。
首先电脑上需要安装好git以及node.js环境以及vue.js环境,安装node.js以及vue.js参考node.js以及vue.js,安装和如何使用git参考git,并且已经注册好阿里云账号。
1. 克隆项目到本地
使用git clone命令将阿里云服务器上的项目克隆到本地来使用,参数为项目的URL地址,例如:
2. 启动项目
然后打开hbuilder,点击左下角的后建议使用npm install --registry=https://registry.npm.taobao.org
来安装依赖,安装成功后使用npm run dev
启动项目。
在浏览器地址栏中输入localhost:80来访问项目。
3. 分析项目组件
3.1树形组件
想要完成的效果为下图所示:
在前端页面中的代码为:
<el-tree
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
@node-click="handleNodeClick"
default-expand-all
/>
其中data
的作用是展示数据,将返回结果赋值给deptOptions
,后端返回的部分接口结构如下图所示:
翻看elementUi官网查看发现它所需的数据结构正好与接口中返回的数据结构一致。其中diffInfo
判断是否是部门还是公司,id
为数据库中取出的唯一标识,label
为树中展示的标签名称,children
为子节点的值。
props
赋值为
defaultProps: {
children: "children",
label: "label"
},
,其中children
对应返回结果中的children,label
对应返回结果中的label。
expand-on-click-node
属性代表是否在点击节点时候展开节点,默认为true,该为false后只有点击左侧箭头时才展开节点。
filter-node-method
属性标识对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏。
ref
表示本页面可以引用的dom元素。
@node-click
表示点击节点的回调方法,参数data表示该节点所对应的对象。js如下所示:
handleNodeClick(data) {
// console.log(data)
// console.log(data.id)
this.queryParams.deptId = data.id;
// console.log(this.queryParams.deptId)
this.queryParams.diffInfo = data.diffInfo
this.getList();
},
default-expand-all
属性表示是否默认展开全部节点。
3.2input组件
想要完成的功能如下图所示:
在前端页面的代码为:
<el-input
v-model="deptName"
placeholder="请输入部门名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
其中v-model
为绑定值,placeholder
为占位文本,即在input框中的内容为空时的默认显示文本,clearable
表示此input框会出现一个可以手动清空的按钮,size
为input框的尺寸,仅仅在type!='textarea'
时有效,并且有medium / small / mini
三个可选项,prefix-icon
为input框首部图标显示,style
为此input框的样式。
3.3select组件
想要完成的功能如下图所示:
在前端页面的代码为:
<el-select
v-model="queryParams.status"
placeholder="用户状态"
clearable
size="small"
style="width: 240px"
>
<el-option
v-for="dict in statusOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
其中el-select
代表此组件是一个下拉框,el-option
代表的是下拉框中的具体条目,并且el-select
中 的v-model值为当前被选中的el-option
中的value值。
v-for
表示便利statusOptions
,其中每一项使用dict
来代替,key
表示此条目的唯一标识,label
表示条目显示的具体内容,value
表示次条目所代表的值。
3.4datepicker组件
想要完成的功能如下图所示:
前端代码如下所示:
<el-date-picker
v-model="dateRange"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
其中value-format
属性表示日期的格式为yyyy-MM-dd
格式,如2021-08-09
,给后台传值时就是这种格式。type
为daterange
表示这个时间选择器为日期范围选择器,包括开始时间和终止时间,type
可选值为year/month/date/dates/ week/datetime/datetimerange/ daterange/monthrange
。range-separator
属性代表起止日期的分隔符使用’-'来分割,start-placeholder
与end-placeholder
分别表示开始时间和终止时间的占位符。
3.5button组件
想要完成如下图所示功能:
前端页面的代码如下所示:
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
其中type
属性代表按钮的类型,可选为primary / success / warning / danger / info / text
,icon
表示按钮的图标,@click
表示点击按钮触发的事件。
3.6table表格
想要完成如下图所示功能:
前端页面的代码如下所示:
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="60" align="center" />
<el-table-column align="center" width="60" label="序号" prop="index"></el-table-column>
<el-table-column label="用户类型" align="center" prop="userType" /> -->
<el-table-column label="登录账号" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="真实姓名" align="center" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column label="部门" align="center" prop="dept.deptName" :show-overflow-tooltip="true" />
<el-table-column label="联系电话" align="center" prop="phonenumber" width="120" />
<el-table-column label="状态" align="center">
<template slot-scope="scope">
<el-switch
v-model="scope.row.status"
active-value="0"
inactive-value="1"
@change="handleStatusChange(scope.row)"
></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="180"
class-name="small-padding fixed-width"
>
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:user:edit']"
>修改</el-button>
<el-button
v-if="scope.row.userId !== 1"
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:user:remove']"
>删除</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-key"
@click="handleResetPwd(scope.row)"
v-hasPermi="['system:user:resetPwd']"
>重置</el-button>
</template>
</el-table-column>
</el-table>
其中el-table
为表格元素,它的v-loading
属性为true时会添加一个加载图案,data
属性为显示的数据,@selection-change
表示当选择项发生变化时会触发该事件。el-table-column
表示表格中的一列,type
表示对应列的类型。如果设置了 selection 则显示多选框;如果设置了 index 则显示该行的索引(从 1 开始计算);如果设置了 expand 则显示为一个可展开的按钮,此处是设置了多选框,当选择项发生变化时触发handleSelectionChange
事件,width
表示此列的宽度,align
表示此列内容的对齐方式,label
表示此列表头显示的标题,prop
表示对应列内容的字段名,show-overflow-tooltip
表示当内容过长被隐藏时在提示信息中显示全部的内容,class-name
表示列的class的name,template
中的slot-scope
表示插槽,scope相当于一行的数据, scope.row相当于当前行的数据对象。这里就是用插槽 拿到当前行 row是个内置的属性 ,vue slot的scope传递值是父作用域中的源数据改变,值会同步改变;
el-switch
表示此组件是一个开关,active-value
属性表示打开开关时的值,inactive-value
属性表示关闭开关时的值,@change
表示switch状态发生变化时的回调函数,也就是说当开关的状态发生改变时触发handleStatusChange
事件。v-hasPermi
表示封装好的进行权限校验的属性,在菜单管理
目录中给对应的按钮设置权限字段时用的就是它的内容。
3.7pagination分页组件
想要完成如下图所示功能:
前端页面的代码如下:
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
我们已经在components/Pagination中的index.vue中封装好了分页组件,代码如下所示:
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scroll-to'
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>
设置layout
,表示需要显示的内容,布局元素会依次显示。prev
表示上一页,next
为下一页,pager
表示页码列表,除此以外还提供了jumper
和total
,size
和特殊的布局符号->
,->
后的元素会靠右显示,jumper
表示跳页元素,total
表示总条目数,size
用于设置每页显示的页码数量。
并且在main.js
中注册且全局挂载了此组件,调用时直接使用Pagination
标签即可,main.js部分代码如下:
import Pagination from "@/components/Pagination";
Vue.component('Pagination', Pagination)
具体解释查看elementUI官网中的pagination。(使用时最好紧跟在el-table
元素下)。
3.8dialog组件和form表单
想要完成如下图所示功能:
前端页面的代码如下所示:
<el-dialog :title="title" :visible.sync="open" width="600px">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="真实姓名" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户真实姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<treeselect v-model="form.deptId" :options="deptOptions" placeholder="请选择归属部门" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="登录账号" prop="userName">
<el-input v-model="form.userName" placeholder="请输入登录账号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option
v-for="dict in sexOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in statusOptions"
:key="dict.dictValue"
:label="dict.dictValue"
>{{dict.dictLabel}}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
v-for="item in postOptions"
:key="item.postId"
:label="item.postName"
:value="item.postId"
:disabled="item.status == 1"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="角色">
<el-select v-model="form.roleIds" multiple placeholder="请选择">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:label="item.roleName"
:value="item.roleId"
:disabled="item.status == 1"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="disabledAdd">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
通常情况下我们新增或修改时会弹出对应的对话框,并且会进行表单的提交,于是用到了dialog
对话框与form
表单。
dialog
对话框的功能为在保留当前页面状态的情况下,告知用户并承载相关操作。title
属性表示Dialog 标题区的内容,visible
表示是否显示 Dialog,支持 .sync 修饰符,在官网中有一段解释为如果 visible 属性绑定的变量位于 Vuex 的 store 内,那么 .sync 不会正常工作。此时需要去除 .sync 修饰符,同时监听 Dialog 的 open 和 close 事件,在事件回调中执行 Vuex 中对应的 mutation 更新 visible 属性绑定的变量的值。
form
表单由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据。现在秃然发现el-form
标签中用的是:model而不是v-model,就很神奇,所以来说一下v-model和:model的区别。
v-model是vue.js中内置的双向数据绑定指令,
用于表单控件以外的标签是不起作用的
(即只对表单控件标签的数据双向绑定有效)。
:model相当于v-bind:model的缩写,
v-bind动态绑定指令,默认情况下标签自带属性的值是固定的,
这种只是将父组件的数据传递到了子组件,并没有实现子组件和父组件数据的双向绑定。
当然引用类型除外,子组件改变引用类型的数据的话,父组件也会改变的。
rules
属性表示表单验证规则,这个相当于每个组件前边的*,具体内容如下所示:
rules: {
userName: [
{ required: true, message: "登录账号不能为空", trigger: "blur" }
],
nickName: [
{ required: true, message: "真实姓名不能为空", trigger: "blur" }
],
deptId: [
{ required: true, message: "归属部门不能为空", trigger: "blur" }
],
password: [
{ required: true, message: "用户密码不能为空", trigger: "blur" }
],
email: [
{
type: "email",
message: "'请输入正确的邮箱地址",
trigger: ["blur", "change"]
}
],
phonenumber: [
{
pattern: /(^(\d{3,4}-)?\d{7,8})$|(^0?(13[0-9]|15[012356789]|18[0-9]|14[57])[0-9]{8})$/,
message: "请输入正确的联系电话",
trigger: "blur"
}
]
}
label-width
表示表单域标签的的宽度,即每个el-form-item
标签中的label
属性内容的宽度;
通过 row
和 col
组件,并通过 col
组件的 span
属性我们就可以自由地组合布局。
prop
属性是表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的。
el-radio-group
为一个单选框组,结合el-radio-group
元素和子元素el-radio
可以实现单选组,在el-radio-group
中绑定v-model
,在el-radio
中设置好label
即可,无需再给每一个el-radio
绑定变量,另外,还提供了change
事件来响应变化,它会传入一个参数value
。在el-select
标签上添加`multiple属性即可使下拉框变为复选框。
3.9upload上传组件
想要完成的功能如下:
前端页面的代码如下所示:
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
<el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
</div>
<div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
</el-upload>
其中el-upload
标签的ref
属性表示可以获取到的本页面的dom元素,limit
表示最大允许上传个数,accept
表示接受上传的文件类型,headers
表示设置上传的请求头部,action
表示必选参数,上传的地址,disabled
表示是否禁用,on-progress
表示文件上传时的钩子,on-success
表示文件上传成功时的钩子,auto-upload
表示是否在选取文件后立即进行上传,drag
表示是否启用拖拽上传。
4.分析功能
4.1.新增与修改
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
let params = this.form;
let diffInfo = this.validDeptId(params.deptId, this.deptOptions);
params.diffInfo = diffInfo;
// console.log(params.diffInfo)
if (this.form.userId != undefined) {
updateUser(params).then(response => {
if (response.code === 200) {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
} else {
this.msgError(response.msg);
}
});
} else {
this.disabledAdd = true;
addUser(params).then(response => {
if (response.code === 200) {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
} else {
this.disabledAdd = false
this.msgError(response.msg);
}
});
}
}
});
},
4.2.删除
handleDelete(row) {
const userIds = row.userId || this.ids;
this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delUser(userIds);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
}).catch(function() {});
},
4.3.查询
getList() {
this.loading = true;
let params = {
types:'01,02,03,04,05,06,07,08,09',
pageNum: 1,
pageSize : 1000
}
findCompanyListByType(params).then((res) => {
if (res.code === 200) {
// console.log(res.data)
this.danWeiOptions = res.data
} else {
this.$message({
type: 'error',
message: res.msg
})
}
})
listUser(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
// console.log(response)
this.userList = response.rows;
this.userList.map((item,index) => {
item.userType = this.selectDictLabel(this.userTypeOptions, item.userType);
item.index = (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1;
})
this.total = response.total;
this.loading = false;
// console.log(response)
}
);
},
4.4.单查
handleUpdate(row) {
this.disabledAdd = false
this.reset();
this.getTreeselect();
const userId = row.userId || this.ids;
getUser(userId).then(response => {
this.form = response.data;
this.postOptions = response.posts;
this.roleOptions = response.roles;
this.form.postIds = response.postIds;
this.form.roleIds = response.roleIds;
this.open = true;
this.title = "修改用户";
this.form.password = "";
});
},
4.5.导入
/** 导入按钮操作 **/
handleImport() {
this.upload.title = "用户导入";
this.upload.open = true;
},
/** 下载模板操作 */
importTemplate() {
importTemplate().then(response => {
this.download(response.msg);
});
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
// 提交上传文件
submitFileForm() {
this.$refs.upload.submit();
},
其中this.download方法封装在其他位置,如图所示具体方法:
export function download(fileName) {
window.location.href = baseURL + "/common/download?fileName=" + encodeURI(fileName) + "&delete=" + true;
}
4.6.导出
下边为使用后端接口进行导出:
handleExport() {
const queryParams = this.queryParams;
this.$confirm('是否确认导出所有用户数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return exportUser(queryParams);
}).then(response => {
this.download(response.msg);
}).catch(function() {});
},
下边为纯前端导出:
使用此功能需要引入两个文件:Export2Excel.js与util.js
此处为导出json格式的数据。
async exportExcel() {
let params = {
pageSize: this.total,
pageNum: 1,
conditions: this.conditions,
effectHours: this.effectHours,
isEffectWork: this.isEffectWork,
windDirection: this.windDirection
}
if (this.searchDate && this.searchDate.length > 0) {
if (typeof (this.searchDate[0]) == 'object') {
params.dateStart = formatDate(this.searchDate[0], 'yyyy-MM-dd')
} else {
params.dateStart = this.searchDate[0]
}
if (typeof (this.searchDate[1]) == 'object') {
params.dateEnd = formatDate(this.searchDate[1], 'yyyy-MM-dd')
} else {
params.dateEnd = this.searchDate[1]
}
} else {
params.dateStart = ''
params.dateEnd = ''
}
let res = await listWeather(params)
if (res.code === 200) {
this.exportList = res.data.list
this.exportList.map((item) => {
if(item.influenceState.indexOf(",") >= 0){
let temp = item.influenceState.split(",")
for(let i = 0;i < temp.length; i++){
temp[i] = this.selectDictLabel(this.effectSituationOpetion, temp[i])
}
item.influenceState = temp.toString()
}else{
item.influenceState = this.selectDictLabel(this.effectSituationOpetion, item.influenceState);
}
// item.influenceState = this.selectDictLabel(this.effectSituationOpetion, item.influenceState);
item.isEffectWork = this.selectDictLabel(this.isEffectWorkOpetion, item.isEffectWork);
item.temperature = item.minTemperature + '~' + item.maxTemperature
item.id = item.id + ''
})
}
//开始尝试
if (this.updateMonth != null) {
this.exportMonth = this.updateMonth
} else {
this.exportMonth = formatDate(new Date(), 'yyyy')
}
import('@/api/tool/Export2Excel1').then(excel => {
const multiHeader = []
const tHeader = ['日期(必填)', '天气情况(必填)', '最低气温(℃)', '最高气温(℃)', '风向', '风力', '降雨量(mm)', '开始时间', '结束时间',
'影响作业', '影响类别', '影响时长(小时)', '影响作业情况描述', '填写人(必填)'
] //表头
const title = []
//表头对应字段
const filterVal = ['reportDate', 'conditions', 'minTemperature', 'maxTemperature', 'windDirection',
'windSize', 'rainfall', 'startTime', 'endTime', 'isEffectWork', 'influenceState', 'effectHours', 'workDescribe', 'creater'
]
const list = this.exportList
const data = this.formatJson(filterVal, list)
data.map(item => {
// console.log(item)
item.map((i, index) => {
if (!i) {
item[index] = ''
}
})
})
const merges = [] //合并单元格
try {
excel.export_json_to_excel({
multiHeader,
header: tHeader,
merges,
data,
filename: '天气情况统计',
autoWidth: true,
bookType: 'xlsx',
type: 'weather',
title
})
this.$message({
type: "success",
message: "导出成功"
})
} catch (e) {
console.log(e)
}
})
//尝试结束
},
此处为导出将数据导出为表格形式。
async exportExcel() {
const that = this
let xiechuanLength = 0 //卸船作业变化
let dangrichukujihua = 0 //当日出库计划变化
let weather = 0 //港口天气设备影响情况变化
let weatherContent = 0 //天气内容
let weatherHeji = 0 //天气合计
let siteHotCoal = 0 //场地高温煤变化
let siteHotCoalContent = 0 //场地高温煤内容
const borderAll = { //单元格外侧框线
top: {
style: 'thin'
},
bottom: {
style: 'thin'
},
left: {
style: 'thin'
},
right: {
style: 'thin'
}
};
const titleClass = {
font: {
name:'黑体',
sz: 10,
bold: false,
},
alignment: {
horizontal: "center",
vertical: "center"
},
border: borderAll,
fill: {
fgColor: {rgb: "cccccc"},
},
}
const numClass = {
font: {
name:'黑体',
sz: 10,
bold: false,
color:{
rgb: 'e43927'
}
},
alignment: {
horizontal: "center",
vertical: "center"
},
border: borderAll,
}
const defaultClass = {
font: {
name:'黑体',
sz: 8,
bold: false,
},
alignment: {
horizontal: "center",
vertical: "center"
},
border: borderAll
}
import("@/api/tool/Export2Excel1").then(excel => {
excel.export_table_to_excel({
id: 'exportTable',
filename: '调度日报',
bookType: 'xlsx',
styleFun: function(ws) { // 自定义样式
//给合并的单元格赋值空,不然四周的线不会显示
const merges = ws['!merges']
merges.map((item) => {
const s = item.s
const e = item.e
let index = 0
for(let i = s.r; i <= e.r; i++){
for(let j = s.c; j <= e.c; j++){
index++
if(index === 1){
//跳过第一个,这是原来应该有的值
continue;
}
const cellTemp = {
v: '',
t: 's'
};
const cell_refTemp = XLSX.utils.encode_cell({
c: j,
r: i
});
ws[cell_refTemp] = cellTemp;
}
}
})
let result = []
for(let i = 0; i < 15; i++){
result.push({'wch':12})
}
ws['!cols'] = result;
for (let item in ws) {
if(item === "!ref"){
continue;
} else if(item === "!merges"){
continue;
} else if(item === "A1"){
ws['A1'].s = {
font: {
name:'黑体',
sz: 12,
bold: false,
color: {
rgb: "ff0000"
}
},
border:{
top: {
style: 'thin'
},
bottom: {
style: 'thin'
},
left: {
style: 'thin'
},
right: {
style: 'thin'
}
},
alignment: {
horizontal: "center",
vertical: "center"
}
};
continue;
} else {
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
let col = item.substring(0,1)
const rowNum = Number(item.substring(1))
if(item.length === 2 && item.indexOf('2') === 1){
let tempPosition = 'left'
if(col === 'A' || col === 'H' || col === 'M'){
tempPosition = 'right'
}
//第二行
ws[item].s = {
font: {
name:'仿宋',
sz: 8,
bold: false,
color: {
rgb: "ff0000"
}
},
alignment: {
horizontal: tempPosition,
vertical: "center"
},
border: {
top: {
style: 'thin'
},
bottom: {
style: 'thin'
},
left: {
style: 'thin'
},
right: {
style: 'thin'
}
}
};
} else if(item.length === 2 && (item.indexOf('3') === 1 || item.indexOf('4') === 1
|| item.indexOf('5') === 1 || item.indexOf('9') === 1)){
//第三四五行,9,
ws[item].s = titleClass
} else if(item.length === 3 && (item.indexOf('10') === 1 || item.indexOf('15') === 1)){
//10 15
ws[item].s = titleClass
} else if(item.length === 2 && (item.indexOf('6') === 1 || item.indexOf('7') === 1 || item.indexOf('8') === 1 )
&& col != 'A' && col != 'B' && col != 'G' ){
// 6 7 8行 不包括 A B G列
ws[item].s = numClass
} else if(item.length === 3 && item.indexOf('11') === 1 ){
//第十一行
if(col === 'A' || col === 'B' || col === 'C' || col === 'D'){
// 第十一行的abcd列
ws[item].s = defaultClass
} else if(col === 'E' || col === 'F'){
// 第十一行的ef列
ws[item].s = numClass
} else {
// 第十一行剩下的列
ws[item].s = titleClass
}
} else if(item.length === 3 && (item.indexOf('12') === 1 || item.indexOf('13') === 1 || item.indexOf('14') === 1)){
//第12,13,14行
if(col === 'A' || col === 'D' || col === 'G'){
ws[item].s = defaultClass
} else {
ws[item].s = numClass
}
} else if(item.length === 3 && (item.indexOf('16') === 1 || item.indexOf('17') === 1
|| item.indexOf('18') === 1 || item.indexOf('19') === 1 || item.indexOf('20') === 1 || item.indexOf('21') === 1)){
//第16-21行
if(col === 'A' || col === 'M' || col === 'N'){
ws[item].s = titleClass
} else if(col === 'O'){
ws[item].s = numClass
} else if(item.indexOf('17') === 1 || item.indexOf('19') === 1 || item.indexOf('21') === 1){
ws[item].s = numClass
} else {
ws[item].s = defaultClass
}
// 22-23行
} else if(item.length === 3 && (item.indexOf('22') === 1 || item.indexOf('23') === 1 )){
if(col === 'O'){
ws[item].s = numClass
}else{
ws[item].s = titleClass
}
// 24
} else if(item.length === 3 && (item.indexOf('24') === 1 && col != 'M')){
if(col === 'A'){
ws[item].s = titleClass
}else
ws[item].s = numClass
// 25 26
} else if(item.length === 3 && (item.indexOf('25') === 1 || item.indexOf('26') === 1 )){
if(col === 'B' || col === 'C' || col === 'D' || col === 'E'){
ws[item].s = numClass
}else{
ws[item].s = titleClass
}
// 27 31 32 36 37
} else if(item.length === 3 && (item.indexOf('27') === 1 || item.indexOf('31') === 1 || item.indexOf('32') === 1 || item.indexOf('36') === 1 || item.indexOf('37') === 1)){
ws[item].s = titleClass
// 28
} else if(item.length === 3 && item.indexOf('28') === 1){
if(col === 'B' || col === 'C' || col === 'D' || col === 'E' || col === 'F'){
ws[item].s = titleClass
} else{
ws[item].s = numClass
}
// 29 30
} else if(item.length === 3 && (item.indexOf('29') === 1 || item.indexOf('30') === 1)){
if(col === 'A' || col === 'F'){
ws[item].s = titleClass
}else{
ws[item].s = numClass
}
// 33 34 35
} else if(item.length === 3 && (item.indexOf('33') === 1 || item.indexOf('34') === 1 || item.indexOf('35') === 1)){
if(col === 'A'){
ws[item].s = titleClass
}else{
ws[item].s = numClass
}
// 卸船作业38+
} else if(item.indexOf('38') === 1){
if(that.dataList_7_xiechuan.length != 0){
for(let i = 0;i < that.dataList_7_xiechuan.length;i++){
xiechuanLength = 38 + i
if(item.indexOf(xiechuanLength + '') === 1){
if(col === 'J' || col === 'M'){
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
ws[item].s = defaultClass
}
ws[item].s = defaultClass
}
}
dangrichukujihua = xiechuanLength + 1
}else{
ws[item].s = titleClass
dangrichukujihua = 38
}
//当日出库计划行
} else if(item.indexOf(dangrichukujihua + '') === 1 || item.indexOf(dangrichukujihua + 1 + '') === 1 || item.indexOf(dangrichukujihua + 2 + '') === 1){
ws[item].s = titleClass
} else if(item.indexOf(dangrichukujihua +3 + '') === 1 || item.indexOf(dangrichukujihua + 4 + '') === 1 || item.indexOf(dangrichukujihua + 5 + '') === 1){
if(col === 'A' || col === 'D' || col === 'G' || col === 'K'){
ws[item].s = titleClass
}else{
ws[item].s = numClass
}
weather = dangrichukujihua + 6
// 天气设备行 标题
} else if(item.indexOf(weather + '') === 1 || item.indexOf(weather + 1 + '') === 1){
ws[item].s = titleClass
weatherContent = weather + 2
// 天气设备行 内容
} else if(item.indexOf(weatherContent + '') === 1){
// 判断长度是否为0 遍历
if(that.dataList_11_tianqi.length != 0){
for(let i = 0;i < that.dataList_11_tianqi.length; i++){
weatherContent = weatherContent + i
if(item.indexOf(weatherContent + '') === 1){
if(col === 'H' || col === 'I'){
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
ws[item].s = defaultClass
}
ws[item].s = defaultClass
}
}
weatherHeji = weatherContent + 1
} else{
if(col === 'A'){
ws[item].s = titleClass
}else{
if(col === 'H' || col === 'I'){
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
ws[item].s = defaultClass
}
ws[item].s = defaultClass
}
weatherHeji = weatherContent
siteHotCoal = weatherHeji + 1
}
// 天气合计行
} else if(item.indexOf(weatherHeji + '') === 1){
if(col === 'A'){
ws[item].s = titleClass
}else{
if(col === 'H' || col === 'I'){
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
ws[item].s = defaultClass
}
ws[item].s = defaultClass
}
siteHotCoal = weatherHeji + 1
// 场地高温煤标题
} else if(item.indexOf(siteHotCoal + '') === 1 || item.indexOf(siteHotCoal + 1 + '') === 1){
ws[item].s = titleClass
siteHotCoalContent = siteHotCoal + 2
} else if(item.indexOf(siteHotCoalContent + '') === 1){
if(that.dataList_12_gaowen.length != 0){
for(let i = 0;i < that.dataList_12_gaowen.length; i++){
siteHotCoalContent = siteHotCoalContent + i
if(item.indexOf(siteHotCoalContent + '') === 1){
if(col === 'D' || col === 'E' || col === 'F' || col === 'G'){
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
ws[item].s = defaultClass
}
ws[item].s = defaultClass
}
}
}
}
else {
ws[item].t = 's'
let temp = ws[item].v
if (typeof(temp)=='string'){
temp = temp.replace("^","")
ws[item].v = temp
}
ws[item].s = defaultClass
}
}
}
}
});
});
},
4.7.echarts的使用
想要完成的效果图如下所示:
前端页面的代码如下所示:
showCharts(){
let myChart = this.$echarts.init(this.$refs.chart_right);
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
}
},
legend: {
left: 'center',
top: '0px',
textStyle: { //图例文字的样式
color: '#444',
fontSize: 12
},
data: ['计划量', '动力煤装出量', '块煤装出量','合计', '超欠']
},
grid: {
left: '6%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
data: this.dateTypeValue,
axisPointer: {
type: 'shadow'
},
axisLabel: {
show: true,
textStyle: {
color: '#444'
}
},
}],
yAxis: [{
type: 'value',
name: '量(吨)',
nameTextStyle: {
color: "#444"
},
// min: 100,
// max: 600,
// interval: 100,
// axisLabel: {
// formatter: '{value} 元'
// },
axisLabel: {
show: true,
textStyle: {
color: '#444'
}
},
},
{
type: 'value',
name: '超/欠(吨)',
nameTextStyle: {
color: "#444"
},
// min: -20000000,
// max: 20000000,
axisLabel: {
margin: 10,
color: function (value, index) {
return value.indexOf('-') >= 0 ? '#ff596f' : '#444444';
},
fontSize: 12
}
}
],
series: [{
name: '计划量',
type: 'bar',
label: {
show: true,
position: 'inside'
},
data: this.taskPlanCount
},
{
name: '动力煤装出量',
type: 'bar',
stack: '总量',
label: {
show: true,
position: 'inside'
},
data: this.powerCoalCount
},
{
name: '块煤装出量',
type: 'bar',
stack: '总量',
label: {
show: true,
position: 'inside'
},
data: this.pieceCoalCount
},
{
name: '合计',
type: 'bar',
label: {
show: true,
position: 'inside'
},
data: this.total
},
{
name: '超欠',
type: 'line',
yAxisIndex: 1,
data: this.overUnder
}
]
};
console.warn(this.overUnder)
myChart.setOption(option)
},
具体使用说明清查看echarts官网。
5.nginx、redis、以及打包、启动项目
5.1.nginx(示例)
1、在/home/zhzy/下创建nginx目录:mkdir nginx
2、在/home/zhzy/下创建pcre目录:mkdir pcre,访问https://ftp.pcre.org/pub/pcre/,选择版本下载,将下载好的pcre-8.33.tar.gz依赖包放入到pcre目录中,并解压:
(1)、tar zxvf pcre-8.33.tar.gz
(2)、解压完后配置自动管理目录:./configure
(3)、编译:make
IF 出现make: *** No targets specified and no makefile found. Stop. 错误
执行 yum update命令 添加环境C++ yum install -y gcc gcc-c++
(5)、编译安装:make install
3、将下载的nginx-1.9.10.tar.gz放在/home/zhzy/nginx目录下,并解压:
(1)tar zxvf nginx-1.9.10.tar.gz
(2)、解压完后配置自动管理目录:./configure
当执行./configure时报下面的错误
则需要安装zlib-devel即可。SSH执行以下命令 yum install -y zlib-devel
(3)、编译:make
(4)、编译安装:make install1
4、此时进入/home/zhzy/nginx/sbin/目录下,启动nginx:./nginx,如出现如下页面则安装成功。
启动完成(注意将防火墙关闭或者改iptables对外提供端口)。
5、进入/home/zhzy/nginx/conf/目录下,编辑nginx.conf文件:
vi nginx.conf
将server监听(listen)端口改为80(所需的端口),在第一个location块内将root的内容改为/home/zhzy/dist(打包后的页面内容),在第二个location块内首先将location改为
location/prod-api/,再将块内的proxy_pass改为http://192.168.1.80:9080/
6、将dist(打包后的页面)放入到/home/zhzy/下
7、进入到/home/zhzy/nginx/sbin/下,重启nginx:
./nginx -s reload
重启成功后访问192.168.1.29:端口号即可,
5.2.redis(示例)
将下载的redis-5.0.3.tar.gz放置/home/zhzy目录下,并解压:
tar xzf redis-5.0.3.tar.gz
tar -xzvf redis-5.0.3.tar.gz
2、进入redis-5.0.3目录下,编译并安装:///yum install gcc-c++
①、make
②、make install
当我执行编译时,报错:
因为未安装gcc,安装gcc及相关依赖包(XXXX为相关版本号,视自己情况而定),可以使用linux光驱挂载,直接使用linux的系统盘内置rpm包:
rpm -ivh glibc-devel-XXXX.rpm
rpm -ivh cpp-XXXX.rpm
rpm -ivh binutils-XXXX.rpm
rpm -ivh gcc-XXXX.rpm
rpm -ivh mpfr-XXXX.rpm
rpm -ivh jemalloc-XXXX.rpm
补充:rpm -ivh jemalloc-XXXX.rpm
需要后加rpm格式的压缩包
3.将光驱挂载到Linux系统上;
创建挂载点: mkdir /mnt/cdrom
挂载光驱 : mount /dev/sr0 /mnt/cdrom
注意:/dev/sr0代表光驱
Packages目录下有4621个rpm包,其中包含了第二步骤里面的6个rpm包和依赖包。
从第一个rpm包开始装,先查看有多少个glibc-devel包:
这里面结果为两个,选择白色框框里面的版本(64)安装,安装时可能会出现 xxx被xxx需要(例:libpng-1.5.13-7.el7_2.i686.rpm 被glibc-devel-2.17-105.el7.x86_64.rpm需要),这时候一步一步查询所需要的rpm,输入ls xxx*(例:ls libpng*)
然后将查询出来的结果按照所需挨个安装(注意只安装64或者32位),每次安装应该都会有所需要的依赖包直到全部安装完成。
4:执行第二步骤
make
make install
5:进入redis-5.0.3目录下的redis.conf
1.将bind的127.0.0.1注掉
2.将daemonize的no改为yes
3.启动redis: redis-server redis.conf
启动完成(注意将防火墙关闭或者改iptables对外提供端口)
5.3.打包启动项目
将后台程序打包后命名为glg_server.jar后放到服务器上,将以前的包保存好后在当前目录下进行启动,
输入命令启动后台jar包,
nohup java -jar glg_server.jar & tail -f nohup.out //启动服务器(自建系统接口)
//打印日志
nohup java -jar glg_webservice-api.jar & tail -f nohup.out //启动服务器(TSW接口)
之后将前台包(dist)打好后放到对应的目录下(看nginx的配置了),
然后进入到nginx的sbin目录下输入
Nginx -s reload
重启nginx,如果第一次启动则输入
Nginx -s start
到此部署完毕。