【VUE】el-upload组件的on-success在jsx里不触发

本文记录了一位开发者在JSX中遇到on-success事件处理不触发的问题及解决方案。关键在于,on-success应该作为组件的属性在render方法中指定,而非通过on绑定的事件。这一常见错误可能导致前端上传功能失效,正确理解并应用props中的on-success属性是确保功能正常的关键。

以上这种写法是不生效的,但是before-upload会生效!!!

正确的写法是:

大佬的文章解决了我折腾很久的问题,在JSX里写on-success不触发的问题,特此记录。

根本问题在于,on-success是按照属性来写的,需要写在render的props中,而不是on绑定的事件中。

<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px" > <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-form-item> </el-form> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['speechAnalysis:keywords:add']" >新增关键词</el-button > </el-col> <el-col :span="1.5"> <el-button type="success" plain icon="el-icon-upload" size="mini" @click="importDialogVisible = true" >批量导入</el-button > </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="!selectedKeywords.length" @click="batchRemoveKeywords" >批量删除</el-button > </el-col> </el-row> <!-- 关键词表格展示 --> <el-table v-loading="loading" :data="keywordsData" @selection-change="handleSelectionChange" > <el-table-column type="selection" width="55" align="center" ></el-table-column> <el-table-column label="序号" type="index" width="60" align="center" :index="indexMethod" ></el-table-column> <el-table-column label="关键词内容" prop="content" align="center"> <template slot-scope="{ row, $index }"> <el-input v-model="row.content" size="mini" @blur="handleKeywordEdit($index)" clearable /> </template> </el-table-column> <el-table-column label="操作" width="150" align="center" class-name="small-padding fixed-width" > <template slot-scope="{ row }"> <!-- 只解构 row --> <el-button size="mini" type="text" icon="el-icon-delete" @click="removeKeyword(row)" >删除</el-button > </template> </el-table-column> </el-table> <!-- 分页控件 --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.pageNum" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="allKeywords.length" class="pagination-container" > </el-pagination> <!-- 批量导入对话框 --> <el-dialog title="批量导入关键词" :visible.sync="importDialogVisible" width="50%" > <el-alert title="支持以下格式导入:" type="info" style="margin-bottom: 15px" > <div>1. 每行一个关键词</div> <div>2. 用 | 竖线分隔的关键词</div> </el-alert> <el-input type="textarea" :rows="8" placeholder="请输入关键词,每行一个或用|分隔" v-model="importKeywordsText" /> <div slot="footer" class="dialog-footer"> <el-button @click="importDialogVisible = false">取 消</el-button> <el-button type="primary" @click="handleImportKeywords" >确 定</el-button > </div> </el-dialog> <!-- 添加关键词对话框 --> <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="30%"> <el-form ref="keywordForm" :model="keywordForm" :rules="keywordRules"> <el-form-item label="关键词内容" prop="content"> <el-input v-model="keywordForm.content" placeholder="请输入关键词" /> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="submitKeywordForm">确 定</el-button> </div> </el-dialog> </div> </template> <script> import { listKeywords, updateKeywords, addKeywords, } from "@/api/speechAnalysis/keywords"; export default { name: "KeywordsManager", data() { return { // 加载状态 loading: true, // 查询参数 queryParams: { pageNum: 1, pageSize: 10, }, // 显示搜索条件 showSearch: true, // 当前页关键词数据 keywordsData: [], // 所有关键词数据 allKeywords: [], // 原始数据 originalKeywords: null, // 选中的关键词索引 selectedKeywords: [], // 导入对话框 importDialogVisible: false, importKeywordsText: "", // 添加/编辑对话框 dialogVisible: false, dialogTitle: "", keywordForm: { content: "", }, keywordRules: { content: [ { required: true, message: "关键词能为空", trigger: "blur" }, { max: 50, message: "长度能超过50个字符", trigger: "blur" }, ], }, // 操作类型: add/edit operateType: "", }; }, created() { this.getKeywordsList(); }, methods: { // 获取关键词列表 getKeywordsList() { this.loading = true; listKeywords(this.queryParams).then((response) => { if (response.rows && response.rows.length > 0) { this.originalKeywords = response.rows[0]; // 处理关键词数据 this.allKeywords = this.originalKeywords.keywordsContent ? this.originalKeywords.keywordsContent .split("|") .filter((item) => item.trim()) .map((item) => ({ content: item.trim() })) : []; this.updateDisplayData(); } else { // 如果没有数据,初始化一个空对象 this.originalKeywords = { guid: null, keywordsContent: "", }; this.allKeywords = []; this.keywordsData = []; } this.loading = false; }); }, // 更新显示数据 updateDisplayData() { const start = (this.queryParams.pageNum - 1) * this.queryParams.pageSize; const end = start + this.queryParams.pageSize; this.keywordsData = this.allKeywords.slice(start, end); }, // 保存关键词到后端 saveKeywords() { // 合并关键词数组为字符串 const keywordsStr = this.allKeywords .map((item) => item.content) .join("|"); this.originalKeywords.keywordsContent = keywordsStr; const request = this.originalKeywords.guid ? updateKeywords(this.originalKeywords) : addKeywords(this.originalKeywords); return request.then(() => { this.$message.success("保存成功"); this.getKeywordsList(); }); }, // 添加关键词 handleAdd() { this.dialogTitle = "添加关键词"; this.operateType = "add"; this.keywordForm.content = ""; this.dialogVisible = true; this.$nextTick(() => { this.$refs.keywordForm?.clearValidate(); }); }, // 提交关键词表单 submitKeywordForm() { this.$refs.keywordForm.validate((valid) => { if (valid) { // 去掉首尾空格 const trimmedContent = this.keywordForm.content.trim(); if (this.operateType === "add") { // 检查是否已存在相同关键词 if ( this.allKeywords.some((item) => item.content === trimmedContent) ) { this.$message.warning("该关键词已存在"); return; } this.allKeywords.push({ content: trimmedContent }); } this.saveKeywords().then(() => { this.dialogVisible = false; this.updateDisplayData(); }); } }); }, // 编辑关键词 handleKeywordEdit(index) { const row = this.keywordsData[index]; if (!row || !row.content) { this.$message.warning("关键词能为空"); // 如果内容为空,恢复原值或删除该行 if (row) { row.content = ""; } return; } // 去掉首尾空格 row.content = row.content.trim(); if (row.content === "") { this.$message.warning("关键词能为空"); return; } // 计算在全量数据中的实际索引 const globalIndex = (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index; // 检查是否已存在相同关键词 const exists = this.allKeywords.some( (item, i) => item.content === row.content && i !== globalIndex ); if (exists) { this.$message.warning("该关键词已存在"); return; } // 更新全量数据 this.allKeywords[globalIndex].content = row.content; this.saveKeywords(); }, // 删除关键词 removeKeyword(index) { this.$confirm("确认删除该关键词吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }).then(() => { // 计算在全量数据中的实际索引 const globalIndex = (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index; this.allKeywords.splice(globalIndex, 1); this.saveKeywords().then(() => { this.updateDisplayData(); }); }); }, // 批量删除关键词 batchRemoveKeywords() { if (this.selectedKeywords.length === 0) { this.$message.warning("请选择要删除的关键词"); return; } this.$confirm( `确认删除选中的 ${this.selectedKeywords.length} 个关键词吗?`, "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", } ).then(() => { // 从大到小排序索引,避免删除时索引变化 const sortedIndexes = [...this.selectedKeywords].sort((a, b) => b - a); // 计算在全量数据中的实际索引并删除 const pageStart = (this.queryParams.pageNum - 1) * this.queryParams.pageSize; sortedIndexes.forEach(index => { const globalIndex = pageStart + index; this.allKeywords.splice(globalIndex, 1); }); this.selectedKeywords = []; this.saveKeywords().then(() => { this.updateDisplayData(); }); }); }, // 导入关键词 handleImportKeywords() { if (!this.importKeywordsText.trim()) { this.$message.warning("请输入要导入的关键词"); return; } // 处理导入文本:支持换行和竖线分隔,并去掉首尾空格 const newKeywords = this.importKeywordsText .split(/[\n|]/) .map((item) => item.trim()) // 去掉每个关键词的首尾空格 .filter((item) => item); // 过滤掉空字符串 if (newKeywords.length === 0) { this.$message.warning("没有检测到有效的关键词"); return; } // 添加到现有关键词中,并去重 const existingKeywords = this.allKeywords.map((item) => item.content); const uniqueNewKeywords = [...new Set(newKeywords)] .filter((item) => !existingKeywords.includes(item)) .map((item) => ({ content: item })); this.allKeywords = [...this.allKeywords, ...uniqueNewKeywords]; this.importKeywordsText = ""; this.importDialogVisible = false; this.saveKeywords().then(() => { this.$message.success(`成功导入 ${uniqueNewKeywords.length} 个关键词`); this.updateDisplayData(); }); }, // 选择变化 handleSelectionChange(selection) { this.selectedKeywords = selection.map((item) => this.keywordsData.indexOf(item) ); }, // 分页大小改变 handleSizeChange(val) { this.queryParams.pageSize = val; this.updateDisplayData(); }, // 当前页改变 handleCurrentChange(val) { this.queryParams.pageNum = val; this.updateDisplayData(); }, // 自定义序号 indexMethod(index) { return (this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1; }, // 搜索 handleQuery() { this.queryParams.pageNum = 1; this.getKeywordsList(); }, // 重置 resetQuery() { this.queryParams = { pageNum: 1, pageSize: 10, }; this.handleQuery(); }, }, }; </script> <style scoped> .app-container { padding: 20px; height: 100%; display: flex; flex-direction: column; } .pagination-container { margin: 15px 0; text-align: right; } .el-table .cell { padding: 0 5px; } .el-table .el-input__inner { border: none; background: transparent; } .el-table .el-input__inner:focus { background: #fff; border: 1px solid #dcdfe6; } </style>这个前端代码有什么需要优化的地方吗 优化后的代码是什么样的
07-23
<template> <div class="app-container"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>发起流程</span> </div> <el-col :span="18" :offset="3"> <div class="form-conf" v-if="formOpen"> <parser :key="new Date().getTime()" :form-conf="formData" @submit="submit" ref="parser" @getData="getData"/> </div> </el-col> </el-card> </div> </template> <script> import { getProcessForm, startProcess } from '@/api/workflow/process' import Parser from '@/utils/generator/parser' export default { name: 'WorkStart', components: { Parser }, data() { return { definitionId: null, deployId: null, procInsId: null, formOpen: false, formData: {}, } }, created() { this.initData(); }, methods: { initData() { this.deployId = this.$route.params && this.$route.params.deployId; this.definitionId = this.$route.query && this.$route.query.definitionId; this.procInsId = this.$route.query && this.$route.query.procInsId; getProcessForm({ definitionId: this.definitionId, deployId: this.deployId, procInsId: this.procInsId }).then(res => { if (res.data) { this.formData = res.data; this.formOpen = true //表单为空的情况也添加按钮 this.formData.formBtns = true; // 添加这一行 } }) }, /** 接收子组件传的值 */ getData(data) { if (data) { const variables = []; data.fields.forEach(item => { let variableData = {}; variableData.label = item.__config__.label // 表单值为多个选项时 if (item.__config__.defaultValue instanceof Array) { const array = []; item.__config__.defaultValue.forEach(val => { array.push(val) }) variableData.val = array; } else { variableData.val = item.__config__.defaultValue } variables.push(variableData) }) this.variables = variables; } }, submit(data) { if (data && this.definitionId) { // 启动流程并将表单数据加入流程变量 startProcess(this.definitionId, JSON.stringify(data.valData)).then(res => { this.$modal.msgSuccess(res.msg); this.$tab.closeOpenPage({ path: '/work/own' }) }) } } } } </script> <style lang="scss" scoped> .form-conf { margin: 15px auto; width: 80%; padding: 15px; } </style> import { deepClone } from '@/utils/index'; import { getToken } from '@/utils/auth'; import render from '@/utils/generator/render'; import axios from 'axios' import Vue from 'vue'; Vue.prototype.$axios = axios const ruleTrigger = { 'el-input': 'blur', 'el-input-number': 'blur', 'el-select': 'change', 'el-radio-group': 'change', 'el-checkbox-group': 'change', 'el-cascader': 'change', 'el-time-picker': 'change', 'el-date-picker': 'change', 'el-rate': 'change', 'el-upload': 'change' } const layouts = { colFormItem(h, scheme) { const config = scheme.__config__ const listeners = buildListeners.call(this, scheme) let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null if (config.showLabel === false) labelWidth = '0' return ( <el-col span={config.span}> <el-form-item label-width={labelWidth} prop={scheme.__vModel__} label={config.showLabel ? config.label : ''}> <render conf={scheme} on={listeners} /> </el-form-item> </el-col> ) }, rowFormItem(h, scheme) { let child = renderChildren.apply(this, arguments) if (scheme.type === 'flex') { child = <el-row type={scheme.type} justify={scheme.justify} align={scheme.align}> {child} </el-row> } return ( <el-col span={scheme.span}> <el-row gutter={scheme.gutter}> {child} </el-row> </el-col> ) } } function renderFrom(h) { const { formConfCopy } = this return ( <el-row gutter={formConfCopy.gutter}> <el-form size={formConfCopy.size} label-position={formConfCopy.labelPosition} disabled={formConfCopy.disabled} label-width={`${formConfCopy.labelWidth}px`} ref={formConfCopy.formRef} // model能直接赋值 https://github.com/vuejs/jsx/issues/49#issuecomment-472013664 props={{ model: this[formConfCopy.formModel] }} rules={this[formConfCopy.formRules]} > {renderFormItem.call(this, h, formConfCopy.fields)} {formConfCopy.formBtns && formBtns.call(this, h)} </el-form> </el-row> ) } function formBtns(h) { return <el-col> <el-form-item size="large"> <el-button type="primary" onClick={this.submitForm}>提交</el-button> <el-button onClick={this.resetForm}>重置</el-button> </el-form-item> </el-col> } function renderFormItem(h, elementList) { return elementList.map(scheme => { const config = scheme.__config__ const layout = layouts[config.layout] if (layout) { return layout.call(this, h, scheme) } throw new Error(`没有与${config.layout}匹配的layout`) }) } function renderChildren(h, scheme) { const config = scheme.__config__ if (!Array.isArray(config.children)) return null return renderFormItem.call(this, h, config.children) } function setValue(event, config, scheme) { this.$set(config, 'defaultValue', event) this.$set(this[this.formConf.formModel], scheme.__vModel__, event) } function buildListeners(scheme) { const config = scheme.__config__ const methods = this.formConf.__methods__ || {} const listeners = {} // 给__methods__中的方法绑定this和event Object.keys(methods).forEach(key => { listeners[key] = event => methods[key].call(this, event) }) // 响应 render.js 中的 vModel $emit('input', val) listeners.input = event => setValue.call(this, event, config, scheme) return listeners } export default { components: { render }, props: { formConf: { type: Object, required: true } }, data() { const data = { formConfCopy: deepClone(this.formConf), [this.formConf.formModel]: {}, [this.formConf.formRules]: {} } this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel]) this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules]) return data }, methods: { initFormData(componentList, formData) { componentList.forEach(cur => { this.buildOptionMethod(cur) const config = cur.__config__; if (cur.__vModel__) { formData[cur.__vModel__] = config.defaultValue; // 初始化文件列表 if (cur.action && config.defaultValue) { cur['file-list'] = config.defaultValue; } } if (cur.action) { cur['headers'] = { Authorization: "Bearer " + getToken(), } cur['on-success'] = (res, file, fileList) => { formData[cur.__vModel__] = fileList; if (res.code === 200 && fileList) { config.defaultValue = fileList; fileList.forEach(val =>{ val.url = val.response.data.url; val.ossId = val.response.data.ossId; }) } }; // 点击文件列表中已上传的文件时的钩子 cur['on-preview'] = (file) => { this.$download.oss(file.ossId) } } if (config.children) { this.initFormData(config.children, formData); } }) }, // 特殊处理的 Option buildOptionMethod(scheme) { const config = scheme.__config__; if (config && config.tag === 'el-cascader') { if (config.dataType === 'dynamic') { this.$axios({ method: config.method, url: config.url }).then(resp => { var { data } = resp scheme[config.dataConsumer] = data[config.dataKey] }); } } }, buildRules(componentList, rules) { componentList.forEach(cur => { const config = cur.__config__ if (Array.isArray(config.regList)) { if (config.required) { const required = { required: config.required, message: cur.placeholder } if (Array.isArray(config.defaultValue)) { required.type = 'array' required.message = `请至少选择一个${config.label}` } required.message === undefined && (required.message = `${config.label}能为空`) config.regList.push(required) } rules[cur.__vModel__] = config.regList.map(item => { item.pattern && (item.pattern = eval(item.pattern)) item.trigger = ruleTrigger && ruleTrigger[config.tag] return item }) } if (config.children) this.buildRules(config.children, rules) }) }, resetForm() { this.formConfCopy = deepClone(this.formConf) this.$refs[this.formConf.formRef].resetFields() }, submitForm() { //这是为了提交可以为空表单 const valid = true; if (!valid) return false; const params = { /* 表单数据 */ }; this.$emit('submit', params); return true; //原本的 // this.$refs[this.formConf.formRef].validate(valid => { // if (!valid) return false // const params = { // formData: this.formConfCopy, // valData: this[this.formConf.formModel] // } // this.$emit('submit', params) // return true // }) }, // 传值给父组件 getData(){ debugger this.$emit('getData', this[this.formConf.formModel]) // this.$emit('getData',this.formConfCopy) } }, render(h) { return renderFrom.call(this, h) } } 为什么还是打开没有提交按钮
07-23
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值