<!--
 ************************************************************
 *
 * Copyright (c) 2024 ysrock Co., Ltd.	<info@ysrock.co.jp>
 * Copyright (c) 2024 Yasuo Sugano	<sugano@ysrock.co.jp>
 *
 * Version	: 1.0.4
 * Update	  : 2024.11.20
 *
 *  マルチ非同期処理
 *    https://demo.ysrock.com/vue3_multi_process/
 *
 ************************************************************
 *
 *  使い方
 *    [html]
 *      <multi-process ref="refMultiProcess"></multi-process>
 *      <button type="button" @click="execute">実行する</button>
 *
 *    [js]
 *      <script scoped>
 *      export default{
 *        setup (props) {
 *          const refMultiProcess = Vue.ref(null);
 *          const execute = async () => {
 *            let tasks = [];
 *            tasks.push([
 *              { func: sleepFunc, args: [1000, "1秒待機"], html: "並列Aの1番目を実行しています" },
 *              { func: sleepFunc, args: [1000, "1秒待機"], html: "並列Aの2番目を実行しています" },
 *            ]);
 *            tasks.push([
 *              { func: sleepFunc, args: [ 500, "0.5秒待機"] },
 *              { func: sleepFunc, args: [ 500, "0.5秒待機"] },
 *              { func: sleepFunc, args: [ 500, "0.5秒待機"] },
 *            ]);
 *            let results = await refMultiProcess.value.execute(tasks);
 *          };
 *          return {
 *            execute,
 *            refMultiProcess,
 *          };
 *        },
 *        components: {
 *          'multi-process': Vue.defineAsyncComponent( () => window['vue3-sfc-loader']['loadModule']('./components/multi_process/main.vue', window['Vue3']['loaderOption']() ) ),
 *        }
 *      </script>
 *
 *
 ************************************************************ -->

<!--
 ******************************
 *
 * template
 *
 ****************************** -->
<template>
 <div class="templateWrap" v-if="proxyObj.isVisible" :style="('zIndex' in proxyObj.styleObj ? 'z-index:' + proxyObj.styleObj.zIndex : '')">
  <div class="progressBox">

   <div class="tasks"
    v-for="(parallelObj, parallelIdx) in proxyObj.parallelProcess"
    :key="'parallel_' + parallelIdx"
   >
    <div class="bar"><div :style="{ 'width':parallelObj.width, 'background-color':parallelObj.backgroundColor }"></div></div>
    <div class="html" v-html="parallelObj.html"></div>
   </div><!-- END div.tasks -->

  </div><!-- END div.progressBox -->
 </div><!-- div.templateWrap -->
</template>




<!--
 ******************************
 *
 * script
 *
 ****************************** -->
<script scoped>
export default{
  setup (props) {
    const proxyObj = Vue.ref({
      parallelProcess: [],
      styleObj: {},
      isVisible: false
    });

    /**
     * リセット
     */
    const reset = (styleObj) => {
      proxyObj.value.parallelProcess = [];
      proxyObj.value.styleObj = {};
      proxyObj.value.isVisible = false;
      if (styleObj !== undefined) setStyle(styleObj);
    };


    /**
     * スタイルシートを適用
     */
    const setStyle = (styleObj) => {
      if ('zIndex' in styleObj) proxyObj.value.styleObj.zIndex = styleObj.zIndex;
      if ('backgroundColor' in styleObj) proxyObj.value.styleObj.backgroundColor = styleObj.backgroundColor;
    };


    const execute = (tasks, styleObj) => {
      return new Promise((resolve, reject) => {
        initialize(tasks);
        if (styleObj) setStyle(styleObj);

        setTimeout(() => {
          Promise.all( tasks.map( (taskAry, taskIdx) => seriesFunc(taskAry, taskIdx) ))
          .then( (results) => {
            setTimeout(() => {
              // 非表示
              proxyObj.value.isVisible = false;
              // 正常終了
              resolve(results);
            }, 1250);
          }).catch( (error) => {
            // 異常終了
            reject(error);
          });
        }, 300);
      });
    };

    /**
     * 初期化
     */
    const initialize = (tasks) => {
      proxyObj.value.parallelProcess = [];
      tasks.forEach( () => {
        proxyObj.value.parallelProcess.push({
          width: '0%',
          html: '処理を開始しています'
        });
      });
      proxyObj.value.isVisible = true;
    };

    /**
     * 直列処理を順番に実行
     */
    const seriesFunc = (taskAry, taskIdx) => {
      return new Promise(async (resolve, reject) => {
        const resultAry = [];
        for (let i=0, len=taskAry.length; i<len; i++) {
          let now = i + 1;
          let max = taskAry.length + 1;
          proxyObj.value.parallelProcess[taskIdx] = {
            width: (now / max * 100) + '%',
            html: 'html' in taskAry[i] ? taskAry[i].html : "処理中（" + now + " / " + taskAry.length + "）"
          };

          resultAry.push(await taskAry[i].func( ...taskAry[i].args ));
        };
        // 直列の処理が終了
        proxyObj.value.parallelProcess[taskIdx] = {
          width: '100%',
          html: '処理が完了しました'
        };
        resolve(resultAry);
      });
    };


    return {
      proxyObj,
      execute,
      setStyle,
      reset,
    };

  }
}
</script>


<!--
 ******************************
 *
 * style
 *
 ****************************** -->
<style scoped>
div.templateWrap {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, .6);
}
div.templateWrap > div.progressBox {
  position: fixed;
  top: 5vh;
  left: 50%;
  z-index: 1;
  transform: translateX(-50%);
  width: 50vw;
  box-shadow: .1em .1em .5em .2em;
  padding: 3em 2em;
  border-radius: .4em;
  background-color: #fff;
  text-align: center;
}


div.templateWrap > div.progressBox > div.tasks > div.bar {
  height: 1.2em;
  border: .1em solid #ddd;
  background-color: #f8f8f8;
}
div.templateWrap > div.progressBox > div.tasks > div.bar > div {
  height: 100%;
  background-color: #9d9de9;
  transition: width 1s ease-in-out;
}
div.templateWrap > div.progressBox > div.tasks > div.html {
  margin-top: .5em;
}

div.templateWrap > div.progressBox > div.tasks + div.tasks {
  margin-top: 1em;
}

@media print, screen and (min-width: 1025px){
  /* px */
}
</style>