I recently encountered a problem that I wanted to pop up the list and then click the list to download the file. Sounds simple, let’s try it 😂
rendering
- Data list
- The attachment list
Train of thought
- Start with a pop-up box that lists the attachments returned from the background
- Add click events to the list of files listed by Saul (key)
- Perfect click events (file download method)
The problem solving
1. Create a function plug-in viewattachments.js
- So let’s start with the popup method
Element uses HTML fragments Pop-up bounced
This $alert (enclosing $createElement method (' div '{}, htmlStr),' attachments' {dangerouslyUseHTMLString: true, confirmButtonText: 'shut down'})Copy the code
Add a list of attachments and bind click events
- Understand the vue. $createElement method
/ / @ returns {VNode} createElement method (/ / {String | Object | Function} / / an HTML tag name, component options Object, Or // resolve an async function of any of the above. Required fields. 'div', // {Object} // a data Object corresponding to the attributes in the template. Optional. {/ / (see the next section)}, / / {String | Array} / / child (VNodes), virtual node by ` createElement method () is constructed from the `, / / you can also use the String to generate the "text" virtual node. Optional. [' write some text ', createElement('h1', 'headline '), createElement(MyComponent, {props: {someProp: 'foobar'}})]Copy the code
One thing to note: Just as v-bind:class and V-bind :style are treated specially in template syntax, they also have corresponding top-level fields in VNode data objects. This object also allows you to bind normal HTML properties, as well as DOM properties such as innerHTML (which overrides v-HTML directives).
{// Same API as' v-bind:class ', // accepts a string, object, or array of strings and objects 'class': {foo: true, bar: False}, // Same API as' v-bind:style ', // accept a string, object, or array of objects style: {color: 'red', fontSize: {color: 'red', fontSize: '14px'}, // Attrs: {id: 'foo'}, // Component prop props: {myProp: 'bar'}, // DOM attribute domProps: {innerHTML: 'baz'}, // Event listeners are in the 'on' property, // but modifiers such as' V-on :keyup.enter 'are no longer supported. // keyCode needs to be checked manually in the handler. On: {click: this.clickHandler}, // for components only, it is used to listen for native events, not events emitted by components using // 'vm.$emit'. NativeOn: {click: this.nativecLickHandler}, // custom directive. Note that you cannot assign a value to 'oldValue' // in 'binding' because Vue has automatically synchronized for you. directives: [ { name: 'my-custom-directive', value: '2', expression: '1 + 1', arg: 'foo', modifiers: { bar: True}}], / / scope of slot format for / / {name: props = > VNode | Array < VNode >} scopedSlots: {default: Props => createElement('span', props. Text)}, // If the component is a child of another component, specify a name for the slot: 'name-of-slot', // other special top-level attributes key: 'myKey', ref: 'myRef', // If you apply the same ref name to multiple elements in the render function, then '$refs.myRef' becomes an array. refInFor: true }Copy the code
Note that all VNodes in the component tree must be unique. This means that the following render functions are not valid:
Var myParagraphVNode = this.$createElement('p', 'multiple') return createElement('div', [// error - Repeated VNode myParagraphVNode, myParagraphVNode])Copy the code
If you really want to create exactly the same element every time, use factory functions, as suggested on the web
Return createElement('div', // Apply converts the second array-like object into an Array of length 20, the equivalent of Array(20) but with each bit empty. Apply splits the Array into multiple arguments and passes them to the Array constructor. Finally, it is converted to an object of length 20 with each bit initialized to undefined, which can be traversed by the map method, creating paragraph labels and returning, Array.apply(null, {length:}) {array. apply(null, {length:}) {array. apply(null, {length:}); 20 }).map(function () { return createElement('p', 'hi') }) )Copy the code
With this knowledge, we can now complete the components we originally intended to implement:
let htmlStr = [] fileList.forEach(element => { htmlStr.push(this.$createElement('p', { style: { 'cursor': 'pointer', 'text-decoration': 'underline'}, attrs: {data: JSON. Stringify (element), title: 'click to download'}, on: {click: downFile } }, element.name)) })Copy the code
Method of downloading files
function downFile (e) { if (e.target.tagName ! Parse (e.target.getAttribute('data')) // Element. MSG is the address of the file on the server if (! Element. The MSG) {Message. Error (' can't find the address ~ ')} / / file background address let fileName = element. The MSG. Replace (/ \ | / g, Split ('$')[1] let data = {fileName: fileName, type: 2} httpRequest({url: Httprequest.adornurl (' /sys/file/down '), responseType: httprequest.adornurl (' /sys/file/down '), responseType: Then (res => {if (res.data) {var blod = new blob ([res.data]) var a = document.createElement('a') a.href = URL.createObjectURL(blod) a.style.display = 'none' document.body.appendChild(a) a.download = downName a.click() document.body.removeChild(a) } }) }Copy the code
use
- It can be introduced in components if it is not widely used
import { viewAttachments } from '@/utils/viewAttachments'
Copy the code
- If it is used on a large scale, as I am, it is introduced globally
Introduce public methods in main.js
Import {viewAttachments} from '@ / utils/viewAttachments Vue. Prototype. ViewAttachments = viewAttachments / / ajax request methodCopy the code
Call the written public method in the data list
<el-table-column fixed="right" header-align="center" align="center" width="150" label=" operation "> <template slot-scope="scope"> <el-button v-if="isAuth('sys:role:update')" type="text" size="small" @click="modificationContract(scope.row.contractid)"> modify </el-button> <el-button V-if ="isAuth('sys:role:delete')" Type ="text" size="small" @click="deleteHandle(scope.row.contractid)"> </el-button> <el-button v-if="isAuth('sys:role:delete')" type="text" size="small" @click="viewAttachments(scope.row.contractfile)"> </el-button> </template> </el-table-column>Copy the code
ViewAttachments complete code
import { Message } from 'element-ui' import httpRequest from '@/utils/httpRequest' function downFile (e) { if (e.target.tagName ! == 'P') { return } let element = JSON.parse(e.target.getAttribute('data')) if (! Element. The MSG) {Message. Error (' can't find the address ~ ')} / / file background address let fileName = element. The MSG. Replace (/ \ | / g, Split ('$')[1] let data = {fileName: fileName} httpRequest({url: httpRequest.adornUrl(`/sys/file/down`), method: 'post', params: data, responseType: Then (res => {if (res.data) {var blod = new blob ([res.data]) var a = document.createElement('a') a.href = URL.createObjectURL(blod) a.style.display = 'none' document.body.appendChild(a) a.download = downName a.click() document.body.removeChild(a) } }) } export function viewAttachments (file) { if (! The file | | file = = = 'string') {Message. Error (' no attachment ~ ')} else {let fileList = JSON. Parse (file) let htmlStr = [] fileList.forEach(element => { htmlStr.push(this.$createElement('p', { style: { 'cursor': 'pointer', 'text-decoration': 'underline'}, attrs: {data: json.stringify (element), title: 'click'}, on: {click: DownFile}}, element name))}) enclosing $alert (enclosing $createElement method (' div '{}, htmlStr),' attachments' {dangerouslyUseHTMLString: True, confirmButtonText: 'close'})}}Copy the code