element.onclick = fun与element onclick="fun()"的区别

原本标题是:element.onclick = fun与<element οnclick="fun()">的区别,但是提示含有非法字符。

问题背景:

在看this指向的时候看到了这个,有的时候通过给DOM元素绑定监听事件,来触发某些事件处理函数(如点击当前元素时修改该元素样式等),如果绑定的监听函数里面用到了this,上面两种方式可能会有不同的效果。

试验代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>区别onclick不同绑定方式</title>
</head>
<body>
<div id="div1" style="width: 80px;height: 80px" >DIV1,点击触发更改颜色</div>
<div id="div2" style="width: 80px;height: 80px" onclick="changeBackground2()">DIV2,点击触发更改颜色</div>
<script>
  //this指向当前元素,本质是调用对象里面的方法
  div1.onclick = changeBackground1;
  function changeBackground1(){
    console.log(this);
    this.style.color = '#cc0000';
  }

  //this指向window对象,本质是调用了嵌套函数,先调用onclick(),再调用changeBackground()
  //嵌套函数中的this要么指向window(非严格模式)要么undefined(严格模式)
  function changeBackground2() {
    console.log(this);
    this.style.color = '#cc0000';  //TypeError:color undefined
  }
</script>
</body>
</html>

试验结果:

可以很明显的看到,第一种方式达到了想要的效果,第二种方式直接报错了。通过控制台打印的内容和报的错能够分析出基本原因,this指向不同,第一个指向了div元素(div1),颜色修改成功,第二个指向了window对象,找不到color属性报错。为什么this的指向会不一样?

原因分析:

1.copying

element.onclick = doSomething;

这个实际上是把doSomthing这个函数复制一份给了element的onclick属性,因此触发点击事件时,this指向的是运行时调用它的对象element。原解释:The function is copied in its entirety to the onclick property (which now becomes a method). So if the event handler is executed this refers to the HTML element and its color is changed.

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

2.referring

<element onclick="doSomething()">

这种方式只是让onclick属性指向了函数doSomething,并没有把doSomething函数的内部实现也包含进来,因此当触发点击事件时,只是找到onclick属性,然后告诉你去找doSomething吧,我这没具体实现,找到doSomething后,这时候里面的this已经指向window了,其实是在window下调用的doSomething函数。原解释:

you do not copy the function! Instead, you refer to it, and the difference is crucial. The onclick property does not contain the actual function, but merely a function call:

doSomething();

So it says “Go to doSomething() and execute it.” When we arrive at doSomething()the this keyword once again refers to the global window object and the function returns error messages.

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------         / \           |
|          | go to doSomething() |          |            |
|          | and execute it      | ---- reference to     |
|          -----------------------       function        |
|                                                        |
----------------------------------------------------------

3.两个的区别就是:第一种方式是完全复制;第二种只是指向了函数。

3.1

element.onclick = doSomething;
alert(element.onclick)
//结果如下

function doSomething()
{
	this.style.color = '#cc0000';
}

doSomething是element对象的一个属性(方法),类似下面:

var element = {
            value: "123",
            onclick: function doSomething() {
                console.log(this.value);
            }
        }
element.onclick();//123

3.2 

<element onclick="doSomething()">
alert(element.onclick)
//结果如下

function onclick()
{
	doSomething()
}

doSomething是一个嵌套函数,类似下面:

var element = {
       value: "123",
       onclick: function onclick(){
           doSomething();
       }
    }
function doSomething() {
    console.log(this.value);
}
element.onclick();//undefined

嵌套函数中的this要么指向window(非严格模式),要么undefined(严格模式)

参考链接:https://www.quirksmode.org/js/this.html

https://blog.youkuaiyun.com/u013584334/article/details/81192899

