Browse Source

BI匯出各縣市已領取授權名單

jeff 6 months ago
parent
commit
ec98673d4e

+ 23 - 0
TEAMModelBI/ClientApp/src/language/lang/zh-cn.js

@@ -921,6 +921,24 @@ const zh_cn = {
         action: '动作',
         import: '汇入',
         searchSchName: '搜寻学校名称',
+        city: '城市',
+        edit: '编辑',
+        export: '汇出',
+        authTotalCount: '总授权数',
+        received: '已领取',
+        notReceived: '未领取',
+        sheetSummary: '总表',
+        authReceivedList: '授权领取清单',
+        receiverName: '领取人姓名',
+        receiverId: '领取人ID',
+        receivedList: '已领取清单',
+        message1: '不可为零',
+        message2: '请选择县市',
+        message3: '请选择产品名称',
+        message4: '请选择使用期限',
+        message5: '请输入名额',
+        message6: '请输入数字',
+        message7: '学校已移除',
     },
     auth:{
         YMPCVCIM: '学情分析模组',
@@ -940,6 +958,9 @@ const zh_cn = {
         VDPGJ6NC: '劳动教育服务',
         YPXSJ6NJ: '五育看板',
         IPALB6EY: 'IES5个人空间',
+        Z6ELB6EZ: 'HiTeach5ID授权',
+        VA67B6EZ: 'HiTeach5县市授权',
+        VAA7B6EY: 'IES5个人空间县市授权',
     },
     aprule: {
         hdcam: 'USB录影支援',
@@ -961,6 +982,8 @@ const zh_cn = {
         sokvtt: '苏格拉底语音转写',
         cowork: '协作',
         aigpt: 'AI/GPT 智连环',
+        noirs: '禁用硬体IRS',
+        schoolinfo: '学校简码',
     },
     commonTimeName: {
         day: '天',

+ 23 - 0
TEAMModelBI/ClientApp/src/language/lang/zh-tw.js

@@ -914,6 +914,24 @@ const zh_tw = {
         action: '動作',
         import: '匯入',
         searchSchName: '搜尋學校名稱',
+        city: '城市',
+        edit: '編輯',
+        export: '匯出',
+        authTotalCount: '總授權數',
+        received: '已領取',
+        notReceived: '未領取',
+        sheetSummary: '總表',
+        authReceivedList: '授權領取清單',
+        receiverName: '領取人姓名',
+        receiverId: '領取人ID',
+        receivedList: '已領取清單',
+        message1: '不可為零',
+        message2: '請選擇縣市',
+        message3: '請選擇產品名稱',
+        message4: '請選擇使用期限',
+        message5: '請輸入名額',
+        message6: '請輸入數字',
+        message7: '學校已移除',
     },
     auth:{
         YMPCVCIM: '學情分析模組',
@@ -933,6 +951,9 @@ const zh_tw = {
         VDPGJ6NC: '勞動教育服務',
         YPXSJ6NJ: '五育看板',
         IPALB6EY: 'IES5個人空間',
+        Z6ELB6EZ: 'HiTeach5ID授權',
+        VA67B6EZ: 'HiTeach5縣市授權',
+        VAA7B6EY: 'IES5個人空間縣市授權',
     },
     aprule: {
         hdcam: 'USB錄影支援',
@@ -954,6 +975,8 @@ const zh_tw = {
         sokvtt: '蘇格拉底語音轉寫',
         cowork: '協作',
         aigpt: 'AI/GPT 智連環',
+        noirs: '禁用硬體IRS',
+        schoolinfo: '學校簡碼',
     },
     commonTimeName: {
         day: '天',

+ 35 - 0
TEAMModelBI/ClientApp/src/until/excel.js

@@ -69,6 +69,39 @@ export const export_table_to_excel = (id, filename) => {
     // XLSX.writeFile(wb, filename);
 }
 
+export const export_json_to_array = (key, jsonData) => {
+    return jsonData.map(v => key.map(j => { return v[j] }));
+}
+
+export const export_auto_width = (ws, data) => {
+    /*set worksheet max width per col*/
+    const colWidth = data.map(row => row.map(val => {
+        /*if null/undefined*/
+        if (val == null) {
+            return { 'wch': 140 };
+        }
+        /*if chinese*/
+        else if (val.toString().charCodeAt(0) > 255) {
+            return { 'wch': val.toString().length * 2 };
+        } else {
+            return { 'wch': val.toString().length };
+        }
+    }))
+    /*start in the first row*/
+    let result = colWidth[0];
+    for (let i = 1; i < colWidth.length; i++) {
+        for (let j = 0; j < colWidth[i].length; j++) {
+            if (typeof result[j] !== "undefined" && typeof colWidth[i][j] !== "undefined") {
+                if (result[j]['wch'] < colWidth[i][j]['wch']) {
+                    result[j]['wch'] = colWidth[i][j]['wch'];
+                }
+            }
+        }
+    }
+    ws['!cols'] = result;
+    return ws;
+}
+
 export const export_json_to_excel = ({ data, key, title, filename, autoWidth }) => {
     const wb = XLSX.utils.book_new();
     data.unshift(title);
@@ -137,6 +170,8 @@ export default {
     export_table_to_excel,
     export_array_to_excel,
     export_json_to_excel,
+    export_json_to_array,
+    export_auto_width,
     excelZipExport,
     read
 }

+ 131 - 45
TEAMModelBI/ClientApp/src/view/htcommunity/adminpanel.vue

@@ -246,14 +246,13 @@ EEEE    120
   import { ref, reactive, onMounted, computed, getCurrentInstance } from 'vue'
   import { ElMessage, ElMessageBox, TableV2SortOrder } from 'element-plus'
   import option_gl from '@/static/regions/region_gl.json'
+  import excel from "../../until/excel"
   const optionsData = option_gl[0].children
-
+  let { proxy } = getCurrentInstance()
 
   //路由
   const routers = useRouter()
   const ruleFormRef = ref(null)
-  let { proxy } = getCurrentInstance()
-
   const dialogFormVisible = ref(false)
   const isRegionDisabled = ref(false)
   const isNameDisabled = ref(false)
@@ -262,11 +261,14 @@ EEEE    120
   const delayAddSchool = ref(false)
   const delayImportSchool = ref(false)
   const delaySaveData = ref(false)
+  const siteValue = computed(() => {
+    return window.location.host.includes(".teammodel.cn") ? 'cn' : 'international'
+  });
 
   // 檢查數字不可為零
   const validateNum = (rule, value, callback) => {
     if (value === 0) {
-      callback(new Error('不可為零'))
+        callback(new Error(proxy.$t(`purchase.message1`)))
     }
     callback();
 
@@ -276,28 +278,34 @@ EEEE    120
     region: [
       {
         required: true,
-        message: '請選擇縣市'
+        message: proxy.$t(`purchase.message2`)
       }
     ],
     name: [
       {
         required: true,
-        message: '請選擇產品名稱'
+        message: proxy.$t(`purchase.message3`)
       }
     ],
     time: [
       {
         required: true,
-        message: '請選擇使用期限'
+        message: proxy.$t(`purchase.message4`)
       }
     ],
     qwen: [
       {
         required: true,
-        message: '請輸入名額'
+        message: proxy.$t(`purchase.message5`)
+      },
+      {
+        type: 'number',
+        message: proxy.$t(`purchase.message6`)
+      },
+      {
+        validator: validateNum,
+        trigger: 'blur'
       },
-      { type: 'number', message: '請輸入數字' },
-      { validator: validateNum, trigger: 'blur' },
     ],
   })
   // 國省市區資料
@@ -325,8 +333,8 @@ EEEE    120
     client_volume: 0
   });
   const nameOptions = reactive([
-    { label: "HiTeach5縣市授權版", value: "VA67B6EZ" },
-    { label: "IES5個人空間縣市授權版", value: "VAA7B6EY" }
+    { label: proxy.$t(`auth.VA67B6EZ`), value: "VA67B6EZ" },
+    { label: proxy.$t(`auth.VAA7B6EY`), value: "VAA7B6EY" }
   ]);
 
   const schooltInput = ref('')
@@ -340,7 +348,6 @@ EEEE    120
         if (line && line !== "") {
             // 每行按逗号或Tab字符分割,获得名字和座号
             const [shortCode, seat] = line.split(/[\t,]/);
-            console.log('shortCode:', shortCode)
             let schInfo = csSchoolList.value.find((element) => element.shortCode == shortCode.trim()); //CS篩選
             if (typeof schInfo !== "undefined") {
                 if (!schools.value.some(sch => sch.shortCode === shortCode.trim())) { // 簡碼未重複者追加
@@ -366,7 +373,7 @@ EEEE    120
   // 移除學校
   const deleteSchool = (index) => {
     schools.value.splice(index, 1)
-    ElMessage.success('學校已移除')
+    ElMessage.success(proxy.$t(`purchase.message7`))
   }
   //新增學校 學校列表
   let csSchoolList = ref([]) //(追加學校時)學校列表
@@ -391,79 +398,79 @@ EEEE    120
   // 擴充項項目
   const extensions = reactive([
     {
-      Name: "AI智能終端",
+      Name: proxy.$t(`aprule.ezs`),
       Key: "ezs"
     },
     {
-      Name: "禁用硬體IRS",
+      Name: proxy.$t(`aprule.noirs`),
       Key: "irs"
     },
     {
-      Name: "USB錄影支援",
+      Name: proxy.$t(`aprule.hdcam`),
       Key: "hdcam"
     },
     {
-      Name: "AI文句分析",
+      Name: proxy.$t(`aprule.wordanls`),
       Key: "wordanls"
     },
     {
-      Name: "分組數",
+      Name: proxy.$t(`aprule.cligroup`),
       Key: "cligroup"
     },
     {
-      Name: "議課人數",
+      Name: proxy.$t(`aprule.soknumber`),
       Key: "soknumber"
     },
     {
-      Name: "IRS連線授權數",
+      Name: proxy.$t(`aprule.clientVolume`),
       Key: "client_volume"
     },
     {
-      Name: "蘇格拉底議課APP",
+      Name: proxy.$t(`aprule.sokapp`),
       Key: "sokapp"
     },
     {
-      Name: "蘇格拉底影片",
+      Name: proxy.$t(`aprule.sokvdo`), 
       Key: "sokvdo"
     },
     {
-      Name: "學校簡碼",
+      Name: proxy.$t(`aprule.schoolinfo`), 
       Key: "schoolinfo"
     },
     {
-      Name: "蘇格拉底桌面",
+      Name: proxy.$t(`aprule.sokdesk`), 
       Key: "sokdesk"
     },
     {
-      Name: "電子學生證",
+      Name: proxy.$t(`aprule.dgistuid`), 
       Key: "dgistuid"
     },
     {
-      Name: "智慧評分系統",
+      Name: proxy.$t(`aprule.scorsys`), 
       Key: "scorsys"
     },
     {
-      Name: "蘇格拉底小數據",
+      Name: proxy.$t(`aprule.soksdata`), 
       Key: "soksdata"
     },
     {
-      Name: "蘇格拉底報告",
+      Name: proxy.$t(`aprule.sokrpt`), 
       Key: "sokrpt"
     },
     {
-      Name: "雲端診斷分析系統",
+      Name: proxy.$t(`aprule.cloudas`), 
       Key: "cloudas"
     },
     {
-      Name: "蘇格拉底語音轉寫",
+      Name: proxy.$t(`aprule.sokvtt`), 
       Key: "sokvtt"
     },
     {
-      Name: "協作",
+      Name: proxy.$t(`aprule.cowork`), 
       Key: "cowork"
     },
     {
-      Name: "AI GPT服務",
+      Name: proxy.$t(`aprule.aigpt`),
       Key: "aigpt"
     },
   ]);
@@ -472,7 +479,7 @@ EEEE    120
     {
       key: "region",
       dataKey: "region",
-      title: "城市",
+      title: proxy.$t(`purchase.city`),
       width: 110,
       headerClass: 'general',
       sortable: true
@@ -480,7 +487,7 @@ EEEE    120
     {
       key: "name",
       dataKey: "name",
-      title: "產品名稱",
+      title: proxy.$t(`purchase.prodName`), 
       width: 250,
       headerClass: 'general',
       //sortable:true
@@ -488,7 +495,7 @@ EEEE    120
     {
       key: "extensions",
       dataKey: "extensions",
-      title: "擴充項",
+      title: proxy.$t(`purchase.extension`),
       width: 600,
       headerClass: 'general',
 
@@ -497,7 +504,7 @@ EEEE    120
     {
       key: "space",
       dataKey: "space",
-      title: "容量(GB)",
+      title: proxy.$t(`purchase.space`)+"(GB)", 
       width: 120,
       headerClass: 'general',
       //sortable:true
@@ -505,14 +512,14 @@ EEEE    120
     {
       key: "qwen",
       dataKey: "qwen",
-      title: "名額",
+      title: proxy.$t(`purchase.quota`), 
       width: 100,
       headerClass: 'general',
     },
     {
       key: "time",
       dataKey: "time",
-      title: "使用期限",
+      title: proxy.$t(`purchase.expirationDate`), 
       width: 300,
       headerClass: 'general',
     },
@@ -525,7 +532,20 @@ EEEE    120
       cellRenderer: (data) =>
       (
         <>
-          <el-button type="primary" onClick={editItem.bind(this, data)}>編輯</el-button>
+              <el-button type="primary" onClick={editItem.bind(this, data)}>{proxy.$t('purchase.edit')}</el-button>
+        </>
+      )
+    },
+    {
+      key: "export",
+      title: "",
+      width: 100,
+      align: "center",
+      headerClass: 'btn-class',
+      cellRenderer: (data) =>
+      (
+        <>
+              <el-button type="primary" onClick={exportByParam.bind(this, data)}>{proxy.$t('purchase.export')}</el-button>
         </>
       )
     },
@@ -635,9 +655,9 @@ EEEE    120
     //routers.push({ name: 'adminpanel'})  
     //routers.forward()  
     // 統購平台大陸先遮蔽
-    let siteValue = window.location.host === 'bi.teammodel.cn' ? 'cn' : window.location.host === 'bitest.teammodel.cn' ? 'cn' : 'international'
+    //let siteValue = window.location.host === 'bi.teammodel.cn' ? 'cn' : window.location.host === 'bitest.teammodel.cn' ? 'cn' : 'international'
     //debugger
-    if (siteValue === 'cn') {
+    if (siteValue.value === 'cn') {
       routers.push('/home/index')
     }   
     getList();    
@@ -743,7 +763,7 @@ EEEE    120
       isShowExtensions.value = false;
     }
   }
-  function editItem(obj1, obj2) {    
+  function editItem(obj1, obj2) {  
     dialogFormVisible.value = true;
     isRegionDisabled.value = true;
     isNameDisabled.value = true;
@@ -753,7 +773,7 @@ EEEE    120
     form.region = apiListData.geo[dataIndex].cityId;    
     form.name = apiListData.geo[dataIndex].prodCode; 
     form.qwen = apiListData.geo[dataIndex].seats;
-      form.time = [proxy.$common.timestampToTime(apiListData.geo[dataIndex].startDate, '', true).trim(), proxy.$common.timestampToTime(apiListData.geo[dataIndex].endDate, '', true).trim()];
+    form.time = [proxy.$common.timestampToTime(apiListData.geo[dataIndex].startDate, '', true).trim(), proxy.$common.timestampToTime(apiListData.geo[dataIndex].endDate, '', true).trim()];
     form.space = apiListData.geo[dataIndex].number;
     apiListData.geo[dataIndex].aprule.forEach(item =>{
       form.extensions.push( item.key );
@@ -770,7 +790,73 @@ EEEE    120
     form.rowIndex = dataIndex;
     handleChange();
     //clearform();
-
+  }
+  //匯出各縣市的統購資料
+  function exportByParam(obj1, obj2) {
+      console.log('excel', excel);
+      let dataIndex = obj1.rowData.index;
+      let purchaseId = obj1.rowData.id;
+      let purchaseIdParam = purchaseId.split('-');
+      let countryId = purchaseIdParam[0];
+      let provinceId = purchaseIdParam[1];
+      let cityId = purchaseIdParam[2];
+      let districtId = purchaseIdParam[3];
+      let prodCode = purchaseIdParam[4];
+      let qryData = { countryId: countryId, provinceId: provinceId, cityId: cityId, prodCode: prodCode, showTeacher:true };
+      let prodName = (prodCode == 'VA67B6EZ') ? proxy.$t(`auth.VA67B6EZ`) : (prodCode == 'VAA7B6EY') ? proxy.$t(`auth.VAA7B6EY`) : '';
+      let geoName = '';
+      proxy.$api.getBBPurchase(qryData).then((res) => {
+          //分頁1 總表
+          geoName = res.geo[0]['geoName'];
+          //let navi = [proxy.$t(`purchase.prodName`) + ':' + prodName, proxy.$t(`purchase.cityOrDistrict`) + ':' + geoName, '', '', ''];
+          let title = [proxy.$t(`purchase.schoolName`), proxy.$t(`purchase.schoolCode`), proxy.$t(`purchase.authTotalCount`), proxy.$t(`purchase.received`), proxy.$t(`purchase.notReceived`)];
+          let key = ['schoolName', 'shortCode', 'seats', 'used', 'remain'];
+          let data = [];
+          Object.entries(res.school).forEach(([purchaseIdNow, purchaseSchoolData]) => {
+              purchaseSchoolData.forEach(function (item) {
+                  data.push({ schoolName: item.schoolName, shortCode: item.shortCode, seats: item.seats, used: item.used, remain: item.seats - item.used });
+              });
+          });
+          let arr = excel.export_json_to_array(key, data);
+          arr.unshift(title);
+          //arr.unshift(navi);
+          let ws = XLSX.utils.aoa_to_sheet(arr);
+          ws = excel.export_auto_width(ws, arr);
+          let wb = XLSX.utils.book_new();
+          let sheetName = proxy.$t(`purchase.sheetSummary`);
+          XLSX.utils.book_append_sheet(wb, ws, sheetName);
+          //分頁2 已領取清單
+          let titleTch = [proxy.$t(`purchase.schoolName`), proxy.$t(`purchase.schoolCode`), proxy.$t(`purchase.receiverName`), proxy.$t(`purchase.receiverId`)];
+          let keyTch = ['schoolName', 'shortCode', 'tmid', 'name'];
+          let dataTch = [];
+          Object.entries(res.school).forEach(([purchaseIdNow, purchaseSchoolData]) => {
+              purchaseSchoolData.forEach(function (item) {
+                  let schName = item.schoolName;
+                  let schCode = item.shortCode;
+                  item.tmids.forEach(function (userId) {
+                      let userInfo = res.teacher.filter(function (el) {
+                          return el.id == userId;
+                      });
+                      let userName = (userInfo.length > 0) ? userInfo[0]['name'] : '';
+                      dataTch.push({ schoolName: schName, shortCode: schCode, tmid: userId, name: userName });
+                  });
+              });
+          });
+          let arrTch = excel.export_json_to_array(keyTch, dataTch);
+          arrTch.unshift(titleTch);
+          let wsTch = XLSX.utils.aoa_to_sheet(arrTch);
+          wsTch = excel.export_auto_width(wsTch, arrTch);
+          let sheetNameTch = proxy.$t(`purchase.receivedList`);
+          XLSX.utils.book_append_sheet(wb, wsTch, sheetNameTch);
+          //檔案做成
+          let filename = proxy.$t(`purchase.authReceivedList`) + '_' + prodName + '_' + geoName;
+          XLSX.writeFile(wb, filename + '.xlsx', {
+              bookType: 'xlsx',
+              bookSST: true,
+              type: 'array'
+          });
+      });
+      
   }
   function quit() {
     routers.push('/login?htype=adminlogin')

+ 15 - 3
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -34,6 +34,7 @@ using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.BI;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using TEAMModelOS.SDK.Models.Service.BI;
+using JsonSerializer = System.Text.Json.JsonSerializer;
 
 namespace TEAMModelBI.Controllers.BISchool
 {
@@ -2618,19 +2619,30 @@ namespace TEAMModelBI.Controllers.BISchool
             List<object> result = new List<object>();
             try
             {
+                string prodCode = (jsonElement.TryGetProperty("prodCode", out JsonElement _prodCode)) ? _prodCode.GetString() : string.Empty;
+                string countryId = (jsonElement.TryGetProperty("countryId", out JsonElement _countryId)) ? _countryId.GetString() : string.Empty;
+                string provinceId = (jsonElement.TryGetProperty("provinceId", out JsonElement _provinceId)) ? _provinceId.GetString() : string.Empty;
+                string cityId = (jsonElement.TryGetProperty("cityId", out JsonElement _cityId)) ? _cityId.GetString() : string.Empty;
                 bool showTeacher = (jsonElement.TryGetProperty("showTeacher", out JsonElement _showTeacher)) ? _showTeacher.GetBoolean() : false;
                 var client = _azureCosmos.GetCosmosClient(name:"CoreServiceV2");
                 //取得縣市統購
-                string sqlPurchase = $"SELECT * FROM c ";
+                string sqlPurchase = $"SELECT * FROM c WHERE 1=1 ";
+                if (!string.IsNullOrWhiteSpace(prodCode)) sqlPurchase += $" AND c.prodCode = '{prodCode}' ";
+                if (!string.IsNullOrWhiteSpace(countryId)) sqlPurchase += $" AND c.countryId = '{countryId}' ";
+                if (!string.IsNullOrWhiteSpace(provinceId)) sqlPurchase += $" AND c.provinceId = '{provinceId}' ";
+                if (!string.IsNullOrWhiteSpace(cityId)) sqlPurchase += $" AND c.cityId = '{cityId}' ";
+                List<string> purchaseIdList = new List<string>();
                 List<PurchaseSeats> purchaseSeatsList = new List<PurchaseSeats>();
                 await foreach (PurchaseSeats item in client.GetContainer("Habb", "Auth").GetItemQueryIteratorSql<PurchaseSeats>(queryText: sqlPurchase, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("purchase") }))
                 {
                     purchaseSeatsList.Add(item);
+                    if (!purchaseIdList.Contains(item.id)) purchaseIdList.Add(item.id);
                 }
                 //取得學校
                 Dictionary<string, List<PurchaseSchoolSeats>> purchaseSchoolSeatsDic = new Dictionary<string, List<PurchaseSchoolSeats>>();
                 HashSet<string> teacherIds = new HashSet<string>();
-                string sqlSchool = $"SELECT * FROM c ";
+                string sqlSchool = $"SELECT * FROM c WHERE 1=1 ";
+                if (purchaseIdList.Count > 0) sqlSchool += $" AND ARRAY_CONTAINS({JsonSerializer.Serialize(purchaseIdList)}, c.purchaseId)";
                 await foreach (PurchaseSchoolSeats item in client.GetContainer("Habb", "Auth").GetItemQueryIteratorSql<PurchaseSchoolSeats>(queryText: sqlSchool, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("purchaseSchool") }))
                 {
                     if(!purchaseSchoolSeatsDic.ContainsKey(item.purchaseId))
@@ -2652,7 +2664,7 @@ namespace TEAMModelBI.Controllers.BISchool
                 }
                 //取得老師
                 List<PurchaseTeacher> teachers = new List<PurchaseTeacher>();
-                string teaIdListStr = System.Text.Json.JsonSerializer.Serialize(teacherIds);
+                string teaIdListStr = JsonSerializer.Serialize(teacherIds);
                 string sqlTeacher = $"SELECT c.id, c.name FROM c WHERE ARRAY_CONTAINS({teaIdListStr}, c.id, true)";
                 await foreach (PurchaseTeacher item in client.GetContainer("Core", "ID2").GetItemQueryIteratorSql<PurchaseTeacher>(queryText: sqlTeacher, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
                 {