Commit dc6ab566 Linchun

第一次提交

0 个父辈
db.json
package-lock.json
yarn.lock
.DS_Store
node_modules
/dist
/build
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
#Electron-builder output
/dist_electron
\ No newline at end of file \ No newline at end of file
# 定时器系统
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
### Solve the problem
定时器系统-安装依赖,启动,打包篇
安装依赖过程中先装根目录下的package.json,接着也要安装 dist_electron下的package.json
1,doone-electron 打包过程中
会出现找不到模块,依赖,不是内部命令 不能执行 比如 vue/cli-service
注意重新安装yarn add @vue/cli-service
2,出现进程占用 注意先关闭当前已打开的程序
3,出现.dl等提示程序阻拦 注意让系统允许该程序文件执行 不要拦截
4,最终大法,如果一直报错提示依赖没装好 可以直接删node_modules 重新安装
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
{
"name": "timersys",
"version": "0.1.0",
"private": true,
"author": "linchun",
"description": "electron+vue+elementui+lowdb",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps",
"start": "electron .",
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"main": "background.js",
"dependencies": {
"element-ui": "^2.14.1",
"lowdb": "^1.0.0",
"node-schedule": "^1.3.2",
"vue": "^2.6.11",
"vue-router": "^3.0.1",
"xlsx": "^0.17.4",
"file-saver":"^2.0.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "^4.5.15",
"babel-eslint": "^10.1.0",
"electron": "^9.0.0",
"electron-builder": "^22.9.1",
"electron-debug": "^1.5.0",
"electron-devtools-installer": "^3.1.1",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-cli-plugin-electron-builder": "~2.0.0-rc.5",
"vue-devtools": "^5.1.4",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
此文件类型无法预览
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- <title><%= htmlWebpackPlugin.options.title %></title> -->
<title>定时器系统</title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'electron-vue-demo'
}
</script>
\ No newline at end of file \ No newline at end of file
此文件类型无法预览
此文件类型无法预览
html,body{
overflow-y: hidden!important;
}
.app-container {
padding: 0 20px;
}
/* // 结构样式 */
.border-line{
background:#e6ebf5;
height:1px;
margin-bottom: 10px;
}
.main-content{
padding-top:10px;
}
/* // 分页器 */
.el-pagination{
text-align: right;
padding: 15px 0;
}
\ No newline at end of file \ No newline at end of file
'use strict'
import { app, protocol, BrowserWindow, Tray, Menu} from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
const isDevelopment = process.env.NODE_ENV !== 'production'
const path = require('path')
// 创建菜单
let contextMenu = Menu.buildFromTemplate([
{ label: '退出', role: 'close', accelerator: 'CmdOrCtrl+Q', click: function() {app.exit()} }
])
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
async function createWindow() {
Menu.setApplicationMenu(null)
// Create the browser window.
const win = new BrowserWindow({
width: 1200,
// width: 1900,
minWidth: 800,
// maxWidth: 1920,
// height: 600,
height: 800,
// maxHeight: 1200,
minHeight: 600,
// fullscreen:true,
center:true,
resizable:true,
// frame:false,
maximizable:true,
title:'定时任务',
// autoHideMenuBar: true, // 隐藏窗口的系统菜单
autoHideMenuBar: false, // 隐藏窗口的系统菜单
webPreferences: {
// icon: path.join(__dirname, './assets/logo/app.ico'),
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
devTools:true // 关闭开发者工具
// devTools:false // 关闭开发者工具
},
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
// win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()//todo
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
// await installExtension(VUEJS_DEVTOOLS)
// 新增的:安装devTools
// const {session}=require('electron');
// const path=require('path');
// session.defaultSession.loadExtension(
// path.resolve(__dirname,'../build/devTools')
// );
// console.log('dirname',__dirname)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
// let iconPath = path.join(__dirname, '../public/traylogo.png')
let iconPath = path.join(__dirname, '../public/logo.png')
// let iconPath = path.join(__dirname, './assets/logo/logo.png')
let tray = new Tray(iconPath)
// 设置托盘提示
tray.setToolTip('定时器系统')
// 设置托盘菜单
tray.setContextMenu(contextMenu)
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
<template>
<div>
<el-upload
:ref="upload.ref"
multiple
:accept="upload.acceptFile"
action
:on-change="handleImportChanged"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleSuccess"
: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">
</div>
<div class="el-upload__tip" style="color: red" slot="tip">
<p style="font-size:16px">温馨提示:</p>
<p>1、文件目前仅支持xlsx,xls,csv格式</p>
<p>2、导入数据量大时请耐心等待</p>
<p>3、数据导入中,请勿关闭本页面</p>
</div>
</el-upload>
<div slot="footer" class="dialog-footer" style="text-align:center;margin-top:20px;">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</div>
</template>
<script>
import {UploadImport} from '@/utils/excel'
export default {
name:"FileUpload",
props:[
'upload',
],
data() {
return {
};
},
computed: {
},
methods: {
handleImportChanged(files,fileList){
this.upload.file=files.raw;
if(fileList.length>1){//单文件上传,后者覆盖前者 配合参数设置 1去除limit=1设置 2允许多传 multiple
fileList.splice(0,1);
}
},
submitForm () { // 提交上传文件
if(this.checkFile(this.upload.file)){
// this.$emit('onFileSubmit',this.upload.file);
UploadImport(this.upload.file).then((data)=>{
this.$emit(this.upload.emitEventName,data);
// this.dealFile(data)
this.handleSuccess();
}).catch((err)=>{
this.$message.error(this.upload.submitErrorMsg);
this.handleSuccess();
console.log('err',err);
});
}
else{
return false;
}
},
cancel(){
this.upload.open=false;
},
checkFile(){
let fileSuffix=this.upload.file.name.substring(this.upload.file.name.lastIndexOf('.')+1);
console.log('whiteList',this.upload.whiteList);
if(this.upload.whiteList.indexOf(fileSuffix)===-1){
this.$message.error(this.upload.formatMsg);
return false;
}
else{
return true
}
},
handleSuccess () { // 文件上传成功处理
this.upload.isUploading = false
this.$refs.upload.clearFiles()
// this.upload.open = false
},
handleFileUploadProgress () {// 文件上传中处理
this.upload.isUploading = true
},
},
created() {
},
};
</script>
\ No newline at end of file \ No newline at end of file
<!-- 调度日志 -->
<template style="background:#e6ebf5;z-index:100;">
<div class="app-container">
<!-- 标题 -->
<div class="main-title" style="padding:10px 0;">
<i class="el-icon-date" style="color:#1890ff"></i>
<span style="padding:0 10px;">调度日志</span>
</div>
<!-- 分割线 -->
<div class="border-line"></div>
<!-- 表单查询条件 -->
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="68px"
>
<el-form-item
label="任务名称"
prop="jobName"
>
<el-input
v-model="queryParams.jobName"
placeholder="请输入任务名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item
label="任务组名"
prop="jobGroup"
>
<el-select
v-model="queryParams.jobGroup"
placeholder="请选择任务组名"
clearable
size="small"
>
<el-option
v-for="dict in jobGroupOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item
label="执行时间"
prop="jobRangeDateTime"
>
<el-date-picker
v-model="queryParams.jobRangeDateTime"
size="small"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
></el-date-picker>
</el-form-item>
<el-form-item
label="执行状态"
prop="status"
>
<el-select
v-model="queryParams.status"
placeholder="请选择执行状态"
clearable
size="small"
>
<el-option
v-for="dict in statusOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item>
<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>
<el-button
icon="el-icon-refresh-right"
size="mini"
@click="handleRefresh"
>刷新</el-button>
<el-button
icon="el-icon-back"
size="mini"
@click="handleBack"
>返回</el-button>
</el-form-item>
</el-form>
<!-- 表格分页列表 -->
<el-table
v-loading="loading"
:data="showData"
:height="tableHeight"
style="width:100%;"
v-tableHeight="{bottomOffset:60}"
>
<el-table-column
min-width="220px"
label="执行时间"
align="center"
prop="jobRangeDateTime"
:show-overflow-tooltip="true"
>
<template slot-scope="scope">
<span>{{scope.row.jobRangeDateTime.join(' ~ ')}}</span>
</template>
</el-table-column>
<el-table-column
label="任务名称"
align="center"
prop="jobName"
:show-overflow-tooltip="true"
/>
<el-table-column
label="任务组名"
align="center"
prop="jobGroup"
:formatter="jobGroupFormat"
/>
<el-table-column
label="任务地址"
align="center"
prop="jobAddr"
:show-overflow-tooltip="true"
/>
<el-table-column
label="执行状态"
align="center"
prop="status"
:formatter="jobStatusFormat"
:show-overflow-tooltip="true"
/>
<el-table-column
label="失败原因"
align="center"
prop="failReason"
:show-overflow-tooltip="true"
/>
<el-table-column
label="任务参数"
align="center"
prop="taskParams"
:show-overflow-tooltip="true"
/>
<el-table-column
label="备注"
align="center"
prop="remark"
:show-overflow-tooltip="true"
/>
</el-table>
<el-pagination
background
v-show="total>0"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 50]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
<!-- 脚垫 -->
<div style="height:10px;"></div>
</div>
</template>
<script>
// 引入json数据库
import lowdb from 'lowdb'
import fileSync from 'lowdb/adapters/FileSync'
let adapter = new fileSync('db.json');
const db = lowdb(adapter);
import {changeArr } from '@/utils/index'
export default {
data () {
return {
tableHeight:450,
showData:[],
jobId:null,
currentPage: 1,
// 遮罩层
loading: true,
// 总条数
total: 0,
// 任务组名字典
jobGroupOptions: [],
// 状态字典
statusOptions: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
jobName: undefined,
jobAddr: undefined,
jobGroup: undefined,
status: undefined,
jobRangeDateTime: undefined,
type: undefined,
},
};
},
created () {
this.jobGroupOptions=[
{dictLabel:'默认',dictValue:'default'},
{dictLabel:'系统',dictValue:'system'},
];
this.statusOptions=[
{dictLabel:'开启',dictValue:'0'},
{dictLabel:'关闭',dictValue:'1'},
];
},
mounted () {
//获取传参jobId
this.jobId=this.$route.params.jobId?this.$route.params.jobId:sessionStorage.getItem('jobId');
// 获取表格数据
this.getList();
},
methods: {
/** 查询调度日志列表 */
getList(params,fun) {
this.loading=true;
params=params||{
pageNum:this.queryParams.pageNum,
pageSize:this.queryParams.pageSize,
type:this.queryParams.type
}
let resData = db.read().get('logDB').value()[this.jobId];
// console.log('resData',resData);
if(params.type==='filter'){
resData=this.getFilterData(resData);
}
this.showData=[];//清空列表
this.total = resData?resData.length:0;
if(this.total>0){
resData=changeArr(resData,params.pageSize);//保存分组后的全部数据,默认一页10条
this.showData=resData[params.pageNum-1];//显示第一页数据
}
this.loading=false;
if(fun){
fun();
}
},
getFilterData(filterData=[]){
// 按条件一层层过滤
// 任务名称
if(this.queryParams['jobName']){
filterData=filterData.filter((item)=>{return item.jobName.indexOf(this.queryParams['jobName'])!=-1});
// console.log('filter-jobName',filterData);
}
// 任务组名
if(this.queryParams['jobGroup']){
filterData=filterData.filter((item)=>{return item.jobGroup===this.queryParams['jobGroup']});
// console.log('filter-jobGroup',filterData);
}
// 执行状态
if(this.queryParams['status']!==undefined){
filterData=filterData.filter((item)=>{return item.status===this.queryParams['status']});
// console.log('filter-status',filterData);
}
// // 任务地址
// if(this.queryParams['jobAddr']){
// filterData=filterData.filter((item)=>{return item.jobAddr.indexOf(this.queryParams['jobAddr'])!=-1});
// // console.log('filter-jobAddr',filterData);
// }
// 执行时间
if(this.queryParams['jobRangeDateTime']&&this.queryParams['jobRangeDateTime'].length===2){
console.log('jobRangeDateTime',this.queryParams['jobRangeDateTime']);
filterData=filterData.filter((item)=>{
let condition1=item.runStartTime>=this.queryParams['jobRangeDateTime'][0]&&item.runEndTime<=this.queryParams['jobRangeDateTime'][1],
condition2=item.runStartTime<this.queryParams['jobRangeDateTime'][0]&&item.runEndTime>this.queryParams['jobRangeDateTime'][0]&&item.runEndTime<this.queryParams['jobRangeDateTime'][1],
condition3=item.runStartTime>this.queryParams['jobRangeDateTime'][0]&&item.runEndTime>this.queryParams['jobRangeDateTime'][1];
if(condition1||condition2||condition3){
return true
}
});
console.log('filterData',filterData);
}
return filterData;
},
selectDictLabel(datas, value) {
var actions = [];
Object.keys(datas).some((key) => {
if (datas[key].dictValue == ('' + value)) {
actions.push(datas[key].dictLabel);
return true;
}
})
return actions.join('');
},
// 任务组名字典翻译
jobGroupFormat(row) {
return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
},
// 执行状态字典翻译
jobStatusFormat(row) {
return this.selectDictLabel(this.statusOptions, row.status);
},
handleQuery(){
this.$refs['queryForm'].validate((valid) => {
if (valid) {
this.queryParams.type='filter';
this.getList()
}
})
},
handleRefresh () {
location.reload()
},
handleBack () {
// window.history.go(-1);
this.$router.push({name:'TimingTask'});
},
resetQuery () {
this.$refs['queryForm'].resetFields()
this.queryParams={
pageNum: 1,
pageSize: 10,
jobName: undefined,
jobAddr: undefined,
jobGroup: undefined,
status: undefined,
jobRangeDateTime: undefined,
type: undefined,
},
this.getList()
},
handleSizeChange (val) {
this.queryParams.pageSize = val
this.getList()
},
handleCurrentChange (val) {
this.queryParams.pageNum = val
this.getList()
},
}
}
</script>
\ No newline at end of file \ No newline at end of file
<!-- 定时任务 -->
<template style="background:#e6ebf5;z-index:100;">
<div class="app-container">
<!-- 标题 -->
<div class="main-title" style="padding:10px 0;">
<i class="el-icon-date" style="color:#1890ff"></i>
<span style="padding:0 10px;">定时任务</span>
</div>
<!-- 分割线 -->
<div class="border-line"></div>
<!-- 表单查询条件 -->
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="68px"
>
<el-form-item
label="任务名称"
prop="jobName"
>
<el-input
v-model="queryParams.jobName"
placeholder="请输入任务名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item
label="任务组名"
prop="jobGroup"
>
<el-select
v-model="queryParams.jobGroup"
placeholder="请选择任务组名"
clearable
size="small"
>
<el-option
v-for="dict in jobGroupOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item
label="执行状态"
prop="status"
>
<el-select
v-model="queryParams.status"
placeholder="请选择执行状态"
clearable
size="small"
>
<el-option
v-for="dict in statusOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item
label="任务地址"
prop="jobAddr"
>
<el-input
v-model="queryParams.jobAddr"
placeholder="请输入任务地址"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<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>
<el-button
icon="el-icon-refresh-right"
size="mini"
@click="handleRefresh"
>刷新</el-button>
</el-form-item>
</el-form>
<!-- 操作栏 -->
<el-row
id="operDom"
:gutter="10"
class="mb8"
>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-upload2"
size="mini"
@click="ExportExcel"
>生成模板</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-upload2"
size="mini"
@click="handleImport"
>导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除</el-button>
</el-col>
</el-row>
<!-- 表格分页列表 -->
<el-table
v-loading="loading"
:data="showData"
:height="tableHeight"
@selection-change="handleSelectionChange"
style="width:100%"
v-tableHeight="{bottomOffset:60}"
>
<el-table-column
type="selection"
width="55"
align="center"
/>
<el-table-column
label="任务编号"
align="center"
prop="jobId"
/>
<el-table-column
label="任务名称"
align="center"
prop="jobName"
:show-overflow-tooltip="true"
/>
<el-table-column
label="任务组名"
align="center"
prop="jobGroup"
:formatter="jobGroupFormat"
/>
<el-table-column
label="任务地址"
align="center"
prop="jobAddr"
:show-overflow-tooltip="true"
/>
<el-table-column
label="执行日期"
align="center"
prop="jobDates"
:show-overflow-tooltip="true"
>
<template slot-scope="scope">
<span v-if="scope.row.jobDates.length===1">{{scope.row.jobDates[0]}}</span>
<span v-if="scope.row.jobDates.length>1">{{scope.row.jobDates.join(' , ')}}</span>
</template>
</el-table-column>
<el-table-column
label="执行时间"
align="center"
prop="jobTime"
:show-overflow-tooltip="true"
/>
<el-table-column
label="状态"
align="center"
>
<template slot-scope="scope">
<el-switch
:value="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"
width="250"
class-name="small-padding fixed-width"
>
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-caret-right"
@click="handleRun(scope.row)"
>执行一次</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleSingleDel(scope.row)"
>删除</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleLog(scope.row)"
>日志</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
v-show="total>0"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-sizes="[10, 20, 30, 50]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
<!-- 脚垫 -->
<div style="height:10px;"></div>
<!-- 添加或修改定时任务对话框 -->
<el-dialog
:title="title"
:visible.sync="open"
width="700px"
append-to-body
>
<el-form
ref="addform"
:model="form"
:rules="rules"
label-width="120px"
>
<el-row>
<el-col :span="12">
<el-form-item
label="任务名称"
prop="jobName"
>
<el-input
v-model="form.jobName"
placeholder="请输入任务名称"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="任务分组"
prop="jobGroup"
>
<el-select
v-model="form.jobGroup"
placeholder="请选择"
>
<el-option
v-for="dict in jobGroupOptions"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="任务地址"
prop="jobAddr"
>
<el-input
v-model="form.jobAddr"
placeholder="请输入任务地址(如:C:\XXX.bat)"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="执行日期"
prop="jobDates"
>
<el-tooltip class="item" effect="dark" :content="datesTip" placement="top">
<el-date-picker
@change="datePickerChange"
style="width:100%;"
type="dates"
v-model="form.jobDates"
value-format="yyyy-MM-dd"
:picker-options="startDatePicker"
placeholder="选择一个或多个日期">
</el-date-picker>
</el-tooltip>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="执行时间"
prop="jobTime"
>
<el-time-picker
v-model="form.jobTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59'
}"
value-format="HH:mm:ss"
placeholder="选择时间">
</el-time-picker>
</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-row>
</el-form>
<div
slot="footer"
class="dialog-footer"
>
<el-button
type="primary"
@click="submitForm"
>确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
<!-- 导入 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" :width="upload.width" append-to-body>
<FileUpload :upload="upload" @onFileSubmit="dealFile"></FileUpload>
</el-dialog>
</div>
</template>
<script>
// 引入json数据库
import lowdb from 'lowdb'
import schedule from 'node-schedule'
import fileSync from 'lowdb/adapters/FileSync'
let adapter = new fileSync('db.json');
const db = lowdb(adapter);
const {shell} = require('electron')
import {create_uuid,formatDate,arrayToMap,changeArr,dateSort,trim,moment1} from '@/utils/index'
import {exportExcel} from '@/utils/excel'
import FileUpload from "@/components/FileUpload";
export default {
name:'TimingTask',
components: {
FileUpload,
},
data () {
return {
tableHeight:450,
//导入参数
upload: {
file:'',
loading: false,
// 是否显示弹出层(批量导入)
open: false,
// 弹出层标题(批量导入)
title: '导入',
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
width:"400px",
ref:"upload",
emitEventName:'onFileSubmit',
submitErrorMsg:'导入数据不为空,请导入正常数据模板!',
whiteList:['xlsx','xls','csv'],
formatMsg:'仅支持xlsx、xls、csv格式',
acceptFile:'.xlsx,.xls,.csv'
},
dateTime:['',''],//保存系统时间 dateTime[0] 格式 年-月-日 dateTime[1] 格式 时-分-秒
showData:[],//分页数据
jobMap:{},
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非多个禁用
multiple: true,
// 总条数
total: 0,
// 定时任务表格数据
jobList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 是否显示详细弹出层
// openView: false,
// 任务组名字典
jobGroupOptions: [],
// 状态字典
statusOptions: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
type: undefined,
jobName: undefined,
jobAddr: undefined,
jobGroup: undefined,
status: undefined,
},
datesTip:'请选择一个或多个日期',
// 表单参数
form: {
status:'1',
jobDates:'',
jobTime:'',
jobName:'',
jobGroup:'',
jobAddr:''
},
// 表单校验
rules: {
jobName: [
{ required: true, message: "任务名称不能为空", trigger: "blur" },
],
jobGroup: [
{
required: true,
message: "任务分组为必填项",
trigger: "blur",
},
],
jobAddr: [
{
required: true,
message: "任务地址为必填项",
trigger: "blur",
},
{ pattern: /\.bat$/, message: "任务地址输入格式有误", trigger: "blur" },
],
jobDates: [
{
required: true,
message: "执行日期为必填项",
trigger: "blur",
},
],
jobTime: [
{
required: true,
message: "执行时间为必填项",
trigger: "blur",
},
]
},
};
},
created () {
db.defaults({taskDB: [],logDB:{}}).write();// 设置默认数据库taskDB 对象数组,logDB 对象Map
this.jobGroupOptions=[
{dictLabel:'默认',dictValue:'default'},
{dictLabel:'系统',dictValue:'system'},
];
this.statusOptions=[
{dictLabel:'开启',dictValue:'0'},
{dictLabel:'关闭',dictValue:'1'},
];
},
mounted () {
// 清数据库
// db.get('taskDB').remove().write();
// db.unset('logDB').write();
// 获取排序后的总数据
this.getList();
// 开启定时任务,每分钟执行一次
schedule.scheduleJob('* * * * * *', ()=> {
this.jobList = db.get('taskDB').sortBy('jobTime').value();
this.dateTime=[moment1('YYYY-MM-DD'),moment1('HH:mm:ss')];
for(let obj of this.jobList) {
if(this.runFlag(obj,this.dateTime)){//满足触发条件,执行相应任务
this.runScheduleJob(obj);
}
}
});
},
computed:{
startDatePicker() {//日期选择过滤 过期禁选
return {
disabledDate(time) {
return (
formatDate(time,'date') < moment1('YYYY-MM-DD')
);
},
};
},
},
methods: {
ExportExcel() {//导出excel模板
exportExcel();
},
runScheduleJob(obj){
let newObj={
runStartTime:'',
runEndTime:'',
failReason:'',
jobRangeDateTime:[],
remark:'',
};
newObj.runStartTime=moment1('YYYY-MM-DD HH:mm:ss');
// 发出通知并弹窗
new Notification(obj.jobName+'-任务触发', {
body: '执行任务地址:'+obj.jobAddr
})
// 执行子进程
shell.openPath(obj.jobAddr.replace(/\\/g,"\\")).then((msg)=>{
newObj.failReason=msg;
if(newObj.failReason=='Failed to open path'){
newObj.remark='文件打开失败,原因:找不到文件。';
}
newObj.runEndTime=moment1('YYYY-MM-DD HH:mm:ss');
newObj.jobRangeDateTime=[newObj.runStartTime,newObj.runEndTime];
// 生成执行日志记录
newObj=Object.assign(newObj,obj)
// console.log('newObj',newObj);
this.saveRunLog(newObj);
})
// this.openServer(obj.jobAddr);
// this.$notify({
// title: obj.jobName+'-触发',
// // duration:0,//不自动关闭
// duration:5000,//5秒后关闭
// type: 'warning',
// onClose:()=>{
// newObj.runEndTime=moment1('YYYY-MM-DD HH:mm:ss');
// newObj.jobRangeDateTime=[newObj.runStartTime,newObj.runEndTime]
// // 生成执行日志记录
// newObj=Object.assign(newObj,obj)
// this.saveRunLog(newObj);
// },
// });
},
saveRunLog(obj){
let resData=db.get('logDB').value()[obj.jobId];
if(!resData){
db.get('logDB').set(obj.jobId,[obj]).write();
}
else{
resData.push(obj);
db.get('logDB').update(obj.jobId,resData).write();
}
},
runFlag(obj,dateTime){
if(obj.jobDates.indexOf(dateTime[0])==-1){//没到这一天不触发
// console.log('没到这一天不触发');
return false;
}
else if(obj.jobTime!==dateTime[1]){//没到这个时间点不触发
// console.log('没到这个时间点不触发');
return false;
}
else if(obj.status=='1'){//开关没启动不触发
// console.log('开关没启动不触发');
return false;
}
else{
return true;
}
},
resetAddForm(){//清空表单
this.form={
status:'1',
jobDates:'',
jobTime:'',
jobName:'',
jobGroup:'',
jobAddr:''
};
this.datesTip='请选择一个或多个日期';
},
submitForm(){//添加/修改--确定
this.$refs['addform'].validate((valid) => {
if (valid) {
if(this.validDateTime(this.form)){// 数据写入前判断执行日期执行时间是否有重复
dateSort(this.form.jobDates);
let tmp={
jobId:this.form.jobId?this.form.jobId:create_uuid(null,12),
// jobDates:this.form.jobDates.join(','),
jobDates:this.form.jobDates,
jobName:trim(this.form.jobName),
jobAddr:trim(this.form.jobAddr),
jobGroup:this.form.jobGroup,
status:this.form.status,
jobTime:this.form.jobTime
};
// console.log(tmp);
if(this.form.jobId){//编辑
// console.log('编辑',this.form.jobId);
db.get('taskDB').find({jobId:this.form.jobId}).assign(tmp).write();
}
else{//新增
db.get('taskDB').push(tmp).write();
}
// this.getList();//刷新列表
this.getList()
// this.$refs['addform'].resetFields();
this.resetAddForm();//清空表单
this.$message({
message: '操作成功',
duration: 3000,
type: 'success'
});
this.open=false;
}
else{
this.$message.error('当前执行日期,执行时间已添加任务,请选择其他时间!');
return false;
}
}
else{
this.$message.info('请检查表单填写规范!');
return false;
}
})
},
validDateTime(obj){
let hasJobTimeData=db.get('taskDB').map({jobTime:obj.jobTime}).value();
if(hasJobTimeData.length>0){
let taskDB=db.get('taskDB').sortBy('jobTime').value();
for(let i=0;i<hasJobTimeData.length;i++){
if(hasJobTimeData[i]===true&&taskDB[i]['jobId']!==obj.jobId&&obj.jobDates.length>0){//非编辑模式,执行时间相同判断执行日期是否包含相同项
for(let j=0;j<obj.jobDates.length;j++){
if(taskDB[i].jobDates.indexOf(obj.jobDates[j])!=-1){//确认存在相同执行日期 抛提示
return false
}
}
}
}
return true;
}
else{
return true;
}
},
cancel(){
this.open=false;
},
datePickerChange(val){
this.datesTip=val.length>0?val.join(' , '):'';
},
/** 查询定时任务列表 */
getList(params,fun) {
this.loading=true;
params=params||{
pageNum:this.queryParams.pageNum,
pageSize:this.queryParams.pageSize,
type:this.queryParams.type
}
// console.log('mergeParams',params);
// this.loading = true;
this.jobMap={};//清空jobMap
this.jobList = db.get('taskDB').sortBy('jobTime').value();
if(params.type==='filter'){
this.jobList=this.getFilterData(this.jobList);
}
this.jobMap=arrayToMap(this.jobList,'jobId');
// console.log('this.jobMap',this.jobMap);
this.showData=[];//清空列表
this.total = this.jobList.length;
if(this.total>0){
this.jobList=changeArr(this.jobList,params.pageSize);//保存分组后的全部数据,默认一页10条
this.showData=this.jobList[params.pageNum-1];//显示第一页数据
}
this.loading=false;
if(fun){
fun();
}
},
/** 任务详细信息 */
handleLog(row) {
this.$router.push({name:'OperLog',params:{jobId:row.jobId}});
sessionStorage.setItem('jobId',row.jobId);
},
/* 立即执行一次 */
handleRun(row) {
this.$confirm('确认要立即执行一次' + ' <' + row.jobName + '> '+'任务吗?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then( ()=> {
// 注意如果立刻执行与其他定时任务时间不存在冲突,高并发
// 注意执行一次与定时任务相互不影响
// let obj=db.get('taskDB').find({jobId:row.jobId}).value();
// if(obj.jobDates.indexOf(this.dateTime[0])===-1){//不存在 写入当前执行日期和执行时间
// obj.jobDates.push(this.dateTime[0]);
// dateSort(obj.jobDates);//给日期排序
// db.get('taskDB').find({jobId:row.jobId}).assign({jobDates:obj.jobDates,jobTime:this.dateTime[1]}).write();
// }
// else{//已存在 只写入执行时间
// db.get('taskDB').find({jobId:row.jobId}).assign({jobTime:this.dateTime[1]}).write();
// }
// this.getList(null,()=>{
// this.$message.success('已执行!')
// });
this.runScheduleJob(row);
})
.catch( ()=> {//取消
// this.$message.error('操作失败!');
this.$message.error('取消操作!');
});
},
// 任务状态修改
handleStatusChange(row) {
let text = row.status === "0" ? "停用" : "启用";
this.$confirm(
'确认要' + text + ' <' + row.jobName + '> '+'任务吗?',
"警告",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(async()=>{
// todo
row.status=row.status === "0" ? "1" : "0";
let modifyRet=db.get('taskDB').find({jobId:row.jobId}).assign({status:row.status}).write();
if(Object.prototype.toString.call(modifyRet)=='[object Object]'){//判断返回是否为对象
this.$message.success(text + "成功!");
// this.getList();
}
else{
this.$message.error("状态修改失败!");
}
}).catch(()=> {//取消
// db.get('taskDB').find({jobId:row.jobId}).update({status:row.status === "0" ? "1" : "0"}).write();
// this.getList();
this.$message.error('取消操作!');
});
},
selectDictLabel(datas, value) {
var actions = [];
Object.keys(datas).some((key) => {
if (datas[key].dictValue == ('' + value)) {
actions.push(datas[key].dictLabel);
return true;
}
})
return actions.join('');
},
// 任务组名字典翻译
jobGroupFormat(row) {
return this.selectDictLabel(this.jobGroupOptions, row.jobGroup);
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map((item) => item.jobId);
// this.single = selection.length != 1;
this.multiple = !selection.length;
},
handleImport(){
this.upload.title = '导入'
this.upload.open = true
},
dealFile(data){// 处理导入的数据
// console.log('excelData',data);
let tmp=[];
data.map((item)=>{
if(item['任务名称']&&item['任务地址']){
db.get('taskDB').push(
{
jobId:create_uuid(null,12),
jobName:trim(item['任务名称']),
jobAddr:trim(item['任务地址']),
jobDates:[],
jobTime:'',
status:'1',//状态默认关闭
jobGroup:'default'//任务组名为默认
}
).write();
tmp.push(item);
}
});
if(tmp.length>0){
this.upload.open=false;//关闭弹窗
this.getList(null,()=>{
this.$message.success('导入成功!');
});
}
else{
this.$message.error('模板格式有误!');
}
},
handleDelete(){
if(this.ids.length>0){
let delArr=[];
this.ids.map((item)=>{
if(this.jobMap[item]){
delArr.push(this.jobMap[item]['jobName']);
}
});
this.$confirm('确定要删除'+delArr.join(' , ')+'?', '提示', { type: 'warning' }).then(
() => {
this.ids.map((item)=>{
//删taskDB数据
this.delTaskDB(item);
//删logDB数据
this.delLogDB(item);
//刷新列表
this.getList();
});
}
).then(()=>{
this.$message.success('删除成功!');
}).catch(()=> {//取消
// this.$message.error('删除失败!');
this.$message.error('取消操作!');
});
}
else{
this.$message.error('至少选择一条记录!');
}
},
handleSingleDel(row){//单条删除
this.$confirm(
'确认要删除'+' <' + row.jobName + '> '+'任务吗?',
"警告",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(()=>{
//删taskDB数据
let delRet=this.delTaskDB(row.jobId);
//删logDB数据
this.delLogDB(row.jobId);
if(Array.isArray(delRet)){//判断返回为数组
this.$message.success("删除成功!");
this.getList();
}
else{
this.$message.error("删除失败!");
}
}).catch( ()=> {//取消
this.$message.error('取消操作!');
});
},
delTaskDB(jobId){
return db.get('taskDB').remove({jobId:jobId}).write();
},
delLogDB(jobId){
if(db.get('logDB').value()[jobId]){
db.get('logDB').unset(jobId).write();
}
},
handleUpdate(row){
this.resetAddForm();//清空表单
this.open=true;
this.title="修改任务";
this.form={
jobId:row.jobId,
jobName:row.jobName,
jobGroup:row.jobGroup,
jobAddr:row.jobAddr,
jobDates:row.jobDates,
jobTime:row.jobTime,
status:row.status
};
this.datesTip=row.jobDates.length>0?row.jobDates.join(' , '):'请选择一个或多个日期';
},
handleAdd(){
this.resetAddForm();//清空表单
this.open=true;
this.title="新增任务";
},
handleQuery(){
this.$refs['queryForm'].validate((valid) => {
if (valid) {
this.queryParams.type='filter';
this.getList()
}
})
},
getFilterData(filterData=[]){
// 按条件一层层过滤
// 任务名称
if(this.queryParams['jobName']){
filterData=filterData.filter((item)=>{return item.jobName.indexOf(this.queryParams['jobName'])!=-1});
// console.log('filter-jobName',filterData);
}
// 任务组名
if(this.queryParams['jobGroup']){
filterData=filterData.filter((item)=>{return item.jobGroup===this.queryParams['jobGroup']});
// console.log('filter-jobGroup',filterData);
}
// 执行状态
if(this.queryParams['status']!==undefined){
filterData=filterData.filter((item)=>{return item.status===this.queryParams['status']});
// console.log('filter-status',filterData);
}
// 任务地址
if(this.queryParams['jobAddr']){
filterData=filterData.filter((item)=>{return item.jobAddr.indexOf(this.queryParams['jobAddr'])!=-1});
// console.log('filter-jobAddr',filterData);
}
return filterData;
},
handleRefresh () {
location.reload()
},
resetQuery () {
this.$refs['queryForm'].resetFields()
this.queryParams={
pageNum: 1,
pageSize: 10,
type: undefined,
jobName: undefined,
jobAddr: undefined,
jobGroup: undefined,
status: undefined
},
this.getList()
},
handleSizeChange (val) {
this.queryParams.pageSize = val
this.getList()
},
handleCurrentChange (val) {
this.queryParams.pageNum = val
this.getList()
},
}
}
</script>
\ No newline at end of file \ No newline at end of file
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import './assets/styles/common.css';
// 公用表格高度计算
import tableHeight from './utils/tableHeight'
// 表格自适应指令
Vue.use(tableHeight)
Vue.config.productionTip = false
Vue.use(ElementUI);
/* eslint-disable no-new */
// new Vue({
// components: { App },
// router,
// template: '<App/>'
// }).$mount('#app')
new Vue({
router,
render: h => h(App),
}).$mount('#app')
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'TimingTask',
component: require('@/components/TimingTask.vue').default,
},
// {
// path: '/timingTask',
// name: 'TimingTask',
// component: require('@/components/TimingTask.vue').default,
// },
{
path: '/operLog',
// name: 'landing-page',
name: 'OperLog',
// component: require('@/components/LandingPage').default
component: require('@/components/OperLog.vue').default,
// children: [
// {
// path: 'log/:tableId',
// component: (resolve) => require(['@/views/orderSys/declineMag/declineConfirm/detail'], resolve),
// name: 'declineConfirmDetail',
// // meta: { title: '调度日志'}
// },
// ]
},
{
path: '*',
redirect: '/'
}
]
})
import xlsx from 'xlsx'
import FileSaver from 'file-saver'
// 读取文件
export const ReadFile=(file)=>{
return new Promise(resolve => {
let reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = ev => { resolve(ev.target.result); }
})
}
// 导入excel
export function UploadImport(file) {
return new Promise((resolve,reject)=>{
// ReadFile(file.raw).then((result)=>{
ReadFile(file).then((result)=>{
let dataBinary= result;// 读取文件
let workBook = xlsx.read(dataBinary,{type:"binary",cellDates:true});
let workSheet = workBook.Sheets[workBook.SheetNames[0]];
let data = xlsx.utils.sheet_to_json(workSheet)||[];
if(data.length>0){
resolve(data);
}
else{
reject();
}
})
});
}
export function importExcel(_this) {
let fullscreenLoading = _this.$loading({
lock: true,
text: '导入中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
let obj = _this.imFile
if (!obj.files) {
fullscreenLoading.close();
return
}
var f = obj.files[0]
var reader = new FileReader()
reader.onload = function (e) {
var data = e.target.result
// console.log('data',data);
if (_this.rABS) {
_this.wb = xlsx.read(btoa(this.fixdata(data)), {// 手动转化
type: 'base64'
})
} else {
_this.wb = xlsx.read(data, {
type: 'binary'
})
}
// console.log('wb',_this.wb.Sheets[_this.wb.SheetNames[0]]);
let json = xlsx.utils.sheet_to_json(_this.wb.Sheets[_this.wb.SheetNames[0]])
// console.log('json',typeof json)
_this.dealFile(analyzeData(json)) // analyzeData: 解析导入数据
fullscreenLoading.close();
}
if (_this.rABS) {
reader.readAsArrayBuffer(f)
}
else {
reader.readAsBinaryString(f)
}
function analyzeData (data) { // 此处可以解析导入数据
return data||[]
}
}
// 导出excel
export function exportExcel(params) {
params=params||{
xlsxData:[["任务名称","任务地址"]],
sheetName:'sheet1',
fileName:'template'
}
const s2ab = s => {
if (typeof ArrayBuffer !== 'undefined') {
let buf = new ArrayBuffer(s.length)
let view = new Uint8Array(buf)
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff
return buf
} else {
let buf = new Array(s.length);
for (let i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
}
let worksheet=xlsx.utils.aoa_to_sheet(params.xlsxData);
let workBook =xlsx.utils.book_new();
xlsx.utils.book_append_sheet(workBook,worksheet,params.sheetName);// 第三个参数可选,是工作表名称
let wopts = { bookType: 'xlsx', bookSST: false, type: 'binary'} //写入的样式
let wbout = xlsx.write(workBook, wopts)
try{
let blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' })
FileSaver.saveAs(blob, params.fileName+'.xlsx')
}
catch(e){
if(typeof console!=='undefined'){
console.log(e,wbout)
}
}
}
\ No newline at end of file \ No newline at end of file
/**
* 获取当前时间并格式化
*/
export function moment1(type='YYYY-MM-DD HH:mm:ss') {
var date = new Date()
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
if(type=='YYYY-MM-DD HH:mm:ss'){
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}
else if(type=='YYYY-MM-DD'){
return year + '-' + month + '-' + day
}
else if(type=='HH:mm:ss'){
return hours + ':' + minutes + ':' + seconds
}
}
/**
* 表格时间格式化
*/
export function formatDate(cellValue,type='datetime') {
if (cellValue == null || cellValue == "") return "";
var date = new Date(cellValue)
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
if(type=='datetime'){
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
}else if(type=='date'){
return year + '-' + month + '-' + day
}
}
/**
* @param {number} time
* @param {string} option
* @returns {string}
*/
/**
* @param {string} url
* @returns {Object}
*/
export function getQueryObject(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
/**
* @param {string} input value
* @returns {number} output value
*/
export function byteLength(str) {
// returns the byte length of an utf8 string
let s = str.length
for (var i = str.length - 1; i >= 0; i--) {
const code = str.charCodeAt(i)
if (code > 0x7f && code <= 0x7ff) s++
else if (code > 0x7ff && code <= 0xffff) s += 2
if (code >= 0xDC00 && code <= 0xDFFF) i--
}
return s
}
/**
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray(actual) {
const newArray = []
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i])
}
}
return newArray
}
/**
* @param {Object} json
* @returns {Array}
*/
export function param(json) {
if (!json) return ''
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return ''
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
})
).join('&')
}
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) {
return {}
}
const obj = {}
const searchArr = search.split('&')
searchArr.forEach(v => {
const index = v.indexOf('=')
if (index !== -1) {
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
obj[name] = val
}
})
return obj
}
/**
* @param {string} val
* @returns {string}
*/
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
/**
* Merges two objects, giving the last one precedence
* @param {Object} target
* @param {(Object|Array)} source
* @returns {Object}
*/
export function objectMerge(target, source) {
if (typeof target !== 'object') {
target = {}
}
if (Array.isArray(source)) {
return source.slice()
}
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = objectMerge(target[property], sourceProperty)
} else {
target[property] = sourceProperty
}
})
return target
}
/**
* @param {HTMLElement} element
* @param {string} className
*/
export function toggleClass(element, className) {
if (!element || !className) {
return
}
let classString = element.className
const nameIndex = classString.indexOf(className)
if (nameIndex === -1) {
classString += '' + className
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
}
element.className = classString
}
/**
* @param {string} type
* @returns {Date}
*/
export function getTime(type) {
if (type === 'start') {
return new Date().getTime() - 3600 * 1000 * 24 * 90
} else {
return new Date(new Date().toDateString())
}
}
/**
* @param {Function} func
* @param {number} wait
* @param {boolean} immediate
* @return {*}
*/
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
/**
* This is just a simple version of deep copy
* Has a lot of edge cases bug
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
* @param {Object} source
* @returns {Object}
*/
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
/**
* @param {Array} arr
* @returns {Array}
*/
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}
/**
* @returns {string}
*/
export function createUniqueString() {
const timestamp = +new Date() + ''
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
return (+(randomNum + timestamp)).toString(32)
}
/**
* Check if an element has a class
* @param {HTMLElement} elm
* @param {string} cls
* @returns {boolean}
*/
export function hasClass(ele, cls) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
/**
* Add class to element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function addClass(ele, cls) {
if (!hasClass(ele, cls)) ele.className += ' ' + cls
}
/**
* Remove class from element
* @param {HTMLElement} elm
* @param {string} cls
*/
export function removeClass(ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
ele.className = ele.className.replace(reg, ' ')
}
}
export function makeMap(str, expectsLowerCase) {
const map = Object.create(null)
const list = str.split(',')
for (let i = 0; i < list.length; i++) {
map[list[i]] = true
}
return expectsLowerCase
? val => map[val.toLowerCase()]
: val => map[val]
}
export const exportDefault = 'export default '
export const beautifierConf = {
html: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'separate',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: false,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
},
js: {
indent_size: '2',
indent_char: ' ',
max_preserve_newlines: '-1',
preserve_newlines: false,
keep_array_indentation: false,
break_chained_methods: false,
indent_scripts: 'normal',
brace_style: 'end-expand',
space_before_conditional: true,
unescape_strings: false,
jslint_happy: true,
end_with_newline: true,
wrap_line_length: '110',
indent_inner_html: true,
comma_first: false,
e4x: true,
indent_empty_lines: true
}
}
// 首字母大小
export function titleCase(str) {
return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
}
// 下划转驼峰
export function camelCase(str) {
return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
}
export function isNumberStr(str) {
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
}
//数字格式化(实现每三位逗号间隔)
export function format_number(n) {
let b = parseInt(n).toString();
let len = b.length;
if (len <= 3) { return b; }
let r = len % 3;
return r > 0 ? b.slice(0, r) + "," + b.slice(r, len).match(/\d{3}/g).join(",") : b.slice(r, len).match(/\d{3}/g).join(",");
}
//生成uuid 码
export function create_uuid(head, number) {
var len = 24,
radix = 24;
if (number) {
len = number;
radix = number;
}
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
var uuid = [];
radix = radix || chars.length;
if (len) {
// Compact form
for (let i = 0; i < len; i++) {
uuid[i] = chars[0 | Math.random() * radix];
}
} else {
// rfc4122, version 4 form
var r;
// rfc4122 requires these characters
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
// Fill in random data. At i==19 set the high bits of clock sequence as
// per rfc4122, sec. 4.1.5
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
if (head) {
return head + uuid.join('')
} else {
return uuid.join('');
}
}
//数组转map data数组 key数组data的某个属性
export function arrayToMap(data, key) {
var map = {};
data = data || [];
if (data.length > 0) {
data.map((item)=>{
if (item[key] && !map[item[key]]) {
map[item[key]] = item;
}
});
}
return map;
}
//将原有数组元素均分形成新数组
export function changeArr(oldArr=[],num=4) {
let newArr=[],len=Math.ceil(oldArr.length/num);
for(var i=0;i<len;i++){
if(i==len-1){
newArr[i]=oldArr.slice(i*num);
// if(newArr[i].length<num){//少于num 拼接到num个
// let arr=new Array(num-newArr[i].length).fill({
// 'key':'',
// 'value':''
// });
// newArr[i]=newArr[i].concat(arr);
// }
}else{
newArr[i]=oldArr.slice(i*num,(i+1)*num);
}
}
return newArr;
}
// 日期排序
export function dateSort(arr,type="asc") {
arr.sort(function(a,b) {
if(type==='asc'){
return Date.parse(a.replace(/-/g,"/"))-Date.parse(b.replace(/-/g,"/"));
}
else{
return Date.parse(b.replace(/-/g,"/"))-Date.parse(a.replace(/-/g,"/"));
}
});
}
// 字符串排空
export function trim(str) {
// console.log('类型',typeof str);
// console.log('str',str);
if(typeof str==='number'){
str+='';
}
return str!==''?str.replace(/(^\s*)|(\s*$)/g, ""):'';
}
\ No newline at end of file \ No newline at end of file
import tableHeight from './tableHeight'
const install = function(Vue) {
// 绑定v-adaptive指令
Vue.directive('tableHeight', tableHeight)
}
if (window.Vue) {
window['tableHeight'] = tableHeight
// eslint-disable-next-line no-undef
Vue.use(install)
}
tableHeight.install = install
export default tableHeight
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
// 设置表格高度
const doResize = async (el, binding, vnode) => {
// 获取表格Dom对象
const { componentInstance: $table } = await vnode
// 获取调用传递过来的数据
const { value } = binding
// if (!$table.height) {
// throw new Error(`el-$table must set the height. Such as height='100px'`)
// }
// console.log($table, '$table$table$table$table')
// 获取距底部距离(用于展示页码等信息)
const bottomOffset = (value && value.bottomOffset) || 30
if (!$table) return
// 计算列表高度并设置
const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset
// $table.layout.setMaxHeight(height)
$table.layout.setHeight(height)
// $table.maxHeight = height
$table.doLayout()
}
export default {
// 初始化设置
bind(el, binding, vnode) {
// 设置resize监听方法
el.resizeListener = async () => {
await doResize(el, binding, vnode)
}
// 绑定监听方法到addResizeListener
addResizeListener(window.document.body, el.resizeListener)
},
// // 绑定默认高度
async inserted(el, binding, vnode) {
await doResize(el, binding, vnode)
},
// // 销毁时设置
unbind(el) {
// 移除resize监听
removeResizeListener(el, el.resizeListener)
}
}
\ No newline at end of file \ No newline at end of file
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
builderOptions: {
productName: "定时器系统",
appId: 'test.com',
win: {
"target": [
"nsis"
],
icon: './src/assets/logo/app.ico',
"requestedExecutionLevel": "requireAdministrator"
},
"nsis": {
"installerIcon": "./src/assets/logo/app.ico",
"uninstallerIcon": "./src/assets/logo/app.ico",
"uninstallDisplayName": "timersys",
// "license": "license.txt",
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
},
}
}
}
\ No newline at end of file \ No newline at end of file
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!