<!-- 任务令扣账管理 --> <template> <div style="overflow: hidden;" class="deduction-config"> <title>{{$route.name}}</title> <h1>{{$route.name}}</h1> <aui-form id="editForm"> <div class="first-field" :class="isShowMore?'showMore':'hideMore'"> <aui-form-item label="工厂全称"> <aui-dropdown size="small" v-model="searchForm.factoryCodeListStr" :op="factoryFullNameOp" ref="factoryDropDown"></aui-dropdown> </aui-form-item> <aui-form-item label="发布状态"> <aui-dropdown size="small" v-model="searchForm.releaseStatusListStr" :op="releaseStatusOp"></aui-dropdown> </aui-form-item> <aui-form-item label="任务令"> <aui-input v-model="searchForm.workOrderNameStr" :op="workOrderNameOp"></aui-input> </aui-form-item> <aui-form-item label="产品型号"> <aui-input v-model="searchForm.prodModelCodeStr" :op="prodModelCodeOp"></aui-input> </aui-form-item> <aui-form-item label="编码"> <aui-input v-model="searchForm.itemCodeStr" :op="itemCodeStrOp"></aui-input> </aui-form-item> <aui-form-item label="扣账时间起" label-width="80px"> <aui-datepicker size="small" v-model="searchForm.startTimeStr" :op="startTimeOp"></aui-datepicker> </aui-form-item> <aui-form-item label="扣账时间止" label-width="80px"> <aui-datepicker size="small" v-model="searchForm.endTimeStr" :op="endTimeOp"></aui-datepicker> </aui-form-item> <aui-form-item label="扣账进度"> <aui-dropdown v-model="searchForm.deductionProgress" :op="deductionProgressOp"></aui-dropdown> </aui-form-item> <aui-form-item class="first-field-btn" label-width="0px"> <i v-show="isTriangleIconShow" class="hae-icon" :class="isShowMore?'icon-down':'icon-left-o'" @click="isShowMore = !isShowMore "></i> <aui-button hue="primary" round="true" size="small" @click="search">查询</aui-button> <aui-button hue="primary" round="true" size="small" @click="reset">重置</aui-button> </aui-form-item> </div> </aui-form> <!-- 导入导出功能 :发布 --> <aui-row class="gridTopImportBtns"> <aui-button size="small" v-show="isReleaseShowFlag" @click="release">发布</aui-button> <i class="hae-icon icon-setup" @click.stop="toolBoxShow = !toolBoxShow"> <div v-show="toolBoxShow"> <aui-fileupload round="true" size="small" :mini-mode="true" class='hae-btn' :op="importop">导入</aui-fileupload> <aui-button round="true" size="small" @click="dynamicExportClick">导出头表数据</aui-button> <aui-button round="true" size="small" @click="recodrDynamicExportClick">导出发料明细</aui-button> <aui-button round="true" size="small" @click="downloadFile" resetTime='5000' style="margin-top: 5px">导入模板下载</aui-button> </div> </i> </aui-row> <!-- 扣账记录页面 --> <aui-dialog-box title="发料明细" v-model="showRecord" width="95%" top="10%" :dragable="true"> <!-- 发料明细导出 --> <aui-row style="margin-top:-4px;"> <aui-button size="small" @click="oneRecodrDynamicExportClick">导出</aui-button> </aui-row> <deduction-record v-bind:selectData="clickRowData" v-if="showRecord" :isChange="isChange"></deduction-record> </aui-dialog-box> <div class="grid-head-config"> <aui-grid ref="grid" :op="gridOp" class="dataArea-field"></aui-grid> </div> </div> </template> <script> import { initHtml } from '../unit/js/html.js' import DeductionRecord from '../unit/wave/WaveWorkOrderDeductionRecord.vue' import { downloadFun } from '../unit/js/fileDownload.js' import { Grid, Form, FormItem, Row, Input, Dropdown, Button, Hae, Pager, DialogBox, Datepicker, Fileupload, $, } from '@aurora/ui3' import { publicFun } from '@/components/unit/js/public.js' export default { components: { AuiButton: Button, AuiDialogBox: DialogBox, AuiGrid: Grid, AuiForm: Form, AuiFormItem: FormItem, AuiRow: Row, AuiInput: Input, AuiDropdown: Dropdown, AuiDatepicker: Datepicker, AuiFileupload: Fileupload, DeductionRecord }, data() { let that = this let curDate = new Date() curDate.setDate(curDate.getDate() - 30) let endDate = new Date() endDate.setDate(curDate.getDate() + 1) return { showRecord: false, // 是否弹出记录页面 clickRowData: {}, // 被双击的数据 saveList: [], // 用于记录刚刚保存的数据,方便打钩 timer: 0, // 定义双击事件的次数 isChange: Math.ceil(publicFun.getSecurityRandom() * 100), // 新版是否显示更多条件 toolBoxShow: false, isShowMore: true, isTriangleIconShow: true, // 发布按钮显示控制 isReleaseShowFlag: false, // 初始化查询对象 searchForm: { releaseStatusListStr: '1', startTimeStr: curDate, endTimeStr: endDate }, // 部门名称 factoryFullNameOp: { id: 'factoryFullNameOp', showClearBtn: true, autoSelect: true, alwaysLoad: true, multi: true, editable: true, textField: 'factoryFullName', valueField: 'factoryCode', placeHolder: '-----请选择-----', dataset: { source: { type: 'post', url: 'services/factoryInfoService/getAllFactoryInfoList' } }, emptyDataMsg: '-----没有数据-----' }, releaseStatusOp: { showClearBtn: true, autoSelect: true, multi: true, editable: true, textField: 'releaseStatusName', valueField: 'releaseStatus', placeHolder: '-----请选择-----', dataset: { value: [ { releaseStatus: 1, releaseStatusName: '待发布' }, { releaseStatus: 2, releaseStatusName: '发布中' }, { releaseStatus: 3, releaseStatusName: '发布成功' }, { releaseStatus: 4, releaseStatusName: '发布失败' } ] }, emptyDataMsg: '-----没有数据-----' }, // 任务令 workOrderNameOp: { id: 'workOrderNameOp', showClearBtn: true, editable: true, placeHolder: '-----可模糊查询-----' }, // 产品型号 prodModelCodeOp: { id: 'prodModelCodeOp', showClearBtn: true, editable: true, placeHolder: '-----可模糊查询-----' }, deductionProgressOp: { showClearBtn: true, autoSelect: false, multi: false, editable: false, textField: 'deductionProgressName', valueField: 'deductionProgress', placeHolder: '-----请选择-----', dataset: { value: [ { deductionProgress: 'all', deductionProgressName: '全部扣账' }, { deductionProgress: 'part', deductionProgressName: '部分扣账' } ] }, emptyDataMsg: '-----没有数据-----' }, // 编码 itemCodeStrOp: { id: 'itemCodeStrOp', showClearBtn: true, editable: true, placeHolder: '-----可模糊查询-----' }, // 扣账时间(起) startTimeOp: { id: 'startTimeOp', dateFormat: 'yyyy-MM-dd', dbDateFormat: 'yyyy-MM-dd 00:00:00', valueField: 'startTimeStr', placeHolder: '-----请选择-----' }, // 扣账时间(止) endTimeOp: { id: 'endTimeOp', dateFormat: 'yyyy-MM-dd', dbDateFormat: 'yyyy-MM-dd 23:59:59', valueField: 'endTimeStr', placeHolder: '-----请选择-----' }, // 导入控件 importop: {}, gridOp: { name: 'baseGrid', // name值,配置个性化customSetting/customized id: 'deductionGrid', showTip: true, tipOp: { type: 'normal' }, customized: true, // 是否打开隐藏列功能(动态列不能打开) allowFreeze: true, // 冻结选项 allowResize: true, allowSort: true, percentWidth: false, showStatus: false, showPager: true, // 展示分页 autoLoad: true, // 自动加载 showSeq: false, // 展示序号 cssName: 'text-center', editMode: true, pagerOp: { mode: 'number', pageSizes: [40, 50, 100], pageVO: { curPage: 1, pageSize: 40 } }, dataset: { value: [] }, operation: { insert: { mode: 'top' // 每次新增数行时,都在表格首行的上方依次添加(可选:top | bottom) }, save: { onBeforeSave(ajax, optOp, grid) { let changeList = grid.data let addList = grid.data.items2Create let updateList = grid.data.items2Update if (addList.length === 0 && updateList.length === 0) { Hae.topBox('未选择要保存的数据', 'error', false, 2000) return false } // 校验新增数据字段是否通过 if (addList.length > 0) { let checkResult = that.checkSaveData(addList) if (!checkResult) { return false } } Hae.ajax({ url: 'services/waveWorkorderDeductionService/saveHeadTableData', type: 'post', async: false, data: { addList, updateList }, dataType: 'text', success(result) { if (result === 'SUCCESS') { Hae.topBox('保存成功!', 'successful', false, 2000) let gridWidger = that.$refs.grid.widget that.search() that.saveList = that.saveList.concat(addList) that.saveList = that.saveList.concat(updateList) } else { Hae.topBox(result, 'error', false, 5000) } } }) return false } }, delete: { icon: 'hae-icon icon-del', text: '删除', onClick: this.delete } }, columns: [ { multi: true, columnType: 'select' }, { field: 'wbrdId', header: 'id', hidden: true, editable: false }, { field: 'factoryCode', header: '工厂全称', width: '130', cssName: 'text-center', headCss: 'text-center', textField: 'factoryFullName', valueField: 'factoryCode', freezable: false, editable: false }, { field: 'workorderName', header: '任务令', width: '110', editable: true, cssName: 'text-center', headCss: 'text-center', alwaysLoad: true, freezable: false, rule: { required: true } }, { field: 'prodModelCode', header: '产品型号', editable: false, width: '120', cssName: 'text-center', headCss: 'text-center', freezable: false }, { field: 'itemCode', header: '编码', width: '110', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false }, { field: 'workorderQty', header: '任务令数量', editable: false, width: '110', cssName: 'text-center', headCss: 'text-center', freezable: false }, { field: 'planStartTime', header: '计划开工时间', width: '120', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false, type: 'dateTime' }, { field: 'planOnlineTime', header: '计划上线时间', width: '120', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false, type: 'dateTime' }, { field: 'planCompleteTime', header: '计划完工时间', width: '120', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false, type: 'dateTime' }, { field: 'actStartDate', header: '实际开工时间', width: '120', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false, type: 'dateTime' }, { field: 'deductionDate', header: '扣账时间', editable: true, width: '120', cssName: 'text-center', headCss: 'text-center', freezable: false, type: 'dateTime', rule: { required: true }, editorOp: { conf: { onShow: () => { setTimeout(() => { $('.hae-popup .input-editable input').removeAttr('readonly') }, 500) } } } }, { field: 'batchNo', header: '扣账批次号', editable: true, cssName: 'text-center', headCss: 'text-center', freezable: false, width: '100', type: 'integer', rule: { required: true, custom: { regular: function(value, ruleOp) { let reg = /^[1-9]\d{0,7}$/ return reg.test(value) }, msg: '请输入不超过8位数字的正整数', priority: 1000 } } }, { field: 'ddiQty', header: '本次扣账数量', editable: true, cssName: 'text-center', headCss: 'text-center', freezable: false, width: '120', type: 'integer', rule: { required: false, custom: { regular: function(value, ruleOp) { if (publicFun.isNull(value)) { return true } let reg = /^[1-9]\d*$/ return reg.test(value) }, msg: '请输入正整数', priority: 1000 } } }, { field: 'ddiQtyHis', header: '历史已扣账数量', editable: false, width: '120', cssName: 'text-center', headCss: 'text-center', freezable: false }, { header: '发布状态', field: 'releaseStatusName', width: '110', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false }, { header: '生产指令进度', field: 'ppoProgressCode', width: '110', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false }, { field: 'errorMessage', header: '失败原因', width: '160', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false, showTip: true }, { field: 'releaseBy', header: '发布用户', width: '120', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false }, { field: 'releaseDate', header: '发布时间', width: '120', type: 'dateTime', editable: false, cssName: 'text-center', headCss: 'text-center', freezable: false } ], // 双击事件 onRowClick(rowData, tr, e) { // 只能点击已发布的数据看发料记录 if (rowData.releaseStatusName === '发布成功') { that.timer++ if (that.timer >= 2) { // 校验是否有发料明细数据 let haveDataInSystem = true Hae.ajax({ url: 'services/waveWorkorderDeductionService/getReleaseRecordList/page/40/1', type: 'post', async: false, data: { workOrderNameStr: rowData.workorderName }, dataType: 'text', success(result) { let resultVo = JSON.parse(result) if (resultVo.result === undefined || resultVo.result.length == '0') { Hae.topBox(`任务令:${rowData.workorderName} 暂无发料数据`, 'error', false, 2000) haveDataInSystem = false return false } else { haveDataInSystem = true } } }) if (haveDataInSystem) { that.timer = 0 // 清空计数器 that.showRecord = true that.clickRowData = rowData } } setTimeout(() => { that.timer = 0 }, 300) } }, // 编辑完后,自动计算数据 onAfterEdit(value, rowData, td, col, oldValue) { let { widget } = that.$refs.grid let row = widget.getCellRow(td) // 单元格所在行jQuery对象 // 1.根据任务令带出相关数据信息 if (col.header === '任务令') { if (publicFun.isNull(value)) { Hae.topBox('任务令必填', 'error', false, 2000) return false } let responseStr = '' Hae.ajax({ url: 'services/waveWorkorderDeductionService/getWorkOrderInfoByWorkOrderName', type: 'post', async: false, data: { workorderName: value }, dataType: 'text', success(result) { let response = JSON.parse(result) if (result === undefined || publicFun.isNull(response.workorderName)) { Hae.topBox(`任务令:[${value}]信息不存在`, 'error', false, 2000) rowData.fieldCheckFlag = false return false } else if (publicFun.isNull(response.factoryCode)) { rowData.fieldCheckFlag = false Hae.topBox(`用户暂无任务令:${value} 对应的工厂权限`, 'error', false, 2000) return false } else { rowData.fieldCheckFlag = true responseStr = result } } }) if (responseStr !== undefined && publicFun.isNoNull(responseStr)) { let response = JSON.parse(responseStr) // 根据任务令反查工厂全称 that.setColValueNew(rowData, td, 'factoryCode', response.factoryCode, response.factoryFullName) // 根据任务令反查产品型号 that.setColValueNew(rowData, td, 'prodModelCode', response.prodModelCode, response.prodModelCode) // 根据任务令反查编码 that.setColValueNew(rowData, td, 'itemCode', response.itemCode, response.itemCode) // 根据任务令反任务令数量 that.setColValueNew(rowData, td, 'workorderQty', response.workorderQty, response.workorderQty) // 根据任务令反查历史已扣账数量 that.setColValueNew(rowData, td, 'ddiQtyHis', response.ddiQtyHis, response.ddiQtyHis) // 根据任务令反查计划开工时间 that.setColValueNew(rowData, td, 'planStartTime', Hae.Date.format(response.planStartTime, 'yyyy-MM-dd hh:mm'), Hae.Date.format(response.planStartTime, 'yyyy-MM-dd hh:mm')) // 根据任务令反查计划上线时间 that.setColValueNew(rowData, td, 'planOnlineTime', Hae.Date.format(response.planOnlineTime, 'yyyy-MM-dd hh:mm'), Hae.Date.format(response.planOnlineTime, 'yyyy-MM-dd hh:mm')) // 根据任务令反查计划完工时间 that.setColValueNew(rowData, td, 'planCompleteTime', Hae.Date.format(response.planCompleteTime, 'yyyy-MM-dd hh:mm'), Hae.Date.format(response.planCompleteTime, 'yyyy-MM-dd hh:mm')) } } }, onBeforeEdit: (cellValue, rowData, td, col) => { let { releaseStatusName } = rowData if (releaseStatusName == '待发布' || releaseStatusName == '发布失败') { // 存量数据只能修改待发布、发布失败的数据 if (col.header !== '扣账时间' && col.header !== '本次扣账数量') { return false } } else if (publicFun.isNoNull(releaseStatusName)) { return false } }, onRenderRow(tr, rowData, rowIdx) { // 表格中的数据加载完成后触发的事件 if (rowData.releaseStatusName === '发布失败') { tr.css('background', 'rgb(241, 127, 123)') } else if (rowData.releaseStatusName === '发布成功') { tr.css('background', 'rgb(214, 252, 214)') } else if (rowData.releaseStatusName === '发布中') { tr.css('background', 'rgb(247, 214, 152)') } }, onBeforeBindData: (op, data, widget) => { let that = this // 页面保存的数据自动打钩 if (that.saveList.length > 0) { let recodeList = that.saveList for (let i = 0; i < data.length; i++) { recodeList.forEach(item => { if (item.workorderName === data[i].workorderName) { widget.setRecordSelected(data[i]) } }) } } }, onSelected(tr, rowData, selected) { // 取消选择的数据从saveList中删除 if (!selected && publicFun.isNoNull(that.saveList)) { for (let a = that.saveList.length - 1; a >= 0; a--) { let row = that.saveList[a] if (row.workorderName === rowData.workorderName && !selected) { that.saveList.splice(a, 1) } } } }, onBeforeSelect(tr, rowData, selected) { if (rowData.releaseStatusName == '发布失败' || rowData.releaseStatusName == '待发布' || publicFun.isNull(rowData.releaseStatusName) ) { return true } else { return false } }, // 组件渲染完成时执行的事件。 onRender(grid) { let fromDom = $('#editForm') initHtml.gridAutoHeight(fromDom) } } } }, created() { let that = this that.importop = publicFun.getImportOp('tmas.waveWorkOrderDeduction', Hae) }, mounted() { // 注册全局单机事件隐藏导入按钮栏 document.addEventListener('click', e => { setTimeout(() => { if ( !$(e.target).hasClass('icon-setup') || !$(e.target).hasClass('gridTopBtnDiv') ) { this.toolBoxShow = false // 收起导入导出按钮 } }, 300) }) this.initUserReleaseMenu() // 判断是否有更多条件显示隐藏小三角 let editFormHeight = $('.first-field').outerHeight() this.isTriangleIconShow = !(editFormHeight < 38) this.isShowMore = false }, methods: { // 判断用户是否有发布权限 initUserReleaseMenu() { let that = this Hae.ajax({ url: 'services/waveWorkorderDeductionService/getReleaseUserInfo', type: 'post', async: false, data: {}, dataType: 'json', success(result) { if (publicFun.isNoNull(result)) { that.isReleaseShowFlag = true } } }) }, setSelect(rowData, a) { let { widget } = this.$refs.grid let row = widget.getRow(rowData) widget.rowSelected(row, true, false) }, // 设置单元格值New setColValueNew(rowData, td, fieldName, valueField, textField) { let { widget } = this.$refs.grid let row = widget.getCellRow(td) // 根据编辑的单元格所在行jQuery对象 let rowCell = widget.getRowCell(row, fieldName) // 获取FieldName需要被设置数据的表格对象 rowData[fieldName] = valueField // 表格数据赋值 rowCell.children().html(textField) // 修改页面表格内容 rowCell.attr('val', textField) }, // 校验搜索 search() { Hae.validForm($('#editForm'), result => { if (result === false) { return false } else { // 查询数据 this.findClick() return true } }) }, // 保存数据校验 checkSaveData(list) { let checkFlag = true list.forEach(element => { if (!element.fieldCheckFlag) { Hae.topBox(`任务令:${element.workorderName} 不存在!`, 'error', false, 2000) checkFlag = false return false } if (publicFun.isNull(element.deductionDate)) { Hae.topBox('页面新增数据时“扣账时间”必填', 'error', false, 2000) checkFlag = false return false } if (publicFun.isNoNull(element.ddiQty) && Number(element.ddiQty) > Number(element.workorderQty)) { Hae.topBox('本次扣账数量不能大于任务令数量', 'error', false, 2000) checkFlag = false return false } }) return checkFlag }, // 判断是否正数 reset() { let curDate = new Date() curDate.setDate(curDate.getDate() - 30) let endDate = new Date() endDate.setDate(curDate.getDate() + 1) this.searchForm = { factoryCodeListStr: '', releaseStatusListStr: '1', workOrderNameStr: '', prodModelCodeStr: '', itemCodeStr: '', startTimeStr: curDate, endTimeStr: endDate, deductionProgress: '' } }, findClick() { let that = this let { searchForm } = that let newcol = { dataset: { source: { data: searchForm, // 带条件的查询 type: 'post', url: 'services/waveWorkorderDeductionService/getHeadTableList/page/{{pageSize}}/{{curPage}}' } } } that.$refs.grid.widget.reInit(newcol) }, delete() { let that = this let { widget } = this.$refs.grid let selectList = widget.getSelectedRecords() if (selectList.length === 0) { Hae.topBox('未选择数据,或未选择有效数据', 'warning', false, 2000) return } selectList.forEach(item => { if (item.releaseStatusName === '发布成功' || item.releaseStatusName === '发布中') { Hae.topBox(`任务令:${item.workorderName} ${item.releaseStatusName},不允许删除`, 'warning', false, 2000) } }) Hae.confirm('是否删除该数据?', function(bool) { if (bool) { // 删除新增但未保存的数据 let unSaveList = [] for (let i = selectList.length - 1; i >= 0; i--) { if (selectList[i]._$index < 0) { let currRow = widget.getRowData(selectList[i]._$index) let unSaveDataJQuery = widget.getRow(currRow) unSaveList.push(unSaveDataJQuery) selectList.splice(i, 1) } } if (unSaveList.length > 0) { unSaveList.forEach(item => { widget.removeRows(item) }) if (!selectList.length > 0) { return false } } // 删除数据已存在的数据 Hae.ajax({ url: 'services/waveWorkorderDeductionService/deleteHeadTableData', type: 'post', async: false, data: { list: selectList }, dataType: 'text', success(result) { if (result === 'SUCCESS') { Hae.topBox('删除成功!', 'successful', false, 2000) that.$refs.grid.widget.reInit() } else { Hae.topBox(result, 'error', false, 2000) } } }) } }) }, release() { let that = this let { widget } = this.$refs.grid let selectList = widget.getSelectedRecords() if (selectList.length === 0) { Hae.topBox('未选择数据,或未选择有效数据', 'warning', false, 2000) return } let changeList = widget.getChangedRecords() if (publicFun.isNoNull(changeList)) { let unSaveList = [] unSaveList = unSaveList.concat(changeList.insertedRecords) unSaveList = unSaveList.concat(changeList.updatedRecords) // 未保存的数据不能释放 let unSave = '' if (unSaveList != undefined && unSaveList.length > 0) { unSaveList.forEach(item => { let key = item.workorderName selectList.forEach(selectVo => { if (key === selectVo.workorderName) { unSave += `${key};` } }) }) } if (publicFun.isNoNull(unSave)) { Hae.topBox(`任务令:${unSave.substring(0, unSave.length - 1)} 未保存,不能发布!`, 'error', false, 2000) return false } } let releaseStatus = true let sendID = [] selectList.forEach(item => { // 只能发布待发布、发布失败的数据 if (item.releaseStatusName !== '待发布' && item.releaseStatusName !== '发布失败') { Hae.topBox(`任务令:${item.workorderName}当前状态不能发布`, 'warning', false, 2000) releaseStatus = false } else { sendID.push(item.wbrdId) } }) if (!releaseStatus) { return true } let gridWidger = that.$refs.grid.widget Hae.confirm('是否提交已选择数据', (bool) => { if (bool) { Hae.ajax({ url: 'services/waveWorkorderDeductionService/publishData', data: { ids: sendID }, type: 'post', dataType: 'text', success: (result) => { if (result === 'SUCCESS') { Hae.topBox('任务令发布成功', 'successful', false, 2000) publicFun.jumpFormerPage(gridWidger) // 保存成功的数据从saveList中移除 that.saveList = [] } else { Hae.topBox(result, 'error', false, 2000) publicFun.jumpFormerPage(gridWidger) } } }) } }) }, // 导出 dynamicExportClick() { Hae.validForm($('#editForm'), result => { if (result === false) { Hae.topBox('必填查询条件不能为空', 'error', false, 2000) return false } else { // 查询数据 this.export() return true } }) }, export() { let { searchForm } = this Hae.ajax({ url: 'services/exportFileUtilService/exportWaveWorkOrderDeduction', data: searchForm, type: 'post', success(data) { Hae.confirm( '导出任务已开始,你可以进入[我的导入导出>导出查询]中查看任务状态并下载导出文件!', function(bool) { if (bool) { window.open('#/ListExport') } } ) } }) }, // 发料明细单条任务令导出 oneRecodrDynamicExportClick() { let clickDatExport = this.clickRowData let searchFormExport = this.searchForm searchFormExport.workOrderNameStr = clickDatExport.workorderName Hae.ajax({ url: 'services/exportFileUtilService/exportWaveWorkOrderDeductionRecord', data: searchFormExport, type: 'post', success(data) { Hae.confirm( '导出任务已开始,你可以进入[我的导入导出>导出查询]中查看任务状态并下载导出文件!', function(bool) { if (bool) { window.open('#/ListExport') } } ) } }) }, recodrDynamicExportClick() { let { searchForm } = this Hae.confirm('为避免大批量数据导出,是否已精确查询条件?', (bool) => { if (bool) { Hae.validForm($('#editForm'), result => { if (result) { Hae.ajax({ url: 'services/exportFileUtilService/exportWaveWorkOrderDeductionRecord', data: searchForm, type: 'post', success(data) { Hae.confirm( '导出任务已开始,你可以进入[我的导入导出>导出查询]中查看任务状态并下载导出文件!', function(bool) { if (bool) { window.open('#/ListExport') } } ) } }) } else { Hae.topBox('必填查询条件不能为空', 'error', false, 2000) } }) } }) }, // 下载导入模板 downloadFile() { downloadFun.getID('任务令扣账管理', '导入') // 功能模块,类型 } } } </script> <style scoped src='./../unit/css/newModelPublic.css'> </style> <style scoped> .gridTopImportBtns { width: 200px; position: absolute; top: 111px; left: 285px; /**6px 84px 174px 266px**/ z-index: 30; } .deduction-config { height: 100%; } .grid-head-config { height: 100%; } .deduction-config :deep(#deductionGrid .grid-body) { height: calc(100% - 48px) !important; } #deductionGrid { height: calc(100% - 120px); } .deduction-config :deep(#deductionGrid .hae-grid-body) { height: 100%; } </style> 在查询列表“历史已扣账数量”“发布状态”之间添加“是否冻结”、“冻结时间”列,可编辑, (1)是否冻结,下拉单选框,非必填,合法值只有“Y”和“N”; (2)冻结时间,日期时间选择框,非必填;
最新发布
09-06
package com.example.kanban import android.annotation.SuppressLint import android.graphics.Bitmap import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log import android.view.WindowManager import android.webkit.* import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import kotlin.math.max class MainActivity : AppCompatActivity() { private lateinit var webView: WebView private val handler = Handler(Looper.getMainLooper()) private var timeoutRunnable: Runnable? = null private var isMonitoringRequests = false private var pendingRequests = 0 private var lastRequestTime: Long = 0 @SuppressLint("SetJavaScriptEnabled") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 启用硬件加速 window.setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED ) webView = findViewById<WebView>(R.id.webView).apply { // 基础设置 settings.apply { databaseEnabled = true setGeolocationEnabled(true) javaScriptCanOpenWindowsAutomatically = true loadsImagesAutomatically = true javaScriptEnabled = true domStorageEnabled = true allowContentAccess = true allowFileAccess = true setSupportZoom(true) builtInZoomControls = true displayZoomControls = false cacheMode = WebSettings.LOAD_DEFAULT useWideViewPort = true loadWithOverviewMode = true // 提高渲染优先级 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE } // 解决ORB和安全问题 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { allowUniversalAccessFromFileURLs = true } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { safeBrowsingEnabled = false } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE } } // 添加JavaScript接口 addJavascriptInterface(WebAppInterface(), "Android") webViewClient = object : WebViewClient() { override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) pendingRequests = 0 isMonitoringRequests = true startRequestMonitoring() } override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) // 延迟注入脚本,确保页面完全初始化 handler.postDelayed({ injectRequestMonitoringScript() // 添加检查机制,防止某些请求未被监控到 webView.evaluateJavascript(""" setTimeout(function() { if (window.requestTracker && window.requestTracker.activeRequests.size > 0) { Android.requestStarted('unmonitored_requests'); setTimeout(function() { Android.requestCompleted('unmonitored_requests', true); }, 5000); } }, 3000); """, null) }, 500) } override fun onReceivedError( view: WebView, request: WebResourceRequest, error: WebResourceError ) { super.onReceivedError(view, request, error) handleWebError("加载错误: ${error.description}") } override fun onReceivedHttpError( view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse ) { super.onReceivedHttpError(view, request, errorResponse) handleWebError("HTTP错误: ${errorResponse.statusCode}") } // 捕获URL变化 override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { if (request != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Log.d("WebView", "Loading URL: ${request.url}") // 这里可以添加处理URL变化的逻辑 } return super.shouldOverrideUrlLoading(view, request) } } } loadUrlWithRetry( "http://192.168.16.69:10011/#/show-draw?v=T1AnDCLWYo1fi0Ngzg5rW47U9W5joYwQ2F8aK1ENVaErtPWTiOlCN5y63ZsMTQObmlqGVKjmUceKYcvRfg%2BSXA%3D%3D&satoken=9a4d0de4-927d-41a0-b5c5-4440a0ddaae0", maxRetries = 2 ) } private fun startRequestMonitoring() { isMonitoringRequests = true pendingRequests = 0 timeoutRunnable?.let { handler.removeCallbacks(it) } timeoutRunnable = Runnable { if (isMonitoringRequests && pendingRequests > 0) { handleWebError("请求超时,仍有 $pendingRequests 个请求未完成") } }.also { handler.postDelayed(it, 30000) // 30秒超时 } } private fun injectRequestMonitoringScript() { webView.evaluateJavascript(""" (function() { if (!window.requestTracker) { window.requestTracker = { activeRequests: new Set(), completedRequests: new Set(), trackRequest: function(id) { this.activeRequests.add(id); Android.requestStarted(id); return function(success, id) { this.activeRequests.delete(id); this.completedRequests.add(id); Android.requestCompleted(id, success); // 检查是否所有请求都完成了 if (this.activeRequests.size === 0) { Android.allRequestsCompleted(); } }.bind(this); }, getActiveRequests: function() { return Array.from(this.activeRequests); } }; // 增强的XMLHttpRequest监控 var originalXHROpen = XMLHttpRequest.prototype.open; var originalXHRSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url, async) { this._requestId = 'xhr_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); this._tracker = window.requestTracker.trackRequest(this._requestId); return originalXHROpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(data) { var self = this; this.addEventListener('readystatechange', function() { if (self.readyState === 4) { self._tracker(self.status >= 200 && self.status < 400, self._requestId); } }); return originalXHRSend.apply(this, arguments); }; // 增强的fetch监控 var originalFetch = window.fetch; window.fetch = function(input, init) { var requestId = 'fetch_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); var tracker = window.requestTracker.trackRequest(requestId); return originalFetch(input, init) .then(function(response) { tracker(response.ok, requestId); return response; }) .catch(function(error) { tracker(false, requestId); throw error; }); }; // 更全面的资源加载监控 function monitorResourceLoading() { // 监控静态资源 ['img', 'script', 'link'].forEach(function(tagName) { var elements = document.getElementsByTagName(tagName); Array.from(elements).forEach(function(el) { if ((tagName === 'link' && el.rel !== 'stylesheet') || (el.complete || el.readyState === 'complete')) { return; } var requestId = tagName + '_' + (el.src || el.href); var tracker = window.requestTracker.trackRequest(requestId); el.addEventListener('load', function() { tracker(true, requestId); }); el.addEventListener('error', function() { tracker(false, requestId); }); }); }); // 监控动态添加的元素 new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { mutation.addedNodes.forEach(function(node) { if (node.nodeType === 1) { // ELEMENT_NODE if (node.tagName === 'IMG') { var requestId = 'img_' + node.src; var tracker = window.requestTracker.trackRequest(requestId); node.addEventListener('load', function() { tracker(true, requestId); }); node.addEventListener('error', function() { tracker(false, requestId); }); } else if (node.tagName === 'SCRIPT' && node.src) { var requestId = 'script_' + node.src; var tracker = window.requestTracker.trackRequest(requestId); node.addEventListener('load', function() { tracker(true, requestId); }); node.addEventListener('error', function() { tracker(false, requestId); }); } else if (node.tagName === 'LINK' && node.rel === 'stylesheet') { var requestId = 'css_' + node.href; var tracker = window.requestTracker.trackRequest(requestId); node.addEventListener('load', function() { tracker(true, requestId); }); node.addEventListener('error', function() { tracker(false, requestId); }); } } }); }); }).observe(document, { childList: true, subtree: true }); } // 延迟执行监控,确保所有初始请求都被捕获 setTimeout(monitorResourceLoading, 500); } })(); """.trimIndent(), null) } private fun loadUrlWithRetry(url: String, maxRetries: Int, currentRetry: Int = 0) { if (currentRetry > 0) { Toast.makeText(this, "正在重试 ($currentRetry/$maxRetries)...", Toast.LENGTH_SHORT).show() } webView.loadUrl(url) handler.postDelayed({ if (webView.progress < 90 && currentRetry < maxRetries) { loadUrlWithRetry(url, maxRetries, currentRetry + 1) } }, 10000) // 10秒后检查 } private fun handleWebError(message: String) { runOnUiThread { webView.loadData(""" <html> <body style='font-family:sans-serif;padding:20px;text-align:center;'> <h3 style='color:#d32f2f;'>加载遇到问题</h3> <p>$message</p> <button onclick="window.location.reload()" style='padding:10px 20px;background:#4285f4;color:white; border:none;border-radius:5px;margin-top:20px;'> 重新加载 </button> </body> </html> """, "text/html", "UTF-8") } } inner class WebAppInterface { @JavascriptInterface fun requestStarted(id: String) { pendingRequests++ lastRequestTime = System.currentTimeMillis() Log.d("WebView", "Request started ($id). Pending: $pendingRequests") // 延长超时时间 timeoutRunnable?.let { handler.removeCallbacks(it) } timeoutRunnable = Runnable { if (isMonitoringRequests && pendingRequests > 0) { handleWebError("请求超时,仍有 $pendingRequests 个请求未完成") } }.also { handler.postDelayed(it, 60000) // 延长到60秒 } } @JavascriptInterface fun requestCompleted(id: String, success: Boolean) { pendingRequests = max(0, pendingRequests - 1) Log.d("WebView", "Request completed ($id, success: $success). Pending: $pendingRequests") if (!success) { Log.e("WebView", "Request failed: $id") } } @JavascriptInterface fun allRequestsCompleted() { handler.post { isMonitoringRequests = false timeoutRunnable?.let { handler.removeCallbacks(it) } Log.d("WebView", "所有请求已完成") // 延迟执行,确保DOM更新完成 handler.postDelayed({ webView.evaluateJavascript(""" if (window.onAllRequestsComplete) { window.onAllRequestsComplete(); } """, null) }, 300) } } } private fun updatePageContent() { // 方法1: 执行JavaScript通知页面数据已更新 webView.evaluateJavascript(""" (function() { // 检查页面是否有更新数据的函数 if (window.onDataUpdated) { window.onDataUpdated(); } else { // 如果没有特定函数,尝试重新加载页面元素 document.dispatchEvent(new Event('dataUpdated')); } })(); """.trimIndent(), null) // 方法2: 可选的页面刷新(谨慎使用,可能导致无限循环) // webView.reload(); } override fun onDestroy() { handler.removeCallbacksAndMessages(null) webView.stopLoading() webView.destroy() super.onDestroy() } }ajax请求没有加载完成,页面就完成不加载了,只加载了一部分
05-14
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值