Răsfoiți Sursa

feat: show upload progress bar when input file

wangweimin 4 ani în urmă
părinte
comite
15c673602e
2 a modificat fișierele cu 77 adăugiri și 12 ștergeri
  1. 36 2
      webiojs/src/handlers/input.ts
  2. 41 10
      webiojs/src/session.ts

+ 36 - 2
webiojs/src/handlers/input.ts

@@ -9,7 +9,7 @@ import {CommandHandler} from "./base"
 * 整个输入区域的控制类
 * 管理当前活跃和非活跃的表单
 * */
-export class InputHandler implements CommandHandler{
+export class InputHandler implements CommandHandler {
     accept_command: string[] = ['input', 'input_group', 'update_input', 'destroy_form'];
 
     session: Session;
@@ -23,6 +23,13 @@ export class InputHandler implements CommandHandler{
     }
 
     private _after_show_form() {
+        // 解决表单显示后动态添加内容,表单宽高不变的问题
+        setTimeout(() => {
+            let curr_card = $('#input-container > .card')[0];
+            curr_card.style.height = "unset";
+            curr_card.style.width = "unset";
+        }, 50);
+
         if (!state.AutoScrollBottom)
             return;
 
@@ -193,17 +200,44 @@ class FormController {
             $.each(that.name2input, (name, ctrl) => {
                 data[name] = ctrl.get_value();
             });
+            let on_process = undefined;
+            // 在有文件上传的表单中显示进度条
+            for (let item of that.spec.inputs) {
+                if (item.type == 'file') {
+                    on_process = that.make_progress();
+                    break;
+                }
+            }
             that.session.send_message({
                 event: "from_submit",
                 task_id: that.task_id,
                 data: data
-            });
+            }, on_process);
         });
 
         this.element = element;
         return element;
     };
 
+    // 显示提交进度条,返回进度更新函数
+    make_progress() {
+        let html = `<div class="progress" style="margin-top: 4px;">
+                        <div class="progress-bar bg-info progress-bar-striped progress-bar-animated" role="progressbar"
+                             style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%
+                        </div>
+                    </div>`;
+        let elem = $(html);
+        this.element.find('.card-body').append(elem);
+
+        let bar = elem.find('.progress-bar');
+        return function (loaded: number, total: number) {
+            let progress = "" + (100.0 * loaded / total).toFixed(1);
+            bar[0].style.width = progress + "%";
+            bar.attr("aria-valuenow", progress);
+            bar.text(progress + "%");
+        }
+    };
+
     dispatch_ctrl_message(spec: any) {
         if (!(spec.target_name in this.name2input)) {
             return console.error('Can\'t find input[name=%s] element in curr form!', spec.target_name);

+ 41 - 10
webiojs/src/session.ts

@@ -25,7 +25,7 @@ export interface Session {
 
     start_session(debug: boolean): void;
 
-    send_message(msg: ClientEvent): void;
+    send_message(msg: ClientEvent, onprogress?: (loaded: number, total: number) => void): void;
 
     close_session(): void;
 
@@ -36,9 +36,12 @@ export class WebSocketSession implements Session {
     ws: WebSocket;
     debug: boolean;
     private _closed: boolean;
-    private _on_session_create: (this: WebSocket, ev: Event) => any = ()=>{};
-    private _on_session_close: (this: WebSocket, ev: CloseEvent) => any = ()=>{};
-    private _on_server_message: (msg: Command) => any = ()=>{};
+    private _on_session_create: (this: WebSocket, ev: Event) => any = () => {
+    };
+    private _on_session_close: (this: WebSocket, ev: CloseEvent) => any = () => {
+    };
+    private _on_server_message: (msg: Command) => any = () => {
+    };
 
     constructor(public ws_api: string, app_name: string = 'index') {
         this.ws = null;
@@ -79,7 +82,17 @@ export class WebSocketSession implements Session {
         };
     }
 
-    send_message(msg: ClientEvent): void {
+    start_onprogress(onprogress?: (loaded: number, total: number) => void): void {
+        let total = this.ws.bufferedAmount;
+        let onprogressID = setInterval(() => {
+            let loaded = total - this.ws.bufferedAmount;
+            onprogress(loaded, total);
+            if (this.ws.bufferedAmount == 0)
+                clearInterval(onprogressID);
+        }, 200);
+    }
+
+    send_message(msg: ClientEvent, onprogress?: (loaded: number, total: number) => void): void {
         if (this.closed())
             return alert("与服务器连接已断开,请刷新页面重新操作");
 
@@ -87,6 +100,10 @@ export class WebSocketSession implements Session {
             return console.error('WebSocketWebIOSession.ws is null when invoke WebSocketWebIOSession.send_message. ' +
                 'Please call WebSocketWebIOSession.start_session first');
         this.ws.send(JSON.stringify(msg));
+
+        if (onprogress)
+            this.start_onprogress(onprogress);
+
         if (this.debug) console.info('<<<', msg);
     }
 
@@ -111,9 +128,12 @@ export class HttpSession implements Session {
     debug = false;
 
     private _closed = false;
-    private _on_session_create: () => void = ()=>{};
-    private _on_session_close: () => void = ()=>{};
-    private _on_server_message: (msg: Command) => void = ()=>{};
+    private _on_session_create: () => void = () => {
+    };
+    private _on_session_close: () => void = () => {
+    };
+    private _on_server_message: (msg: Command) => void = () => {
+    };
 
 
     constructor(public api_url: string, app_name = 'index', public pull_interval_ms = 1000) {
@@ -169,7 +189,7 @@ export class HttpSession implements Session {
         }
     };
 
-    send_message(msg: ClientEvent): void {
+    send_message(msg: ClientEvent, onprogress?: (loaded: number, total: number) => void): void {
         if (this.closed())
             return alert("与服务器连接已断开,请刷新页面重新操作");
 
@@ -182,10 +202,21 @@ export class HttpSession implements Session {
             dataType: "json",
             headers: {"webio-session-id": this.webio_session_id},
             success: this._on_request_success.bind(this),
+            xhr: function () {
+                let xhr = new window.XMLHttpRequest();
+                // Upload progress
+                xhr.upload.addEventListener("progress", function (evt) {
+                    if (evt.lengthComputable && onprogress) {
+                        onprogress(evt.loaded, evt.total);
+                    }
+                }, false);
+                return xhr;
+            },
             error: function () {  // todo
                 console.error('Http push event failed, event data: %s', msg);
             }
-        })
+        });
+
     }
 
     close_session(): void {