20 Commits

Author SHA1 Message Date
minom a14fef3841 merge 2026-06-08 08:57:37 +08:00
minom 08d8e27bc9 批次報名 統計報表修正 2026-06-03 16:36:52 +08:00
minom 8affaaf344 統計報表,批次報名 2026-06-02 17:50:55 +08:00
minom 5abdad74bb 統計 2026-05-29 17:51:33 +08:00
minom 251c52311d 畫面調整,增加統計 2026-05-29 16:43:47 +08:00
minom a3a9968e62 功能調整 2026-05-28 17:52:06 +08:00
minom 2b9e266eb0 功能調整 2026-05-27 17:52:36 +08:00
minom 669b29d183 牌位設計功能調整 2026-05-26 17:55:36 +08:00
minom a58e6ac7ac 快速報名,功德內容設定調整:只要選擇預設版型 2026-05-25 17:41:32 +08:00
minom 889e5679aa 快速報名增加可選哪一次法會,複製另一個牌位內容成新一張牌位 2026-05-24 10:48:18 +08:00
minom 2724173216 返回 2026-05-22 18:02:52 +08:00
minom ccc188013b 快速報名 2026-05-22 17:51:57 +08:00
minom 77f642b2d2 快速報名 2026-05-21 17:47:47 +08:00
minom 9fa8ef90cf 快速報名 2026-05-19 17:32:46 +08:00
minom a1751e4b99 匯款沖帳功能調整 2026-05-15 17:04:49 +08:00
minom 07e29c32aa 調整匯款沖帳 2026-05-11 15:59:23 +08:00
minom 3c5617b403 merge dapper and 0.1 into dapper 2026-04-28 17:09:04 +08:00
minomdu 50c2203ebf Merge remote-tracking branch 'origin/0.1' into dapper
# Conflicts:
#	web/App_Code/Model/Model.Designer.cs
#	web/App_Code/Model/Model.cs
#	web/App_Code/Model/Model.edmx
#	web/web.config
2026-04-28 16:26:19 +08:00
EnChia 7644df57d0 1. 優化報名頁面:彈出查詢頁面、顯示剛新增資料
2. 新增報名頁面加上取消鍵
3. 優化登入頁面:按下 enter 自動換格/送出
4. 修復新增報名頁面中,不同 search_dialog 中的 page 參數相互連動之異常
5. 修改報名頁面列印格式
6. 修復報名頁面匯出功能
7. 優化報到功能
8. 報名頁面中,無查詢資料時不可點選匯出/列印按鈕
9. 匯出/列印報名管理報表時,若無資料則顯示提示
10. 修復列印管理報表後父視窗 UI 不能點擊的問題
11. 新增報名管理表單匯出 excel 功能
12. 於新增信眾、新增活動頁面加上取消鍵
13. 優化報名管理匯出功能:若篩選條件包含特定活動,自動於「匯出條件」欄位標註活動名稱
14. 優化報名查詢匯出功能:匯出之文件中加上「匯出條件」欄位
15. 修復信眾資料頁面中,使用「生日」作為篩選基準時,後續執行「列印查詢資料」與「匯出查詢資料」會報錯
16. 修復「列印信眾查詢」功能中,電話搜尋欄位未正確帶入查詢條件之異常
17. 解決中文輸入法輸入電話號碼的跳字問題
18. 新增品項管理介面排序功能
2026-04-21 09:03:57 +08:00
EnChia aa5941a324 1. 加上返回鍵
2. 修改報名記錄中的活動開始結束時間
3. 修正列印問題
4. 信眾資料及報名管理起始不會出現資料
5. 信眾不得重複報名相同活動
6. 信眾資料的 cache(含結果與搜尋條件)
7. 修復信眾、活動、品項刪除功能
8. 增加自訂是否自動編號
9. 優化信眾資料頁面(彈出查詢頁面、顯示剛新增資料)
10. 新增管理表單匯出 excel 功能
11. 無查詢資料時不可點選匯出/列印按鈕
12. 匯出/列印管理報表時,若無資料則顯示提示
13. 新增信眾資料時,加入日期預設為今日
2026-04-09 17:37:00 +08:00
66 changed files with 10131 additions and 753 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
// 已啟用模型 'C:\project\17168ERP\web\App_Code\Model\Model.edmx' 的 T4 程式碼產生。
// 已啟用模型 'D:\17168ERP\web\App_Code\Model\Model.edmx' 的 T4 程式碼產生。
// 若要啟用舊版程式碼產生,請將 [程式碼產生策略] 設計工具屬性的值
//變更為 [舊版 ObjectContext]。當模型在設計工具中開啟時,這個屬性便可
//以在 [屬性] 視窗中使用。
+1
View File
@@ -187,6 +187,7 @@ namespace Model
public string pageSize { get; set; }
public string printSize { get; set; }
public string defaultStyle { get; set; }
public Nullable<int> sort_order { get; set; }
public virtual actItem_kind actItem_kind { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
+22 -9
View File
@@ -94,6 +94,7 @@
<Property Name="pageSize" Type="varchar" MaxLength="50" />
<Property Name="printSize" Type="varchar" MaxLength="50" />
<Property Name="defaultStyle" Type="varchar" MaxLength="50" />
<Property Name="sort_order" Type="int" />
</EntityType>
<EntityType Name="actItem_files">
<Key>
@@ -607,8 +608,8 @@
</Key>
<Property Name="num" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="kind" Type="nvarchar" MaxLength="100" />
<Property Name="starttime" Type="time" Precision="0" />
<Property Name="offtime" Type="time" Precision="0" />
<Property Name="starttime" Type="time" Precision="7" />
<Property Name="offtime" Type="time" Precision="7" />
<Property Name="resttime" Type="int" />
<Property Name="root" Type="int" />
<Property Name="range" Type="int" />
@@ -1083,7 +1084,9 @@
</ReferentialConstraint>
</Association>
<Association Name="FK_act_bom_actItem1">
<End Role="actItem" Type="Self.actItem" Multiplicity="0..1" />
<End Role="actItem" Type="Self.actItem" Multiplicity="0..1">
<OnDelete Action="Cascade" />
</End>
<End Role="act_bom" Type="Self.act_bom" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="actItem">
@@ -1203,7 +1206,9 @@
</ReferentialConstraint>
</Association>
<Association Name="FK_activity_relating_activity">
<End Role="activity" Type="Self.activity" Multiplicity="1" />
<End Role="activity" Type="Self.activity" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="activity_relating" Type="Self.activity_relating" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="activity">
@@ -1629,7 +1634,9 @@
</ReferentialConstraint>
</Association>
<Association Name="FK_pro_order_activity">
<End Role="activity" Type="Self.activity" Multiplicity="0..1" />
<End Role="activity" Type="Self.activity" Multiplicity="0..1">
<OnDelete Action="Cascade" />
</End>
<End Role="pro_order" Type="Self.pro_order" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="activity">
@@ -1679,7 +1686,9 @@
</ReferentialConstraint>
</Association>
<Association Name="FK_pro_order_detail_pro_order">
<End Role="pro_order" Type="Self.pro_order" Multiplicity="1" />
<End Role="pro_order" Type="Self.pro_order" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="pro_order_detail" Type="Self.pro_order_detail" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="pro_order">
@@ -1691,7 +1700,9 @@
</ReferentialConstraint>
</Association>
<Association Name="FK_pro_order_followers">
<End Role="followers" Type="Self.followers" Multiplicity="0..1" />
<End Role="followers" Type="Self.followers" Multiplicity="0..1">
<OnDelete Action="Cascade" />
</End>
<End Role="pro_order" Type="Self.pro_order" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="followers">
@@ -2604,6 +2615,7 @@
<Property Name="pageSize" Type="String" MaxLength="50" FixedLength="false" Unicode="false" />
<Property Name="printSize" Type="String" MaxLength="50" FixedLength="false" Unicode="false" />
<Property Name="defaultStyle" Type="String" MaxLength="50" FixedLength="false" Unicode="false" />
<Property Name="sort_order" Type="Int32" ConcurrencyMode="None" />
</EntityType>
<EntityType Name="actItem_files">
<Key>
@@ -3053,8 +3065,8 @@
</Key>
<Property Name="num" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Name="kind" Type="String" MaxLength="100" FixedLength="false" Unicode="true" />
<Property Name="starttime" Type="Time" Precision="0" />
<Property Name="offtime" Type="Time" Precision="0" />
<Property Name="starttime" Type="Time" Precision="7" />
<Property Name="offtime" Type="Time" Precision="7" />
<Property Name="resttime" Type="Int32" />
<Property Name="root" Type="Int32" />
<Property Name="range" Type="Int32" />
@@ -5326,6 +5338,7 @@
<EntitySetMapping Name="actItems">
<EntityTypeMapping TypeName="Model.actItem">
<MappingFragment StoreEntitySet="actItem">
<ScalarProperty Name="sort_order" ColumnName="sort_order" />
<ScalarProperty Name="defaultStyle" ColumnName="defaultStyle" />
<ScalarProperty Name="printSize" ColumnName="printSize" />
<ScalarProperty Name="pageSize" ColumnName="pageSize" />
+1 -1
View File
@@ -9,7 +9,7 @@
<EntityTypeShape EntityType="Model.accounting_files" Width="1.5" PointX="15.75" PointY="27.75" IsExpanded="true" />
<EntityTypeShape EntityType="Model.accounting_kind" Width="1.5" PointX="11.25" PointY="27.625" IsExpanded="true" />
<EntityTypeShape EntityType="Model.accounting_kind2" Width="1.5" PointX="13.5" PointY="11.25" IsExpanded="true" />
<EntityTypeShape EntityType="Model.actItem" Width="1.5" PointX="9.25" PointY="7.875" IsExpanded="true" />
<EntityTypeShape EntityType="Model.actItem" Width="1.5" PointX="8.375" PointY="27.25" IsExpanded="true" />
<EntityTypeShape EntityType="Model.actItem_files" Width="1.5" PointX="16.5" PointY="5.875" IsExpanded="true" />
<EntityTypeShape EntityType="Model.actItem_kind" Width="1.5" PointX="6.125" PointY="7.875" IsExpanded="true" />
<EntityTypeShape EntityType="Model.activity" Width="1.5" PointX="3" PointY="8.875" IsExpanded="true" />
+3
View File
@@ -32,6 +32,9 @@ namespace Model.ViewModel
public string subject { get; set; }
public int? num { get; set; }
public string is_reconcile { get; set; }
public string up_time1 { get; set; }
public string up_time2 { get; set; }
public string deadline { get; set; }
}
+1
View File
@@ -43,6 +43,7 @@ namespace Model.ViewModel
public string country { get; set; }
public string country2 { get; set; }
public string phone_idcode { get; set; } // 電話/證號搜尋欄位
public string cellphone { get; set; }
}
public class followers_tablet
{
+12
View File
@@ -41,6 +41,13 @@ namespace Model.ViewModel
public string country { get; set; }
public string country2 { get; set; }
public List<pro_order_detail> details { get; set; }
public follower f_user { get; set; }
public List<string> orders { get; set; }
public Nullable<int> old_activity_num { get; set; }
}
public class pro_order_detail
@@ -83,6 +90,11 @@ namespace Model.ViewModel
public string follower_name { get; set; }
public string full_description { get; set; }
public string order_info { get; set; }
public string style { get; set; }
public int? change_item_num { get; set; }
public List<family_members> mid_items { get; set; }
public List<family_members> left_items { get; set; }
}
}
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Model.ViewModel
{
/// <summary>
/// statistic 的摘要描述
/// </summary>
public class statistic
{
public statistic()
{
//
// TODO: 在這裡新增建構函式邏輯
//
}
public int id { get; set; }
public int num { get; set; }
public int kind { get; set; }
public string kindName { get; set; }
public string order_no { get; set; }
public string subject { get; set; }
public string u_name { get; set; }
public string pay_type { get; set; }
public decimal amount { get; set; }
public float price { get; set; }
public string pay_mode { get; set; }
public string status { get; set; }
public int f_num { get; set; }
public int acc_num { get; set; }
public int acc_kind { get; set; }
public decimal check_amount { get; set; }
public decimal remain_amount { get; set; }
public string check_status { get; set; }
public int actItem_num { get; set; }
public string draft { get; set; }
public int qty { get; set; }
public int d_num { get; set; }
public string s_date { get; set; }
public string e_date { get; set; }
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ namespace MyWeb
public AdmItem info { get; set; }
//定義欄位cookie==================start
public class AdmItem
public class AdmItem
{
public int num { get; set; }
public string u_id { get; set; }
+208 -11
View File
@@ -103,17 +103,18 @@ public class FollowerController : ApiController
{
foreach (var item in prod)
{
foreach (var item2 in item.pro_order_detail1)
item2.from_id = null; //清空訂單明細的陽上報恩者from_id //f_num設定串聯刪除
//foreach (var item2 in item.pro_order_detail1)
// item2.from_id = null; //清空訂單明細的陽上報恩者from_id //f_num設定串聯刪除
foreach (var item2 in item.pro_order)
item2.introducer = null;
//foreach (var item2 in item.pro_order)
// item2.introducer = null;
item.leader = null;//清空leader
//item.leader = null;//清空leader
_db.followers.RemoveRange(prod);
}
_db.followers.RemoveRange(prod);
//_db.followers.RemoveRange(prod);
_db.SaveChanges();
Model.admin_log admin_log = new Model.admin_log();
@@ -136,6 +137,198 @@ public class FollowerController : ApiController
var count = _db.followers.Count();
return count;
}
[HttpPost]
[Route("api/follower/GetFollowerNew")]
public IHttpActionResult GetFollowerNew([FromBody] Model.ViewModel.follower q)
{
////var qry = _db.followers.Where(a => a.IsDel == false).AsQueryable();////不確定是否新增欄位? 先註解
var qry = _db.followers.AsQueryable();
if (!string.IsNullOrEmpty(q.f_number))
qry = qry.Where(o => o.f_number.Contains(q.f_number.Trim()));
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.u_name.Contains(q.u_name.Trim()));
if (q.birthday.HasValue)
qry = qry.Where(o => o.birthday >= q.birthday.Value);
if (q.birthday2.HasValue)
{
var tmpBirthday2 = Convert.ToDateTime(q.birthday2.Value).AddDays(1);
qry = qry.Where(o => o.birthday < tmpBirthday2);
}
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address != null && o.address.Contains(q.address.Trim()));
//if (q.num.HasValue && q.num.Value>0)
// qry = qry.Where(o => o.num==q.num.Value);
if (q.ept_self.HasValue && q.ept_self.Value)//排除自己
{
qry = qry.Where(o => o.num != q.num.Value);
}
if (!string.IsNullOrEmpty(q.country))
qry = qry.Where(o => o.country == q.country);
if (!string.IsNullOrEmpty(q.country2))
{
if (q.country2 == "1")
{
qry = qry.Where(o => o.country == "158");
}
else if (q.country2 == "2")
{
qry = qry.Where(o => o.country != "158");
}
}
//if (!string.IsNullOrEmpty(q.phone_idcode)){
// MyWeb.encrypt enc = new MyWeb.encrypt();
// qry = qry.Where(o => o.phone ==enc.DecryptAutoKey(q.phone_idcode) || o.cellphone==enc.DecryptAutoKey(q.phone_idcode));
//}
// 電話/證號搜尋 (使用 search_keywords HEX 編碼)
if (!string.IsNullOrEmpty(q.phone_idcode) && GlobalVariables.UseSearchKeywords)
{
MyWeb.encrypt enc = new MyWeb.encrypt();
string hexSearch = enc.ConvertToHex(q.phone_idcode.Trim());
if (!string.IsNullOrEmpty(hexSearch))
{
qry = qry.Where(o => o.search_keywords != null && o.search_keywords.Contains(hexSearch));
}
}
MyWeb.encrypt encrypt = new MyWeb.encrypt();
var tdesc = publicFun.enum_desc<Model.follower.type>();
var ret = new
{
list = qry.AsEnumerable().Select(x => new
{
num = x.num,
f_number = x.f_number,
u_name = x.u_name,
sex = x.sex,
birthday = x.birthday, //?.ToString("yyyy/MM/dd"),
birthday2 = publicFun.chagenDate(x.birthday), //?.ToString("yyyy/MM/dd"),
sign = Model.follower.chagenSign(x.birthday), //NULL??
sexagenary = Model.follower.sexagenary(x.birthday),
identity_type = x.identity_type,
identity_type_desc = tdesc[x.identity_type ?? 1],//TryGetValue..
id_code = x.id_code,
id_codeDes = encrypt.DecryptAutoKey(x.id_code),
passport = x.passport,
passportDes = encrypt.DecryptAutoKey(x.passport),
phone = x.phone,
phoneDes = encrypt.DecryptAutoKey(x.phone), //--MyWeb.function X
refugedate = x.refugedate,
refuge_name = x.refuge_name,
email = x.email,
address = x.address,
cellphone = x.cellphone,
cellphoneDes = encrypt.DecryptAutoKey(x.cellphone),
}).ToList()
};
if (ret.list == null) {
return Ok(new {result="N",message="查無信眾"});
} //throw new HttpResponseException(HttpStatusCode);
return Ok(ret);
}
[HttpPost]
[Route("api/follower/GetFollower")]
public IHttpActionResult GetFollower([FromBody] Model.ViewModel.follower q)
{
////var qry = _db.followers.Where(a => a.IsDel == false).AsQueryable();////不確定是否新增欄位? 先註解
var qry = _db.followers.AsQueryable();
if (!string.IsNullOrEmpty(q.f_number))
qry = qry.Where(o => o.f_number.Contains(q.f_number.Trim()));
if (!string.IsNullOrEmpty(q.u_name))
qry = qry.Where(o => o.u_name.Contains(q.u_name.Trim()));
if (q.birthday.HasValue)
qry = qry.Where(o => o.birthday >= q.birthday.Value);
if (q.birthday2.HasValue)
{
var tmpBirthday2 = Convert.ToDateTime(q.birthday2.Value).AddDays(1);
qry = qry.Where(o => o.birthday < tmpBirthday2);
}
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address != null && o.address.Contains(q.address.Trim()));
//if (q.num.HasValue && q.num.Value>0)
// qry = qry.Where(o => o.num==q.num.Value);
if (q.ept_self.HasValue && q.ept_self.Value)//排除自己
{
qry = qry.Where(o => o.num != q.num.Value);
}
if (!string.IsNullOrEmpty(q.country))
qry = qry.Where(o => o.country == q.country);
if (!string.IsNullOrEmpty(q.country2))
{
if (q.country2 == "1")
{
qry = qry.Where(o => o.country == "158");
}
else if (q.country2 == "2")
{
qry = qry.Where(o => o.country != "158");
}
}
// 電話/證號搜尋 (使用 search_keywords HEX 編碼)
if (!string.IsNullOrEmpty(q.phone_idcode) && GlobalVariables.UseSearchKeywords)
{
MyWeb.encrypt enc = new MyWeb.encrypt();
string hexSearch = enc.ConvertToHex(q.phone_idcode.Trim());
if (!string.IsNullOrEmpty(hexSearch))
{
qry = qry.Where(o => o.search_keywords != null && o.search_keywords.Contains(hexSearch));
}
}
MyWeb.encrypt encrypt = new MyWeb.encrypt();
var tdesc = publicFun.enum_desc<Model.follower.type>();
var ret = new
{
list = qry.AsEnumerable().Select(x => new
{
num = x.num,
f_number = x.f_number,
u_name = x.u_name,
sex = x.sex,
birthday = x.birthday, //?.ToString("yyyy/MM/dd"),
birthday2 = publicFun.chagenDate(x.birthday), //?.ToString("yyyy/MM/dd"),
sign = Model.follower.chagenSign(x.birthday), //NULL??
sexagenary = Model.follower.sexagenary(x.birthday),
identity_type = x.identity_type,
identity_type_desc = tdesc[x.identity_type ?? 1],//TryGetValue..
id_code=x.id_code,
id_codeDes=encrypt.DecryptAutoKey(x.id_code),
passport=x.passport,
passportDes=encrypt.DecryptAutoKey(x.passport),
phone = x.phone,
phoneDes = encrypt.DecryptAutoKey(x.phone), //--MyWeb.function X
refugedate = x.refugedate,
refuge_name = x.refuge_name,
email = x.email,
address = x.address,
cellphone = x.cellphone,
cellphoneDes = encrypt.DecryptAutoKey(x.cellphone),
}).FirstOrDefault()
};
if (ret.list == null) throw new HttpResponseException(HttpStatusCode.NotFound);
return Ok(ret);
}
[HttpPost]
[Route("api/follower/GetList")]
public IHttpActionResult GetList([FromBody] Model.ViewModel.follower q,
@@ -150,8 +343,10 @@ public class FollowerController : ApiController
qry = qry.Where(o => o.u_name.Contains(q.u_name.Trim()));
if (q.birthday.HasValue)
qry = qry.Where(o => o.birthday >= q.birthday.Value);
if (q.birthday2.HasValue)
qry = qry.Where(o => o.birthday < Convert.ToDateTime(q.birthday2.Value).AddDays(1));
if (q.birthday2.HasValue) {
var tmpBirthday2 = Convert.ToDateTime(q.birthday2.Value).AddDays(1);
qry = qry.Where(o => o.birthday < tmpBirthday2);
}
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address !=null && o.address.Contains(q.address.Trim()));
//if (q.num.HasValue && q.num.Value>0)
@@ -173,7 +368,7 @@ public class FollowerController : ApiController
}
}
// 電話/證號搜尋 (使用 search_keywords HEX 編碼)
if (!string.IsNullOrEmpty(q.phone_idcode) && GlobalVariables.UseSearchKeywords)
{
@@ -655,8 +850,10 @@ public class FollowerController : ApiController
list = orderrecord.Select(x => new
{
orderno = x.order_no,
startdate = x.reg_time,
endtime = x.up_time,
//startdate = x.reg_time,
//endtime = x.up_time,
startdate = x.activity.startDate_solar,
enddate = x.activity.endDate_solar,
pwcount = x.pro_order_detail.Where(a => a.actItem.act_bom.Where(b => b.item_num == a.actItem_num && b.package_num == null).Count() == 0).Count(),
amount = x.pro_order_detail.Select(o => (float?)o.price).Sum(),
activityname = x.activity.subject,
+210 -13
View File
@@ -1,14 +1,18 @@
using System;
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using Model;
using Newtonsoft.Json;
using Org.BouncyCastle.Crypto;
using PagedList;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using PagedList;
using Newtonsoft.Json;
using System.Collections;
using System.Web.Services;
using static TreeView;
using System.Data.Entity;
// api/activity
//[ezAuthorize(Roles = "admin")]//群組:*
@@ -126,6 +130,12 @@ public class activityController : ApiController
if (prod != null)
{
////prod.IsDel = true; ////不確定是否新增欄位? 先註解
// 先刪除子項目
var prod2 = _db.act_bom.Where(q => q.package_num == prod.num).ToList();
_db.act_bom.RemoveRange(prod2);
_db.actItems.Remove(prod);
_db.SaveChanges();
Model.admin_log admin_log = new Model.admin_log();
MyWeb.admin admin = new MyWeb.admin();//api裡不可以用MyWeb
@@ -190,12 +200,18 @@ public class activityController : ApiController
if (prod.Count() > 0)
{
//var prod2 = _db.actItem_files.AsEnumerable().Where(q => ids.Contains(Convert.ToInt32(q.actItem_num))).ToList();
var prod2 = _db.actItem_files.Where(q => ids.Contains(q.actItem_num)).ToList();
if (prod2.Count > 0)
{
_db.actItem_files.RemoveRange(prod2);
//_db.SaveChanges();
}
//var prod2 = _db.actItem_files.Where(q => ids.Contains(q.actItem_num)).ToList();
//if (prod2.Count > 0)
//{
// _db.actItem_files.RemoveRange(prod2);
// //_db.SaveChanges();
//}
// 先刪除子項目
var parentBoms = _db.act_bom.Where(q => q.item_num.HasValue && ids.Contains(q.item_num.Value)).ToList();
var parentIds = parentBoms.Select(x => x.num).ToList(); // 取得母件 id
var childBoms = _db.act_bom.Where(q => q.package_num.HasValue && parentIds.Contains(q.package_num.Value)).ToList();
_db.act_bom.RemoveRange(childBoms);
_db.actItems.RemoveRange(prod);
_db.SaveChanges();
@@ -219,6 +235,149 @@ public class activityController : ApiController
var count = _db.activities.Count();
return count;
}
[HttpPost]
[Route("api/activity/CopyActivity")]
public IHttpActionResult CopyActivity([FromBody] Model.ViewModel.activity act)
{
try
{
var activity = _db.activities.Where(x => x.num == act.num).AsNoTracking().FirstOrDefault();
var relations = _db.activity_relating.AsNoTracking().Where(x => x.activity_num == act.num).AsNoTracking().ToList();
activity.num = 0;
activity.subject = act.subject;
activity.startDate_solar = DateTime.Parse(act.up_time1);
activity.endDate_solar = DateTime.Parse(act.up_time2);
activity.dueDate = DateTime.Parse(act.deadline);
_db.activities.Add(activity);
_db.SaveChanges();
foreach (var item in relations)
{
item.num = 0;
item.activity_num = activity.num;
item.reg_time = DateTime.Now;
_db.activity_relating.Add(item);
}
_db.SaveChanges();
return Ok(new { result = "Y", message =$"完成 {activity.num} " });
}
catch (Exception ex)
{
return Ok(new {result="N",message=ex.Message});
// throw;
}
}
[HttpPost]
[Route("api/activity/GetListNew")]
public IHttpActionResult GetListNew([FromBody] Model.ViewModel.activity q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.activities.AsQueryable();
if (!string.IsNullOrEmpty(q.subject))
qry = qry.Where(o => o.subject.Contains(q.subject));
if (q.kind.HasValue && q.kind > 0)
{
var _subKinds = new TreeView().subKinds(_db.activity_kind.Select(o => new TreeItem()
{
num = o.num,
root = o.root,
}).ToList(), q.kind.Value);
//qry = qry.Where(o => o.kind == q.kind);
qry = qry.Where(o => o.kind == q.kind.Value || _subKinds.Any(s => s == o.kind));
}
if (!string.IsNullOrEmpty(q.up_time1))
{
var s = DateTime.Parse(q.up_time1);
qry = qry.Where(o => o.startDate_solar <=s&&o.endDate_solar>=s);
}
//if (!string.IsNullOrEmpty(q.up_time2))
//{
// var end = DateTime.Parse(q.up_time2);
// qry = qry.Where(o => o.endDate_solar<=end);
//}
if (!string.IsNullOrEmpty(q.kindTxt))
qry = qry.Where(o => o.activity_kind.kind.Contains(q.kindTxt));
if (sortBy.Equals("subject"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.subject);
else
qry = qry.OrderBy(o => o.subject);
}
else if (sortBy.Equals("kind"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.kind);
else
qry = qry.OrderBy(o => o.kind);
}
else if (sortBy.Equals("startDate_solar"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.startDate_solar);
else
qry = qry.OrderBy(o => o.startDate_solar);
}
else if (sortBy.Equals("endDate_solar"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.endDate_solar);
else
qry = qry.OrderBy(o => o.endDate_solar);
}
else if (sortBy.Equals("dueDate"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.dueDate);
else
qry = qry.OrderBy(o => o.dueDate);
}
else
qry = qry.OrderByDescending(o => o.num);
var count = qry.Count(); //pageSize = count;//一次取回??
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
var ret = new
{
list = qryList.Select(x => new
{
num = x.num,
subject = x.subject,
kind = x.kind,
kindTxt = x.kind.HasValue ? x.activity_kind.kind : "",
kindsTxt = x.kind.HasValue ? new TreeView().kindText(_db.activity_kind.Select(o => new TreeItem()
{
kind = o.kind,
num = o.num,
root = o.root,
}).ToList(), x.kind) : "",
startDate_solar = x.startDate_solar,
endDate_solar = x.endDate_solar,
startDate_lunar = x.startDate_lunar,
endDate_lunar = x.endDate_lunar,
dueDate = x.dueDate,
orderCounts = _db.pro_order.Where(y => y.activity_num == x.num).Count(),
}),
count = count,
};
if (ret.list == null) throw new HttpResponseException(HttpStatusCode.NotFound);
return Ok(ret);
}
[HttpPost]
[Route("api/activity/GetList")]
public IHttpActionResult GetList([FromBody] Model.ViewModel.activity q, int page, int pageSize = 10,
@@ -314,6 +473,36 @@ public class activityController : ApiController
return Ok(ret);
}
public class SortOrderRequest
{
public List<int> ids { get; set; }
}
[HttpPost]
[Route("api/activity/SaveItemList")]
public IHttpActionResult UpdateSortOrder([FromBody] SortOrderRequest request)
{
if (request == null || request.ids == null) return BadRequest();
using (Model.ezEntities _db = new Model.ezEntities())
{
int totalCount = request.ids.Count;
for (int i = 0; i < totalCount; i++)
{
int id = request.ids[i];
var item = _db.actItems.FirstOrDefault(x => x.num == id);
if (item != null)
{
// 改成總數減去索引,這樣第一筆 (i=0) 會拿到最大的數字
item.sort_order = totalCount - i;
}
}
_db.SaveChanges();
}
return Ok();
}
[HttpPost]
[Route("api/activity/GetItemList")]
public IHttpActionResult GetItemList([FromBody] Model.ViewModel.actItem q, int page, int pageSize = 10,
@@ -418,8 +607,14 @@ public class activityController : ApiController
else
qry = qry.OrderBy(o => o.status);
}
else
else if (sortBy.Equals("num"))
{
qry = qry.OrderByDescending(o => o.num);
}
else
{
qry = qry.OrderByDescending(o => o.sort_order);
}
var tdesc = publicFun.enum_desc<Model.activity.category>();
var count = qry.Count(); //pageSize = count;//一次取回??
@@ -905,6 +1100,7 @@ public class activityController : ApiController
{
text = x.actItem?.subject,
val = x.actItem_num,
style=x.actItem.defaultStyle
},
price = x.price ?? 0,
qty = x.qty ?? 0,
@@ -956,6 +1152,7 @@ public class activityController : ApiController
{
text = x.actItem?.subject,
val = x.actItem_num,
style=x.actItem.defaultStyle
},
price = x.price ?? 0,
qty = x.qty ?? 0,
@@ -1303,7 +1500,7 @@ public class activityController : ApiController
[Route("api/activity/OrderCheckIn")]
public IHttpActionResult OrderCheckIn([FromBody] Model.activity_check item)
{
if (item.f_num.HasValue && item.activity_num.HasValue && item.qty.HasValue && item.status.HasValue)
if (item.f_num.HasValue && item.activity_num.HasValue && item.status.HasValue)
{
//同一天不能簽到兩次以上
Model.activity_check check = _db.activity_check
+392 -18
View File
@@ -1,17 +1,24 @@
using System;
using DocumentFormat.OpenXml.Drawing.Charts;
using Model;
using MyWeb;
using Newtonsoft.Json;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using Org.BouncyCastle.Utilities.Encoders;
using PagedList;
using System;
using System.Activities.Expressions;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.IdentityModel.Metadata;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Helpers;
using System.Web.Http;
using PagedList;
using Newtonsoft.Json;
using System.Collections;
using static TreeView;
using Model;
using System.IdentityModel.Metadata;
using MyWeb;
using DocumentFormat.OpenXml.Drawing.Charts;
// api/order
@@ -200,13 +207,233 @@ public class orderController : ApiController
var count = _db.pro_order.Count();
return count;
}
[HttpPost]
[Route("api/order/BatchSave")]
public IHttpActionResult BatchSave([FromBody] Model.ViewModel.pro_order po)
{
try
{
var query = _db.pro_order.AsNoTracking().Where(x => x.activity_num == po.old_activity_num).AsQueryable();
query = query.Include(x => x.pro_order_detail).AsNoTracking();
query = query.Where(x => po.orders.Contains(x.order_no));
var list = query.ToList();
List<Model.pro_order> orders = new List<Model.pro_order>();
List<Model.pro_order_detail> order_details = new List<pro_order_detail>();
var act=_db.activities.Where(x=>x.num==po.activity_num).FirstOrDefault();
var hasList = _db.pro_order.AsNoTracking().Where(x => x.activity_num == po.activity_num).AsNoTracking().ToList();
List<int> rp=new List<int>();
//要檢查是否已經報名
foreach (var item in list)
{
var already=hasList.Where(x => x.f_num == item.f_num).FirstOrDefault();
if (already != null&&!string.IsNullOrEmpty(already.order_no))
{
rp.Add((int)already.f_num);
continue;
}
//Model.pro_order order = new Model.pro_order();
//order.phone = item.phone;
//order.follower1 = item.follower1;
//order.address = item.address;
//order.activity_num = po.activity_num;
//order.up_time = DateTime.Now;
//order.reg_time = DateTime.Now;
////order.follower = item.follower;//加了會去寫followers
//order.f_num = item.f_num;
//order.keyin1 = item.keyin1;
//order.introducer = item.introducer;
//order.send_receipt = item.send_receipt;
//order.receipt_title = item.receipt_title;
//order.order_no = createOrderNumber();
item.reg_time= DateTime.Now;
item.up_time= DateTime.Now;
item.order_no= createOrderNumber();
item.activity_num= po.activity_num;
_db.pro_order.Add(item);
orders.Add(item);
foreach (var detail in item.pro_order_detail)
{
//Model.pro_order_detail order_detail = new Model.pro_order_detail();
//order_detail.order_no = order.order_no;
//order_detail.actItem_num = detail.actItem_num;
//order_detail.parent_num = detail.parent_num;
//order_detail.print_id = detail.print_id;
//order_detail.f_num = detail.f_num;
//order_detail.f_num_tablet = detail.f_num_tablet;
//order_detail.from_id = detail.from_id;
//order_detail.from_id_tablet = detail.from_id_tablet;
//order_detail.price = detail.price;
//order_detail.qty = detail.qty;
//order_detail.printed_files = detail.printed_files;
//order_detail.style = detail.style;
//order_detail.UpdateTime=DateTime.Now;
//order_details.Add(order_detail);
detail.order_no=item.order_no;
detail.UpdateTime= DateTime.Now;
detail.start_date= act.startDate_solar;
detail.due_date= act.endDate_solar;
_db.pro_order_detail.Add(detail);
}
}
//_db.pro_order.AddRange(orders);
//_db.SaveChanges();
//_db.pro_order_detail.AddRange(order_details);
_db.SaveChanges();
return Ok(new {result="Y",message="完成",data=rp});
}
catch (Exception ex)
{
return Ok(new { result = "Y",message=ex.Message+ex.StackTrace });
}
}
[HttpPost]
[Route("api/order/AddOrderWithDetail")]
public IHttpActionResult AddOrderWithDetail([FromBody] Model.ViewModel.pro_order q)
{
try
{
MyWeb.encrypt encrypt = new MyWeb.encrypt();
//先塞信眾
Model.follower follower = new Model.follower();
follower.u_name = q.f_user.u_name;
follower.phone = encrypt.EncryptAutoKey(q.f_user.phone);
follower.cellphone = encrypt.EncryptAutoKey(q.f_user.cellphone);
follower.country = q.f_user.country;
follower.id_code = q.f_user.id_code;
follower.sex = q.f_user.sex;
follower.identity_type = q.f_user.identity_type;
follower.birthday = q.f_user.birthday;
string sex = q.f_user.sex;
follower.f_number = Model.follower.generate_f_number(sex);
_db.followers.Add(follower);
_db.SaveChanges();
int _id = follower.num;//取回信眾的sql server自動編號
Hashtable ht= new Hashtable();
//再塞親友
foreach (var detail in q.details)
{
//要考慮相同名字只能塞一次,下一個遇到要幫他塞回num
foreach (var mid in detail.mid_items)
{
if (ht.ContainsKey(mid.fam_name))
{
mid.num = int.Parse(ht[mid.fam_name].ToString());
continue;
}
Model.family_members fam = new Model.family_members();
fam.follower_num = _id;
fam.fam_name = mid.fam_name;
fam.deceased = mid.deceased;
_db.family_members.Add(fam);
_db.SaveChanges();
mid.num = fam.num;
ht.Add(mid.fam_name, fam.num);
}
foreach (var left in detail.left_items)
{
if (ht.ContainsKey(left.fam_name))
{
left.num =int.Parse( ht[left.fam_name].ToString());
continue;
}
Model.family_members fam = new Model.family_members();
fam.follower_num = _id;
fam.fam_name = left.fam_name;
fam.deceased = left.deceased;
_db.family_members.Add(fam);
_db.SaveChanges();
left.num = fam.num;
ht.Add(left.fam_name, fam.num);
}
}
//再來報名主檔
Model.pro_order order = new Model.pro_order();
order.order_no = createOrderNumber();
order.up_time = DateTime.Now;
order.reg_time = DateTime.Now;
order.keyin1 = "A01";
order.f_num = _id;
order.phone = !string.IsNullOrEmpty(q.f_user.phone)?
encrypt.EncryptAutoKey(q.f_user.phone): encrypt.EncryptAutoKey(q.f_user.cellphone);
order.activity_num = q.activity_num;
order.address = q.f_user.address;
order.demo = "";
order.send_receipt=false;
_db.pro_order.Add(order);
//最後功德項目
foreach (var detail in q.details)
{
Model.pro_order_detail o_detail = new Model.pro_order_detail();
o_detail.order_no = order.order_no;
o_detail.actItem_num = detail.actItem_num;
o_detail.price = detail.price;
o_detail.qty = detail.qty;
o_detail.printed_files = detail.printed_files;
o_detail.style = detail.style;
Newtonsoft.Json.Linq.JArray mid_items = new Newtonsoft.Json.Linq.JArray();
foreach (var data in detail.mid_items)
{
Newtonsoft.Json.Linq.JObject item = new Newtonsoft.Json.Linq.JObject();
item["num"] = data.num;
item["fam_name"] = data.fam_name;
item["fam_gender"] = "";
item["deceased"] = data.deceased;
item["fam_title"] = "";
item["option_break"] = false;
item["IsShuWen"] = false;
mid_items.Add(item);
}
Newtonsoft.Json.Linq.JArray left_items = new Newtonsoft.Json.Linq.JArray();
foreach (var data in detail.left_items)
{
Newtonsoft.Json.Linq.JObject item = new Newtonsoft.Json.Linq.JObject();
item["num"] = data.num;
item["fam_name"] = data.fam_name;
item["fam_gender"] = "";
item["deceased"] = data.deceased;
item["fam_title"] = "";
item["option_break"] = false;
item["IsShuWen"] = false;
left_items.Add(item);
}
Newtonsoft.Json.Linq.JObject tablet = new Newtonsoft.Json.Linq.JObject();
tablet["mid_items"] = mid_items;
tablet["left_items"] = left_items;
o_detail.f_num_tablet = tablet.ToString();
_db.pro_order_detail.Add(o_detail);
}
_db.SaveChanges();
return Ok(new
{
result = "Y",message="報名成功"
});
}
catch (Exception ex)
{
return Ok(new
{
result = "N",message=ex.Message
});
}
}
[HttpPost]
[Route("api/order/GetList")]
public IHttpActionResult GetList([FromBody] Model.ViewModel.pro_order q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
{
var qry = _db.pro_order.AsQueryable();
MyWeb.encrypt encrypt = new MyWeb.encrypt();
var qry = _db.pro_order.Include("activity").Include("activity.activity_check").AsQueryable();
//var aIDt = _db.actItems.AsEnumerable().Where(f => f.subject.Contains(q.actItemTxt.Trim())).Select(f => f.num);//品項
@@ -221,7 +448,10 @@ public class orderController : ApiController
if (q.up_time1.HasValue)
qry = qry.Where(o => o.up_time >= q.up_time1.Value);
if (q.up_time2.HasValue)
qry = qry.Where(o => o.up_time < Convert.ToDateTime(q.up_time2.Value).AddDays(1));
{
var tmp_up_time2 = Convert.ToDateTime(q.up_time2.Value).AddDays(1);
qry = qry.Where(o => o.up_time < tmp_up_time2);
}
if (!string.IsNullOrEmpty(q.address))
qry = qry.Where(o => o.address.Contains(q.address.Trim()));
if (!string.IsNullOrEmpty(q.subject))
@@ -255,6 +485,21 @@ public class orderController : ApiController
}
}
if (q.f_user!=null&&!string.IsNullOrEmpty(q.f_user.u_name)) {
qry = qry.Where(o => o.f_num != null && o.follower != null && o.follower.u_name ==q.f_user.u_name);
}
if (q.f_user != null && !string.IsNullOrEmpty(q.f_user.phone))
{
qry = qry.Where(o => o.f_num != null && o.follower != null && encrypt.DecryptAutoKey(o.follower.phone) == q.f_user.phone);
}
if (q.f_user != null && !string.IsNullOrEmpty(q.f_user.cellphone))
{
qry = qry.Where(o => o.f_num != null && o.follower != null && encrypt.DecryptAutoKey(o.follower.cellphone) == q.f_user.cellphone);
}
if (q.f_user != null && !string.IsNullOrEmpty(q.f_user.id_code))
{
qry = qry.Where(o => o.f_num != null && o.follower != null && encrypt.DecryptAutoKey(o.follower.id_code) == q.f_user.id_code);
}
if (sortBy.Equals("order_no"))
{
@@ -291,10 +536,23 @@ public class orderController : ApiController
else
qry = qry.OrderBy(o => o.activity != null ? o.activity.subject : "");
}
else if(sortBy.Equals("status"))
{
if (sortDesc)
qry = qry.OrderByDescending(o => o.activity.activity_check.FirstOrDefault(a => o.activity_num == a.activity_num && o.f_num == a.f_num).status ?? 0);
else
qry = qry.OrderBy(o => o.activity.activity_check.FirstOrDefault(a => o.activity_num == a.activity_num && o.f_num == a.f_num).status ?? 0);
}
else
qry = qry.OrderByDescending(o => o.reg_time);
var count = qry.Count(); //pageSize = count;//一次取回??
if (pageSize==-1)
{
pageSize = count;
}
if(pageSize==0)
pageSize = 10;
var ret = new
{
list = qry.ToPagedList(page, pageSize).Select(x => new
@@ -307,6 +565,7 @@ public class orderController : ApiController
keyin1 = x.keyin1,
up_time = x.up_time,
keyin1_txt = Model.pro_order.keyin1_value_to_text(x.keyin1),
status = x.activity.activity_check.FirstOrDefault(a => x.activity_num == a.activity_num && x.f_num == a.f_num)?.status ?? 0,
}),
count = count
};
@@ -316,7 +575,7 @@ public class orderController : ApiController
return Ok(ret);
}
[HttpPost]
[HttpPost,HttpGet]
[Route("api/order/GetItemList")]
public IHttpActionResult GetItemList([FromBody] Model.ViewModel.pro_order_detail q, int page, int pageSize = 10,
string sortBy = "", bool sortDesc = false)
@@ -338,7 +597,7 @@ public class orderController : ApiController
//var qry1 = _db.pro_order_detail.AsEnumerable();
//qry1 = qry1.Where(o => o.order_no == order_no);
//var qry1 = prod.pro_order_detail.AsEnumerable();
var qry1 = prod.pro_order_detail.AsQueryable();
var qry1 = prod.pro_order_detail.AsQueryable().include(o=>o.pro_order.activity.activity_relating);
//if (!string.IsNullOrEmpty(q.subject))
// qry = qry.Where(o => o.subject.Contains(q.subject));
@@ -379,13 +638,19 @@ public class orderController : ApiController
var ret = new
{
list = qry1_list.Select(x => new
list = qry1_list.Select(x => {
var tmpActivityRelating = x.pro_order?.activity?.activity_relating?.Where(a => a.actItem_num == x.actItem_num).FirstOrDefault();
return new
{
has_yang_limit = tmpActivityRelating?.has_yang_limit ?? false,
has_chao_limit = tmpActivityRelating?.has_chao_limit ?? false,
yang_limit_count = tmpActivityRelating?.yang_limit_count ?? 0,
chao_limit_count = tmpActivityRelating?.chao_limit_count ?? 0,
id = i++,
num = x.num,
order_no = x.order_no,
actitem_num_selected = new
{
{
text = x.actItem_num.HasValue ? x.actItem.subject : "",
val = x.actItem_num.HasValue ? x.actItem_num.Value : 0,
},
@@ -499,6 +764,10 @@ public class orderController : ApiController
text = ar.actItem.subject,
val = ar.actItem_num,
},
has_yang_limit = ar.has_yang_limit ?? false,
has_chao_limit = ar.has_chao_limit ?? false,
yang_limit_count = ar.yang_limit_count ?? 0,
chao_limit_count = ar.chao_limit_count ?? 0,
parent_num = q.num,
f_num_selected = new
{
@@ -579,6 +848,10 @@ public class orderController : ApiController
text = x.actItem.subject,
val = x.actItem.num,
},
has_yang_limit = x.has_yang_limit,
has_chao_limit = x.has_chao_limit,
yang_limit_count = x.yang_limit_count,
chao_limit_count = x.chao_limit_count,
f_num_selected = new
{
text = "",
@@ -663,11 +936,112 @@ public class orderController : ApiController
{
return NotFound();
}
}
[HttpPost]
[Route("api/order/SaveWithDetails")]
public IHttpActionResult SaveWithDetails([FromBody] Model.ViewModel.pro_order item)
{
try
{
MyWeb.encrypt encrypt = new MyWeb.encrypt();
Model.pro_order new_pro_order = new Model.pro_order();//新增
new_pro_order.order_no = createOrderNumber();
var details = item.details;
var last_order_no = details[0].order_no;
//撈前次報名的資料塞進去
var last_order = _db.pro_order.Where(x => x.order_no == last_order_no).FirstOrDefault();
if (last_order != null)
{
new_pro_order.up_time = DateTime.Now;
new_pro_order.reg_time = DateTime.Now;
new_pro_order.phone = last_order.phone;
new_pro_order.address = last_order.address;
new_pro_order.keyin1 = last_order.keyin1;
new_pro_order.f_num = item.f_num;
new_pro_order.activity_num = item.activity_num;
new_pro_order.demo = last_order.demo;
new_pro_order.customize_data = last_order.customize_data;
new_pro_order.send_receipt = last_order.send_receipt;
new_pro_order.receipt_title = last_order.receipt_title;
_db.pro_order.Add(new_pro_order);
foreach (var item1 in details)
{//如果item1 change_item_num 有值且不為0,表示要複製過來
var last_detail = _db.pro_order_detail.Where(x => x.num == item1.num).FirstOrDefault();
var detail = new pro_order_detail();
detail.order_no = new_pro_order.order_no;
detail.actItem_num = (item1.change_item_num!=null&&item1.change_item_num!=0)?item1.change_item_num: last_detail.actItem_num;
detail.f_num_tablet = last_detail.f_num_tablet;
detail.from_id = (item1.change_item_num != null && item1.change_item_num != 0) ? item.f_num: last_detail.from_id;
detail.price = (item1.change_item_num != null && item1.change_item_num != 0) ? item1.price : last_detail.price;
detail.qty = (item1.change_item_num != null && item1.change_item_num != 0) ? item1.qty : last_detail.qty;
detail.printed_files = (item1.change_item_num != null && item1.change_item_num != 0) ? item1.printed_files : last_detail.printed_files;
detail.style = (item1.change_item_num != null && item1.change_item_num != 0) ? item1.style : last_detail.style;
detail.UpdateTime = DateTime.Now;
detail.start_date = DateTime.Now;//這兩個要用活動的
detail.due_date = DateTime.Now;//
_db.pro_order_detail.Add(detail);
}
_db.SaveChanges();
return Ok(new
{
result = "Y",
message = "報名成功"
});
}
else
{
return Ok(new
{
result = "N",
message = "沒有勾選任何功德項目"
});
}
}
catch (Exception ex)
{
return Ok(new
{
result = "N",
message = ex.Message+ex.InnerException+ex.StackTrace
});
}
}
protected string createOrderNumber()
{
//Application.Lock();
string order_no = "AA" + DateTime.Now.ToString("yyMMdd");
var qry = _db.companies.AsQueryable();
//var prod = qry.Where(q => q.last_order_no.Contains(order_no)).FirstOrDefault();
var prod = qry.Where(q => q.num == 1).FirstOrDefault();
if (prod != null)
{
if (!string.IsNullOrEmpty(prod.last_order_no) && prod.last_order_no.Contains(order_no))
{
int tmp = Convert.ToInt32(prod.last_order_no.Replace(order_no, "")) + 1;
order_no = order_no + tmp.ToString("0000");
}
else
{
order_no = order_no + "0001";
}
prod.last_order_no = order_no;
_db.SaveChanges();
}
else
order_no = "";
//Application.UnLock();
return order_no;
}
[HttpPost]
[Route("api/order/SaveDetailData")]
@@ -684,7 +1058,7 @@ public class orderController : ApiController
.Where(q => q.num == item.num)
.FirstOrDefault();//修改
if (order != null)
{
{
order.actItem_num = (item.actItem_num.HasValue && item.actItem_num.Value > 0)
? item.actItem_num : null;
order.f_num = (item.f_num.HasValue && item.f_num.Value > 0)
+228 -1
View File
@@ -1,6 +1,16 @@
using System;
using DocumentFormat.OpenXml.Wordprocessing;
using MINOM.COM.Utility;
using Model.ViewModel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.SS.UserModel;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Http;
@@ -135,4 +145,221 @@ public class statisticsController: ApiController
};
return Ok(data);
}
[HttpPost]
[Route("api/statistics/GetStatistic")]
public IHttpActionResult GetStatistic(string statistic_mode)
{
try
{
var drafts = (from a in _db.transfer_register
select a).ToList();
string rootPath = HttpRuntime.AppDomainAppPath;
string name = $"output{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx";
string fileName = Path.Combine(new string[] { rootPath, "rpt", name });
var file = new FileInfo($"{fileName}"); // 檔案路徑
using (var excel = new ExcelPackage())
{
LogUtility log = new LogUtility();
// 建立分頁
var ws = excel.Workbook.Worksheets.Add("MySheet");
var data = (from a in _db.activities
join b in _db.pro_order on a.num equals b.activity_num
join c in _db.pro_order_detail on b.order_no equals c.order_no
//join d in _db.activity_relating on a.num equals d.activity_num
join f in _db.actItems on c.actItem_num equals f.num
select new
{
a.num,
a.subject,
b.order_no,
b.f_num,
order_detail_num = c.num,
c.actItem_num,
item_subject = f.subject,
c.qty,
c.price
}).ToList();
int i = 0;
i++;
if (statistic_mode == "01")
{
ws.Cells[i, 1].Value = "序號";
ws.Cells[i, 2].Value = "法會名稱";
ws.Cells[i, 3].Value = "功德項目";
ws.Cells[i, 4].Value = "數量";
ws.Cells[i, 5].Value = "單價";
ws.Cells[i, 6].Value = "小計";
ws.Cells[i, 7].Value = "實收";
ws.Cells[i, 8].Value = "應收";
ws.Cells[i, 9].Value = "應付";
i++;
var list = data.GroupBy(x => new { x.num, x.subject, x.actItem_num, x.item_subject }).Select(y =>
new
{
y.Key.num,
y.Key.subject,
y.Key.actItem_num,
y.Key.item_subject,
qty = y.Where(q => q.num == y.Key.num && q.actItem_num == y.Key.actItem_num).Select(q => q.qty).Sum(),
price = y.Where(p => p.num == y.Key.num && p.actItem_num == y.Key.actItem_num).Select(p => p.price).FirstOrDefault()
}).ToList();
foreach (var item in list)
{
ws.Cells[i, 1].Value = i - 1;
ws.Cells[i, 2].Value = item.subject;
ws.Cells[i, 3].Value = item.item_subject;
ws.Cells[i, 4].Value = item.qty.Value;
ws.Cells[i, 5].Value = item.price.Value;
ws.Cells[i, 6].Formula = $"=D{i}*E{i}";
var draftList = (from a in drafts
where a.activity_num == item.num
select a).ToList();
decimal payed = 0;
foreach (var items in draftList)
{
if (items.draft != null)
{
if (items.draft .StartsWith("["))
{
var jj = JsonConvert.DeserializeObject<JArray>(items.draft);
log.writeLogPath(jj.ToString());
foreach (var j in jj)
{
if (item.subject == j["activity_name"].ToString() &&
item.item_subject == j["actitem_name"].ToString())
{
payed = payed + decimal.Parse(j["reconcile"].ToString());
}
}
}
else
{
var jj = JsonConvert.DeserializeObject<JObject>(items.draft);
foreach (var j in jj["pro_order_detail_items"])
{
if (item.subject == j["activity_name"].ToString() &&
item.item_subject == j["actitem_name"].ToString())
{
payed = payed + decimal.Parse(j["reconcile"].ToString());
}
}
}
}
}
ws.Cells[i, 7].Value = payed;
ws.Cells[i, 8].Formula = $"=if(F{i}>G{i},F{i}-G{i},0)";
ws.Cells[i, 9].Formula = $"=if(F{i}<G{i},G{i}-F{i},0)";
i++;
}
}
else if (statistic_mode == "02")
{
var list = (from a in _db.pro_order
join b in _db.pro_order_detail on a.order_no equals b.order_no
join c in _db.followers on a.f_num equals c.num
join d in _db.activities on a.activity_num equals d.num
select new
{
a.order_no,
a.f_num,
a.activity_num,
b.qty,
b.price,
c.u_name
}).ToList();
ws.Cells[i, 1].Value = "序號";
ws.Cells[i, 2].Value = "信眾編號";
ws.Cells[i, 3].Value = "信眾姓名";
ws.Cells[i, 4].Value = "總金額";
ws.Cells[i, 5].Value = "實收";
ws.Cells[i, 6].Value = "應收";
ws.Cells[i, 7].Value = "應付";
i++;
var g = list.GroupBy(y => new { y.f_num, y.u_name }).Select(y => new
{
y.Key.f_num,
y.Key.u_name,
total = y.Where(x => x.f_num == y.Key.f_num).Select(x => x.price * x.qty).Sum()
});
foreach (var item in g)
{
ws.Cells[i, 1].Value = i - 1;
ws.Cells[i, 2].Value = item.f_num;
ws.Cells[i, 3].Value = item.u_name;
ws.Cells[i, 4].Value = item.total;
var dd = (from a in drafts
where a.f_num == item.f_num
select a).ToList();
decimal payed = 0;
foreach (var d in dd)
{
payed = payed + (decimal)d.check_amount;// -(decimal)d.remain_amount;
}
ws.Cells[i, 5].Value = payed;
ws.Cells[i, 6].Formula = $"=if(D{i}>E{i},D{i}-E{i},0)";
ws.Cells[i, 7].Formula = $"=if(D{i}<E{i},E{i}-D{i},0)";
i++;
}
}
else if (statistic_mode == "03")
{
var list = (from a in _db.activities
join b in _db.pro_order on a.num equals b.activity_num
join c in _db.pro_order_detail on b.order_no equals c.order_no
join d in _db.followers on b.f_num equals d.num
select new
{
a.num,a.subject,b.order_no,order_detail_num=c.num,
b.f_num,d.u_name,c.price,c.qty
}).ToList();
ws.Cells[i, 1].Value = "序號";
ws.Cells[i, 2].Value = "法會名稱";
ws.Cells[i, 3].Value = "信眾姓名";
ws.Cells[i, 4].Value = "總金額";
ws.Cells[i, 5].Value = "實收";
ws.Cells[i, 6].Value = "應收";
ws.Cells[i, 7].Value = "應付";
i++;
var g = list.GroupBy(y => new { y.num, y.subject, y.f_num, y.u_name }).Select(y=>new
{
y.Key.num,y.Key.subject,y.Key.f_num,y.Key.u_name,
total=y.Where(x=>x.num==y.Key.num&&x.f_num==y.Key.f_num).Select(x=>x.price*x.qty).Sum()
});
foreach (var item in g)
{
ws.Cells[i, 1].Value = i - 1;
ws.Cells[i, 2].Value = item.subject;
ws.Cells[i, 3].Value = item.u_name;
ws.Cells[i, 4].Value = item.total;
var dd = drafts.Where (y=>y.activity_num==item.num&&y.f_num==item.f_num).
Select(y => new { y.check_amount, y.remain_amount }).ToList();
ws.Cells[i, 5].Value = dd.Sum(y=>y.check_amount);
ws.Cells[i, 6].Formula = $"=if(D{i}>E{i},D{i}-E{i},0)";
ws.Cells[i, 7].Formula = $"=if(D{i}<E{i},E{i}-D{i},0)";
i++;
}
}
// 儲存 Excel
excel.SaveAs(file);
}
return Ok(new { result = "Y", filename = $"rpt/{name}" });
}
catch (Exception ex)
{
return Ok(new { result = "N", message = ex.Message});
//return BadRequest($"{ex.Message}{ex.StackTrace}");
}
}
}
@@ -10,6 +10,7 @@ using System.Collections;
using MyWeb;
using System.Web.WebPages;
using System.Data.Entity;
using Model;
[ezAuthorize]
public class transfer_registerController : ApiController
@@ -269,6 +270,7 @@ public class transfer_registerController : ApiController
public string check_memo { get; set; }
public string draft { get; set; }
public int? acc_kind { get; set; } // 新增關聯欄位
public int? kind { get; set; }
}
[HttpPost]
@@ -315,7 +317,7 @@ public class transfer_registerController : ApiController
{
uptime = dto.check_date,
category = 1, // 收入
kind = 27, // 固定值:法會收入/功德項目
kind = dto.kind,//27, // 固定值:法會收入/功德項目
kind2 = dto.acc_num,
price = (float)(dto.check_amount ?? 0),
tax = 0,
@@ -346,6 +348,7 @@ public class transfer_registerController : ApiController
item.check_memo = dto.check_memo;
item.draft = dto.draft;
item.acc_kind = dto.acc_kind;
}
}
_db.SaveChanges();
@@ -958,7 +961,10 @@ public class transfer_registerController : ApiController
activity_name = x.activity != null ? x.activity.subject : "",
activity_num = x.activity_num,
acc_name = x.acc_num != null ? _db.accounting_kind2.Where(a => a.num == x.acc_num).Select(a => a.kind).FirstOrDefault() : "",
price_totals=_db.pro_order_detail.
Where(a => _db.pro_order.Where (po=>po.f_num==x.f_num&&po.activity_num==x.activity_num).
Select(po => po.order_no).Any(p=>p.Equals(a.order_no))).Sum(a => a.price*a.qty),
pay_totals=_db.transfer_register.Where(a=>a.activity_num==x.activity_num&&a.f_num==x.f_num).Sum(a=>a.check_amount),
// pro_order_record 資訊 (透過 transfer_id 關聯)
pro_order_records = x.pro_order_record.Select(pr => new {
pr.num,
@@ -1274,4 +1280,23 @@ public class transfer_registerController : ApiController
}
}
}
}
}
//select ord.order_no, ord.activity_num, ord.f_num, ord.u_name, ord.totals as cost, isnull(reg.totals, 0) totals
//from (
//select o.order_no, o.activity_num, o.f_num, o.u_name, sum(o.totals) as totals from (
//select a.order_no, a.f_num, d.u_name, a.activity_num, b.num, b.f_num_tablet,
//b.price, b.qty, totals = b.price * b.qty, c.kind, c.subject
//from pro_order a
//left join pro_order_detail b on a.order_no=b.order_no
//left join actItem c on b.actItem_num=c.num
//left join followers d on a.f_num=d.num )o
//group by o.order_no, o.activity_num, o.f_num, o.u_name) ord
//left join (
//select a.activity_num, a.f_num, sum(a.amount) totals from transfer_register a
//where status='2' and check_status='99'
//group by a.activity_num, a.f_num
//) reg on ord.activity_num=reg.activity_num and ord.f_num=reg.f_num
@@ -8,13 +8,14 @@
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>C:\17168web</PublishUrl>
<PublishUrl>D:\17168web</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<PrecompileBeforePublish>true</PrecompileBeforePublish>
<EnableUpdateable>true</EnableUpdateable>
<DebugSymbols>false</DebugSymbols>
<WDPMergeOption>DonotMerge</WDPMergeOption>
<WDPMergeOption>CreateSeparateAssembly</WDPMergeOption>
<UseFixedNames>true</UseFixedNames>
</PropertyGroup>
</Project>
@@ -16,6 +16,8 @@
<asp:Repeater ID="Repeater2" runat="server" OnItemDataBound="Repeater2_ItemDataBound">
<ItemTemplate>
<a class="nav-link" href="<%#ResolveUrl(ValString(Eval("url"))) %>"
onclick="sessionStorage.removeItem('member_list_cache'); sessionStorage.removeItem('member_query_params');
sessionStorage.removeItem('order_list_cache'); sessionStorage.removeItem('order_query_params');"
target="<%#(ValString(Eval("target"))=="B"?"_blank":"_self") %>">
<%#Eval("title") %></a>
</ItemTemplate>
+122 -1
View File
@@ -2,6 +2,11 @@
<%@ Register Src="~/admin/_uc/alert.ascx" TagPrefix="uc1" TagName="alert" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
<style>
.v-dialog{
width:80% !important;
}
</style>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="page_nav" runat="Server">
<div class="mb-2 mb-sm-0">
@@ -53,7 +58,19 @@
search: {
kind: 0,
subject: '',
}
},
signData: {
subject: "",
up_time1: "",
up_time2: "",
deadline: "",
num:null
},
startmenu: false,
endmenu: false,
deadmenu: false,
copy_dialog: { show: false, show_confirm: false, message: "" },
message_dialog: { show: false, show_confirm: false, message: "" },
}
},
watch: {
@@ -84,6 +101,30 @@
editItem(item) {
console.log("edit", item);
},
copyItem(item) {
this.signData.num=item.num
this.copy_dialog.show = true
},
gotoCopy() {
if (this.signData.subject == "" || this.signData.up_time1 == "" || this.signData.up_time2 == "" || this.signData.deadline == "") {
this.message_dialog.show = true
this.message_dialog.message = "所有欄位都必須填寫"
return false;
}
axios
.post(HTTP_HOST + "api/activity/CopyActivity", this.signData)
.then(response => {
this.message_dialog.show = true
this.message_dialog.message = response.data.message
if (response.data.result === "Y") {
this.copy_dialog.show = false;
}
})
.catch(error => console.log(error))
},
deleteItem(item) {
if (confirm('是否確定刪除此筆資料?')) {
const index = this.data_table.list.indexOf(item)
@@ -166,6 +207,7 @@
</template>
<template #item.slot_btn="{ item }">
<a :href="'reg.aspx?num='+item.num" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-pencil-box-outline"></i>修改</a>
<a @click="copyItem(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-pencil-box-outline"></i>複製</a>
<a @click="deleteItem(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-trash-can"></i>刪除</a>
</template>
</v-data-table>
@@ -194,6 +236,85 @@
</v-row>
</v-container>
</div>
<v-dialog v-model="message_dialog.show" class="dialog_width">
<template>
<v-card>
<v-card-title>
{{ message_dialog.message }}
</v-card-title>
<v-card-text>
<v-btn @click.prevent="message_dialog.show=false" v-if="!message_dialog.show_confirm">關閉</v-btn>
</v-card-text>
</v-card>
</template>
</v-dialog>
<v-dialog v-model="copy_dialog.show" class="dialog_width">
<template>
<v-card>
<v-card-title>
<v-text-field v-model="signData.subject"
label="請輸入法會名稱"></v-text-field>
<v-menu v-model="startmenu"
:close-on-content-click="false"
:nudge-right="40"
transition="scale-transition"
offset-y
min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="signData.up_time1"
label="開始日期"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"></v-text-field>
</template>
<v-date-picker v-model="signData.up_time1"
@input="startmenu = false"></v-date-picker>
</v-menu>
<v-menu v-model="endmenu"
:close-on-content-click="false"
:nudge-right="40"
transition="scale-transition"
offset-y
min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="signData.up_time2"
label="結束日期"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"></v-text-field>
</template>
<v-date-picker v-model="signData.up_time2"
@input="endmenu = false"></v-date-picker>
</v-menu>
<v-menu v-model="deadmenu"
:close-on-content-click="false"
:nudge-right="40"
transition="scale-transition"
offset-y
min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="signData.deadline"
label="報名截止日"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"></v-text-field>
</template>
<v-date-picker v-model="signData.deadline"
@input="deadmenu = false"></v-date-picker>
</v-menu>
</v-card-title>
<v-card-text>
<v-btn @click.prevent="copy_dialog.show=false">關閉</v-btn>
<v-btn @click.prevent="gotoCopy()">確認</v-btn>
</v-card-text>
</v-card>
</template>
</v-dialog>
<div id="print_data">
</div>
</asp:Content>
+216 -115
View File
@@ -7,19 +7,20 @@
<div class="mb-2 mb-sm-0">
<ul class="nav ps-0">
<li class="nav-item pe-3">
<select class="form-select" v-model="search.kind" @change="btn_search">
<select class="form-select" v-model="search.kind" @change="btn_search" :disabled="isEditing">
<option value="">選擇分類</option>
<option v-for="item in itemKindList" :value="item.num">{{item.kind}}</option>
</select>
</li>
<li class="nav-item pe-1">
<a href="item_reg.aspx" class="btn btn-primary">
<a href="item_reg.aspx" class="btn btn-primary" :class="{ 'disabled': isEditing }" >
<i class="mdi mdi-plus"></i>新增
</a>
</li>
<li class="nav-item pe-1">
<a @click="deleteAll" class="btn btn-outline-danger" title="刪除勾選的資料" ><i class="mdi mdi-trash-can"></i> 刪除勾選</a>
</li>
<li class="nav-item pe-1">
<a @click="deleteAll" class="btn btn-outline-danger" title="刪除勾選的資料" :class="{ 'disabled': isEditing }" ><i class="mdi mdi-trash-can"></i> 刪除勾選</a>
</li>
</ul>
<%-- <div class="input-group mb-3" data-search-control="search1" @click="search_show(search_dialog.controls.search1)">
<input class="form-control search-text" type="text" readonly
@@ -29,26 +30,31 @@
<i class="mdi mdi-view-list-outline"></i>
</button>
</div>--%>
</div>
</div>
<div>
<div class="">
<asp:LinkButton ID="excel" runat="server" CssClass="btn btn-outline-success" OnClick="excel_Click"><span class="fa-solid fa-file-excel"></span> 匯出Excel</asp:LinkButton>
<a v-if="!isEditing" @click="editClick" class="btn btn-outline-secondary" title="編輯排列順序"><i class="mdi mdi-swap-vertical"></i> 編輯排列順序</a>
<a v-else @click="editClick" class="btn btn-outline-secondary" title="完成編輯排列順序"><i class="mdi mdi-swap-vertical"></i> 完成編輯</a>
<div :style="isEditing ? 'pointer-events: none; opacity: 0.5;' : ''" style="display:inline-block;">
<asp:LinkButton ID="excel" runat="server" CssClass="btn btn-outline-success" OnClick="excel_Click" ><span class="fa-solid fa-file-excel"></span> 匯出Excel</asp:LinkButton>
</div>
</div> </div>
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" runat="Server">
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.14.0/Sortable.min.js"></script>
<script>
Vue.filter('timeString', function (value, myFormat) {
return value == null || value == "" ? "" : moment(value).format(myFormat || 'YYYY-MM-DD, HH:mm:ss');
});
let VueApp=new Vue({
let VueApp = new Vue({
el: '#app',
vuetify: new Vuetify(vuetify_options),
data() {
return {
isEditing: false,
options: { multiSort: false },
data_table: {
loading: true,
@@ -63,20 +69,20 @@
{ text: '項目分類', value: 'kindsTxt' },
{ text: '類別', value: 'categoryTxt' },
{ text: '預設金額', value: 'price' },
{ text: '庫存狀態', value: 'stock' },
{ text: '庫存狀態', value: 'stock' },
{ text: '停用', value: 'status' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' },
],
footer:{
footer: {
showFirstLastPage: true,
itemsPerPageOptions:[5,10,20,30],
itemsPerPageOptions: [5, 10, 20, 30],
},
},
search: {
kind: '',
subject: '',
selltime1:'',
selltime1: '',
selltime2: '',
uptime1: '',
uptime2: '',
@@ -98,7 +104,7 @@
{ id: 'kind', title: '品項分類', value: '' },
],
selected: {},
select(t,data) {
select(t, data) {
data.search.kind = t.num;
data.btn_search()
console.log("select search1", t);
@@ -120,9 +126,9 @@
}, snackbar: {
show: false,
text: "",
}, itemKindList:{
}, itemKindList: {
num: 0,
kind:''
kind: ''
}
}
},
@@ -149,9 +155,9 @@
if (!isNaN(actItemPage) && actItemPage !== 1) {
this.options.page = actItemPage;
}
}
}
},
methods: {
methods: {
initKindList() {
axios
//.get(HTTP_HOST + 'api/activity_kind')
@@ -174,26 +180,45 @@
},
getDefault(clearpage = false) {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: sortBy[0], sortDesc: sortDesc[0],
page: clearpage ? '1' :page, pageSize: itemsPerPage
};
this.data_table.loading = true
axios
.post(HTTP_HOST + 'api/activity/GetItemList', this.search, { params: params })
.then(response => {
this.data_table.list = response.data.list
this.data_table.count = response.data.count;
this.data_table.loading = false
})
.catch(error => console.log(error))
},
editItem(item) {
console.log("edit", item);
if (this.isEditing) {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: "sort_order", sortDesc: sortDesc[0],
page: clearpage ? '1' : page, pageSize: 0
};
this.data_table.loading = true
axios
.post(HTTP_HOST + 'api/activity/GetItemList', this.search, { params: params })
.then(response => {
this.data_table.list = response.data.list
this.data_table.count = response.data.count;
this.data_table.loading = false
})
.catch(error => console.log(error))
}
else {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: sortBy[0], sortDesc: sortDesc[0],
page: clearpage ? '1' : page, pageSize: itemsPerPage
};
this.data_table.loading = true
axios
.post(HTTP_HOST + 'api/activity/GetItemList', this.search, { params: params })
.then(response => {
this.data_table.list = response.data.list
this.data_table.count = response.data.count;
this.data_table.loading = false
})
.catch(error => console.log(error))
}
},
deleteItem(item) {
editItem(item) {
console.log("edit", item);
},
deleteItem(item) {
if (confirm('是否確定刪除此筆資料?')) {
const index = this.data_table.list.indexOf(item)
if (index != -1) {
@@ -203,7 +228,7 @@
this.getDefault();
})
.catch(error => console.log(error))
}
}
}
},
deleteAll() {
@@ -225,100 +250,155 @@
.catch(error => console.log(error))
}
},
btn_search() {
editClick() {
if (this.isEditing) {
this.isEditing = false;
const sortedIds = this.data_table.list.map(item => item.num);
axios.post(HTTP_HOST + 'api/activity/SaveItemList', { ids: sortedIds })
.then(response => {
this.isEditing = false;
this.getDefault();
})
.catch(error => {
alert("儲存排序失敗:" + error);
})
.finally(() => {
this.data_table.loading = false;
});
}
else {
this.isEditing = true;
this.getDefault()
}
},
btn_search() {
this.getDefault(true)
},
btn_all() {
clearObjProps(this.search);
this.btn_search()
},
//===
search_show(curr) {
//console.log("btn_click:", curr, curr.api_url);
this.search_dialog.current = curr;
this.search_clear()
//this.search_get()//清除完自動會重抓, 故取消
this.search_dialog.show = true;
},
search_clear() {
if (!this.search_dialog.current.keys) return;
this.search_dialog.current.keys.forEach((t, i) => { t.value = '' })
this.search_get()
},
search_get() {
if (!this.search_dialog.current.keys) return;
let api_url = this.search_dialog.current.api_url;
let keys = this.search_dialog.current.keys;
//const { page, itemsPerPage } = this.options
//const { sortBy, sortDesc, page, itemsPerPage } = this.options
this.search_dialog.page = this.options.page ?? 1
let params = { page: this.search_dialog.page, pageSize: 10 };//url params
var search = {};//post body
keys.forEach((t, i) => {
search[t.id] = t.value;
});
},
//===
search_show(curr) {
//console.log("btn_click:", curr, curr.api_url);
this.search_dialog.current = curr;
this.search_clear()
//this.search_get()//清除完自動會重抓, 故取消
this.search_dialog.show = true;
},
search_clear() {
if (!this.search_dialog.current.keys) return;
this.search_dialog.current.keys.forEach((t, i) => { t.value = '' })
this.search_get()
},
search_get() {
if (!this.search_dialog.current.keys) return;
let api_url = this.search_dialog.current.api_url;
let keys = this.search_dialog.current.keys;
//const { page, itemsPerPage } = this.options
//const { sortBy, sortDesc, page, itemsPerPage } = this.options
this.search_dialog.page = this.options.page ?? 1
let params = { page: this.search_dialog.page, pageSize: 10 };//url params
var search = {};//post body
keys.forEach((t, i) => {
search[t.id] = t.value;
});
console.log("search_get", api_url, search, params, this.options);
this.search_dialog.loading = true
axios.post(api_url, search, { params: params })
.then(response => {
this.search_dialog.list = response.data.list
this.search_dialog.count = response.data.count
this.search_dialog.loading = false
console.log("search_get", api_url, search, params, this.options);
this.search_dialog.loading = true
axios.post(api_url, search, { params: params })
.then(response => {
this.search_dialog.list = response.data.list
this.search_dialog.count = response.data.count
this.search_dialog.loading = false
})
.catch(error => {
console.log(error)
this.search_dialog.list = []
this.search_dialog.count = 0
this.search_dialog.loading = false
this.snackbar.text = "錯誤:" + error
this.snackbar.show = true
})
},
search_headers() {
if (!this.search_dialog.current.columns) return;
r = [];
this.search_dialog.current.columns.forEach((t, i) => {
r.push({
text: t.title,
align: 'start',
sortable: false,
value: t.id,
})
})
.catch(error => {
console.log(error)
this.search_dialog.list = []
this.search_dialog.count = 0
this.search_dialog.loading = false
this.snackbar.text = "錯誤:" + error
this.snackbar.show = true
})
},
search_headers() {
if (!this.search_dialog.current.columns) return;
r = [];
this.search_dialog.current.columns.forEach((t, i) => {
r.push({
text: t.title,
align: 'start',
sortable: false,
value: t.id,
})
})
return r
},
search_select(row) {
let curr = this.search_dialog.current;
let target = $(`[data-search-control=${curr.id}]`);
curr.selected = row;
target.children("input.search-text").val(curr.selected[curr.text_prop])//text
target.children("input:hidden").val(curr.selected[curr.value_prop])//value
if (curr.select instanceof Function) {
curr.select(row,this);
}
this.search_dialog.show = false;
},
return r
},
search_select(row) {
let curr = this.search_dialog.current;
let target = $(`[data-search-control=${curr.id}]`);
curr.selected = row;
target.children("input.search-text").val(curr.selected[curr.text_prop])//text
target.children("input:hidden").val(curr.selected[curr.value_prop])//value
if (curr.select instanceof Function) {
curr.select(row, this);
}
this.search_dialog.show = false;
},
saveOrder(event) {
const movedItem = this.data_table.list.splice(event.oldIndex, 1)[0];
this.data_table.list.splice(event.newIndex, 0, movedItem);
},
},
computed: {
pageCount() {
return Math.ceil(this.data_table.count / this.data_table.pageSize)
},
computedHeaders() {
return this.data_table.header.map(h => {
if (h.value === 'slot_btn') {
return { ...h, sortable: false };
}
return {
...h,
sortable: this.isEditing ? false : (h.sortable !== false)
};
});
},
},
filters: {
currency: function (value) {
return value == null || value == "" ? "" :
('$' + parseFloat(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, "$1,").replace(".00", ""));
},
}
},
directives: {
sortableDataTable: {
bind(el, binding, vnode) {
const options = {
animation: 150,
onUpdate: function (event) {
vnode.child.$emit('sorted', event)
}
}
Sortable.create(el.getElementsByTagName('tbody')[0], options)
}
}
},
})
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<uc1:alert runat="server" ID="L_msg" Text="" />
<div id="content" class="container-fluid">
<v-data-table
<v-data-table
v-sortable-data-table
@sorted="saveOrder"
:headers="computedHeaders"
v-model="data_table.selected"
:items="data_table.list"
:search-props="search"
@@ -332,8 +412,29 @@
show-select
hide-default-footer
:page.sync="data_table.page"
:items-per-page.sync="data_table.pageSize"
:items-per-page.sync= "isEditing ? -1 :data_table.pageSize"
class="elevation-1">
<template v-slot:header.data-table-select="{ on, props }">
<v-simple-checkbox
v-if="!isEditing"
v-bind="props"
v-on="on"
></v-simple-checkbox>
<v-icon v-else small>mdi-swap-vertical</v-icon>
</template>
<template v-slot:item.data-table-select="{ item, isSelected, select }">
<v-simple-checkbox
v-if="!isEditing"
:value="isSelected"
@input="select($event)"
></v-simple-checkbox>
<div v-else class="handle" style="cursor: grab;">
<v-icon color="grey darken-1">mdi-drag-vertical</v-icon>
</div>
</template>
<template #item.price="{ item }" >
{{item.price | currency }}
</template>
@@ -352,12 +453,12 @@
</template>
<template #item.slot_btn="{ item }">
<a :href="'item_reg.aspx?num='+item.num" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-pencil-box-outline"></i>修改</a>
<a @click="deleteItem(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-trash-can"></i>刪除</a>
<template #item.slot_btn="{ item }" >
<a v-if="!isEditing" :href="'item_reg.aspx?num='+item.num" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-pencil-box-outline"></i>修改</a>
<a v-if="!isEditing" @click="deleteItem(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-trash-can"></i>刪除</a>
</template>
</v-data-table>
<v-container>
<v-container v-if="!isEditing">
<v-row class="align-baseline" wrap>
<v-col cols="12" md="9">
<v-pagination
+1 -1
View File
@@ -148,7 +148,7 @@ public partial class admin_activity_index2 : MyWeb.config
}
var tdesc = publicFun.enum_desc<Model.activity.category>();
qry = qry.OrderByDescending(o => o.num);
qry = qry.OrderByDescending(o => o.sort_order);
var list = qry.ToList();
if (list.Count > 0)
{
+45 -72
View File
@@ -199,19 +199,24 @@
val: 0
},
},
style_datas: [],
paper_datas: [],
defaultStyle: "",
paperID: "",
printID:""
}
},
mounted() {
this.search_dialog.current = this.search_dialog.controls.search1
//console.log("mounted");
this.initialize()
},
watch: {
options: {
handler() {
//console.log("watch1", this.search_dialog, this.search_dialog.page);
this.search_get()
console.log("watch2", this.search_dialog, this.search_dialog.page);
},
},
deep: true,
},
optionsDetail: {
@@ -248,7 +253,6 @@
if (this.this_id == "")
search['status'] = "Y";//啟用
}
console.log("search_get", api_url, search, params, this.options);
this.search_dialog.loading = true
axios.post(api_url, search, { params: params })
.then(response => {
@@ -289,7 +293,7 @@
else {
}
console.log("search_select", row, editem);
//debugger;
target.children("input.search-text").val(curr.selected[curr.text_prop])//text
target.children("input:hidden").val(curr.selected[curr.value_prop])//value
@@ -336,46 +340,19 @@
)
}
axios
.post(HTTP_HOST + 'api/tablet/GetPaperSize', {})
.then(response => {
console.log(response);
if (response.status == "200") {
let data = response.data;
if (data.result == "Y") {
data.data.forEach(x => {
this.paperlist.push({ name: x.paperName, id: x.paperID, width: x.width, height: x.height })
$('#<%= ddlPageSize.ClientID %>').append(`<option value="${x.paperID}">${x.paperName}</option>`);
$('#<%= ddlPrintSize.ClientID %>').append(`<option value="${x.paperID}">${x.paperName}</option>`);
});
$('#<%= ddlPageSize.ClientID %>').val($("#<%= hidPageSize.ClientID %>").val())
$('#<%= ddlPrintSize.ClientID %>').val($("#<%= hidPrintSize.ClientID %>").val())
}
}
})
.catch(
error => console.log(error)
)
this.paper_datas.length = 0
this.style_datas.length = 0
axios
.post(HTTP_HOST + 'api/tablet/GetStyleData', {})
.then(response => {
console.log(response);
if (response.status == "200") {
let data = response.data;
if (data.result == "Y") {
data.data.forEach(x => {
if (x.styleID != "000001") {
this.stylelist.push({ styleID: x.styleID, name: x.name })
$('#<%= ddlDefaultStyle.ClientID %>').append(`<option value="${x.styleID}">${x.name}</option>`);
//$("#defaultStyle").append(`<option value="${x.styleID}">${x.name}</option>`);
}
});
$('#<%= ddlDefaultStyle.ClientID %>').val($("#<%= hidDefaultStyle.ClientID %>").val())
}
}
})
this.style_datas = data.data
this.defaultStyle=$("#<%=hidDefaultStyle.ClientID %>").val()
}
}})
.catch(
error => console.log(error)
)
@@ -492,7 +469,7 @@
bom_editItem(item) {
this.bom_editedIndex = this.bom_list.indexOf(item);
this.bom_editedItem = $.extend(true, {}, item);
console.log("bom_editItem:", this.bom_editedIndex, this.bom_editedItem);
//debugger;
},
bom_deleteItem(item) {
@@ -572,13 +549,13 @@
}
},
changeSel(selType) {
if (selType=="ddlPageSize") {
$("#<%= hidPageSize.ClientID %>").val($("#<%= ddlPageSize.ClientID %>").val())
} else if (selType == "ddlPrintSize") {
$("#<%= hidPrintSize.ClientID %>").val($("#<%=ddlPrintSize.ClientID %>").val())
} else if (selType == "ddlDefaultStyle") {
$("#<%= hidDefaultStyle.ClientID %>").val($("#<%= ddlDefaultStyle.ClientID %>").val())
}
let style = this.style_datas.find(x => x.styleID == this.defaultStyle)
this.paperID = style.paperSize
this.printID = style.printSize
$("#<%= hidPageSize.ClientID %>").val(this.paperID)
$("#<%=hidPrintSize.ClientID %>").val(this.printID)
$("#<%=hidDefaultStyle.ClientID %>").val(this.defaultStyle)
}
},
computed: {
@@ -659,47 +636,43 @@
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">料號</label>
<%-- <label class="col-sm-2 col-form-label">料號</label>
<div class="col-sm-4">
<asp:TextBox ID="PARTNO" MaxLength="100" runat="server" CssClass="form-control" placeholder="請輸入料號"></asp:TextBox>
</div>
</div>--%>
<label class="col-sm-2 col-form-label">項目名稱 *</label>
<div class="col-sm-4">
<asp:TextBox ID="subject" MaxLength="100" runat="server" CssClass="form-control" placeholder="請輸入品項名稱"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" ControlToValidate="subject" runat="server" ErrorMessage="必填!" Display="Dynamic" SetFocusOnError="true"></asp:RequiredFieldValidator>
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">牌位編號開頭</label>
<div class="col-sm-4">
<asp:TextBox ID="print_init" MaxLength="20" runat="server" CssClass="form-control" placeholder="請輸入牌位編號開頭"></asp:TextBox>
</div>
<asp:TextBox ID="print_init" MaxLength="20" runat="server" CssClass="form-control" placeholder="請輸入牌位編號開頭"></asp:TextBox>
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">預設金額</label>
<div class="col-sm-4">
<asp:TextBox ID="price" MaxLength="7" runat="server" CssClass="form-control" placeholder="請輸入預設金額"></asp:TextBox>
<asp:RegularExpressionValidator ControlToValidate="price" Display="Dynamic" SetFocusOnError="true" ErrorMessage="只能輸入數字" ID="RegularExpressionValidator3" runat="server" ValidationExpression="^(-?\d+)(\.\d+)?$" />
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">預設頁面尺寸</label>
<div class="col-sm-4">
<select ID="ddlPageSize" runat="server" onchange="VueApp.changeSel('ddlPageSize')"></select>
<asp:HiddenField ID="hidPageSize" runat="server" />
</div>
<label class="col-sm-2 col-form-label">預設列印尺寸</label>
<label class="col-sm-2 col-form-label">預設版型</label>
<div class="col-sm-4">
<select ID="ddlPrintSize" runat="server" onchange="VueApp.changeSel('ddlPrintSize')"></select>
<asp:HiddenField ID="hidPrintSize" runat="server" />
<v-select
:items="style_datas"
item-text="name" @change="changeSel('ddlDefaultStyle')"
item-value="styleID" v-model="defaultStyle">
</v-select>
<%-- <select ID="ddlDefaultStyle" runat="server"
onchange="VueApp.changeSel('ddlDefaultStyle')"></select>--%>
<asp:HiddenField ID="hidDefaultStyle" runat="server" />
<asp:HiddenField ID="hidPrintSize" runat="server" />
<asp:HiddenField ID="hidPageSize" runat="server" />
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">預設版型</label>
<div class="col-sm-4">
<select ID="ddlDefaultStyle" runat="server" onchange="VueApp.changeSel('ddlDefaultStyle')"></select>
<asp:HiddenField ID="hidDefaultStyle" runat="server" />
</div>
</div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" class="row mb-1 label-sm-right">
<ContentTemplate>
@@ -734,12 +707,12 @@
<asp:TextBox ID="demo" runat="server" CssClass="ckeditor" TextMode="MultiLine"></asp:TextBox>
</div>
</div>
<div class="row mb-1 label-sm-right">
<%-- <div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">自定義欄位預設值</label>
<div class="col-sm-10">
<asp:TextBox ID="customize_data" runat="server" Rows="3" TextMode="MultiLine" CssClass="form-control" placeholder="一行輸入一個定義欄位預設值,如:&#10;$品名:醬油&#10;$金額:100" ToolTip="一行輸入一個定義欄位預設值,格式: $變數名稱:值"></asp:TextBox>
</div>
</div>
</div>--%>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-form-label">狀態 *</label>
@@ -909,7 +882,7 @@
</v-card-title>
<v-card-text >
<v-row>
<v-col v-for="item in search_dialog.current.keys"
<v-col v-for="item in search_dialog.current.keys" :key="item.value"
:cols="search_dialog.current.keys.length>1?6:12" >
<v-text-field v-model="item.value" :label="item.title" v-if="item.visible===undefined || item.visible==true "></v-text-field>
</v-col>
+17 -15
View File
@@ -42,21 +42,21 @@ public partial class admin_activity_item_reg : MyWeb.config
BuildKind();
subject.Text = prod.subject;
print_init.Text = prod.print_init;
PARTNO.Text = prod.partno;
//PARTNO.Text = prod.partno;
//kind.SelectedValue = prod.kind.ToString();
if (!isStrNull(prod.pageSize))
{
ddlPageSize.Value = prod.pageSize.ToString();
//ddlPageSize.Value = prod.pageSize.ToString();
hidPageSize.Value = prod.pageSize.ToString();
}
if (!isStrNull(prod.printSize))
{
ddlPrintSize.Value = prod.printSize.ToString();
//ddlPrintSize.Value = prod.printSize.ToString();
hidPrintSize.Value = prod.printSize.ToString();
}
if (!isStrNull(prod.defaultStyle))
{
ddlDefaultStyle.Value = prod.defaultStyle.ToString();
//ddlDefaultStyle.Value = prod.defaultStyle.ToString();
hidDefaultStyle.Value = prod.defaultStyle.ToString();
}
if (prod.kind.HasValue)
@@ -78,7 +78,7 @@ public partial class admin_activity_item_reg : MyWeb.config
if (prod.cycle.HasValue)
cycle.Text = prod.cycle.Value.ToString();
customize_data.Text = prod.customize_data?.ToString();
//customize_data.Text = prod.customize_data?.ToString();
edit.Visible = true;
@@ -133,20 +133,21 @@ public partial class admin_activity_item_reg : MyWeb.config
L_msg.Text = "";
Model.actItem actItem = new Model.actItem();//新增
int maxSort = _db.actItems.Max(x => (int?)x.sort_order) ?? 0;
actItem.subject = subject.Text;
actItem.print_init = print_init.Text;
actItem.partno = PARTNO.Text;
if (!isStrNull(ddlPageSize.Value))
//actItem.partno = PARTNO.Text;
if (!isStrNull(hidPageSize.Value))
{
actItem.pageSize = ddlPageSize.Value;
actItem.pageSize = hidPageSize.Value;
}
if (!isStrNull(ddlPrintSize.Value))
if (!isStrNull(hidPrintSize.Value))
{
actItem.printSize = ddlPrintSize.Value;
actItem.printSize = hidPrintSize.Value;
}
if (!isStrNull(ddlDefaultStyle.Value))
if (!isStrNull(hidDefaultStyle.Value))
{
actItem.defaultStyle = (ddlDefaultStyle.Value);
actItem.defaultStyle = (hidDefaultStyle.Value);
}
//if (!isStrNull(kind.SelectedValue)) { actItem.kind = Val(kind.SelectedValue); } else { actItem.kind = null; }
if (!isStrNull(category.SelectedValue)) { actItem.category = Val(category.SelectedValue); } else { actItem.category = null; }
@@ -157,7 +158,8 @@ public partial class admin_activity_item_reg : MyWeb.config
actItem.extend = extend.Checked ? "Y" : "N";
actItem.is_reconcile = is_reconcile_item.Checked ? "Y" : "N";
actItem.demo = demo.Text;
actItem.customize_data = customize_data.Text;
//actItem.customize_data = customize_data.Text;
actItem.sort_order = maxSort + 1;
_db.actItems.Add(actItem);
_db.SaveChanges();
@@ -194,7 +196,7 @@ public partial class admin_activity_item_reg : MyWeb.config
{
actItem.subject = subject.Text;
actItem.print_init = print_init.Text;
actItem.partno = PARTNO.Text;
//actItem.partno = PARTNO.Text;
if (!isStrNull(hidPageSize.Value))
{
actItem.pageSize = hidPageSize.Value;
@@ -216,7 +218,7 @@ public partial class admin_activity_item_reg : MyWeb.config
actItem.extend = extend.Checked ? "Y" : "N";
actItem.is_reconcile = is_reconcile_item.Checked ? "Y" : "N";
actItem.demo = demo.Text;
actItem.customize_data = customize_data.Text;
//actItem.customize_data = customize_data.Text;
_db.SaveChanges();
Model.admin_log admin_log = new Model.admin_log();
+241 -20
View File
@@ -165,6 +165,8 @@
{ text: '* 品項名稱', value: 'act_item_selected.text', sortable: false },
{ text: '預設金額', value: 'price', sortable: false },
{ text: '數量', value: 'qty', sortable: false },
{ text: '陽上/祈福人數限制', value: 'limit_yang', sortable: false },
{ text: '超渡人數限制', value: 'limit_chao', sortable: false },
{ text: '', value: 'actions', sortable: false, width: "200px" },
],
footersDetail: {
@@ -191,6 +193,10 @@
price: 0,
qty: 0,
files: [],
has_yang_limit: false,
has_chao_limit: false,
yang_limit_count: 0,
chao_limit_count: 0,
},
defaultItem: {
id: 0,
@@ -203,6 +209,10 @@
price: 0,
qty: 0,
files: [],
has_yang_limit: false,
has_chao_limit: false,
yang_limit_count: 0,
chao_limit_count: 0,
},
//列印
data_table: {
@@ -267,10 +277,23 @@
order_dialog: {
show: false,
},
loading: false,
search: '',
headers: [
{ text: '訂單編號', value: 'order_no' },
{ text: '信眾編號', value: 'f_number' },
{ text: '信眾姓名', value: 'u_name' },
{ text: '聯絡電話', value: 'phone' },
{ text: '建立日期', value: 'order_date' },
],
items: [
{ order_no: "D", member_name: "S", order_date:"2025-12-07"}
],
}
},
mounted() {
this.search_dialog.current = this.search_dialog.controls.search1
this.loadUnfilledList();
//console.log("mounted");
},
watch: {
@@ -482,6 +505,10 @@
actItem_num: this.editedItem.act_item_selected.val,
price: this.editedItem.price,
qty: this.editedItem.qty,
has_yang_limit: this.editedItem.has_yang_limit,
has_chao_limit: this.editedItem.has_chao_limit,
yang_limit_count: this.editedItem.yang_limit_count,
chao_limit_count: this.editedItem.chao_limit_count,
}
axios
.post(HTTP_HOST + 'api/activity/SaveRelatingData', pro_order_detail)
@@ -886,6 +913,113 @@
const _date = $('#<%= startDate_solar.ClientID%>').val();
return _subject + " " + _date;
},
loadUnfilledList() {
if (!this.this_id) return;
this.unfilled_list.loading = true;
axios.get(HTTP_HOST + 'api/activity/GetUnfilledOrdersByActivity/' + this.this_id)
.then(response => {
this.unfilled_list.items = response.data.list || [];
})
.catch(error => {
console.log(error);
this.snackbar.text = "名單載入失敗:" + error;
this.snackbar.show = true;
})
.finally(() => {
this.unfilled_list.loading = false;
});
},
async printUnfilledList() {
if (!this.this_id) return;
try {
const response = await axios.get(
HTTP_HOST + `api/activity/GetUnfilledOrdersByActivity/${this.this_id}`,
{ params: { page: 1, pageSize: 99999 } }
);
const allItems = response.data.list || [];
if (allItems.length === 0) {
this.snackbar.text = "目前沒有未填寫品項的資料可供列印!";
this.snackbar.show = true;
return;
}
const activityTitle = this.titleword();
const rows = allItems.map(item => `
<tr>
<td>${item.order_no}</td>
<td>${item.f_number || ''}</td>
<td>${item.u_name}</td>
<td>${item.phone}</td>
<td>${item.order_date}</td>
</tr>
`).join('');
const win = window.open('', '_blank');
win.document.write(`
<html>
<head>
<title>未填寫品項名單 - ${activityTitle}</title>
<style>
body { font-family: "Microsoft JhengHei", Arial, sans-serif; padding: 20px; color: #000; }
.header-container { text-align: center; margin-bottom: 30px; }
h2 { margin-bottom: 5px; font-size: 24px; }
h4 { margin-top: 0; color: #333; font-weight: normal; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #000; padding: 10px 8px; text-align: left; font-size: 14px; }
th { background-color: #eee; font-weight: bold; }
.text-center { text-align: center; }
@media print {
@page { margin: 1.5cm; }
button { display: none; }
}
</style>
</head>
<body>
<div class="header-container">
<h2>尚未報名品項之信眾清單</h2>
<h4>活動名稱:${activityTitle}</h4>
</div>
<table>
<thead>
<tr>
<th width="18%">訂單編號</th>
<th width="15%">信眾編號</th>
<th width="20%">信眾姓名</th>
<th width="20%">聯絡電話</th>
<th width="27%">建單日期</th>
</tr>
</thead>
<tbody>${rows}</tbody>
</table>
<div style="margin-top: 20px; text-align: right; font-size: 12px;">
列印時間:${new Date().toLocaleString()}
</div>
</body>
</html>
`);
win.document.close();
// 確保內容載入完成後再呼叫列印
setTimeout(() => {
win.print();
win.close();
}, 300);
} catch (error) {
console.error("列印出錯:", error);
this.snackbar.text = "讀取列印資料失敗:" + error;
this.snackbar.show = true;
}
}
},
computed: {
},
@@ -905,12 +1039,12 @@
<div class="">
<asp:Button ID="add" runat="server" Text="送出" OnClick="add_Click" CssClass="btn btn-primary" />
<asp:Button ID="edit" runat="server" Text="修改" Visible="false" OnClick="edit_Click" CssClass="btn btn-primary" />
<asp:Button ID="goback" runat="server" Text="回列表" Visible="false" CausesValidation="false" OnClick="goback_Click" CssClass="btn btn-outline-secondary" />
<asp:Button ID="goback" runat="server" Text="取消" Visible="true" CausesValidation="false" OnClick="goback_Click" CssClass="btn btn-outline-secondary" />
</div>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<uc1:alert runat="server" ID="L_msg" Text="" />
<div id="content" class="container-md">
<div id="content" class="container-lg">
<div class="card shadow-sm my-2" id="sec2">
<div class="card-header py-0">
@@ -922,9 +1056,13 @@
<button v-if="this_id!=''" class="nav-link" id="sec2-tab2" data-bs-toggle="tab" data-bs-target="#sec2-page2"
type="button" role="tab" aria-controls="profile" aria-selected="false">
{{this_category_kindTxt}}項目</button>
<button v-if="this_id!=''" class="nav-link" id="sec2-tab3" data-bs-toggle="tab" data-bs-target="#sec2-page3"
<%-- <button v-if="this_id!=''" class="nav-link" id="sec2-tab3" data-bs-toggle="tab" data-bs-target="#sec2-page3"
type="button" role="tab" aria-controls="profile" aria-selected="false">
備品項目</button>
備品項目</button>--%>
<button v-if="this_id!=''" class="nav-link" id="sec2-tab4" data-bs-toggle="tab" data-bs-target="#sec2-page4"
type="button" role="tab" aria-controls="profile" aria-selected="false" @click="loadUnfilledList">
未填寫名單
</button>
</div>
</nav>
</div>
@@ -936,13 +1074,13 @@
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-lg-1 col-form-label">活動名稱 *</label>
<label class="col-sm-2 col-lg-1 col-form-label">活動(法會)名稱 *</label>
<div class="col-sm-10 col-lg-3">
<asp:TextBox ID="subject" MaxLength="100" runat="server" CssClass="form-control" placeholder="請輸入活動名稱"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" ControlToValidate="subject" runat="server" ErrorMessage="必填!" Display="Dynamic" SetFocusOnError="true"></asp:RequiredFieldValidator>
</div>
<label class="col-sm-2 col-lg-1 col-form-label">活動主類型 *</label>
<%-- <label class="col-sm-2 col-lg-1 col-form-label">活動主類型 *</label>
<div class="col-sm-10 col-lg-3" >
<div class="input-group mb-3" data-search-control="search4" @click="search_show(search_dialog.controls.search4)" >
<input class="form-control search-text" type="text" readonly
@@ -953,14 +1091,14 @@
</button>
</div>
<asp:RequiredFieldValidator ID="RequiredFieldValidator3" ControlToValidate="category_kind_txt" runat="server" ErrorMessage="必填!" Display="Dynamic" SetFocusOnError="true"></asp:RequiredFieldValidator>
</div>
</div>--%>
<label class="col-sm-2 col-lg-1 col-form-label">活動詳細分類 *</label>
<label class="col-sm-2 col-lg-1 col-form-label">活動(法會)分類 *</label>
<div class="col-sm-10 col-lg-3">
<asp:PlaceHolder ID="kindPH" runat="server">
<div class="input-group mb-3" data-search-control="search1" @click="this_id=='' && search_show(search_dialog.controls.search1)">
<input class="form-control search-text" type="text" readonly
id="kind_txt" runat="server" placeholder="活動詳細分類" value="">
id="kind_txt" runat="server" placeholder="活動(法會)分類" value="">
<asp:HiddenField ID="kind" runat="server" Value="" />
<button class="btn btn-outline-secondary" type="button">
<i class="mdi mdi-view-list-outline"></i>
@@ -970,13 +1108,19 @@
</asp:PlaceHolder>
<asp:Literal ID="kind_txt2" runat="server"></asp:Literal>
</div>
<label class="col-sm-2 col-lg-1 col-form-label">報名截止日 *</label>
<div class="col-sm-4 col-lg-3">
<asp:TextBox ID="dueDate" TextMode="Date" runat="server" CssClass="form-control"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator5" ControlToValidate="dueDate" runat="server" ErrorMessage="必填!" Display="Dynamic" SetFocusOnError="true"></asp:RequiredFieldValidator>
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-lg-1 col-form-label">牌位編號開頭</label>
<div class="col-sm-10 col-lg-3">
<asp:TextBox ID="print_init" MaxLength="50" runat="server" CssClass="form-control" placeholder="請輸入牌位編號開頭"></asp:TextBox>
</div>
<label class="col-sm-2 col-lg-1 col-form-label">活動地點</label>
<label class="col-sm-2 col-lg-1 col-form-label">活動(法會)地點</label>
<div class="col-sm-10 col-lg-3">
<asp:TextBox ID="address" MaxLength="200" runat="server" CssClass="form-control" placeholder="請輸入活動地址"></asp:TextBox>
</div>
@@ -986,7 +1130,7 @@
</div>
</div>
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-lg-1 col-form-label">開始日期(西元)</label>
<label class="col-sm-2 col-lg-1 col-form-label">開始日期(西元)*</label>
<div class="col-sm-4 col-lg-3">
<asp:TextBox ID="startDate_solar" TextMode="Date" runat="server" CssClass="form-control" onchange="chagenDate(this)"></asp:TextBox>
</div>
@@ -1003,7 +1147,7 @@
<div class="row mb-1 label-sm-right">
<label class="col-sm-2 col-lg-1 col-form-label">結束日期(西元)</label>
<label class="col-sm-2 col-lg-1 col-form-label">結束日期(西元)*</label>
<div class="col-sm-4 col-lg-3">
<asp:TextBox ID="endDate_solar" TextMode="Date" runat="server" CssClass="form-control" onchange="chagenDate(this)"></asp:TextBox>
</div>
@@ -1011,12 +1155,7 @@
<div class="col-sm-4 col-lg-3 lunar pt-2">
<asp:Literal ID="endDate_lunar" runat="server"></asp:Literal>
</div>
<label class="col-sm-2 col-lg-1 col-form-label">報名截止日 *</label>
<div class="col-sm-4 col-lg-3">
<asp:TextBox ID="dueDate" TextMode="Date" runat="server" CssClass="form-control"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator5" ControlToValidate="dueDate" runat="server" ErrorMessage="必填!" Display="Dynamic" SetFocusOnError="true"></asp:RequiredFieldValidator>
</div>
</div>
@@ -1043,12 +1182,12 @@
<asp:TextBox ID="demo" runat="server" Rows="5" TextMode="MultiLine" CssClass="form-control" placeholder="請輸入備註"></asp:TextBox>
</div>
</div>
<div class="row mb-1">
<%-- <div class="row mb-1">
<label class="col-form-label">自定義欄位預設值</label>
<div class="">
<asp:TextBox ID="customize_data" runat="server" Rows="3" TextMode="MultiLine" CssClass="form-control" placeholder="一行輸入一個定義欄位預設值,如:&#10;$活動:OO年法會&#10;$地點:台中市OO區OO路" ToolTip="一行輸入一個定義欄位預設值,格式: $變數名稱:值"></asp:TextBox>
</div>
</div>
</div>--%>
<div class="row mb-1">
<asp:Panel ID="timePanel1" runat="server" CssClass="col-12 text-right text-primary" Visible="false">建檔時間:<asp:Literal ID="reg_time" runat="server"></asp:Literal></asp:Panel>
</div>
@@ -1097,6 +1236,53 @@
<v-text-field v-model="editedItem.qty" :hide-details="true" dense single-line v-if="item.id === editedItem.id" ></v-text-field>
<span v-else>{{item.qty}}</span>
</template>
<template v-slot:item.limit_yang="{ item }">
<div class="d-flex align-center">
<v-text-field v-model="editedItem.yang_limit_count" v-if="item.id === editedItem.id"
type="number"
dense
:hide-details="true"
single-line
:disabled="!editedItem.has_yang_limit"
suffix="人" >
</v-text-field>
<v-checkbox v-if="item.id === editedItem.id"
v-model="editedItem.has_yang_limit"
label="不限"
hide-details
class="ml-2 mt-0"
:true-value="false"
:false-value="true"
:disabled="item.id !== editedItem.id">
</v-checkbox>
<span v-if="item.id !== editedItem.id && !item.has_yang_limit">不限</span>
<span v-if="item.id !== editedItem.id && item.has_yang_limit">{{item.yang_limit_count}} 人</span>
</div>
</template>
<template v-slot:item.limit_chao="{ item }">
<div class="d-flex align-center">
<v-text-field v-model="editedItem.chao_limit_count" v-if="item.id === editedItem.id"
type="number"
dense
:hide-details="true"
single-line
:disabled="!editedItem.has_chao_limit"
suffix="人" >
</v-text-field>
<v-checkbox v-if="item.id === editedItem.id"
v-model="editedItem.has_chao_limit"
label="不限"
hide-details
class="ml-2 mt-0"
:true-value="false"
:false-value="true"
:disabled="item.id !== editedItem.id">
</v-checkbox>
<span v-if="item.id !== editedItem.id && !item.has_chao_limit">不限</span>
<span v-if="item.id !== editedItem.id && item.has_chao_limit">{{item.chao_limit_count}} 人</span>
</div>
</template>
<template v-slot:item.actions="{ item }">
<template v-if="item.id === editedItem.id">
<v-icon color="red" class="mr-2" @click="cancel">
@@ -1206,6 +1392,41 @@
</div>
<div class="tab-pane fade" id="sec2-page4" role="tabpanel" aria-labelledby="sec2-tab4" v-if="this_id!=''">
<v-card class="mx-auto" outlined id="print-unfilled-area">
<v-data-table
:headers="unfilled_list.headers"
:items="unfilled_list.items"
:loading="unfilled_list.loading"
:search="unfilled_list.search"
class="elevation-1"
fixed-header
height="350px">
<template v-slot:top>
<v-toolbar flat color="white" class="d-print-none">
<div class="d-flex w-100">
<v-text-field
v-model="unfilled_list.search"
append-icon="mdi-magnify"
label="搜尋姓名或訂單號"
dense outlined single-line hide-details>
</v-text-field>
<v-btn color="primary" class="ml-2 white--text" @click="printUnfilledList">
<v-icon left>mdi-printer</v-icon>列印所有名單
</v-btn>
</div>
</v-toolbar>
</template>
<template v-slot:item.status="{ item }">
<v-chip small color="error">未選品項</v-chip>
</template>
</v-data-table>
</v-card>
</div>
</div>
</div>
</asp:Panel>
</div>
+149 -11
View File
@@ -78,8 +78,8 @@ public partial class admin_activity_reg : MyWeb.config
if (prod.category_kind.HasValue)
{
_category_kindTxt = prod.activity_category_kind.kind;
category_kind_txt.Value = prod.activity_category_kind.kind;
category_kind.Value = prod.category_kind.Value.ToString();
//category_kind_txt.Value = prod.activity_category_kind.kind;
//category_kind.Value = prod.category_kind.Value.ToString();
}
if (prod.startDate_solar.HasValue)
{
@@ -235,7 +235,36 @@ public partial class admin_activity_reg : MyWeb.config
#endregion
#region
protected string createOrderNumber()
{
Application.Lock();
string order_no = "AA" + DateTime.Now.ToString("yyMMdd");
var qry = _db.companies.AsQueryable();
//var prod = qry.Where(q => q.last_order_no.Contains(order_no)).FirstOrDefault();
var prod = qry.Where(q => q.num == 1).FirstOrDefault();
if (prod != null)
{
if (!isStrNull(prod.last_order_no) && prod.last_order_no.Contains(order_no))
{
int tmp = Convert.ToInt32(prod.last_order_no.Replace(order_no, "")) + 1;
order_no = order_no + tmp.ToString("0000");
}
else
{
order_no = order_no + "0001";
}
prod.last_order_no = order_no;
_db.SaveChanges();
}
else
order_no = "";
Application.UnLock();
return order_no;
}
protected void add_Click(object sender, EventArgs e)
{
if (Page.IsValid) {
@@ -254,7 +283,7 @@ public partial class admin_activity_reg : MyWeb.config
{
if (textBox.TextMode == TextBoxMode.Date)
ObjValue.SetValue(activity, selectDate(textBox));
else
ObjValue.SetValue(activity, ((TextBox)obj).Text.Trim());
}
@@ -281,7 +310,6 @@ public partial class admin_activity_reg : MyWeb.config
// printInit 欄位
//var printInitValue = printInit.Text.Trim();
//activity.print_init = !string.IsNullOrEmpty(printInitValue) ? printInitValue : null;
activity.kind = Val(kind.Value);
activity.category_kind = Val(category_kind.Value);
activity.reg_time = DateTime.Now;
@@ -290,15 +318,37 @@ public partial class admin_activity_reg : MyWeb.config
int _id = activity.num;
if (_id > 0)
{
RunAutoEnroll(_id);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Activity, (int)Model.admin_log.Status.Insert, subject.Text);
Response.Redirect("index.aspx");
}
else if (activity.startDate_solar == null || activity.endDate_solar == null)
{
L_msg.Type = alert_type.danger;
L_msg.Text = "開始、結束日期必須填寫";
}
else
{
L_msg.Type = alert_type.danger;
L_msg.Text = "Error";
activity.kind = Val(kind.Value);
//activity.category_kind = Val(category_kind.Value);
activity.reg_time = DateTime.Now;
_db.activities.Add(activity);
_db.SaveChanges();
int _id = activity.num;
if (_id > 0)
{
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Activity, (int)Model.admin_log.Status.Insert, subject.Text);
Response.Redirect("index.aspx");
}
else
{
L_msg.Type = alert_type.danger;
L_msg.Text = "Error";
}
}
}
catch (Exception ex)
@@ -363,12 +413,22 @@ public partial class admin_activity_reg : MyWeb.config
// printInit 欄位
//var printInitValue = printInit.Text.Trim();
//activity.print_init = !string.IsNullOrEmpty(printInitValue) ? printInitValue : null;
if (activity.startDate_solar == null || activity.endDate_solar == null)
{
L_msg.Type = alert_type.danger;
L_msg.Text = "開始、結束日期必須填寫";
}
else
{
//activity.category_kind = Val(category_kind.Value);
activity.category_kind = Val(category_kind.Value);
_db.SaveChanges();
activity.category_kind = Val(category_kind.Value);
_db.SaveChanges();
RunAutoEnroll(activity.num);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Activity, (int)Model.admin_log.Status.Update, subject.Text);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Activity, (int)Model.admin_log.Status.Update, subject.Text);
}
}
catch (Exception ex)
{
@@ -385,6 +445,84 @@ public partial class admin_activity_reg : MyWeb.config
}
}
}
private void RunAutoEnroll(int activityNum)
{
var activity = _db.activities.FirstOrDefault(a => a.num == activityNum);
if (activity == null || !activity.startDate_solar.HasValue) return;
DateTime actDate = activity.startDate_solar.Value;
var validConfigs = _db.auto_enroll
.Where(ae => actDate >= ae.start_date && actDate <= ae.end_date)
.ToList();
foreach (var config in validConfigs)
{
if (_db.pro_order.Any(o => o.activity_num == activity.num && o.f_num == config.f_num))
continue;
var follower = _db.followers.FirstOrDefault(f => f.num == config.f_num);
if (follower == null) continue;
string newOrderNo = AutoOrderService.CreateAutoOrderNumber(_db);
Model.pro_order proOrder = new Model.pro_order
{
order_no = newOrderNo,
up_time = DateTime.Now,
reg_time = DateTime.Now,
keyin1 = "A01",
f_num = follower.num,
au_num = config.num,
phone = !string.IsNullOrEmpty(follower.cellphone) ? follower.cellphone : (follower.phone ?? ""),
activity_num = activity.num,
address = config.receipt_address ?? "",
receipt_title = config.receipt_title ?? "",
demo = "",
customize_data = ""
};
_db.pro_order.Add(proOrder);
CopyLatestOrderDetails(newOrderNo, config.f_num, (int)activity.kind);
}
_db.SaveChanges();
}
private void CopyLatestOrderDetails(string newOrderNo, int f_num, int activityKind)
{
var latestOrder = _db.pro_order
.Where(o => o.f_num == f_num && o.activity.kind == activityKind && _db.pro_order_detail.Any(d => d.order_no == o.order_no))
.OrderByDescending(o => o.order_no)
.FirstOrDefault();
if (latestOrder != null)
{
var prevDetails = _db.pro_order_detail.Where(d => d.order_no == latestOrder.order_no).ToList();
foreach (var detail in prevDetails)
{
Model.pro_order_detail newDetail = new Model.pro_order_detail
{
order_no = newOrderNo,
actItem_num = detail.actItem_num,
parent_num = detail.parent_num,
print_id = detail.print_id,
f_num = detail.f_num,
f_num_tablet = detail.f_num_tablet,
address = detail.address,
from_id = detail.from_id,
from_id_tablet = detail.from_id_tablet,
qty = detail.qty,
price = detail.price,
start_date = DateTime.Today,
pay = 0,
UpdateTime = DateTime.Now
};
_db.pro_order_detail.Add(newDetail);
}
}
}
#endregion
+868
View File
@@ -0,0 +1,868 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-warning white--text text-center">
<h5 class="mb-0">餘額核銷 - 處理剩餘金額</h5>
<button class="btn btn-default ms-auto"><i class="mdi mdi-close"></i></button>
</v-card-title>
<v-card-text>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1 balance-reconcile-table"
item-key="id">
<template v-slot:item.follower="{ item }">
<div>
<div class="font-weight-bold">
<a :href="`../follower/reg.aspx?num=${item.f_num}`"
target="_blank"
class="text-decoration-none"
:title="`查看 ${item.follower} 的詳細資料`">
{{ item.follower }}
<v-icon small color="primary" class="ml-1">mdi-open-in-new</v-icon>
</a>
</div>
<div class="caption text--secondary" v-if="item.phone">
{{ item.phone }}
</div>
<div class="caption text--secondary" v-if="item.phone2">
{{ item.phone2 }}
</div>
</div>
</template>
<template v-slot:item.bank_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.acc_name }}</div>
<div class="caption text--secondary">{{ item.check_date | date }}</div>
</div>
</template>
<template v-slot:item.amount_info="{ item }">
<div>
<div class="font-weight-bold text-primary">
已入帳{{ item.check_amount }}
</div>
<div class="text-success">
已沖帳{{ item.check_amount - item.remain_amount }}
</div>
<div class="text-warning font-weight-bold">
待核銷{{ item.remain_amount }}
</div>
</div>
</template>
<template v-slot:item.detail_info="{ item }">
<v-btn color="info"
outlined
@click="showDetailDialog(item)">
<v-icon small class="mr-1">mdi-information-outline</v-icon>
詳細
</v-btn>
</template>
<!-- 選取項目欄位 -->
<template v-slot:item.select_items="{ item }">
<div style="min-width: 200px;">
<v-text-field v-model="item.selected_items_text"
placeholder="項目名稱"
dense
outlined
readonly
hide-details
style="font-size: 12px;">
<template v-slot:append>
<v-btn small
color="primary"
icon
@click="showItemSelectDialog(item)"
style="margin-right: -8px;">
<v-icon small>mdi-table</v-icon>
</v-btn>
</template>
</v-text-field>
</div>
</template>
<!-- 狀態與操作欄位 -->
<template v-slot:item.status_action="{ item }">
<div style="min-width: 280px;">
<v-row no-gutters>
<v-col cols="6" class="pr-1">
<v-select v-model="item.new_status"
:items="statusOptions"
dense
outlined
hide-details
placeholder="狀態"
style="font-size: 12px;"></v-select>
</v-col>
<v-col cols="6" class="pl-1">
<v-btn color="primary"
block
@click="processBalance(item)"
:disabled="!canUpdate(item)">
更新狀態
</v-btn>
</v-col>
</v-row>
<v-row no-gutters class="mt-2">
<v-col cols="12">
<v-text-field v-model="item.verify_note"
dense
outlined
hide-details
placeholder="核對記錄"
style="font-size: 12px;"></v-text-field>
</v-col>
</v-row>
</div>
</template>
</v-data-table>
</v-card-text>
</v-card>
<!-- 餘額處理對話框 -->
<v-dialog v-model="dialog.show" max-width="800px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="warning" class="mr-2">mdi-cash-multiple</v-icon>
餘額處理
<v-spacer></v-spacer>
<v-btn icon @click="dialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="pb-0">
<div v-if="dialog.selected">
<v-row class="my-3">
<v-col cols="12" md="6">
<div class="mb-2">
<span class="font-weight-bold">信眾</span>
{{ dialog.selected.follower }}
</div>
<div class="mb-2">
<span class="font-weight-bold">入帳銀行</span>
{{ dialog.selected.acc_name }}
</div>
<div class="mb-2">
<span class="font-weight-bold">入帳日期</span>
{{ dialog.selected.check_date | date }}
</div>
</v-col>
<v-col cols="12" md="6">
<div class="mb-2">
<span class="font-weight-bold">入帳金額</span>
<span class="text-primary">{{ dialog.selected.check_amount | currency }}</span>
</div>
<div class="mb-2">
<span class="font-weight-bold">剩餘金額</span>
<span class="text-warning font-weight-bold">{{ dialog.selected.remain_amount | currency }}</span>
</div>
<div class="mb-2">
<span class="font-weight-bold">狀態</span>
<v-chip small color="warning" text-color="white">
{{ getStatusText(dialog.selected.check_status) }}
</v-chip>
</div>
</v-col>
</v-row>
<!-- 歷史沖帳記錄 -->
<v-divider class="my-3"></v-divider>
<h6 class="mb-3">
<v-icon color="info" class="mr-1">mdi-history</v-icon>
歷史沖帳記錄
</h6>
<v-data-table :headers="dialog.historyHeaders"
:items="dialog.historyItems"
class="elevation-1 mb-4"
hide-default-footer
:disable-pagination="true"
dense>
<template v-slot:item.reconcile_amount="{ item }">
<span>{{ item.reconcile_amount | currency }}</span>
</template>
<template v-slot:item.reconcile_date="{ item }">
<span>{{ item.reconcile_date | date }}</span>
</template>
</v-data-table>
<!-- 餘額處理選項 -->
<v-divider class="my-3"></v-divider>
<h6 class="mb-3">
<v-icon color="warning" class="mr-1">mdi-cash-multiple</v-icon>
餘額處理方式
</h6>
<v-radio-group v-model="dialog.balanceAction" class="mt-0">
<v-radio label="轉入下次活動"
value="transfer"
color="primary"></v-radio>
<v-radio label="現金退費"
value="refund_cash"
color="success"></v-radio>
<v-radio label="銀行轉帳退費"
value="refund_bank"
color="success"></v-radio>
<v-radio label="結餘處理"
value="balance_off"
color="grey"></v-radio>
</v-radio-group>
<!-- 處理說明 -->
<v-textarea v-model="dialog.memo"
label="處理說明"
outlined
rows="3"
placeholder="請輸入處理說明..."
class="mt-3"></v-textarea>
</div>
</v-card-text>
<v-card-actions class="pa-4">
<div v-if="dialog.errorMessage" class="text-danger">
{{ dialog.errorMessage }}
</div>
<v-spacer></v-spacer>
<v-btn color="grey" text @click="dialog.show = false">取消</v-btn>
<v-btn color="warning"
@click="confirmBalance"
:disabled="!dialog.balanceAction || dialog.loading"
:loading="dialog.loading">
確認處理
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 詳細資訊對話框 -->
<v-dialog v-model="detailDialog.show" max-width="900px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
詳細資訊
<v-spacer></v-spacer>
<v-btn icon @click="detailDialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-tabs v-model="detailDialog.tab" background-color="grey lighten-4">
<v-tab>
<v-icon small class="mr-2">mdi-information</v-icon>
基本資訊
</v-tab>
<v-tab>
<v-icon small class="mr-2">mdi-table</v-icon>
沖帳明細
</v-tab>
<v-tab>
<v-icon small class="mr-2">mdi-image</v-icon>
證明圖片
</v-tab>
</v-tabs>
<v-tabs-items v-model="detailDialog.tab">
<!-- Tab 1: 基本資訊 -->
<v-tab-item>
<v-card-text>
<div v-if="detailDialog.selected">
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-primary">
<v-icon color="primary" small class="mr-1">mdi-account</v-icon>
匯款人資訊
</h6>
<div class="mb-2">
<span class="font-weight-bold">姓名</span>
{{ detailDialog.selected.name }}
</div>
<div class="mb-2">
<span class="font-weight-bold">支付方式</span>
{{ payTypeText[detailDialog.selected.pay_type] || detailDialog.selected.pay_type }}
</div>
<div class="mb-2">
<span class="font-weight-bold">帳號後5碼</span>
{{ detailDialog.selected.account_last5 || '-' }}
</div>
<div class="mb-2">
<span class="font-weight-bold">支付型態</span>
{{ detailDialog.selected.pay_mode }}
</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-success">
<v-icon color="success" small class="mr-1">mdi-cash</v-icon>
入帳資訊
</h6>
<div class="mb-2">
<div class="font-weight-bold">{{ detailDialog.selected.acc_name }}</div>
<div class="caption text--secondary">{{ detailDialog.selected.check_date | date }}</div>
</div>
<div class="mb-2">
<span class="font-weight-bold text-primary">已入帳{{ detailDialog.selected.check_amount }}</span>
</div>
<div class="mb-2">
<span class="text-success">已沖帳{{ detailDialog.selected.check_amount - detailDialog.selected.remain_amount }}</span>
</div>
<div class="mb-2">
<span class="text-warning font-weight-bold">待核銷{{ detailDialog.selected.remain_amount }}</span>
</div>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-purple">
<v-icon color="purple" small class="mr-1">mdi-calendar-check</v-icon>
活動資訊
</h6>
<div class="mb-2">
<span class="font-weight-bold">活動名稱</span>
{{ detailDialog.selected.activity_name || '-' }}
</div>
<div class="mb-2">
<span class="font-weight-bold">開始日期</span>
{{ detailDialog.selected.activity_start_date | date }}
</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-info">
<v-icon color="info" small class="mr-1">mdi-clipboard-check</v-icon>
核對記錄
</h6>
<div class="mb-2">
<span class="font-weight-bold">核對記錄</span>
{{ detailDialog.selected.verify_note || '-' }}
</div>
<div class="mb-2">
<span class="font-weight-bold">帳簿備註</span>
{{ detailDialog.selected.check_memo || '-' }}
</div>
</v-col>
</v-row>
</div>
</v-card-text>
</v-tab-item>
<!-- Tab 2: 沖帳明細表格 -->
<v-tab-item>
<v-card-text>
<div v-if="detailDialog.selected">
<v-data-table :headers="detailDialog.reconcileHeaders"
:items="detailDialog.reconcileItems"
class="elevation-1"
hide-default-footer
:disable-pagination="true">
<template v-slot:item.reconcile="{ item }">
<span class="font-weight-bold text-success">{{ item.reconcile | currency }}</span>
</template>
<template v-slot:item.register_date="{ item }">
<span>{{ item.register_date | date }}</span>
</template>
<template v-slot:item.price="{ item }">
<span>{{ item.price | currency }}</span>
</template>
</v-data-table>
</div>
</v-card-text>
</v-tab-item>
<!-- Tab 3: 證明圖片 -->
<v-tab-item>
<v-card-text>
<div v-if="detailDialog.selected">
<div v-if="detailDialog.selected.proof_img">
<h6 class="mb-3 text-orange">
<v-icon color="orange" small class="mr-1">mdi-image</v-icon>
證明圖片
</h6>
<div class="text-center">
<img :src="detailDialog.selected.proof_img"
alt="證明圖片"
style="max-width: 100%; max-height: 500px;"
class="elevation-3" />
</div>
</div>
<div v-else class="text-center py-5">
<v-icon size="64" color="grey lighten-2">mdi-image-off</v-icon>
<div class="mt-3 text--secondary">無證明圖片</div>
</div>
</div>
</v-card-text>
</v-tab-item>
</v-tabs-items>
</v-card>
</v-dialog>
<!-- 活動品項選擇對話框 -->
<v-dialog v-model="search_dialog.show" max-width="700px">
<v-card>
<v-card-title class="justify-space-between grey lighten-2">
查詢:{{search_dialog.current.title}}
<v-btn icon @click="search_dialog.show=false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text>
<v-row>
<v-col v-for="item in search_dialog.current.keys"
:cols="search_dialog.current.keys.length>1?6:12">
<v-text-field v-model="item.value" :label="item.title" v-if="item.visible===undefined || item.visible==true "></v-text-field>
</v-col>
<v-col cols="12" sm="4" md="3">
<v-checkbox v-model="search_is_reconcile"
label="核銷項目"
:true-value="'Y'"
:false-value="''"
hide-details
@change="search_get"></v-checkbox>
</v-col>
<v-col cols="12" class="text-end">
<v-btn color="primary" elevation="0" @click="search_get()">查詢</v-btn>
<v-btn elevation="0" @click="search_clear()">清除條件</v-btn>
</v-col>
</v-row>
<v-data-table :headers="search_headers()"
:items="search_dialog.list"
:footer-props="search_dialog.footer"
:items-per-page="10"
:server-items-length="search_dialog.count"
:page.sync="search_dialog.page"
:options.sync="options"
@click:row="search_select"
:loading="search_dialog.loading"></v-data-table>
</v-card-text>
<v-card-actions>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
module.exports = {
data() {
return {
loading: false,
headers: [
{ text: '信眾', value: 'follower' },
{ text: '入帳帳戶/日期', value: 'bank_info' },
{ text: '金額', value: 'amount_info' },
{ text: '詳細資訊', value: 'detail_info', sortable: false },
{ text: '選取項目', value: 'select_items', sortable: false, width: '200px' },
{ text: '狀態 | 核對記錄', value: 'status_action', sortable: false, width: '280px' }
],
items: [],
dialog: {
show: false,
selected: null,
balanceAction: null,
memo: '',
loading: false,
errorMessage: '',
historyHeaders: [
{ text: '法會', value: 'activity_name' },
{ text: '項目', value: 'item_name' },
{ text: '沖帳金額', value: 'reconcile_amount' },
{ text: '沖帳日期', value: 'reconcile_date' }
],
historyItems: [
{
activity_name: '2025新春法會',
item_name: '護持金',
reconcile_amount: 12000,
reconcile_date: '2025-01-15T00:00:00'
}
]
},
detailDialog: {
show: false,
selected: null,
tab: 0,
reconcileHeaders: [
{ text: '法會活動', value: 'activity_name' },
{ text: '報名項目', value: 'actitem_name' },
{ text: '原始金額', value: 'price' },
{ text: '沖帳金額', value: 'reconcile' },
{ text: '報名日期', value: 'register_date' },
{ text: '訂單編號', value: 'order_no' }
],
reconcileItems: []
},
statusOptions: [
{ text: '沖帳有剩餘', value: '90' },
{ text: '未聯絡', value: '91' },
{ text: '已聯絡', value: '92' },
{ text: '餘額核銷', value: '95' }
],
search_dialog: {
controls: {
search5: {
id: 'search5',
title: '活動品項',
text_prop: 'subject',
value_prop: 'num',
keys: [
{ id: 'subject', title: '項目名稱', value: '' },
{ id: 'kindTxt', title: '項目分類' },
{ id: 'num', visible: false },
],
api_url: '../../api/activity/GetOrderList',
columns: [
{ id: 'subject', title: '項目名稱' },
{ id: 'kindTxt', title: '項目分類' },
{ id: 'price', title: '價格' },
],
selected: {},
currentRow: null, //
select(vueInstance, selectedItem) {
//
if (this.currentRow) {
// actItem num balance_act_item
this.currentRow.balance_act_item = selectedItem.num;
//
this.currentRow.selected_items_text = selectedItem.subject;
//
if (selectedItem.price) {
this.currentRow.selected_items_text += ` (NT$${Number(selectedItem.price).toLocaleString()})`;
}
console.log('已選擇項目:', selectedItem.subject, '項目編號:', selectedItem.num);
}
}
}
},
show: false,
current: {},
list: [],
count: 0,
page: 1,
loading: false,
footer: {
showFirstLastPage: true,
disableItemsPerPage: true,
itemsPerPageAllText: '',
itemsPerPageText: '',
},
},
options: {},
payTypeText: {
1: '現金',
2: '匯款',
3: '支票'
},
search_is_reconcile: "Y",
}
},
mounted() {
this.loadTableData();
this.search_dialog.current = this.search_dialog.controls.search5; //
},
filters: {
currency(val) {
if (!val) return '0';
return Number(val).toLocaleString();
},
date(val) {
if (!val) return '';
const date = new Date(val);
return date.toLocaleDateString();
}
},
methods: {
// 使 DraftUtils
loadTableData() {
this.loading = true;
axios.get('../../api/transfer_register/balance_reconcile_list')
.then(res => {
this.items = res.data.map(item => ({
...item,
//
selected_items_text: item.balance_act_item ? (item.balance_actitem_name || '已選擇項目') : '', //
new_status: item.check_status,
balance_act_item: item.balance_act_item, //
verify_note: item.verify_note || '' //
}));
})
.catch(err => {
console.error('載入資料失敗:', err);
alert('載入資料失敗,請重新整理頁面');
this.items = [];
})
.finally(() => {
this.loading = false;
});
},
getStatusColor(status) {
switch (status) {
case '90': return 'warning';
case '99': return 'success';
default: return 'grey';
}
},
getStatusText(status) {
switch (status) {
case '90': return '沖帳有剩餘';
case '91': return '未聯絡';
case '92': return '已聯絡';
case '95': return '餘額核銷';
case '99': return '沖帳完成';
default: return '未知狀態';
}
},
showBalanceDialog(item) {
this.dialog.selected = item;
this.dialog.balanceAction = null;
this.dialog.memo = '';
this.dialog.errorMessage = '';
this.dialog.show = true;
//
if (item.draft) {
const detailItems = window.DraftUtils.getDraftField(item.draft, 'pro_order_detail_items');
if (detailItems && Array.isArray(detailItems)) {
this.dialog.historyItems = detailItems.map(item => ({
activity_name: item.activity_name || '未知活動',
item_name: item.actitem_name || '未知項目',
reconcile_amount: item.reconcile || 0,
reconcile_date: item.register_date || '2025-01-15T00:00:00'
}));
} else {
this.dialog.historyItems = [];
}
}
},
confirmBalance() {
if (!this.dialog.balanceAction) {
this.dialog.errorMessage = '請選擇處理方式';
return;
}
if (!this.dialog.memo.trim()) {
this.dialog.errorMessage = '請輸入處理說明';
return;
}
this.dialog.loading = true;
this.dialog.errorMessage = '';
// API 調
setTimeout(() => {
alert(`餘額處理完成:${this.getActionText(this.dialog.balanceAction)}`);
this.dialog.show = false;
this.dialog.loading = false;
//
}, 1500);
},
getActionText(action) {
switch (action) {
case 'transfer': return '轉入下次活動';
case 'refund_cash': return '現金退費';
case 'refund_bank': return '銀行轉帳退費';
case 'balance_off': return '結餘處理';
default: return '未知操作';
}
},
showDetailDialog(item) {
this.detailDialog.selected = item;
this.detailDialog.tab = 0; // tab
// JSON
this.loadReconcileItems(item.draft, item.id);
this.detailDialog.show = true;
},
async loadReconcileItems(draft, transferRegisterId) {
this.detailDialog.reconcileItems = [];
if (!draft) return;
const detailItems = window.DraftUtils.getDraftField(draft, 'pro_order_detail_items');
if (detailItems && Array.isArray(detailItems)) {
// 使 pro_order_detail_items
this.detailDialog.reconcileItems = detailItems.map((item, index) => {
return {
pro_order_detail_num: item.pro_order_detail_num,
activity_name: item.activity_name || '未知活動',
actitem_name: item.actitem_name || '未知項目',
price: item.price || 0,
reconcile: item.reconcile,
register_date: item.register_date,
order_no: item.order_no || ''
};
});
} else {
// 使 API
try {
const response = await axios.get(`../../api/transfer_register/reconcile_detail?transfer_register_id=${transferRegisterId}`);
this.detailDialog.reconcileItems = response.data;
} catch (error) {
console.error('載入沖帳明細失敗:', error);
}
}
},
search_show(curr) {
this.search_dialog.current = curr;
this.search_clear();
this.search_dialog.show = true;
},
search_clear() {
if (!this.search_dialog.current.keys) return;
this.search_dialog.current.keys.forEach((t, i) => { t.value = '' });
this.search_get();
},
search_get() {
if (!this.search_dialog.current.keys) return;
let api_url = this.search_dialog.current.api_url;
let keys = this.search_dialog.current.keys;
this.search_dialog.page = this.options.page ?? 1;
let params = { page: this.search_dialog.page, pageSize: 10 };
var search = {};
keys.forEach((t, i) => {
search[t.id] = t.value;
});
// Dialog is_reconcile
if (this.search_dialog.current.id === 'search5' && this.search_is_reconcile === 'Y') {
search.is_reconcile = 'Y';
}
console.log("search_get", api_url, search, params, this.options);
this.search_dialog.loading = true;
axios.post(api_url, search, { params: params })
.then(response => {
this.search_dialog.list = response.data.list;
this.search_dialog.count = response.data.count;
this.search_dialog.loading = false;
})
.catch(error => {
console.log(error);
this.search_dialog.list = [];
this.search_dialog.count = 0;
this.search_dialog.loading = false;
alert("錯誤:" + error);
});
},
search_headers() {
if (!this.search_dialog.current.columns) return;
let r = [];
this.search_dialog.current.columns.forEach((t, i) => {
r.push({
text: t.title,
width: t.width,
align: 'start',
sortable: false,
value: t.id,
});
});
return r;
},
search_select(row) {
let curr = this.search_dialog.current;
curr.selected = row;
if (curr.select instanceof Function) {
curr.select(this, row);
}
this.search_dialog.show = false;
console.log('Selected row:', row);
},
showItemSelectDialog(item) {
//
this.search_dialog.controls.search5.currentRow = item;
this.search_dialog.current = this.search_dialog.controls.search5;
this.search_is_reconcile = "Y"; //
this.search_clear();
this.search_get(); // Dialog
this.search_dialog.show = true;
},
processBalance(item) {
if (!item.new_status) {
alert('請選擇狀態');
return;
}
if (!item.balance_act_item) {
alert('請先選擇項目');
return;
}
//
const updateData = {
id: item.id,
check_status: item.new_status,
balance_act_item: item.balance_act_item,
verify_note: item.verify_note || ''
};
//
axios.post('../../api/transfer_register/update_balance', updateData)
.then(response => {
const statusText = this.statusOptions.find(opt => opt.value === item.new_status)?.text || '';
alert(`核銷餘額處理完成\n信眾:${item.follower}\n狀態:${statusText}\n項目:${item.selected_items_text}`);
// (95)
if (item.new_status === '95') {
const index = this.items.findIndex(i => i.id === item.id);
if (index !== -1) {
this.items.splice(index, 1);
}
} else {
//
const index = this.items.findIndex(i => i.id === item.id);
if (index !== -1) {
this.items[index].check_status = item.new_status;
this.items[index].verify_note = item.verify_note || '';
this.items[index].balance_act_item = item.balance_act_item;
// selected_items_text
this.items[index].selected_items_text = item.selected_items_text;
}
}
})
.catch(error => {
console.error('更新失敗:', error);
alert('更新失敗,請重試');
});
},
canUpdate(item) {
//
if (!item.balance_act_item) {
return false;
}
//
if (item.new_status === '95') {
return true;
}
//
if (item.new_status === '91' || item.new_status === '92') {
return true;
}
//
return false;
},
}
};
</script>
<style>
.code-textarea textarea {
font-family: 'Courier New', monospace !important;
font-size: 12px !important;
}
/* 增加表格每列高度,讓內容不擁擠 */
.v-data-table.balance-reconcile-table .v-data-table__wrapper tr {
}
.v-data-table.balance-reconcile-table .v-data-table__wrapper td {
min-height: 110px !important;
height: 110px !important;
vertical-align: middle !important;
}
</style>
+300
View File
@@ -0,0 +1,300 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-info white--text text-center">
<h5 class="mb-0">餘額核銷查詢</h5>
<button class="btn btn-default ms-auto"><i class="mdi mdi-close"></i></button>
</v-card-title>
<v-card-text>
<v-row class="mb-0 mt-4">
<v-col cols="12" md="2">
<v-text-field v-model="query.start_date" label="起始日" type="date" dense outlined></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="query.end_date" label="結束日" type="date" dense outlined></v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field v-model="query.activity_name" label="法會" dense outlined readonly>
<template v-slot:append>
<v-btn icon @click="showActivityDialog"><v-icon>mdi-magnify</v-icon></v-btn>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field v-model="query.follower_name" label="信眾" dense outlined readonly>
<template v-slot:append>
<v-btn icon @click="showFollowerDialog"><v-icon>mdi-magnify</v-icon></v-btn>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="2" class="d-flex justify-end">
<v-btn color="primary" class="mr-2" @click="search">查詢</v-btn>
<v-btn color="grey" @click="reset">重設</v-btn>
</v-col>
</v-row>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1 balance-reconcile-table"
item-key="id"
:footer-props="{ 'items-per-page-options': [10, 20, 50] }">
<template v-slot:item.follower="{ item }">
<div>
<div class="font-weight-bold">{{ item.follower }}</div>
<div class="caption text--secondary" v-if="item.phone">{{ item.phone }}</div>
<div class="caption text--secondary" v-if="item.phone2">{{ item.phone2 }}</div>
</div>
</template>
<template v-slot:item.bank_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.acc_name }}</div>
<div class="caption text--secondary">{{ item.check_date | date }}</div>
</div>
</template>
<template v-slot:item.amount_info="{ item }">
<div>
<div class="font-weight-bold text-primary">入帳金額{{ item.check_amount || 0 }}</div>
<div class="text-success">核銷金額{{ (item.remain_amount || 0) }}</div>
<!-- 狀態95不會有待核銷不顯示這一行 -->
</div>
</template>
<template v-slot:item.remain_amount="{ item }">
<div class="text-success">{{ item.remain_amount || 0 }}</div>
</template>
<template v-slot:item.select_items="{ item }">
<div style="min-width: 120px;">
{{ item.balance_actitem_name || '未選擇項目' }}
</div>
</template>
<template v-slot:item.detail_info="{ item }">
<v-btn color="info" outlined @click="showDetailDialog(item)">
<v-icon small class="mr-1">mdi-information-outline</v-icon> 詳細
</v-btn>
</template>
<template v-slot:item.verify_note="{ item }">
<div style="white-space: pre-line;">{{ item.verify_note }}</div>
</template>
<template v-slot:item.check_date="{ item }">
<span>{{ item.check_date | date }}</span>
</template>
</v-data-table>
</v-card-text>
</v-card>
<!-- 法會選取 Dialog -->
<v-dialog v-model="activityDialog.show" max-width="700px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-table</v-icon>
選擇法會
<v-spacer></v-spacer>
<v-btn icon @click="activityDialog.show = false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text class="mt-4">
<v-text-field v-model="activityDialog.search" label="搜尋法會" dense outlined @keyup.enter="searchActivity"></v-text-field>
<v-data-table :headers="activityDialog.headers"
:items="activityDialog.items"
:loading="activityDialog.loading"
item-key="num"
class="elevation-1 mt-2"
@click:row="selectActivity"
hide-default-footer></v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<!-- 信眾選取 Dialog -->
<v-dialog v-model="followerDialog.show" max-width="700px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-account</v-icon>
選擇信眾
<v-spacer></v-spacer>
<v-btn icon @click="followerDialog.show = false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text class="mt-4">
<v-text-field v-model="followerDialog.search" label="搜尋信眾" dense outlined @keyup.enter="searchFollower"></v-text-field>
<v-data-table :headers="followerDialog.headers"
:items="followerDialog.items"
:loading="followerDialog.loading"
item-key="num"
class="elevation-1 mt-2"
@click:row="selectFollower"
hide-default-footer></v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<!-- 詳細資訊對話框 -->
<v-dialog v-model="dialog.show" max-width="900px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
詳細資訊
<v-spacer></v-spacer>
<v-btn icon @click="dialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<div v-if="dialog.selected">
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-primary">匯款人資訊</h6>
<div class="mb-2"><span class="font-weight-bold">姓名</span>{{ dialog.selected.name }}</div>
<div class="mb-2"><span class="font-weight-bold">電話</span>{{ dialog.selected.phone }}</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-success">入帳資訊</h6>
<div class="mb-2"><span class="font-weight-bold">入帳帳戶</span>{{ dialog.selected.acc_name }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳日期</span>{{ dialog.selected.check_date | date }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳金額</span>{{ dialog.selected.check_amount }}</div>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-purple">活動資訊</h6>
<div class="mb-2"><span class="font-weight-bold">活動名稱</span>{{ dialog.selected.activity_name || '-' }}</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-info">核對記錄</h6>
<div class="mb-2"><span class="font-weight-bold">核對記錄</span>{{ dialog.selected.verify_note || '-' }}</div>
</v-col>
</v-row>
</div>
</v-card-text>
</v-card>
</v-dialog>
</v-container>
</template>
<style>
.v-data-table.balance-reconcile-table .v-data-table__wrapper tbody tr {
min-height: 90px !important;
height: 90px !important;
}
.v-data-table.balance-reconcile-table .v-data-table__wrapper tbody td {
min-height: 90px !important;
height: 90px !important;
vertical-align: middle !important;
}
</style>
<script>
module.exports = {
data() {
return {
loading: false,
query: {
start_date: '',
end_date: '',
activity_num: '',
activity_name: '',
follower_num: '',
follower_name: ''
},
headers: [
{ text: '信眾', value: 'follower' },
{ text: '入帳帳戶/日期', value: 'bank_info' },
{ text: '核銷日期', value: 'check_date', sortable: true, width: '120px' },
{ text: '核銷項目', value: 'select_items', sortable: false, width: '120px' },
{ text: '核銷金額', value: 'reconcile_amount', sortable: false, width: '120px' },
{ text: '詳細資訊', value: 'detail_info', sortable: false },
// { text: '', value: 'status', sortable: false, width: '100px' }, //
{ text: '核對記錄', value: 'verify_note', sortable: false, width: '180px' },
],
items: [],
dialog: {
show: false,
selected: null
},
activityDialog: {
show: false,
search: '',
loading: false,
items: [],
headers: [
{ text: '編號', value: 'num' },
{ text: '名稱', value: 'subject' }
]
},
followerDialog: {
show: false,
search: '',
loading: false,
items: [],
headers: [
{ text: '編號', value: 'num' },
{ text: '姓名', value: 'u_name' }
]
}
}
},
filters: {
date(val) {
if (!val) return '';
const date = new Date(val);
return date.toLocaleDateString();
}
},
methods: {
// 使 DraftUtils
search() {
this.loading = true;
// API
axios.get('../../api/transfer_register/balance_reconcile_query', { params: this.query })
.then(res => { this.items = res.data; })
.catch(() => { this.items = []; })
.finally(() => { this.loading = false; });
},
reset() {
this.query.start_date = '';
this.query.end_date = '';
this.query.activity_num = '';
this.query.activity_name = '';
this.query.follower_num = '';
this.query.follower_name = '';
this.items = [];
},
showDetailDialog(item) {
this.dialog.selected = item;
this.dialog.show = true;
},
showActivityDialog() {
this.activityDialog.show = true;
this.searchActivity();
},
searchActivity() {
this.activityDialog.loading = true;
axios.get('../../api/activity', { params: { keyword: this.activityDialog.search } })
.then(res => { this.activityDialog.items = res.data; })
.catch(() => { this.activityDialog.items = []; })
.finally(() => { this.activityDialog.loading = false; });
},
selectActivity(row) {
this.query.activity_num = row.num;
this.query.activity_name = row.subject;
this.activityDialog.show = false;
},
showFollowerDialog() {
this.followerDialog.show = true;
this.searchFollower();
},
searchFollower() {
this.followerDialog.loading = true;
axios.post('../../api/follower/GetList', { u_name: this.followerDialog.search }, { params: { page: 1, pageSize: 10 } })
.then(res => { this.followerDialog.items = res.data.list; })
.catch(() => { this.followerDialog.items = []; })
.finally(() => { this.followerDialog.loading = false; });
},
selectFollower(row) {
this.query.follower_num = row.num;
this.query.follower_name = row.u_name;
this.followerDialog.show = false;
}
}
};
</script>
+657
View File
@@ -0,0 +1,657 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h5 class="mb-0">共同 - 沖帳流程</h5>
<button class="btn btn-default ms-auto"><i class="mdi mdi-close"></i></button>
</v-card-title>
<v-card-text>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1"
item-key="id">
<template v-slot:item.follower="{ item }">
<span :title="item.f_num">{{ item.follower }}</span>
</template>
<template v-slot:item.activity_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.activity_name }}</div>
<div class="caption text--secondary">
<span v-if="hasFollowerList(item.draft)" style="margin-right: 4px;">👥</span>
{{ item.acc_name }}
</div>
</div>
</template>
<template v-slot:item.check_date="{ item }">
<span>{{ item.check_date | date }}</span>
</template>
<template v-slot:item.check_memo="{ item }">
<span>{{ item.check_memo }}</span>
</template>
<template v-slot:item.verify_note="{ item }">
<span>{{ item.verify_note }}</span>
</template>
<template v-slot:item.follower_selection="{ item }">
<div>
<v-btn small
color="primary"
outlined
@click="showFollowerSelection(item)"
class="mb-1">
<v-icon left small>mdi-account-multiple</v-icon>
選擇支付人
</v-btn>
<div v-if="hasFollowerList(item.draft)" class="caption text-success mt-1">
<v-icon small color="success">mdi-check-circle</v-icon>
已選擇 {{ getFollowerCount(item.draft) }}
</div>
</div>
</template>
<template v-slot:item.actions="{ item }">
<v-btn small
color="success"
@click="showGroupReconcileDialog(item)"
:disabled="!hasFollowerList(item.draft)">
<v-icon left small>mdi-receipt</v-icon>
{{ hasFollowerList(item.draft) ? '共同沖帳' : '請先選擇支付人' }}
</v-btn>
</template>
</v-data-table>
</v-card-text>
</v-card>
<!-- 選擇共同支付人對話框 -->
<v-dialog v-model="followerDialog.show" max-width="800px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-account-multiple</v-icon>
選擇共同支付人
<v-spacer></v-spacer>
<v-btn icon @click="followerDialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="mt-4">
<div v-if="followerDialog.selected">
<h6 class="mb-3">法會{{ followerDialog.selected.activity_name }}</h6>
<v-text-field v-model="followerDialog.search"
label="搜尋信眾"
dense
outlined
prepend-inner-icon="mdi-magnify"
@input="searchActivityFollowers"></v-text-field>
<v-data-table :headers="followerDialog.headers"
:items="followerDialog.items"
:loading="followerDialog.loading"
item-key="f_num"
class="elevation-1 mt-2"
show-select
v-model="followerDialog.selectedFollowers"
hide-default-footer
:disable-pagination="true">
<template v-slot:item.follower_name="{ item }">
<div>
<div class="font-weight-bold">{{ item.follower_name }}</div>
<div class="caption text--secondary">編號: {{ item.f_num }}</div>
</div>
</template>
<template v-slot:item.total_due="{ item }">
<span class="text-danger">{{ item.total_due | currency }}</span>
</template>
<template v-slot:item.item_count="{ item }">
<span class="text-info">{{ item.item_count }} </span>
</template>
</v-data-table>
</div>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer></v-spacer>
<v-btn color="grey" @click="followerDialog.show = false">取消</v-btn>
<v-btn color="primary" @click="confirmFollowerSelection" :disabled="followerDialog.selectedFollowers.length === 0">
確認選擇 ({{ followerDialog.selectedFollowers.length }})
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 共同沖帳明細對話框 -->
<v-dialog v-model="reconcileDialog.show" max-width="1200px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="success" class="mr-2">mdi-receipt</v-icon>
共同沖帳明細
<v-spacer></v-spacer>
<v-btn icon @click="reconcileDialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="mb-0 pb-0">
<div v-if="reconcileDialog.selected">
<div class="row my-2">
<h6 class="col">共同支付人{{ getSelectedFollowersText() }}</h6>
<div class="col">
<span class="font-weight-bold">入帳金額</span>
<span class="text-primary">{{ reconcileDialog.selected.check_amount | currency }}</span>
</div>
<div class="col">
<span class="font-weight-bold">已沖金額</span>
<span :class="{'text-danger': isOverPaid, 'text-dark': !isOverPaid}">{{ sumReconcile | currency }}</span>
</div>
<div class="col">
<span class="font-weight-bold">未繳餘款</span>
<span class="text-danger">{{ remainDue | currency }}</span>
</div>
<div class="col">
<span class="font-weight-bold">入帳後餘額</span>
<span class="text-success">{{ overPaid | currency }}</span>
</div>
</div>
<v-data-table :headers="reconcileDialog.headers"
:items="reconcileDialog.items"
class="elevation-1 mt-3"
hide-default-footer
:disable-pagination="true">
<template v-slot:item.follower_info="{ item }">
<div>
<div class="font-weight-bold text-primary">{{ item.follower_name }}</div>
<div class="text-muted" style="font-size:12px;">{{ item.order_no }}</div>
</div>
</template>
<template v-slot:item.activity_name="{ item }">
<div>
<div>{{ item.activity_name }}</div>
<div class="text-muted" style="font-size:12px;">{{ item.reg_time | date }}</div>
</div>
</template>
<template v-slot:item.paid="{ item }">
<span>{{ item.paid | currency }}</span>
</template>
<template v-slot:item.due="{ item }">
<span>{{ item.due | currency }}</span>
</template>
<template v-slot:item.reconcile="{ item, index }">
<v-text-field v-model="item.reconcile"
type="number"
dense
outlined
hide-details="auto"
:rules="[
v=>
!isNaN(Number(v)) || '請輸入數字',
v => Number(v) >= 0 || '不可小於 0',
v => Number(v) <= Number(item.due) || `不可大於待繳金額 ${item.due}`
]"
@blur="validateAndUpdateReconcile($event.target.value, item, index)"
>
</v-text-field>
</template>
</v-data-table>
</div>
</v-card-text>
<v-card-actions class="pa-4">
<div>
<div v-if="hasUnallocated && remainDue > 0" class="text-dark mb-2">
尚有未分配的入帳金額請確認是否全部分配
</div>
<div v-if="hasUnallocated && remainDue === 0" class="text-info mb-2">
已無未繳項目剩餘入帳金額將成為餘額
</div>
<div v-if="!canConfirm && buttonErrorMessage" class="text-danger">
{{ buttonErrorMessage }}
</div>
</div>
<v-spacer></v-spacer>
<v-btn color="info" @click="redistributeReconcile" class="mr-2">重新分配</v-btn>
<v-btn color="orange" @click="saveDraft" class="mr-2">暫存</v-btn>
<v-btn color="primary" @click="confirmGroupReconcile" :disabled="!canConfirm">確認沖帳</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
if (typeof window.DraftUtils === 'undefined') {
console.warn('DraftUtils 未載入,使用備用方案');
window.DraftUtils = {
hasFollowerList: function (draft) { return false; },
getFollowerList: function (draft) { return []; },
updateFollowerList: function (draft, list) {
return { follower_list: list };
},
getDraftField: function (draft, field) { return null; },
updateDraftField: function (draft, field, value) {
return { [field]: value };
}
};
}
module.exports = {
data() {
return {
loading: false,
headers: [
{ text: '匯款人', value: 'follower' },
{ text: '法會/入帳帳戶', value: 'activity_info' },
{ text: '入帳日期', value: 'check_date' },
{ text: '帳簿備註', value: 'check_memo' },
{ text: '入帳金額', value: 'check_amount' },
{ text: '核對記錄', value: 'verify_note' },
{ text: '選擇共同支付人', value: 'follower_selection', sortable: false },
{ text: '共同沖帳', value: 'actions', sortable: false }
],
items: [],
followerDialog: {
show: false,
selected: null,
search: '',
loading: false,
items: [],
selectedFollowers: [],
headers: [
{ text: '信眾', value: 'follower_name' },
{ text: '待繳金額', value: 'total_due' },
{ text: '項目數', value: 'item_count' }
]
},
reconcileDialog: {
show: false,
selected: null,
headers: [
{ text: '信眾/報名單號', value: 'follower_info' },
{ text: '法會/報名日期', value: 'activity_name' },
{ text: '項目', value: 'actitem_name' },
{ text: '應繳金額', value: 'price', sortable: false },
{ text: '已繳金額', value: 'paid', sortable: false },
{ text: '待繳金額', value: 'due', sortable: false },
{ text: '沖帳金額', value: 'reconcile', sortable: false }
],
items: [],
loading: false,
errorMessage: ''
},
hasUnallocated: false,
draftDataChanged: false
}
},
computed: {
sumReconcile() {
return this.reconcileDialog.items.reduce((sum, item) => sum + (Number(item.reconcile) || 0), 0);
},
remainDue() {
const totalDue = this.reconcileDialog.items.reduce((sum, item) => sum + (Number(item.due) || 0), 0);
return totalDue - this.sumReconcile;
},
overPaid() {
if (!this.reconcileDialog.selected) return 0;
return this.reconcileDialog.selected.check_amount - this.sumReconcile;
},
isOverPaid() {
if (!this.reconcileDialog.selected) return false;
return this.sumReconcile > this.reconcileDialog.selected.check_amount;
},
canConfirm() {
if (!this.reconcileDialog.selected) return false;
const hasReconcile = this.reconcileDialog.items.some(item => Number(item.reconcile) > 0);
if (!hasReconcile) return false;
const validAmounts = this.reconcileDialog.items.every(item => {
const amount = Number(item.reconcile) || 0;
return amount >= 0 && amount <= Number(item.due);
});
if (!validAmounts) return false;
if (this.sumReconcile > this.reconcileDialog.selected.check_amount) return false;
if (this.hasUnallocated && this.remainDue > 0) return false;
return true;
},
buttonErrorMessage() {
if (!this.reconcileDialog.selected) return '';
const hasReconcile = this.reconcileDialog.items.some(item => Number(item.reconcile) > 0);
if (!hasReconcile) return '請至少輸入一筆沖帳金額';
const invalidItem = this.reconcileDialog.items.find(item => {
const amount = Number(item.reconcile) || 0;
return amount < 0 || amount > Number(item.due);
});
if (invalidItem) return '沖帳金額超出可沖帳範圍';
if (this.sumReconcile > this.reconcileDialog.selected.check_amount) {
return '已沖金額不可大於入帳金額';
}
if (this.hasUnallocated && this.remainDue > 0) {
return '尚有未繳項目,請將入帳金額完全分配';
}
return '';
}
},
mounted() {
this.loadTableData();
},
filters: {
currency(val) {
if (!val) return '0';
return Number(val).toLocaleString();
},
date(val) {
if (!val) return '';
const date = new Date(val);
return date.toLocaleDateString();
}
},
methods: {
// follower_list
hasFollowerList(draft) {
return window.DraftUtils && window.DraftUtils.hasFollowerList && window.DraftUtils.hasFollowerList(draft);
},
//
getFollowerCount(draft) {
if (!window.DraftUtils || !window.DraftUtils.getFollowerList) return 0;
const followerList = window.DraftUtils.getFollowerList(draft);
return followerList ? followerList.length : 0;
},
loadTableData() {
this.loading = true;
axios.get('../../api/transfer_register/group_reconcile_list')
.then(res => {
this.items = res.data;
})
.catch(() => {
this.items = [];
})
.finally(() => {
this.loading = false;
});
},
showGroupReconcileDialog(item) {
//
const followerList = window.DraftUtils.getFollowerList(item.draft);
if (followerList && followerList.length > 0) {
//
this.showReconcileDetail(item, followerList);
} else {
//
this.showFollowerSelection(item);
}
},
showFollowerSelection(item) {
this.followerDialog.selected = item;
this.followerDialog.selectedFollowers = [];
this.followerDialog.items = [];
this.followerDialog.show = true;
this.searchActivityFollowers();
},
searchActivityFollowers() {
if (!this.followerDialog.selected || !this.followerDialog.selected.activity_num) return;
this.followerDialog.loading = true;
axios.get('../../api/transfer_register/activity_followers', {
params: { activity_num: this.followerDialog.selected.activity_num }
})
.then(res => {
let items = res.data;
//
if (this.followerDialog.search && this.followerDialog.search.trim()) {
const keyword = this.followerDialog.search.trim().toLowerCase();
items = items.filter(item =>
item.follower_name.toLowerCase().includes(keyword)
);
}
this.followerDialog.items = items;
})
.catch(() => {
this.followerDialog.items = [];
})
.finally(() => {
this.followerDialog.loading = false;
});
},
confirmFollowerSelection() {
if (this.followerDialog.selectedFollowers.length === 0) {
alert('請選擇至少一位共同支付人');
return;
}
if (!this.followerDialog.selected) {
alert('系統錯誤:未選擇記錄');
return;
}
// draft
const followerList = this.followerDialog.selectedFollowers.map(f => ({
f_num: f.f_num,
f_name: f.follower_name,
activity_num: this.followerDialog.selected.activity_num
}));
const draftData = window.DraftUtils.updateFollowerList(this.followerDialog.selected.draft, followerList);
this.updateFollowerDraftToDB(this.followerDialog.selected.id, draftData)
.then(() => {
this.followerDialog.selected.draft = JSON.stringify(draftData);
this.followerDialog.show = false;
//
this.loadTableData();
//
this.showReconcileDetail(this.followerDialog.selected, followerList);
})
.catch(err => {
alert('儲存失敗,請重試');
console.error(err);
});
},
showReconcileDetail(item, followerList) {
this.reconcileDialog.selected = item;
this.reconcileDialog.items = [];
this.reconcileDialog.show = true;
//
const fNums = followerList.map(f => f.f_num).join(',');
axios.get('../../api/transfer_register/group_follower_orders', {
params: {
activity_num: item.activity_num,
f_nums: fNums
}
})
.then(res => {
this.reconcileDialog.items = res.data.map(item => ({
...item,
reconcile: 0 //
}));
this.autoDistributeReconcile();
})
.catch(() => {
this.reconcileDialog.items = [];
this.updateSumReconcile();
});
},
getSelectedFollowersText() {
if (!this.reconcileDialog.selected) return '';
const followerList = window.DraftUtils.getFollowerList(this.reconcileDialog.selected.draft);
return followerList.map(f => f.f_name).join('、');
},
autoDistributeReconcile() {
//
let remainAmount = this.reconcileDialog.selected ? this.reconcileDialog.selected.check_amount : 0;
// reconcile 0
this.reconcileDialog.items.forEach(item => {
this.$set(item, 'reconcile', 0);
});
//
const sortedItems = [...this.reconcileDialog.items].sort((a, b) =>
new Date(a.reg_time) - new Date(b.reg_time)
);
//
sortedItems.forEach(item => {
if (remainAmount > 0) {
const canPay = Math.min(item.due, remainAmount);
const originalItem = this.reconcileDialog.items.find(i => i === item);
if (originalItem) {
this.$set(originalItem, 'reconcile', canPay);
}
remainAmount -= canPay;
}
});
this.updateSumReconcile();
},
redistributeReconcile() {
this.autoDistributeReconcile();
},
updateSumReconcile() {
const maxTotal = this.reconcileDialog.selected ? (this.reconcileDialog.selected.check_amount || 0) : 0;
this.hasUnallocated = maxTotal > this.sumReconcile;
},
saveDraft() {
try {
//
const followerList = window.DraftUtils.getFollowerList(this.reconcileDialog.selected.draft);
const reconcileData = this.reconcileDialog.items
.filter(item => Number(item.reconcile) > 0)
.map(item => ({
f_num: item.f_num,
follower_name: item.follower_name,
pro_order_detail_num: item.num,
reconcile: Number(item.reconcile)
}));
const draftData = {
transfer_draft: [],
pro_order_detail_items: reconcileData,
follower_list: followerList
};
this.updateDraftToDB(this.reconcileDialog.selected.id, draftData)
.then(() => {
alert('暫存成功!');
this.reconcileDialog.selected.draft = JSON.stringify(draftData);
});
} catch (e) {
alert('暫存失敗:資料格式錯誤');
}
},
updateFollowerDraftToDB(id, draftData) {
const selectedItem = this.followerDialog.selected;
const updateData = {
id: id,
activity_num: selectedItem.activity_num,
name: selectedItem.name,
phone: selectedItem.phone,
pay_type: selectedItem.pay_type,
account_last5: selectedItem.account_last5,
amount: selectedItem.amount,
pay_mode: selectedItem.pay_mode,
note: selectedItem.note,
proof_img: selectedItem.proof_img,
status: selectedItem.status,
f_num_match: selectedItem.f_num_match,
f_num: selectedItem.f_num,
acc_num: selectedItem.acc_num,
check_date: selectedItem.check_date,
check_amount: selectedItem.check_amount,
check_memo: selectedItem.check_memo,
check_status: selectedItem.check_status,
acc_kind: selectedItem.acc_kind,
member_num: selectedItem.member_num,
verify_time: selectedItem.verify_time,
verify_note: selectedItem.verify_note,
draft: JSON.stringify(draftData)
};
return axios.put(`../../api/transfer_register/${id}`, updateData);
},
updateDraftToDB(id, draftData) {
const updateData = {
id: id,
activity_num: this.reconcileDialog.selected.activity_num,
name: this.reconcileDialog.selected.name,
phone: this.reconcileDialog.selected.phone,
pay_type: this.reconcileDialog.selected.pay_type,
account_last5: this.reconcileDialog.selected.account_last5,
amount: this.reconcileDialog.selected.amount,
pay_mode: this.reconcileDialog.selected.pay_mode,
note: this.reconcileDialog.selected.note,
proof_img: this.reconcileDialog.selected.proof_img,
status: this.reconcileDialog.selected.status,
f_num_match: this.reconcileDialog.selected.f_num_match,
f_num: this.reconcileDialog.selected.f_num,
acc_num: this.reconcileDialog.selected.acc_num,
check_date: this.reconcileDialog.selected.check_date,
check_amount: this.reconcileDialog.selected.check_amount,
check_memo: this.reconcileDialog.selected.check_memo,
check_status: this.reconcileDialog.selected.check_status,
acc_kind: this.reconcileDialog.selected.acc_kind,
member_num: this.reconcileDialog.selected.member_num,
verify_time: this.reconcileDialog.selected.verify_time,
verify_note: this.reconcileDialog.selected.verify_note,
draft: JSON.stringify(draftData)
};
return axios.put(`../../api/transfer_register/${id}`, updateData);
},
confirmGroupReconcile() {
if (!this.canConfirm) return;
this.reconcileDialog.loading = true;
const overPayment = this.reconcileDialog.selected.check_amount - this.sumReconcile;
const postData = {
transfer_register_id: this.reconcileDialog.selected.id,
details: this.reconcileDialog.items
.filter(item => item.reconcile > 0)
.map(item => ({
f_num: item.f_num,
pro_order_detail_num: item.num,
amount: Number(item.reconcile),
reg_time: new Date().toISOString()
})),
over_payment: overPayment
};
axios.post('../../api/transfer_register/group_reconcile', postData)
.then(res => {
const message = res.data && res.data.message ? res.data.message : '共同沖帳完成';
alert(message);
this.reconcileDialog.show = false;
this.loadTableData(); //
})
.catch(err => {
let errorMessage = '沖帳失敗,請聯繫系統管理員';
if (err.response && err.response.data) {
if (typeof err.response.data === 'string') {
errorMessage = err.response.data;
} else if (err.response.data.message) {
errorMessage = err.response.data.message;
}
}
alert(errorMessage);
})
.finally(() => {
this.reconcileDialog.loading = false;
});
},
validateAndUpdateReconcile(value, item, index) {
let numValue = Number(value);
if (isNaN(numValue)) {
numValue = 0;
}
numValue = Math.round(numValue);
this.$set(item, 'reconcile', numValue);
this.updateSumReconcile();
}
}
};
</script>
+300
View File
@@ -0,0 +1,300 @@
<%@ Page Title="匯款/沖帳管理" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="index.aspx.cs" Inherits="admin_bill_index" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
<link rel="stylesheet" href="../../js/_bootstrap-icons-1.8.1/bootstrap-icons.css">
<style>
.function-icon {
font-size: 2em;
line-height: 1;
align-content: center;
}
.external-link-icon {
font-size: 0.8em;
}
.modify_modal{
width:calc(100vw-20%);
background-color:white;
opacity:0;
}
</style>
<script src="/js//httpVueLoader.js"></script>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="page_nav" runat="Server">
<h2 class="mb-3">匯款/沖帳管理</h2>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<v-container>
<div id="content" class="container py-4">
<div class="row">
<!-- 第一欄:匯款登錄與核對 -->
<div class="col-lg-4 mb-4">
<h5 class="text-primary mb-3">
<i class="bi bi-upload"></i> 匯款登錄與核對
</h5>
<div class="list-group">
<a @click="show_register()" class="list-group-item list-group-item-action" >
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-plus-circle text-success me-3 function-icon"></i>
<div>
<div>
登錄匯款資料
<i class="bi bi-box-arrow-up-right text-muted ms-1 external-link-icon"></i>
</div>
<small class="text-muted">報名者自行填寫匯款相關資訊</small>
</div>
</div>
<span class="badge bg-primary">報名者</span>
</div>
</a>
<a @click="show_verify()" class="list-group-item list-group-item-action d-none">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-person-check text-info me-3 function-icon"></i>
<div>
<div>出納核對匯款人</div>
<small class="text-muted">核對匯款人身份與報名資料</small>
</div>
</div>
<span class="badge bg-info">出納</span>
</div>
</a>
<a @click="show_verify()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-person-check text-info me-3 function-icon"></i>
<div>
<div>出納核對匯款人</div>
<small class="text-muted">初步核對匯款人身份資料</small>
</div>
</div>
<span class="badge bg-info">出納</span>
</div>
</a>
<%-- <a href="verify2.aspx" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-currency-dollar text-warning me-3 function-icon"></i>
<div>
<div>出納核對金額(階段2</div>
<small class="text-muted">核對匯款金額與入帳資料</small>
</div>
</div>
<span class="badge bg-warning text-dark">出納</span>
</div>
</a>--%>
</div>
</div>
<!-- 第二欄:沖帳流程 -->
<div class="col-lg-4 mb-4">
<h5 class="text-primary mb-3">
<i class="bi bi-receipt"></i> 沖帳流程
</h5>
<div class="list-group">
<a @click="personal_reconcile()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-person text-primary me-3 function-icon"></i>
<div>
<div>個人-沖帳流程</div>
<small class="text-muted">處理個人匯款的沖帳作業</small>
</div>
</div>
<span class="badge bg-success">會計</span>
</div>
</a>
<a @click="group_reconcile()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-people text-success me-3 function-icon"></i>
<div>
<div>共同-沖帳流程</div>
<small class="text-muted">處理多人共同支付的沖帳作業</small>
</div>
</div>
<span class="badge bg-success">會計</span>
</div>
</a>
<a @click="balance_reconcile()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-calculator text-danger me-3 function-icon"></i>
<div>
<div>餘額核銷</div>
<small class="text-muted">處理沖帳後剩餘金額的核銷</small>
</div>
</div>
<span class="badge bg-danger">會計</span>
</div>
</a>
</div>
</div>
<!-- 第三欄:查詢功能 -->
<div class="col-lg-4 mb-4">
<h5 class="text-primary mb-3">
<i class="bi bi-search"></i> 查詢功能
</h5>
<div class="list-group">
<a @click="verify_query()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-journal-check text-info me-3 function-icon"></i>
<div>
<div>沖帳查詢</div>
<small class="text-muted">查詢所有沖帳記錄與明細</small>
</div>
</div>
<span class="badge bg-info">會計</span>
</div>
</a>
<a @click="balance_query()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-file-text text-secondary me-3 function-icon"></i>
<div>
<div>餘額核銷查詢</div>
<small class="text-muted">查詢已完成的餘額核銷記錄</small>
</div>
</div>
<span class="badge bg-secondary">會計</span>
</div>
</a>
<a @click="statistic_query()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-file-spreadsheet text-secondary me-3 function-icon"></i>
<div>
<div>統計報表</div>
<small class="text-muted"></small>
</div>
</div>
<span class="badge bg-secondary">會計</span>
</div>
</a>
<%-- <a @click="balance_query()" class="list-group-item list-group-item-action">
<div class="d-flex justify-content-between align-items-start">
<div class="d-flex">
<i class="bi bi-file-spreadsheet text-secondary me-3 function-icon"></i>
<div>
<div>帳務查詢</div>
<small class="text-muted"></small>
</div>
</div>
<span class="badge bg-secondary">會計</span>
</div>
</a>--%>
</div>
</div>
</div>
<!-- 統計資訊 -->
<div class="row mt-4">
<div class="col-12">
<div class="alert alert-info">
<h6 class="alert-heading">
<i class="bi bi-info-circle"></i> 系統說明
</h6>
<ul class="mb-0">
<li><strong>匯款登錄與核對</strong>:處理報名者匯款資料的登錄與出納核對作業</li>
<li><strong>沖帳流程</strong>:處理個人與共同支付的沖帳作業,以及剩餘金額的核銷</li>
<li><strong>查詢功能</strong>:提供各類沖帳記錄的查詢與統計功能</li>
</ul>
</div>
</div>
</div>
</div>
<v-dialog v-model="modify_dialog.show" class="modify_modal">
<template>
<keep-alive>
<component :is="currentView" @close-dialog="modify_dialog.show=false"></component>
</keep-alive>
</template>
</v-dialog>
</v-container>
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" runat="Server">
<script src="../transfer/draft-utils.js"></script>
<script>
let VueApp = new Vue({
el: '#app',
vuetify: new Vuetify(vuetify_options),
data() {
return {
modify_dialog: {
show:false
},
externalHtml: "",
currentView:null
};
},
created() {
//this.loadActivities();
},
components: {
'register-component': httpVueLoader('./register.vue'),
'verify-component': httpVueLoader('./verify.vue'),
'group-component': httpVueLoader('./group_reconcile.vue'),
'personal-component': httpVueLoader('./personal_reconcile.vue'),
'balance-component': httpVueLoader('./balance_reconcile.vue'),
'verify_query-component': httpVueLoader('./verify_order_record_query.vue'),
'balance_query-component': httpVueLoader('./balance_reconcile_query.vue'),
'statistic_query-component': httpVueLoader('./statistic.vue'),
},
methods: {
async statistic_query(){
this.modify_dialog.show = true
this.currentView = 'statistic_query-component'
},
async balance_query() {
this.modify_dialog.show = true
this.currentView = 'balance_query-component'
},
async verify_query(){
this.modify_dialog.show = true
this.currentView = 'verify_query-component'
},
async balance_reconcile() {
this.modify_dialog.show = true
this.currentView = 'balance-component'
},
async personal_reconcile() {
this.modify_dialog.show = true
this.currentView = 'personal-component'
},
async group_reconcile() {
this.modify_dialog.show = true
this.currentView = 'group-component'
},
async show_verify() {
this.modify_dialog.show = true
this.currentView = 'verify-component'
},
async show_register() {
this.modify_dialog.show = true
this.currentView ='register-component'
},
loadActivities() {
const today = new Date().toISOString().split('T')[0];
axios.get(`../../api/activity?endDate<=${today}`)
.then(response => {
this.activities = response.data;
})
.catch(error => {
console.error('載入活動列表失敗:', error);
alert('載入活動列表失敗,請重新整理頁面');
});
},
}
})
</script>
</asp:Content>
+15
View File
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class admin_bill_index : MyWeb.config
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
+510
View File
@@ -0,0 +1,510 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h5 class="mb-0">個人 - 沖帳流程</h5>
<button class="btn btn-default ms-auto" ><i class="mdi mdi-close"></i></button>
</v-card-title>
<v-card-text>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1"
item-key="id">
<template v-slot:item.follower="{ item }">
<span :title="item.f_num">{{ item.follower }}</span>
</template>
<template v-slot:item.acc_name="{ item }">
<span>
<span v-if="hasTransferDraft(item.draft)" style="margin-right: 4px;">📝</span>{{ item.acc_name }}
</span>
</template>
<template v-slot:item.check_date="{ item }">
<span>{{ item.check_date | date }}</span>
</template>
<template v-slot:item.check_memo="{ item }">
<span>{{ item.check_memo }}</span>
</template>
<template v-slot:item.check_status="{ item }">
<span>{{ item.check_status }}</span>
</template>
<template v-slot:item.verify_note="{ item }">
<span>{{ item.verify_note }}</span>
</template>
<template v-slot:item.actions="{ item }">
<v-btn small
color="success"
@click="showReconcileDialog(item)">
沖帳
</v-btn>
</template>
</v-data-table>
</v-card-text>
</v-card>
<!-- 沖帳明細對話框 -->
<v-dialog v-model="dialog.show" max-width="960px">
<v-card>
<v-card-title class="grey lighten-2">
<span v-if="dialog.selected && hasTransferDraft(dialog.selected.draft)" style="margin-right: 8px;">📝</span>沖帳明細
<v-spacer></v-spacer>
<v-btn icon @click="dialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="mb-0 pb-0">
<div v-if="dialog.selected">
<div class="row my-2">
<h6 class="col">信眾{{ dialog.selected.follower }}</h6>
<div class="col">
<span class="font-weight-bold">入帳金額</span>
<span class="text-primary">{{ dialog.selected.check_amount | currency }}</span>
</div>
<div class="col">
<span class="font-weight-bold">已沖金額</span>
<span :class="{'text-danger': isOverPaid, 'text-dark': !isOverPaid}">{{ sumReconcile | currency }}</span>
</div>
<div class="col">
<span class="font-weight-bold">未繳餘款</span>
<span class="text-danger">{{ remainDue | currency }}</span>
</div>
<div class="col">
<span class="font-weight-bold">入帳後餘額</span>
<span class="text-success">{{ overPaid | currency }}</span>
</div>
</div>
<v-data-table :headers="dialog.headers"
:items="dialog.items"
class="elevation-1 mt-3"
hide-default-footer
:disable-pagination="true">
<template v-slot:item.activity_name="{ item }">
<div>
<div>{{ item.activity_name }}</div>
<div class="text-muted" style="font-size:12px;">{{ item.order_no }}</div>
</div>
</template>
<template v-slot:item.paid="{ item }">
<span>{{ item.paid | currency }}</span>
</template>
<template v-slot:item.due="{ item }">
<span>{{ item.due | currency }}</span>
</template>
<template v-slot:item.reg_time="{ item }">
<span>{{ item.reg_time | date }}</span>
</template>
<template v-slot:item.reconcile="{ item, index }">
<v-text-field v-model="item.reconcile"
type="number"
dense
outlined
hide-details="auto"
:rules="[
v=>
!isNaN(Number(v)) || '請輸入數字',
v => Number(v) >= 0 || '不可小於 0',
v => Number(v) <= Number(item.due) || `不可大於待繳金額 ${item.due}`
]"
@blur="validateAndUpdateReconcile($event.target.value, item, index)"
>
</v-text-field>
</template>
</v-data-table>
</div>
</v-card-text>
<v-card-actions class="pa-4">
<div>
<div v-if="hasUnallocated && remainDue > 0" class="text-dark mb-2">
尚有未分配的入帳金額請確認是否全部分配
</div>
<div v-if="hasUnallocated && remainDue === 0" class="text-info mb-2">
已無未繳項目剩餘入帳金額將成為餘額
</div>
<div v-if="!canConfirm && buttonErrorMessage" class="text-danger">
{{ buttonErrorMessage }}
</div>
</div>
<v-spacer></v-spacer>
<v-btn color="info" @click="redistributeReconcile" class="mr-2">重新分配</v-btn>
<v-btn color="orange" @click="saveDraft" class="mr-2">暫存</v-btn>
<v-btn color="primary" @click="confirmReconcile" :disabled="!canConfirm">確認沖帳</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
if (typeof window.DraftUtils === 'undefined') {
console.warn('DraftUtils 未載入,使用備用方案');
window.DraftUtils = {
hasTransferDraft: function (draft) { return false; },
getDraftField: function (draft, field) { return null; },
updateDraftField: function (draft, field, value) {
return { [field]: value };
}
};
}
module.exports = {
data() {
return {
loading: false,
headers: [
//{ text: '#', value: 'f_num' },
{ text: '信眾', value: 'follower' },
{ text: '入帳銀行/帳戶', value: 'acc_name' },
{ text: '入帳日期', value: 'check_date' },
{ text: '帳簿備註', value: 'check_memo' },
{ text: '入帳金額', value: 'check_amount' },
//{ text: '', value: 'check_status' },
{ text: '核對記錄', value: 'verify_note' },
{ text: '沖帳', value: 'actions', sortable: false }
],
items: [],
dialog: {
show: false,
selected: null,
headers: [
{ text: '法會/報名單號', value: 'activity_name' },
{ text: '報名日期', value: 'reg_time' },
{ text: '項目', value: 'actitem_name' },
{ text: '應繳金額', value: 'price', sortable: false },
{ text: '已繳金額', value: 'paid', sortable: false },
{ text: '待繳金額', value: 'due', sortable: false },
{ text: '沖帳金額', value: 'reconcile', sortable: false }
],
items: [],
loading: false,
errorMessage: ''
},
hasUnallocated: false,
draftDataChanged: false
}
},
computed: {
sumReconcile() {
return this.dialog.items.reduce((sum, item) => sum + (Number(item.reconcile) || 0), 0);
},
remainDue() {
const totalDue = this.dialog.items.reduce((sum, item) => sum + (Number(item.due) || 0), 0);
return totalDue - this.sumReconcile;
},
overPaid() {
if (!this.dialog.selected) return 0;
return this.dialog.selected.check_amount - this.sumReconcile;
},
isOverPaid() {
if (!this.dialog.selected) return false;
return this.sumReconcile > this.dialog.selected.check_amount;
},
canConfirm() {
if (!this.dialog.selected) return false;
const hasReconcile = this.dialog.items.some(item => Number(item.reconcile) > 0);
if (!hasReconcile) return false;
const validAmounts = this.dialog.items.every(item => {
const amount = Number(item.reconcile) || 0;
return amount >= 0 && amount <= Number(item.due);
});
if (!validAmounts) return false;
if (this.sumReconcile > this.dialog.selected.check_amount) return false;
//
// 0
if (this.hasUnallocated && this.remainDue > 0) return false;
return true;
},
buttonErrorMessage() {
if (!this.dialog.selected) return '';
const hasReconcile = this.dialog.items.some(item => Number(item.reconcile) > 0);
if (!hasReconcile) return '請至少輸入一筆沖帳金額';
const invalidItem = this.dialog.items.find(item => {
const amount = Number(item.reconcile) || 0;
return amount < 0 || amount > Number(item.due);
});
if (invalidItem) return '沖帳金額超出可沖帳範圍';
if (this.sumReconcile > this.dialog.selected.check_amount) {
return '已沖金額不可大於入帳金額';
}
if (this.hasUnallocated && this.remainDue > 0) {
return '尚有未繳項目,請將入帳金額完全分配';
}
return '';
}
},
mounted() {
this.loadTableData();
},
filters: {
currency(val) {
if (!val) return '0';
return Number(val).toLocaleString();
},
date(val) {
if (!val) return '';
const date = new Date(val);
return date.toLocaleDateString();
}
},
methods: {
// DraftUtils
hasTransferDraft(draft) {
return window.DraftUtils && window.DraftUtils.hasTransferDraft && window.DraftUtils.hasTransferDraft(draft);
},
// 使 DraftUtils
showError(message) {
this.dialog.errorMessage = message;
},
clearError() {
this.dialog.errorMessage = '';
},
loadTableData() {
this.loading = true;
axios.get('../../api/transfer_register/personal_reconcile_list')
.then(res => {
this.items = res.data;
})
.catch(() => {
this.items = [];
})
.finally(() => {
this.loading = false;
});
},
showReconcileDialog(item) {
this.dialog.selected = item;
this.dialog.items = [];
this.dialog.show = true;
// f_num API
if (item.f_num) {
axios.get('../../api/transfer_register/follower_orders', { params: { f_num: item.f_num } })
.then(res => {
this.dialog.items = res.data;
this.loadFromDraftOrAutoDistribute();
})
.catch(() => {
this.dialog.items = [];
this.updateSumReconcile();
});
} else {
this.dialog.items = [];
this.updateSumReconcile();
}
},
autoDistributeReconcile() {
//
let remainAmount = this.dialog.selected ? this.dialog.selected.check_amount : 0;
// reconcile 0
this.dialog.items.forEach(item => {
this.$set(item, 'reconcile', 0);
});
//
const sortedItems = [...this.dialog.items].sort((a, b) =>
new Date(a.reg_time) - new Date(b.reg_time)
);
//
sortedItems.forEach(item => {
if (remainAmount > 0) {
const canPay = Math.min(item.due, remainAmount);
const originalItem = this.dialog.items.find(i => i === item);
if (originalItem) {
this.$set(originalItem, 'reconcile', canPay);
}
remainAmount -= canPay;
}
});
this.updateSumReconcile();
},
redistributeReconcile() {
//
this.autoDistributeReconcile();
},
updateSumReconcile() {
// hasUnallocated
const maxTotal = this.dialog.selected ? (this.dialog.selected.check_amount || 0) : 0;
this.hasUnallocated = maxTotal > this.sumReconcile;
},
loadFromDraftOrAutoDistribute() {
const draft = this.dialog.selected ? this.dialog.selected.draft : null;
if (draft && draft.trim() && window.DraftUtils && window.DraftUtils.getDraftField) {
const transferDraft = window.DraftUtils.getDraftField(draft, 'transfer_draft');
if (transferDraft && Array.isArray(transferDraft)) {
this.loadFromDraft(transferDraft);
} else {
this.autoDistributeReconcile();
}
} else {
this.autoDistributeReconcile();
}
},
loadFromDraft(draftData) {
this.draftDataChanged = false;
// reconcile 0
this.dialog.items.forEach(item => {
this.$set(item, 'reconcile', 0);
});
// draft
draftData.forEach(draftItem => {
const matchedItem = this.dialog.items.find(item =>
item.num === draftItem.pro_order_detail_num
);
if (matchedItem) {
this.$set(matchedItem, 'reconcile', draftItem.reconcile || 0);
} else {
//
this.draftDataChanged = true;
}
});
this.updateSumReconcile();
//
if (this.draftDataChanged) {
setTimeout(() => {
alert('資料已改動,請注意金額一致性');
}, 100);
}
},
saveDraft() {
try {
// transfer_draft
const transferDraftData = this.dialog.items
.filter(item => Number(item.reconcile) > 0)
.map(item => ({
pro_order_detail_num: item.num,
reconcile: Number(item.reconcile)
}));
// 使 draft
const currentDraft = this.dialog.selected.draft || '';
let draftJson = '';
if (window.DraftUtils && window.DraftUtils.updateDraftField) {
const newDraftObj = window.DraftUtils.updateDraftField(currentDraft, 'transfer_draft', transferDraftData);
draftJson = JSON.stringify(newDraftObj);
} else {
// DraftUtils 使
draftJson = JSON.stringify({ transfer_draft: transferDraftData });
}
// draft
const updateData = {
id: this.dialog.selected.id,
activity_num: this.dialog.selected.activity_num,
name: this.dialog.selected.name,
phone: this.dialog.selected.phone,
pay_type: this.dialog.selected.pay_type,
account_last5: this.dialog.selected.account_last5,
amount: this.dialog.selected.amount,
pay_mode: this.dialog.selected.pay_mode,
note: this.dialog.selected.note,
proof_img: this.dialog.selected.proof_img,
status: this.dialog.selected.status,
f_num_match: this.dialog.selected.f_num_match,
f_num: this.dialog.selected.f_num,
acc_num: this.dialog.selected.acc_num,
check_date: this.dialog.selected.check_date,
check_amount: this.dialog.selected.check_amount,
check_memo: this.dialog.selected.check_memo,
check_status: this.dialog.selected.check_status,
acc_kind: this.dialog.selected.acc_kind,
member_num: this.dialog.selected.member_num,
verify_time: this.dialog.selected.verify_time,
verify_note: this.dialog.selected.verify_note,
draft: draftJson // draft
};
axios.put(`../../api/transfer_register/${this.dialog.selected.id}`, updateData)
.then(() => {
alert('暫存成功!');
//
this.dialog.selected.draft = draftJson;
})
.catch(err => {
alert('暫存失敗:' + (err.response?.data?.message || err.message));
});
} catch (e) {
alert('暫存失敗:資料格式錯誤');
}
},
confirmReconcile() {
if (!this.canConfirm) {
this.showError(this.buttonErrorMessage);
return;
}
this.dialog.loading = true;
const over_payment = this.dialog.selected.check_amount - this.sumReconcile;
const postData = {
transfer_register_id: this.dialog.selected.id,
details: this.dialog.items
.filter(item => item.reconcile > 0)
.map(item => ({
pro_order_detail_num: item.num,
amount: Number(item.reconcile),
reg_time: new Date().toISOString(),
demo: `${this.dialog.selected.check_memo || ''}`
})),
over_payment: over_payment
};
axios.post('../../api/transfer_register/reconcile', postData)
.then(res => {
//
const message = res.data && res.data.message ? res.data.message : '沖帳完成';
msgtop(message, 'success');
this.dialog.show = false;
this.loadTableData(); //
})
.catch(err => {
let errorMessage = '沖帳失敗,請聯繫系統管理員';
if (err.response && err.response.data) {
if (typeof err.response.data === 'string') {
errorMessage = err.response.data;
} else if (err.response.data.message) {
errorMessage = err.response.data.message;
if (err.response.data.exceptionMessage) {
errorMessage += `\\n${err.response.data.exceptionMessage}`;
}
}
}
alert(errorMessage);
})
.finally(() => {
this.dialog.loading = false;
});
},
validateAndUpdateReconcile(value, item, index) {
let numValue = Number(value);
if (isNaN(numValue)) {
numValue = 0;
}
numValue = Math.round(numValue);
this.$set(item, 'reconcile', numValue);
this.updateSumReconcile();
}
}
};
</script>
+193
View File
@@ -0,0 +1,193 @@
<template>
<div class="row justify-content-center">
<div class="col-12 col-md-8 col-lg-6">
<div class="card shadow">
<div class="card-header bg-primary text-white text-center">
<h4 class="mb-0">登錄匯款資料</h4>
</div>
<div class="card-body">
<form id="transferForm" @submit.prevent="submitForm" enctype="multipart/form-data" autocomplete="off">
<!-- 法會名稱 -->
<div class="mb-3">
<label for="activity_num" class="form-label">法會名稱 <span class="text-danger">*</span></label>
<select class="form-select" id="activity_num" v-model="formData.activity_num" required>
<option value="">請選擇</option>
<option v-for="activity in activities" :key="activity.num" :value="activity.num">
{{activity.subject}}
</option>
</select>
</div>
<!-- 姓名 -->
<div class="mb-3">
<label for="name" class="form-label">姓名 <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" v-model="formData.name" required maxlength="50" placeholder="請輸入姓名" @blur="onNameBlur">
</div>
<!-- 電話 -->
<div class="mb-3">
<label for="phone" class="form-label">電話 <span class="text-danger">*</span></label>
<input type="tel" class="form-control" id="phone" v-model="formData.phone" required maxlength="30" placeholder="請輸入聯絡電話">
</div>
<!-- 支付方式 -->
<div class="mb-3">
<label for="pay_type" class="form-label">支付方式 <span class="text-danger">*</span></label>
<select class="form-select" id="pay_type" v-model="formData.pay_type" required>
<option value="">請選擇</option>
<option v-for="(desc, value) in payTypes" :key="value" :value="value">
{{desc}}
</option>
</select>
</div>
<!-- 帳號後五碼 -->
<div class="mb-3">
<label for="account_last5" class="form-label">帳號後五碼(若無免填)</label>
<input type="text" class="form-control" id="account_last5" v-model="formData.account_last5" maxlength="10" placeholder="如有匯款請填寫">
</div>
<!-- 支付金額 -->
<div class="mb-3">
<label for="amount" class="form-label">支付金額 <span class="text-danger">*</span></label>
<input type="number" class="form-control" id="amount" v-model="formData.amount" required min="1" step="1" placeholder="請輸入金額">
</div>
<!-- 支付型態 -->
<div class="mb-3">
<label class="form-label">支付型態 <span class="text-danger">*</span></label>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="pay_mode" id="pay_mode1" value="個人" v-model="formData.pay_mode" checked>
<label class="form-check-label" for="pay_mode1">個人支付</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="pay_mode" id="pay_mode2" value="共同" v-model="formData.pay_mode">
<label class="form-check-label" for="pay_mode2">共同支付</label>
</div>
</div>
<!-- 備註 -->
<div class="mb-3">
<label for="note" class="form-label">備註</label>
<textarea class="form-control" id="note" v-model="formData.note" rows="2" maxlength="200" placeholder="如有分攤人名、金額、項目等請說明"></textarea>
</div>
<!-- 憑證上傳 -->
<div class="mb-3">
<label for="proof_img" class="form-label">上傳匯款憑證可拍照</label>
<input class="form-control" type="file" id="proof_img" @change="handleFileUpload" accept="image/*" capture="environment">
</div>
<!-- 送出/關閉 -->
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary btn-lg" :disabled="isSubmitting || !canSubmit">
{{ isSubmitting ? '處理中...' : '送出資料' }}
</button>
<button type="button" class="btn btn-outline-secondary" @click="closeWindow">離開</button>
</div>
</form>
<div class="mt-3 text-muted small">
<ul>
<li>如一位組頭幫多人匯款請在備註欄分拆明細人名金額項目等</li>
<li>送出後直接新增記錄不顯示查核</li>
<li>如無姓名/電話資料系統會提示</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
module.exports = {
// 1. el: '#register'
// 2. data function
data() {
return {
activities: [],
formData: {
activity_num: '',
name: '',
phone: '',
pay_type: '',
account_last5: '',
amount: '',
pay_mode: '個人',
note: '',
proof_img: null
},
payTypes: {
'1': '現金',
'2': '匯款',
'3': '支票'
},
isSubmitting: false,
canSubmit: true
};
},
created() {
this.loadActivities();
},
methods: {
loadActivities() {
const today = new Date().toISOString().split('T')[0];
axios.get(`../../api/activity?endDate=${today}`)
.then(response => {
this.activities = response.data;
})
.catch(error => {
console.error('載入活動列表失敗:', error);
alert('載入活動列表失敗,請重新整理頁面');
});
},
handleFileUpload(event) {
this.formData.proof_img = event.target.files[0];
},
submitForm() {
if (this.isSubmitting || !this.canSubmit) return;
this.isSubmitting = true;
const formData = new FormData();
Object.keys(this.formData).forEach(key => {
if (this.formData[key] !== null) {
formData.append(key, this.formData[key]);
}
});
axios.post('../../api/transfer_register', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
alert('資料已成功送出!');
this.formData = {
activity_num: '',
name: '',
phone: '',
pay_type: '',
account_last5: '',
amount: '',
pay_mode: '個人',
note: '',
proof_img: null
};
this.canSubmit = false;
})
.catch(error => {
console.error('送出失敗:', error);
// response.data.message
const errorMsg = error.response && error.response.data && error.response.data.message
? error.response.data.message
: '請檢查網路連線或系統狀態';
alert('送出失敗 :' + errorMsg);
})
.finally(() => {
this.isSubmitting = false;
});
},
closeWindow() {
// 3. Dialog window.close()
// (index.aspx)
this.$emit('close-dialog');
},
onNameBlur() {
this.canSubmit = !!this.formData.name.trim();
}
}
};
</script>
+382
View File
@@ -0,0 +1,382 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h5 class="mb-0">帳務查詢</h5>
<button class="btn btn-default ms-auto"><i class="mdi mdi-close"></i></button>
</v-card-title>
<v-card-text>
<v-row class="mb-0 mt-4">
<v-col cols="12" md="2">
<v-text-field v-model="query.start_date" label="起始日" type="date" dense outlined></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="query.end_date" label="結束日" type="date" dense outlined></v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field v-model="query.activity_name" label="法會" dense outlined readonly>
<template v-slot:append>
<v-btn icon @click="showActivityDialog"><v-icon>mdi-magnify</v-icon></v-btn>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field v-model="query.follower_name" label="信眾" dense outlined readonly>
<template v-slot:append>
<v-btn icon @click="showFollowerDialog"><v-icon>mdi-magnify</v-icon></v-btn>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="2" class="d-flex justify-end">
<v-btn color="primary" class="mr-2" @click="search">查詢</v-btn>
<v-btn color="grey" @click="reset">重設</v-btn>
</v-col>
</v-row>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1 verify-query-table"
item-key="transfer_id"
:footer-props="{ 'items-per-page-options': [10, 20, 50] }"
:expanded.sync="expanded"
show-expand>
<template v-slot:item.follower="{ item }">
<div>
<div class="font-weight-bold">{{ item.follower }}</div>
<div class="caption text--secondary">{{ item.transfer_name }}</div>
<div class="caption text--secondary">{{ item.transfer_phone }}</div>
</div>
</template>
<template v-slot:item.activity_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.activity_name }}</div>
<div class="caption text--secondary">{{ item.acc_name }}</div>
</div>
</template>
<template v-slot:item.transfer_info="{ item }">
<div>
<div class="font-weight-bold text-primary">應收總額{{ item.price_totals | currency }}</div>
<div class="font-weight-bold text-primary">入帳金額{{ item.transfer_check_amount | currency }}</div>
<div class="text-success">沖帳日期{{ item.transfer_check_date | date }}</div>
<div v-if="item.transfer_remain_amount > 0" class="text-warning">
剩餘金額{{ item.transfer_remain_amount | currency }}
</div>
</div>
</template>
<template v-slot:item.status_info="{ item }">
<div>
<v-chip small :color="getStatusColor(item.transfer_check_status)" text-color="white">
{{ getStatusText(item.transfer_check_status) }}
</v-chip>
<div class="caption text--secondary mt-1">{{ item.transfer_check_memo }}</div>
</div>
</template>
<template v-slot:item.actions="{ item }">
<v-btn color="info" outlined small @click="showDetailDialog(item)">
<v-icon small class="mr-1">mdi-information-outline</v-icon> 詳細
</v-btn>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<div class="pa-4">
<h6 class="mb-3 text-primary">
<v-icon color="primary" small class="mr-1">mdi-receipt</v-icon>
沖帳明細
</h6>
<v-data-table :headers="detailHeaders"
:items="item.pro_order_records"
class="elevation-1"
hide-default-footer
:disable-pagination="true"
dense>
<template v-slot:item.order_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.pro_order_detail?.pro_order?.order_no }}</div>
<div class="caption text--secondary">{{ item.pro_order_detail?.actitem_name }}</div>
</div>
</template>
<template v-slot:item.payment_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.payment_name }}</div>
<div class="caption text--secondary">{{ item.bank_code }}</div>
</div>
</template>
<template v-slot:item.amount_info="{ item }">
<div>
<div class="font-weight-bold text-success">{{ item.price | currency }}</div>
<div class="caption text--secondary">{{ item.pay_date | date }}</div>
</div>
</template>
<template v-slot:item.memo="{ item }">
<div style="white-space: pre-line;">{{ item.reconcile_memo }}</div>
</template>
</v-data-table>
</div>
</td>
</template>
</v-data-table>
</v-card-text>
</v-card>
<!-- 法會選取 Dialog -->
<v-dialog v-model="activityDialog.show" max-width="700px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-table</v-icon>
選擇法會
<v-spacer></v-spacer>
<v-btn icon @click="activityDialog.show = false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text class="mt-4">
<v-text-field v-model="activityDialog.search" label="搜尋法會" dense outlined @keyup.enter="searchActivity"></v-text-field>
<v-data-table :headers="activityDialog.headers"
:items="activityDialog.items"
:loading="activityDialog.loading"
item-key="num"
class="elevation-1 mt-2"
@click:row="selectActivity"
hide-default-footer></v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<!-- 信眾選取 Dialog -->
<v-dialog v-model="followerDialog.show" max-width="700px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-account</v-icon>
選擇信眾
<v-spacer></v-spacer>
<v-btn icon @click="followerDialog.show = false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text class="mt-4">
<v-text-field v-model="followerDialog.search" label="搜尋信眾" dense outlined @keyup.enter="searchFollower"></v-text-field>
<v-data-table :headers="followerDialog.headers"
:items="followerDialog.items"
:loading="followerDialog.loading"
item-key="num"
class="elevation-1 mt-2"
@click:row="selectFollower"
hide-default-footer></v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<!-- 詳細資訊對話框 -->
<v-dialog v-model="dialog.show" max-width="900px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
詳細資訊
<v-spacer></v-spacer>
<v-btn icon @click="dialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<div v-if="dialog.selected">
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-primary">匯款人資訊</h6>
<div class="mb-2"><span class="font-weight-bold">姓名</span>{{ dialog.selected.transfer_name }}</div>
<div class="mb-2"><span class="font-weight-bold">電話</span>{{ dialog.selected.transfer_phone }}</div>
<div class="mb-2"><span class="font-weight-bold">信眾</span>{{ dialog.selected.follower }}</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-success">入帳資訊</h6>
<div class="mb-2"><span class="font-weight-bold">入帳帳戶</span>{{ dialog.selected.acc_name }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳日期</span>{{ dialog.selected.transfer_check_date | date }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳金額</span>{{ dialog.selected.transfer_check_amount | currency }}</div>
<div class="mb-2"><span class="font-weight-bold">應收總額</span>{{ dialog.selected.price_totals | currency }}</div>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-purple">活動資訊</h6>
<div class="mb-2"><span class="font-weight-bold">活動名稱</span>{{ dialog.selected.activity_name || '-' }}</div>
<div class="mb-2"><span class="font-weight-bold">支付方式</span>{{ payTypeText[dialog.selected.transfer_pay_type] || dialog.selected.transfer_pay_type }}</div>
<div class="mb-2"><span class="font-weight-bold">帳號後5碼</span>{{ dialog.selected.transfer_account_last5 || '-' }}</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-info">核對記錄</h6>
<div class="mb-2"><span class="font-weight-bold">核對記錄</span>{{ dialog.selected.transfer_verify_note || '-' }}</div>
<div class="mb-2"><span class="font-weight-bold">帳簿備註</span>{{ dialog.selected.transfer_check_memo || '-' }}</div>
<div v-if="dialog.selected.transfer_proof_img" class="mb-2">
<span class="font-weight-bold">證明圖片</span>
<a :href="'../../upload/transfer_proof/' + dialog.selected.transfer_proof_img" target="_blank">查看相片</a>
</div>
</v-col>
</v-row>
</div>
</v-card-text>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
module.exports = {
data() {
return {
loading: false,
expanded: [],
query: {
start_date: '',
end_date: '',
activity_num: '',
activity_name: '',
follower_num: '',
follower_name: ''
},
headers: [
{ text: '信眾/匯款人', value: 'follower' },
{ text: '法會/入帳帳戶', value: 'activity_info' },
{ text: '入帳資訊', value: 'transfer_info' },
{ text: '狀態/備註', value: 'status_info' },
{ text: '操作', value: 'actions', sortable: false }
],
detailHeaders: [
{ text: '訂單/項目', value: 'order_info' },
{ text: '付款機構', value: 'payment_info' },
{ text: '沖帳金額/日期', value: 'amount_info' },
{ text: '備註', value: 'memo' }
],
items: [],
dialog: {
show: false,
selected: null
},
activityDialog: {
show: false,
search: '',
loading: false,
items: [],
headers: [
{ text: '編號', value: 'num' },
{ text: '名稱', value: 'subject' }
]
},
followerDialog: {
show: false,
search: '',
loading: false,
items: [],
headers: [
{ text: '編號', value: 'num' },
{ text: '姓名', value: 'u_name' }
]
},
payTypeText: {
1: '現金',
2: '匯款',
3: '支票'
}
}
},
filters: {
currency(val) {
if (!val) return '0';
return Number(val).toLocaleString();
},
date(val) {
if (!val) return '';
const date = new Date(val);
return date.toLocaleDateString();
}
},
methods: {
search() {
this.loading = true;
// API
axios.get('../../api/transfer_register/verify_order_record_query', { params: this.query })
.then(res => {
this.items = res.data;
this.expanded = []; //
})
.catch(() => {
this.items = [];
this.expanded = [];
})
.finally(() => {
this.loading = false;
});
},
reset() {
this.query.start_date = '';
this.query.end_date = '';
this.query.activity_num = '';
this.query.activity_name = '';
this.query.follower_num = '';
this.query.follower_name = '';
this.items = [];
this.expanded = [];
},
showDetailDialog(item) {
this.dialog.selected = item;
this.dialog.show = true;
},
showActivityDialog() {
this.activityDialog.show = true;
this.searchActivity();
},
searchActivity() {
this.activityDialog.loading = true;
axios.get('../../api/activity', { params: { keyword: this.activityDialog.search } })
.then(res => { this.activityDialog.items = res.data; })
.catch(() => { this.activityDialog.items = []; })
.finally(() => { this.activityDialog.loading = false; });
},
selectActivity(row) {
this.query.activity_num = row.num;
this.query.activity_name = row.subject;
this.activityDialog.show = false;
},
showFollowerDialog() {
this.followerDialog.show = true;
this.searchFollower();
},
searchFollower() {
this.followerDialog.loading = true;
axios.post('../..../..../../api/follower/GetList', { u_name: this.followerDialog.search }, { params: { page: 1, pageSize: 10 } })
.then(res => { this.followerDialog.items = res.data.list; })
.catch(() => { this.followerDialog.items = []; })
.finally(() => { this.followerDialog.loading = false; });
},
selectFollower(row) {
this.query.follower_num = row.num;
this.query.follower_name = row.u_name;
this.followerDialog.show = false;
},
getStatusColor(status) {
switch (status) {
case '90': return 'warning';
case '99': return 'success';
default: return 'grey';
}
},
getStatusText(status) {
switch (status) {
case '90': return '沖帳有剩餘';
case '99': return '沖帳完成';
default: return '未知狀態';
}
}
}
};
</script>
<style>
.v-data-table.verify-query-table .v-data-table__wrapper tbody tr {
min-height: 90px !important;
height: 90px !important;
}
.v-data-table.verify-query-table .v-data-table__wrapper tbody td {
min-height: 90px !important;
height: 90px !important;
vertical-align: middle !important;
}
</style>
+116
View File
@@ -0,0 +1,116 @@
<template>
<div class="container-fluid">
<v-card>
<v-card-title class="bg-success white--text text-center">
<h5 class="mb-0">統計報表</h5>
<v-spacer></v-spacer>
<!--<v-btn color="primary" @click="submitData">確認送出</v-btn>-->
<v-btn color="primary" class="ml-2" @click="closeWindow">關閉</v-btn>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="2" md="2">
<v-select :items="category"
label="統計分類"
item-text="name"
item-value="id" v-model="category_id">
</v-select>
</v-col>
<v-col cols="2" md="2">
<v-text-field label="法會時間(起)" v-model="query.s_date"></v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field label="法會時間(迄)" v-model="query.e_date"></v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-btn @click.prevent="getRpt()" :disabled="isdisabled">
匯出
</v-btn>
<img src="/admin/images/loading.gif" v-show="isloading"/>
</v-col>
</v-row>
</v-card-text>
</v-card>
<v-dialog v-model="message_dialog.show" class="dialog_width">
<v-card>
<v-card-title>
{{ message_dialog.message }}
</v-card-title>
<v-card-text>
<v-btn @click.prevent="message_dialog.show=false" v-if="!message_dialog.show_confirm">關閉</v-btn>
</v-card-text>
</v-card>
</v-dialog>
</div>
</template>
<script>
module.exports = {
data() {
return {
message_dialog: { show: false, show_confirm: false, message: "" },
category: [
{ id: "01", name: "依法會+功德項目", selected: "" },
{ id: "02", name: "依信眾", selected: "" },
{ id: "03", name: "依法會+信眾", selected: "" },
],
category_id: "",
isdisabled: false,
isloading: false,
query: {s_date:"",e_date:""}
}
},
methods: {
closeWindow() {
this.$emit('close-dialog');
},
getRpt() {
if (this.category_id == "") {
this.message_dialog.message = "請選擇統計類型";
this.message_dialog.show = true;
return false;
}
const params = { statistic_mode: this.category_id }
const search = {
num: 0, kind: 0, kindName: "", order_no: "", u_name: "", pay_type: "", subject: "", amount: 0, price: 0, pay_mode: "", status: "",
acc_num: 0, f_num: 0, acc_kind: 0, check_amount: 0, remain_amount: 0, check_status: "", actItem_num: 0, draft: "", d_nnum: 0
}
this.isdisabled = true;
this.isloading = true;
axios
.post(HTTP_HOST + 'api/statistics/GetStatistic', search, { params: params })
.then(response => {
if (response.data.result == "N") {
this.message_dialog.message = response.data.message;
this.message_dialog.show = true;
} else if (response.data.result == "Y") {
const link = document.createElement('a');
link.href = HTTP_HOST + "/" + response.data.filename;
link.target = "_blank";
// 5. Specify the filename for the download
//link.setAttribute('download', 'my-downloaded-file.pdf');
// 6. Append link to the body, click it, and remove it immediately
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 7. Free up memory by revoking the object URL
//window.URL.revokeObjectURL(blobUrl);
//window.open(HTTP_HOST +"/" +response.data.filename, "_blank");
}
this.isdisabled = false;
this.isloading = false;
})
.catch(error => console.log(error))
}
}
}
</script>
<style>
.v-dialog {
width: 80% !important;
}
</style>
+461
View File
@@ -0,0 +1,461 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-success white--text text-center">
<h5 class="mb-0">出納核對匯款人</h5>
<v-spacer></v-spacer>
<v-btn color="primary" @click="submitData">確認送出</v-btn>
<v-btn color="primary" class="ml-2" @click="closeWindow">關閉</v-btn>
</v-card-title>
<v-card-text>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1 mt-3"
item-key="id"
:expanded.sync="expanded"
show-expand>
<template v-slot:item.info="{ item }">
<div>
<div><span class="text-muted">姓名</span>{{ item.name }}</div>
<div><span class="text-muted">電話</span>{{ item.phone }}</div>
<div><span class="text-muted">法會</span>{{ getActivityName(item.activity_num) }}</div>
</div>
</template>
<template v-slot:item.f_num="{ item }">
<span v-if="item.f_num && item.follower">
<a :href="'/admin/follower/reg.aspx?num=' + item.follower.num"
class="text-success"
target="_blank">
{{ item.follower.u_name }}F{{ item.f_num }}
</a>
<div class="small text-muted">
電話{{ item.follower.phone }}<br>
手機{{ item.follower.cellphone }}
</div>
</span>
<span v-else class="text-danger">未自動比對</span>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<div class="pa-4">
<h6 class="mb-3 text-primary">
<v-icon color="primary" small class="mr-1">mdi-receipt</v-icon>
沖帳明細
</h6>
<v-container>
<v-row class="font-weight-bold grey--text text--darken-2">
<v-col>*入帳銀行/帳戶 | 支付資訊/帳號後5碼</v-col>
<v-col>*入帳日期</v-col>
<v-col>*入帳金額</v-col>
<v-col>*收支項目</v-col>
<!--<v-col>備註/狀態 | 核對記錄</v-col>-->
</v-row>
<v-row>
<v-col>
<div class="mb-2">
<span class="badge bg-primary me-1" title="支付方式">{{ payTypeText[item.pay_type] || item.pay_type }}</span>
<span class="badge bg-secondary" title="型態:個人/共同">{{ item.pay_mode }}</span>
<span class="font-weight-bold text-primary" title="帳號後5碼">{{ item.account_last5 }}</span>
</div>
<div>
<v-select :items="bankOptions"
v-model="item.acc_num"
dense
outlined
hide-details
style="max-width: 200px"></v-select>
</div>
</v-col>
<v-col>
<div class="mb-2">
<span class="text-muted small">登記日期</span>
<span class="font-weight-bold error--text"
style="cursor:pointer"
@click="$set(item, 'check_date', item.create_time ? item.create_time.split('T')[0] : '')"
title="點擊帶入日期">
{{ item.create_time | date }}
</span>
</div>
<div class="mb-2">
<v-text-field v-model="item.check_date"
type="date"
dense
outlined
hide-details
style="max-width: 140px"></v-text-field>
</div>
</v-col>
<v-col>
<div class="mb-2">
<span class="text-muted small">金額</span>
<span class="font-weight-bold error--text"
style="cursor:pointer"
@click="$set(item, 'check_amount', item.amount)"
title="點擊帶入金額">
{{ item.amount | currency }}
</span>
</div>
<div>
<v-text-field v-model="item.check_amount"
type="number"
dense
outlined
hide-details
style="max-width: 100px"></v-text-field>
</div>
</v-col>
<v-col>
<div class="mb-2">
<span class="badge bg-primary me-1" title="支付方式">收支項目</span>
</div>
<div>
<v-select :items="accountingKinds"
v-model="item.kind"
dense
outlined
hide-details
style="max-width: 200px"></v-select>
</div>
</v-col>
</v-row>
</v-container>
</div>
</td>
</template>
<template v-slot:item.actions="{ item }">
<v-btn small outlined color="primary" @click="openFollowerDialog(item)">選擇信眾</v-btn>
</template>
<template v-slot:item.check_memo="{ item }">
<div class="d-flex align-center my-2" style="min-width: 300px">
<v-text-field v-model="item.check_memo"
dense
outlined
hide-details
class="mr-2"
style="width: 180px"
placeholder="帳簿備註"></v-text-field>
<v-select :items="checkStatusOptions"
v-model="item.check_status"
item-text="text"
item-value="value"
dense
outlined
hide-details
style="width: 110px"
:menu-props="{ contentClass: 'mini-dropdown', maxHeight: 200 }"></v-select>
</div>
</template>
</v-data-table>
</v-card-text>
</v-card>
<v-dialog v-model="follower_dialog.show" max-width="800px">
<v-card>
<v-card-title class="grey lighten-2">
選擇信眾
<v-spacer></v-spacer>
<v-btn icon @click="follower_dialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="pt-4">
<v-row>
<v-col cols="12">
<v-text-field v-model="follower_dialog.search"
label="搜尋信眾 (按 Enter 搜尋)"
prepend-icon="mdi-magnify"
@keyup.enter="searchFollowers"
clearable></v-text-field>
</v-col>
</v-row>
<v-data-table :headers="follower_dialog.headers"
:items="follower_dialog.items"
:loading="follower_dialog.loading"
item-key="num"
class="elevation-1"
@click:row="selectFollower">
<template v-slot:item.actions="{ item }">
<v-btn small color="primary" @click.stop="selectFollower(item)">
選擇
</v-btn>
</template>
</v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<v-snackbar v-model="snackbar.show" :timeout="3000" top color="error">
{{ snackbar.text }}
<template v-slot:action="{ attrs }">
<v-btn dark text v-bind="attrs" @click="snackbar.show = false">關閉</v-btn>
</template>
</v-snackbar>
</v-container>
</template>
<script>
module.exports = {
data() {
return {
// 3 snackbar
snackbar: {
show: false,
text: ''
},
headers: [
{ text: '匯款人資訊', value: 'info' },
{ text: '對應信眾', value: 'f_num' },
{ text: '選擇信眾', value: 'actions', sortable: false },
{ text: '備註/狀態 | 核對記錄', value: 'check_memo' }
//{ text: ' | ', value: 'status' },
],
// detailHeaders expanded
detailHeaders: [
{ text: '匯款人資訊', value: 'info' },
{ text: '匯款備註/相片', value: 'note' },
{ text: '*入帳銀行/帳戶 | 支付資訊/帳號後5碼', value: 'acc_num' },
{ text: '*入帳日期', value: 'check_date' },
{ text: '*入帳金額', value: 'check_amount' },
{ text: '*收支項目', value: 'kind' },
{ text: '備註/狀態 | 核對記錄', value: 'check_memo' }
],
items: [],
bankOptions: [],
accountingKinds: [],
checkStatusOptions: [
{ text: '', value: '' },
{ text: '未核對', value: '1' },
{ text: '核對', value: '2' },
{ text: '金額不符', value: '3' },
{ text: '其他問題', value: '4' },
{ text: '作廢', value: '5' }
],
payTypeText: {
1: '現金',
2: '匯款',
3: '支票'
},
activities: [],
expanded: [],
loading: false,
statusOptions: [
{ text: '', value: '' },
{ text: '待確認', value: '1' },
{ text: '確認', value: '2' },
{ text: '作廢', value: '3' }
],
follower_dialog: {
show: false,
loading: false,
search: '',
headers: [
{ text: '編號', value: 'num' },
{ text: '姓名', value: 'u_name' },
{ text: '地址', value: 'address' },
// 2
{ text: '操作', value: 'actions', sortable: false }
],
items: [],
selected: null,
current_item: null
}
};
},
filters: {
currency(val) {
if (!val) return '';
return Number(val).toLocaleString();
},
date(val) {
if (!val) return '';
return val.split('T')[0];
}
},
methods: {
getActivityName(num) {
const act = this.activities.find(a => a.num === num);
return act ? act.subject : '';
},
openFollowerDialog(item) {
this.follower_dialog.current_item = item;
this.follower_dialog.show = true;
//
// this.searchFollowers();
},
async searchFollowers() {
if (!this.follower_dialog.search) return;
this.follower_dialog.loading = true;
try {
const response = await axios.post(HTTP_HOST + 'api/follower/GetList', {
f_number: this.follower_dialog.search,
u_name: this.follower_dialog.search
}, {
params: {
page: 1,
pageSize: 10
}
});
this.follower_dialog.items = response.data.list || [];
} catch (error) {
console.error('Error fetching followers:', error);
this.snackbar.text = "查詢信眾失敗,請確認網路或 API 狀態";
this.snackbar.show = true;
} finally {
this.follower_dialog.loading = false;
}
},
async selectFollower(follower) {
if (this.follower_dialog.current_item) {
try {
this.follower_dialog.show = false;
const selectedFollower = this.follower_dialog.items.find(item => item.num === follower.num);
if (selectedFollower) {
this.follower_dialog.current_item.f_num = follower.num;
this.follower_dialog.current_item.follower = {
num: selectedFollower.num,
u_name: selectedFollower.u_name,
address: selectedFollower.address || '',
phone: selectedFollower.phoneDes || '',
cellphone: selectedFollower.cellphoneDes || ''
};
} else {
throw new Error('找不到信眾詳細資料');
}
} catch (error) {
console.error('取得信眾詳細資料失敗:', error);
this.snackbar.text = '取得信眾詳細資料失敗,請重試';
this.snackbar.show = true;
this.follower_dialog.current_item.f_num = follower.num;
this.follower_dialog.current_item.follower = {
num: follower.num,
u_name: follower.u_name,
address: follower.address || '',
phone: '',
cellphone: ''
};
}
}
},
async loadData() {
this.loading = true;
try {
const actRes = await axios.get('../../api/activity');
this.activities = actRes.data;
const res = await axios.get('../../api/transfer_register/pending');
this.items = res.data.map(item => ({
...item,
status: item.status ? String(item.status) : ''
}));
const bankRes = await axios.post('../../api/accounting/GetAccountKindList', {}, { params: { page: 1, pageSize: 1000 } });
this.bankOptions = bankRes.data.list.map(x => ({
text: x.kind + (x.bank_name ? ' - ' + x.bank_name : '') + (x.bank_id ? ' (' + x.bank_id + ')' : ''),
value: x.num
}));
const kindRes = await axios.post('../../api/accounting/GetTitleKindList', {}, { params: { page: 1, pageSize: 1000 } });
this.accountingKinds = kindRes.data.list.map(x => ({
text: x.kind,
value: x.num
}));
} catch (e) {
console.error('載入資料失敗:', e);
this.snackbar.text = '載入資料失敗';
this.snackbar.show = true;
} finally {
this.loading = false;
}
},
async saveData() {
const updateList = this.items.map(item => ({
id: item.id,
f_num: item.f_num,
status: item.status ? String(item.status) : '',
verify_note: item.verify_note
// check_date, check_amount API
}));
try {
const res = await axios.post('../../api/transfer_register/batch_update', updateList);
if (res.data && res.data.success) {
alert('儲存成功!');
await this.loadData();
} else {
alert('儲存失敗,請重試!!');
}
} catch (e) {
alert('儲存失敗,請重試:' + e.message);
}
},
async submitData() {
// - /
const missingAccNum = this.items.filter(item => !item.acc_num);
if (missingAccNum.length > 0) {
alert('請選擇入帳銀行/帳戶!有 ' + missingAccNum.length + ' 筆資料未選擇。');
return;
}
// -
const missingCheckDate = this.items.filter(item => !item.check_date);
if (missingCheckDate.length > 0) {
alert('請填寫入帳日期!有 ' + missingCheckDate.length + ' 筆資料未填寫。');
return;
}
// -
const missingCheckAmount = this.items.filter(item => !item.check_amount || item.check_amount <= 0);
if (missingCheckAmount.length > 0) {
alert('請填寫入帳金額!有 ' + missingCheckAmount.length + ' 筆資料未填寫或金額無效。');
return;
}
//
const updateList = this.items.map(item => ({
id: item.id,
f_num: item.f_num,
status: '2',
check_status: item.check_status ? String(item.check_status) : '',
verify_note: item.verify_note,
acc_num: item.acc_num,
check_date: item.check_date,
check_amount: item.check_amount,
check_memo: item.check_memo,
kind: item.kind,
verify_note: item.verify_note
}));
try {
const res = await axios.post('../../api/transfer_register/batch_update', updateList);
if (res.data && res.data.success) {
alert('送出成功!');
//
await this.loadData();
} else {
alert('送出失敗,請重試 :' + res.data.message);
}
} catch (e) {
console.error('送出失敗:', e);
alert('送出失敗,請再試一次!');
}
},
closeWindow() {
this.$emit('close-dialog');
},
},
created() {
this.loadData();
}
}
</script>
@@ -0,0 +1,382 @@
<template>
<v-container>
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h5 class="mb-0">沖帳查詢</h5>
<button class="btn btn-default ms-auto"><i class="mdi mdi-close"></i></button>
</v-card-title>
<v-card-text>
<v-row class="mb-0 mt-4">
<v-col cols="12" md="2">
<v-text-field v-model="query.start_date" label="起始日" type="date" dense outlined></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="query.end_date" label="結束日" type="date" dense outlined></v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field v-model="query.activity_name" label="法會" dense outlined readonly>
<template v-slot:append>
<v-btn icon @click="showActivityDialog"><v-icon>mdi-magnify</v-icon></v-btn>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="3">
<v-text-field v-model="query.follower_name" label="信眾" dense outlined readonly>
<template v-slot:append>
<v-btn icon @click="showFollowerDialog"><v-icon>mdi-magnify</v-icon></v-btn>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="2" class="d-flex justify-end">
<v-btn color="primary" class="mr-2" @click="search">查詢</v-btn>
<v-btn color="grey" @click="reset">重設</v-btn>
</v-col>
</v-row>
<v-data-table :headers="headers"
:items="items"
:loading="loading"
loading-text="載入中..."
class="elevation-1 verify-query-table"
item-key="transfer_id"
:footer-props="{ 'items-per-page-options': [10, 20, 50] }"
:expanded.sync="expanded"
show-expand>
<template v-slot:item.follower="{ item }">
<div>
<div class="font-weight-bold">{{ item.follower }}</div>
<div class="caption text--secondary">{{ item.transfer_name }}</div>
<div class="caption text--secondary">{{ item.transfer_phone }}</div>
</div>
</template>
<template v-slot:item.activity_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.activity_name }}</div>
<div class="caption text--secondary">{{ item.acc_name }}</div>
</div>
</template>
<template v-slot:item.transfer_info="{ item }">
<div>
<div class="font-weight-bold text-primary">應收總額{{ item.price_totals | currency }}</div>
<div class="font-weight-bold text-primary">入帳金額{{ item.transfer_check_amount | currency }}</div>
<div class="text-success">沖帳日期{{ item.transfer_check_date | date }}</div>
<div v-if="item.transfer_remain_amount > 0" class="text-warning">
剩餘金額{{ item.transfer_remain_amount | currency }}
</div>
</div>
</template>
<template v-slot:item.status_info="{ item }">
<div>
<v-chip small :color="getStatusColor(item.transfer_check_status)" text-color="white">
{{ getStatusText(item.transfer_check_status) }}
</v-chip>
<div class="caption text--secondary mt-1">{{ item.transfer_check_memo }}</div>
</div>
</template>
<template v-slot:item.actions="{ item }">
<v-btn color="info" outlined small @click="showDetailDialog(item)">
<v-icon small class="mr-1">mdi-information-outline</v-icon> 詳細
</v-btn>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<div class="pa-4">
<h6 class="mb-3 text-primary">
<v-icon color="primary" small class="mr-1">mdi-receipt</v-icon>
沖帳明細
</h6>
<v-data-table :headers="detailHeaders"
:items="item.pro_order_records"
class="elevation-1"
hide-default-footer
:disable-pagination="true"
dense>
<template v-slot:item.order_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.pro_order_detail?.pro_order?.order_no }}</div>
<div class="caption text--secondary">{{ item.pro_order_detail?.actitem_name }}</div>
</div>
</template>
<template v-slot:item.payment_info="{ item }">
<div>
<div class="font-weight-bold">{{ item.payment_name }}</div>
<div class="caption text--secondary">{{ item.bank_code }}</div>
</div>
</template>
<template v-slot:item.amount_info="{ item }">
<div>
<div class="font-weight-bold text-success">{{ item.price | currency }}</div>
<div class="caption text--secondary">{{ item.pay_date | date }}</div>
</div>
</template>
<template v-slot:item.memo="{ item }">
<div style="white-space: pre-line;">{{ item.reconcile_memo }}</div>
</template>
</v-data-table>
</div>
</td>
</template>
</v-data-table>
</v-card-text>
</v-card>
<!-- 法會選取 Dialog -->
<v-dialog v-model="activityDialog.show" max-width="700px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-table</v-icon>
選擇法會
<v-spacer></v-spacer>
<v-btn icon @click="activityDialog.show = false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text class="mt-4">
<v-text-field v-model="activityDialog.search" label="搜尋法會" dense outlined @keyup.enter="searchActivity"></v-text-field>
<v-data-table :headers="activityDialog.headers"
:items="activityDialog.items"
:loading="activityDialog.loading"
item-key="num"
class="elevation-1 mt-2"
@click:row="selectActivity"
hide-default-footer></v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<!-- 信眾選取 Dialog -->
<v-dialog v-model="followerDialog.show" max-width="700px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="primary" class="mr-2">mdi-account</v-icon>
選擇信眾
<v-spacer></v-spacer>
<v-btn icon @click="followerDialog.show = false"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text class="mt-4">
<v-text-field v-model="followerDialog.search" label="搜尋信眾" dense outlined @keyup.enter="searchFollower"></v-text-field>
<v-data-table :headers="followerDialog.headers"
:items="followerDialog.items"
:loading="followerDialog.loading"
item-key="num"
class="elevation-1 mt-2"
@click:row="selectFollower"
hide-default-footer></v-data-table>
</v-card-text>
</v-card>
</v-dialog>
<!-- 詳細資訊對話框 -->
<v-dialog v-model="dialog.show" max-width="900px">
<v-card>
<v-card-title class="grey lighten-2">
<v-icon color="info" class="mr-2">mdi-information-outline</v-icon>
詳細資訊
<v-spacer></v-spacer>
<v-btn icon @click="dialog.show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text>
<div v-if="dialog.selected">
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-primary">匯款人資訊</h6>
<div class="mb-2"><span class="font-weight-bold">姓名</span>{{ dialog.selected.transfer_name }}</div>
<div class="mb-2"><span class="font-weight-bold">電話</span>{{ dialog.selected.transfer_phone }}</div>
<div class="mb-2"><span class="font-weight-bold">信眾</span>{{ dialog.selected.follower }}</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-success">入帳資訊</h6>
<div class="mb-2"><span class="font-weight-bold">入帳帳戶</span>{{ dialog.selected.acc_name }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳日期</span>{{ dialog.selected.transfer_check_date | date }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳金額</span>{{ dialog.selected.transfer_check_amount | currency }}</div>
<div class="mb-2"><span class="font-weight-bold">應收總額</span>{{ dialog.selected.price_totals | currency }}</div>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
<v-row>
<v-col cols="12" md="6">
<h6 class="mb-3 text-purple">活動資訊</h6>
<div class="mb-2"><span class="font-weight-bold">活動名稱</span>{{ dialog.selected.activity_name || '-' }}</div>
<div class="mb-2"><span class="font-weight-bold">支付方式</span>{{ payTypeText[dialog.selected.transfer_pay_type] || dialog.selected.transfer_pay_type }}</div>
<div class="mb-2"><span class="font-weight-bold">帳號後5碼</span>{{ dialog.selected.transfer_account_last5 || '-' }}</div>
</v-col>
<v-col cols="12" md="6">
<h6 class="mb-3 text-info">核對記錄</h6>
<div class="mb-2"><span class="font-weight-bold">核對記錄</span>{{ dialog.selected.transfer_verify_note || '-' }}</div>
<div class="mb-2"><span class="font-weight-bold">帳簿備註</span>{{ dialog.selected.transfer_check_memo || '-' }}</div>
<div v-if="dialog.selected.transfer_proof_img" class="mb-2">
<span class="font-weight-bold">證明圖片</span>
<a :href="'../../upload/transfer_proof/' + dialog.selected.transfer_proof_img" target="_blank">查看相片</a>
</div>
</v-col>
</v-row>
</div>
</v-card-text>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
module.exports = {
data() {
return {
loading: false,
expanded: [],
query: {
start_date: '',
end_date: '',
activity_num: '',
activity_name: '',
follower_num: '',
follower_name: ''
},
headers: [
{ text: '信眾/匯款人', value: 'follower' },
{ text: '法會/入帳帳戶', value: 'activity_info' },
{ text: '入帳資訊', value: 'transfer_info' },
{ text: '狀態/備註', value: 'status_info' },
{ text: '操作', value: 'actions', sortable: false }
],
detailHeaders: [
{ text: '訂單/項目', value: 'order_info' },
{ text: '付款機構', value: 'payment_info' },
{ text: '沖帳金額/日期', value: 'amount_info' },
{ text: '備註', value: 'memo' }
],
items: [],
dialog: {
show: false,
selected: null
},
activityDialog: {
show: false,
search: '',
loading: false,
items: [],
headers: [
{ text: '編號', value: 'num' },
{ text: '名稱', value: 'subject' }
]
},
followerDialog: {
show: false,
search: '',
loading: false,
items: [],
headers: [
{ text: '編號', value: 'num' },
{ text: '姓名', value: 'u_name' }
]
},
payTypeText: {
1: '現金',
2: '匯款',
3: '支票'
}
}
},
filters: {
currency(val) {
if (!val) return '0';
return Number(val).toLocaleString();
},
date(val) {
if (!val) return '';
const date = new Date(val);
return date.toLocaleDateString();
}
},
methods: {
search() {
this.loading = true;
// API
axios.get('../../api/transfer_register/verify_order_record_query', { params: this.query })
.then(res => {
this.items = res.data;
this.expanded = []; //
})
.catch(() => {
this.items = [];
this.expanded = [];
})
.finally(() => {
this.loading = false;
});
},
reset() {
this.query.start_date = '';
this.query.end_date = '';
this.query.activity_num = '';
this.query.activity_name = '';
this.query.follower_num = '';
this.query.follower_name = '';
this.items = [];
this.expanded = [];
},
showDetailDialog(item) {
this.dialog.selected = item;
this.dialog.show = true;
},
showActivityDialog() {
this.activityDialog.show = true;
this.searchActivity();
},
searchActivity() {
this.activityDialog.loading = true;
axios.get('../../api/activity', { params: { keyword: this.activityDialog.search } })
.then(res => { this.activityDialog.items = res.data; })
.catch(() => { this.activityDialog.items = []; })
.finally(() => { this.activityDialog.loading = false; });
},
selectActivity(row) {
this.query.activity_num = row.num;
this.query.activity_name = row.subject;
this.activityDialog.show = false;
},
showFollowerDialog() {
this.followerDialog.show = true;
this.searchFollower();
},
searchFollower() {
this.followerDialog.loading = true;
axios.post('../..../..../../api/follower/GetList', { u_name: this.followerDialog.search }, { params: { page: 1, pageSize: 10 } })
.then(res => { this.followerDialog.items = res.data.list; })
.catch(() => { this.followerDialog.items = []; })
.finally(() => { this.followerDialog.loading = false; });
},
selectFollower(row) {
this.query.follower_num = row.num;
this.query.follower_name = row.u_name;
this.followerDialog.show = false;
},
getStatusColor(status) {
switch (status) {
case '90': return 'warning';
case '99': return 'success';
default: return 'grey';
}
},
getStatusText(status) {
switch (status) {
case '90': return '沖帳有剩餘';
case '99': return '沖帳完成';
default: return '未知狀態';
}
}
}
};
</script>
<style>
.v-data-table.verify-query-table .v-data-table__wrapper tbody tr {
min-height: 90px !important;
height: 90px !important;
}
.v-data-table.verify-query-table .v-data-table__wrapper tbody td {
min-height: 90px !important;
height: 90px !important;
vertical-align: middle !important;
}
</style>
+1 -1
View File
@@ -260,7 +260,7 @@ public partial class admin_follower_import : MyWeb.config
follower.introducer = ValString(sheet.Cells[currentRow, 17].Text.Trim());
//檢查國籍代碼是否存在
var country = _country.Where(x => x.name_zh == ValString(sheet.Cells[currentRow, 18].Text.Trim())).FirstOrDefault();
var country = _country.Where(x => x.name_zh == ValString(sheet.Cells[currentRow, 18].Text.Trim())).FirstOrDefault();
if(country != null)
{
//follower.country = ValString(sheet.Cells[currentRow, 18].Text.Trim());
+146 -20
View File
@@ -13,10 +13,12 @@
<a @click="print_dialog.show=true" class="btn btn-outline-primary btn-print" target="_blank">
<i class="mdi mdi-printer"></i>列印管理報表
</a>
<a @click="goPrint" class="btn btn-outline-primary btn-print" target="_blank">
<a @click="goPrint" class="btn btn-outline-primary btn-print" :class="{ 'disabled': data_table.list.length === 0 }" target="_blank">
<i class="mdi mdi-printer"></i>列印查詢資料
</a>
<asp:LinkButton ID="excel" runat="server" CssClass="btn btn-outline-success" OnClick="excel_Click"><span class="fa-solid fa-file-excel"></span> 匯出Excel</asp:LinkButton>
<div :style="data_table.list.length === 0 ? 'pointer-events: none; opacity: 0.5;' : ''" style="display:inline-block;">
<asp:LinkButton ID="excel" runat="server" CssClass="btn btn-outline-success" OnClick="export_Click"><span class="fa-solid fa-file-excel"></span> 匯出查詢資料(Excel</asp:LinkButton>
</div>
</div>
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" runat="Server">
@@ -24,11 +26,13 @@
Vue.filter('timeString', function (value, myFormat) {
return value == null || value == "" ? "" : moment(value).format(myFormat || 'YYYY-MM-DD, HH:mm:ss');
});
let VueApp=new Vue({
let VueApp = new Vue({
el: '#app',
vuetify: new Vuetify(vuetify_options),
data() {
return {
isSearched: false,
print_error_msg: '',
options: { multiSort: false },
search_options: { multiSort: false },
data_table: {
@@ -38,10 +42,10 @@
singleSelect: false,
count: 0,
page: 1,
pageSize: 10,
pageSize: 10,
header: [
{ text: '信眾編號', value: 'f_number', align: 'start' },
{ text: '信眾姓名', value: 'u_name'},
{ text: '信眾姓名', value: 'u_name' },
{ text: '身分別', value: 'identity_type_desc' },
{ text: '性別', value: 'sex' },
{ text: '生日', value: 'birthday' },
@@ -49,9 +53,9 @@
{ text: '', value: 'slot', sortable: false },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' },
],
footer:{
footer: {
showFirstLastPage: true,
pageSizeOptions:[5,10,20,30],
pageSizeOptions: [5, 10, 20, 30],
},
},
search: {
@@ -60,7 +64,7 @@
sex: '',
//birthday: new Date().toISOString().split('T')[0],
//birthday2: new Date().toISOString().split('T')[0]
birthday:'',
birthday: '',
birthday2: '',
address: '',
country: '',
@@ -69,14 +73,14 @@
/*注意這邊的參數不能跟下方print_search重複*/
},
//列印管理報表
print_conditions:'yy',
print_conditions: 'yy',
print_search: {
year: '',
month: '',
season: '',
season: '',
},
select_items: {
month: [{
month: [{
text: "請選擇",
val: 0
},],
@@ -126,13 +130,18 @@
itemsPerPageText: '',
},
},
}
},
watch: {
options: {
handler() {
this.getList()
handler() {
if (this.isSearched) {
this.getList()
}
else {
this.data_table.loading = false;
}
},
deep: true,
},
@@ -141,23 +150,101 @@
this.search_get()
},
deep: true,
},
}, mounted() {
}
}, mounted() {
const printResult = document.getElementById('<%= hid_err_msg.ClientID %>').value;
document.getElementById('<%= hid_err_msg.ClientID %>').value = '';
window._printResult = printResult
this.search_dialog.current = this.search_dialog.controls.search1 ///default
this.initPrintSearch();
const navEntries = performance.getEntriesByType("navigation");
const isReload = navEntries.length > 0 && navEntries[0].type === "reload";
const url = new URL(window.location.href);
let params = url.searchParams;
if (params.get('dirty') === '1') { // 資料有更新時執行 getlist
this.search = JSON.parse(sessionStorage.getItem("member_query_params"));
this.getList();
params.delete('dirty');
window.history.replaceState({}, '', url.pathname + url.search);
}
if (isReload) {
sessionStorage.removeItem("followerpage");
sessionStorage.removeItem("member_list_cache");
sessionStorage.removeItem("member_query_params");
}
else if ("<%=lastAddedID%>" !== "") {
const newQuery = { f_number: '<%=lastAddedID%>' };
sessionStorage.setItem('member_query_params', JSON.stringify(newQuery));
this.search = newQuery;
this.isSearched = true;
}
else {
const savedPage = parseInt(sessionStorage.getItem('followerpage'));
const savedData = sessionStorage.getItem("member_list_cache");
const savedQuery = JSON.parse(sessionStorage.getItem("member_query_params"));
if (savedQuery) {
this.search = savedQuery;
this.isSearched = true;
}
if (savedPage) {
this.options.page = savedPage;
}
if (savedData && savedData !== "undefined") {
this.data_table = JSON.parse(savedData);
this.isSearched = true;
}
}
if (printResult === 'nodata' || printResult === 'success') {
this.$nextTick(() => {
this.print_search.year = parseInt(document.getElementById('<%= hid_print_year.ClientID %>').value) || this.print_search.year;
this.print_search.month = parseInt(document.getElementById('<%= hid_print_month.ClientID %>').value) || this.print_search.month;
this.print_search.season = parseInt(document.getElementById('<%= hid_print_season.ClientID %>').value) || this.print_search.season;
this.print_conditions = document.getElementById('<%= hid_print_mode.ClientID %>').value || 'yy';
this.print_dialog.show = true;
if (printResult === 'nodata') {
this.print_error_msg = "查無資料,請重新選擇區間";
}
});
}
this.$nextTick(() => {
setTimeout(() => {
// 清空 URL
const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
window.history.replaceState({}, '', cleanUrl);
}, 100);
});
},
methods: {
triggerManagementExport(mode) {
this.print_dialog.show = false;
this.print_error_msg = "";
if (this.print_search.year == '') {
msgbox('請輸入年份');
return;
}
document.getElementById('<%= hid_print_mode.ClientID %>').value = this.print_conditions;
document.getElementById('<%= hid_print_year.ClientID %>').value = this.print_search.year;
if (this.print_conditions == 'mm') {
document.getElementById('<%= hid_print_month.ClientID %>').value = this.print_search.month;
}
else if (this.print_conditions == 'ss') {
document.getElementById('<%= hid_print_season.ClientID %>').value = this.print_search.season;
}
if (mode === 'print') {
document.getElementById('<%= print_management.ClientID %>').click();
}
else if (mode === "excel") {
document.getElementById('<%= excel_management.ClientID %>').click();
}
},
search_show(curr) {
//console.log("btn_click:", curr, curr.api_url);
this.search_dialog.current = curr;
@@ -228,6 +315,7 @@
//console.log(row, row["u_name"], row["f_number"], curr.id, target);
},
getList(clearpage = false) {
console.log("do getlist")
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: sortBy[0], sortDesc: sortDesc[0],
@@ -241,6 +329,9 @@
this.data_table.list = response.data.list
this.data_table.count = response.data.count;
this.data_table.loading = false
const dataToStore = JSON.stringify(this.data_table);
sessionStorage.setItem("member_list_cache", dataToStore);
})
.catch(
error => console.log(error)
@@ -262,7 +353,7 @@
const index = this.data_table.list.indexOf(item)
if (index != -1) {
axios
.delete(HTTP_HOST + 'api/follower/' + item.num)
.delete(HTTP_HOST + 'api/follower/Delete/' + item.num)
.then(response => {
this.getList();
})
@@ -282,18 +373,23 @@
//}
//this.data_table.selected = [];
//this.data_table.count = this.data_table.list.length
location.reload();
//location.reload();
this.getList();
})
.catch(error => console.log(error))
}
},
btn_search() {
this.isSearched = true;
sessionStorage.setItem("member_query_params", JSON.stringify(this.search));
this.getList(true)
bootstrap.Offcanvas.getInstance(document.getElementById("offcanvasRight")).hide()
},
btn_all() {
this.isSearched = false;
clearObjProps(this.search);
this.btn_search()
sessionStorage.setItem("member_query_params", JSON.stringify(this.search));
//this.btn_search()
},
goPrint() {
//debugger;
@@ -309,6 +405,7 @@
//列印管理報表
print_close() {
this.print_dialog.show = false;
this.print_error_msg = "";
}
,
initPrintSearch() {
@@ -391,10 +488,31 @@
$('#country2').val('');
VueApp.search.country2 = '';
});
$(document).ready(function () {
// 判斷是否彈出 search dialog
let hasSearchResult = sessionStorage.getItem("member_list_cache") !== null;
if (!hasSearchResult && window._printResult === '') {
let $btn = $("a[data-bs-target='#offcanvasRight'][href='#search_panel']");
$btn.click();
let el = document.getElementById('offcanvasRight');
let offcanvas = bootstrap.Offcanvas.getOrCreateInstance(el);
offcanvas.show();
}
});
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<uc1:alert runat="server" ID="L_msg" Text="" />
<asp:HiddenField ID="hid_err_msg" runat="server" />
<asp:HiddenField ID="hid_print_year" runat="server" />
<asp:HiddenField ID="hid_print_month" runat="server" />
<asp:HiddenField ID="hid_print_season" runat="server" />
<asp:HiddenField ID="hid_print_mode" runat="server" />
<asp:HiddenField ID="hid_qry" runat="server" />
<asp:LinkButton ID="excel_management" runat="server" OnClick="export_Click" style="display:none;" />
<asp:LinkButton ID="print_management" runat="server" OnClick="export_Click" style="display:none;" />
<div id="content" class="container-fluid">
<v-data-table
v-model="data_table.selected"
@@ -508,9 +626,17 @@
</v-col>
</v-row>
<v-row>
<v-col>
<div v-if="print_error_msg" class="red--text mt-2 text-center" style="font-weight: bold;">
{{ print_error_msg }}
</div>
</v-col>
</v-row>
<v-row densee class="pt-3" >
<v-col :cols="12" class="pt-3 text-center" >
<v-btn class="ma-2" color="primary" dark @click="goPrint2" > 列印 </v-btn>
<v-btn class="ma-2" color="primary" dark @click="triggerManagementExport('print')" > 列印 </v-btn>
<v-btn class="ma-2" color="primary" dark @click="triggerManagementExport('excel')"> 匯出 Excel </v-btn>
<v-btn class="ma-2" color="green" dark @click="print_close" > 取消 </v-btn>
</v-col>
</v-row>
+294 -183
View File
@@ -1,17 +1,20 @@
using System;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Interop;
using static TreeView;
@@ -19,16 +22,22 @@ public partial class admin_follower_index : MyWeb.config
{
public int page = 1;
private Model.ezEntities _db = new Model.ezEntities();
protected string lastAddedID;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (Session["LastAddedID"] != null)
{
lastAddedID = Session["LastAddedID"].ToString();
Session.Remove("LastAddedID");
}
BuildKind();
}
else
{
}
}
@@ -42,10 +51,10 @@ public partial class admin_follower_index : MyWeb.config
//國籍
s_country.Items.Clear();
s_country.Items.Add(new ListItem("請選擇", ""));
var qry =_db.countries.OrderBy(x => x.range).ThenBy(x => x.name_en).ToList();
var qry = _db.countries.OrderBy(x => x.range).ThenBy(x => x.name_en).ToList();
if (qry.Count > 0)
{
foreach(var x in qry)
{
foreach (var x in qry)
s_country.Items.Add(new ListItem(x.name_zh, x.ID));
}
@@ -68,208 +77,310 @@ public partial class admin_follower_index : MyWeb.config
#endregion
#region Excel
#region
protected void excel_Click(object sender, EventArgs e)
protected void export_Click(object sender, EventArgs e)
{
var memoryStream = new MemoryStream();
using (var doc = SpreadsheetDocument.Create(memoryStream, SpreadsheetDocumentType.Workbook))
LinkButton btn = sender as LinkButton;
if (btn == null) return;
bool isPrintMode = (btn.ID == "print_management");
bool isExcelMode = (btn.ID == "excel_management" || btn.ID == "excel");
bool isManagementMode = (btn.ID == "excel_management" || btn.ID == "print_management");
//查詢要匯出的資料
string _query = ""; // 紀錄匯出條件
var list = searchData(ref _query, isManagementMode);
if (isExcelMode)
{
var wb = doc.AddWorkbookPart();
wb.Workbook = new Workbook();
var sheets = wb.Workbook.AppendChild(new Sheets());
//建立第一個頁籤
var ws = wb.AddNewPart<WorksheetPart>();
ws.Worksheet = new Worksheet();
sheets.Append(new Sheet()
{
Id = wb.GetIdOfPart(ws),
SheetId = 1,
Name = "信眾資料"
});
//設定欄寬
var cu = new Columns();
cu.Append(
new Column { Min = 1, Max = 2, Width = 15, CustomWidth = true },
new Column { Min = 3, Max = 3, Width = 10, CustomWidth = true },
new Column { Min = 4, Max = 11, Width = 15, CustomWidth = true },
new Column { Min = 12, Max = 12, Width = 25, CustomWidth = true },
new Column { Min = 13, Max = 13, Width = 8, CustomWidth = true },
new Column { Min = 14, Max = 14, Width = 35, CustomWidth = true },
new Column { Min = 15, Max = 16, Width = 15, CustomWidth = true }
);
ws.Worksheet.Append(cu);
//建立資料頁
var sd = new SheetData();
ws.Worksheet.AppendChild(sd);
//第一列資料
var tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue("信眾編號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("信眾姓名"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("性別"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("身分別"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("生日(國曆)"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("聯絡電話"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("手機號碼"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("皈依道場"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("皈依法名"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("皈依日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("加入日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("緊急連絡人"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("緊急連絡人電話"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("血型"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("國籍"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("收件地址"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("介紹人"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("LINE帳號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("其它社群帳號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("建檔日期"), DataType = CellValues.String }
,new Cell() { CellValue = new CellValue("身分證號"), DataType = CellValues.String }
//,new Cell() { CellValue = new CellValue("SHA"), DataType = CellValues.String }
);
sd.AppendChild(tr);
//查詢要匯出的資料
//紀錄匯出條件
string _query = "";
var list = searchData(ref _query);
if (list.Count > 0)
{
MyWeb.encrypt encrypt = new MyWeb.encrypt();
Model.country country = new Model.country();
var tdesc = publicFun.enum_desc<Model.follower.type>();
foreach (var item in list)
using (var doc = SpreadsheetDocument.Create(memoryStream, SpreadsheetDocumentType.Workbook))
{
//新增資料列
tr = new Row();
string s1, s2, sha;
s1= encrypt.DecryptAutoKey(item.phone);
s2= encrypt.DecryptAutoKey(item.id_code);
sha = encrypt.followerHash(s1, s2);
var wb = doc.AddWorkbookPart();
wb.Workbook = new Workbook();
var sheets = wb.Workbook.AppendChild(new Sheets());
//建立第一個頁籤
var ws = wb.AddNewPart<WorksheetPart>();
ws.Worksheet = new Worksheet();
sheets.Append(new Sheet()
{
Id = wb.GetIdOfPart(ws),
SheetId = 1,
Name = "信眾資料"
});
//設定欄寬
var cu = new Columns();
cu.Append(
new Column { Min = 1, Max = 2, Width = 15, CustomWidth = true },
new Column { Min = 3, Max = 3, Width = 10, CustomWidth = true },
new Column { Min = 4, Max = 11, Width = 15, CustomWidth = true },
new Column { Min = 12, Max = 12, Width = 25, CustomWidth = true },
new Column { Min = 13, Max = 13, Width = 8, CustomWidth = true },
new Column { Min = 14, Max = 14, Width = 35, CustomWidth = true },
new Column { Min = 15, Max = 16, Width = 15, CustomWidth = true }
);
ws.Worksheet.Append(cu);
//建立資料頁
var sd = new SheetData();
ws.Worksheet.AppendChild(sd);
//第一列資料
var tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(item.f_number), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.u_name), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.sex), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(!isStrNull(item.identity_type)? tdesc[item.identity_type ?? 1] :""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.birthday.HasValue ? ValDate(item.birthday.Value).ToString("yyyy/MM/dd") : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.phone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.cellphone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.refuge_area), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.refuge_name), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.refugedate.HasValue ? ValDate(item.refugedate.Value).ToString("yyyy/MM/dd") : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.join_date.HasValue ? ValDate(item.join_date.Value).ToString("yyyy/MM/dd") : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.contactor), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.contactor_phone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.blood), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(!isStrNull(item.country) ? item.country1.name_zh : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.address), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.introducer), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.socialid1), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.socialid2), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.reg_time.HasValue ? ValDate(item.reg_time.Value).ToString("yyyy/MM/dd HH:mm:ss") : ""), DataType = CellValues.String }
,new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.id_code)), DataType = CellValues.String }
//, new Cell() { CellValue = new CellValue(sha), DataType = CellValues.String }
);
new Cell() { CellValue = new CellValue("信眾編號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("信眾姓名"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("性別"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("身分別"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("生日(國曆)"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("聯絡電話"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("手機號碼"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("皈依道場"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("皈依法名"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("皈依日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("加入日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("緊急連絡人"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("緊急連絡人電話"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("血型"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("國籍"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("收件地址"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("介紹人"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("LINE帳號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("其它社群帳號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("建檔日期"), DataType = CellValues.String }
, new Cell() { CellValue = new CellValue("身分證號"), DataType = CellValues.String }
//,new Cell() { CellValue = new CellValue("SHA"), DataType = CellValues.String }
);
sd.AppendChild(tr);
MyWeb.encrypt encrypt = new MyWeb.encrypt();
Model.country country = new Model.country();
var tdesc = publicFun.enum_desc<Model.follower.type>();
foreach (var item in list)
{
//新增資料列
tr = new Row();
string s1, s2, sha;
s1 = encrypt.DecryptAutoKey(item.phone);
s2 = encrypt.DecryptAutoKey(item.id_code);
sha = encrypt.followerHash(s1, s2);
tr.Append(
new Cell() { CellValue = new CellValue(item.f_number), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.u_name), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.sex), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(!isStrNull(item.identity_type) ? tdesc[item.identity_type ?? 1] : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.birthday.HasValue ? ValDate(item.birthday.Value).ToString("yyyy/MM/dd") : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.phone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.cellphone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.refuge_area), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.refuge_name), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.refugedate.HasValue ? ValDate(item.refugedate.Value).ToString("yyyy/MM/dd") : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.join_date.HasValue ? ValDate(item.join_date.Value).ToString("yyyy/MM/dd") : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.contactor), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.contactor_phone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.blood), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(!isStrNull(item.country) ? item.country1.name_zh : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.address), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.introducer), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.socialid1), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.socialid2), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.reg_time.HasValue ? ValDate(item.reg_time.Value).ToString("yyyy/MM/dd HH:mm:ss") : ""), DataType = CellValues.String }
, new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.id_code)), DataType = CellValues.String }
//, new Cell() { CellValue = new CellValue(sha), DataType = CellValues.String }
);
sd.AppendChild(tr);
}
//空一列
tr = new Row();
sd.AppendChild(tr);
//匯出資訊
string _data = "匯出時間 : " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
_data += " " + admin.info.u_id;
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
_data = "匯出條件 : " + (!isStrNull(_query) ? _query : "-");
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Follower, (int)Model.admin_log.Status.Excel, admin_log.LogViewBtn(list.Select(x => x.f_number + x.u_name).ToList()));
}
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=信眾_data.xlsx");
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.BinaryWrite(memoryStream.ToArray());
HttpContext.Current.Response.End();
//空一列
tr = new Row();
sd.AppendChild(tr);
//匯出資訊
string _data = "匯出時間 : " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
_data += " " + admin.info.u_id;
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
_data = "匯出條件 : " + (!isStrNull(_query) ? _query : "-");
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Follower, (int)Model.admin_log.Status.Excel, admin_log.LogViewBtn(list.Select(x => x.f_number + x.u_name).ToList()));
hid_err_msg.Value = "success";
}
else
{
ScriptMsg2("查無資料");
//ScriptMsg2("查無資料");
hid_err_msg.Value = "nodata";
}
}
else if (isPrintMode)
{
string urlParams = "";
int selYear = !string.IsNullOrEmpty(hid_print_year.Value) ? int.Parse(hid_print_year.Value) : 0;
int selMonth = !string.IsNullOrEmpty(hid_print_month.Value) ? int.Parse(hid_print_month.Value) : 0;
int selSeason = !string.IsNullOrEmpty(hid_print_season.Value) ? int.Parse(hid_print_season.Value) : 0;
string selMode = !string.IsNullOrEmpty(hid_print_mode.Value) ? hid_print_mode.Value : "";
var qry = _db.followers.AsQueryable();
if (selYear > 0)
{
urlParams += "&year=" + selYear;
}
if (selMode == "mm" && selMonth > 0)
{
urlParams += "&month=" + selMonth;
}
else if (selMode == "ss" && selSeason > 0)
{
urlParams += "&season=" + selSeason;
}
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=信眾_data.xlsx");
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.BinaryWrite(memoryStream.ToArray());
HttpContext.Current.Response.End();
if (list.Count > 0)
{
hid_err_msg.Value = "success";
hid_print_year.Value = selYear.ToString();
hid_print_month.Value = selMonth.ToString();
hid_print_season.Value = selSeason.ToString();
hid_print_mode.Value = selMode;
string script = $@"window.open('print.aspx?{urlParams}&mode={selMode}', '列印信眾資料', 'noopener,noreferrer');";
ScriptManager.RegisterStartupScript(this, GetType(), "ExecutePrint", script, true);
}
else
{
hid_err_msg.Value = "nodata";
}
}
}
protected List<Model.follower> searchData(ref string _query)
protected List<Model.follower> searchData(ref string _query, bool isManagementMode = false)
{
//查詢要匯出的資料
var qry = _db.followers.AsQueryable();
//紀錄匯出條件
if (!isStrNull(s_f_number.Value))
// 管理報表
if (isManagementMode)
{
qry = qry.Where(o => o.f_number.Contains(s_f_number.Value.Trim()));
_query += "信眾編號:" + s_f_number.Value.Trim() + "\n";
}
int selYear = !string.IsNullOrEmpty(hid_print_year.Value) ? int.Parse(hid_print_year.Value) : 0;
int selMonth = !string.IsNullOrEmpty(hid_print_month.Value) ? int.Parse(hid_print_month.Value) : 0;
int selSeason = !string.IsNullOrEmpty(hid_print_season.Value) ? int.Parse(hid_print_season.Value) : 0;
string selMode = !string.IsNullOrEmpty(hid_print_mode.Value) ? hid_print_mode.Value : "";
if (!isStrNull(s_u_name.Value))
{
qry = qry.Where(o => o.u_name.Contains(s_u_name.Value.Trim()));
_query += "信眾姓名:" + s_u_name.Value.Trim() + "\n";
}
if (!isStrNull(s_address.Value))
{
qry = qry.Where(o => o.address.Contains(s_address.Value.Trim()));
_query += "地址:" + s_u_name.Value.Trim() + "\n";
}
// 電話/證號搜尋 (使用 search_keywords HEX 編碼)
if (!isStrNull(s_phone_idcode.Value) && GlobalVariables.UseSearchKeywords)
{
MyWeb.encrypt encrypt = new MyWeb.encrypt();
string hexSearch = encrypt.ConvertToHex(s_phone_idcode.Value.Trim());
if (!string.IsNullOrEmpty(hexSearch))
if (selYear > 0)
{
qry = qry.Where(o => o.search_keywords != null && o.search_keywords.Contains(hexSearch));
_query += "電話/證號:" + s_phone_idcode.Value.Trim() + "\n";
qry = qry.Where(o => o.join_date.HasValue && o.join_date.Value.Year == selYear);
_query += "年份:" + selYear + "\n";
}
if (selMode == "mm" && selMonth > 0)
{
qry = qry.Where(o => o.join_date.HasValue && o.join_date.Value.Month == selMonth);
_query += "月份:" + selMonth + "\n";
}
else if (selMode == "ss" && selSeason > 0)
{
if (selSeason == 1)
{
qry = qry.Where(o => o.join_date.HasValue)
.Where(o => o.join_date.Value.Month == 1 || o.join_date.Value.Month == 2 || o.join_date.Value.Month == 3);
}
else if (selSeason == 2)
{
qry = qry.Where(o => o.join_date.HasValue)
.Where(o => o.join_date.Value.Month == 4 || o.join_date.Value.Month == 5 || o.join_date.Value.Month == 6);
}
else if (selSeason == 3)
{
qry = qry.Where(o => o.join_date.HasValue)
.Where(o => o.join_date.Value.Month == 7 || o.join_date.Value.Month == 8 || o.join_date.Value.Month == 9);
}
else if (selSeason == 4)
{
qry = qry.Where(o => o.join_date.HasValue)
.Where(o => o.join_date.Value.Month == 10 || o.join_date.Value.Month == 11 || o.join_date.Value.Month == 12);
}
_query += "季度:" + selSeason + "\n";
}
qry = qry.OrderByDescending(o => o.num);
return qry.ToList();
}
if (!isStrNull(s_birthday.Value) && isDate(s_birthday.Value))
else
// 匯出查詢資料
{
qry = qry.Where(o => o.birthday >= ValDate(s_birthday.Value));
_query += "生日(起):" + s_birthday.Value.Trim() + "\n";
//紀錄匯出條件
if (!isStrNull(s_f_number.Value))
{
qry = qry.Where(o => o.f_number.Contains(s_f_number.Value.Trim()));
_query += "信眾編號:" + s_f_number.Value.Trim() + "\n";
}
if (!isStrNull(s_u_name.Value))
{
qry = qry.Where(o => o.u_name.Contains(s_u_name.Value.Trim()));
_query += "信眾姓名:" + s_u_name.Value.Trim() + "\n";
}
if (!isStrNull(s_address.Value))
{
qry = qry.Where(o => o.address.Contains(s_address.Value.Trim()));
_query += "地址:" + s_address.Value.Trim() + "\n";
}
// 電話/證號搜尋 (使用 search_keywords HEX 編碼)
if (!isStrNull(s_phone_idcode.Value) && GlobalVariables.UseSearchKeywords)
{
MyWeb.encrypt encrypt = new MyWeb.encrypt();
string hexSearch = encrypt.ConvertToHex(s_phone_idcode.Value.Trim());
if (!string.IsNullOrEmpty(hexSearch))
{
qry = qry.Where(o => o.search_keywords != null && o.search_keywords.Contains(hexSearch));
_query += "電話/證號:" + s_phone_idcode.Value.Trim() + "\n";
}
}
if (!isStrNull(s_birthday.Value) && isDate(s_birthday.Value))
{
var tmp_s_birthday = ValDate(s_birthday.Value);
qry = qry.Where(o => o.birthday >= tmp_s_birthday);
_query += "生日(起):" + s_birthday.Value.Trim() + "\n";
}
if (!isStrNull(s_birthday2.Value) && isDate(s_birthday2.Value))
{
var tmp_s_birthday2 = Convert.ToDateTime(s_birthday2.Value).AddDays(1);
qry = qry.Where(o => o.birthday < tmp_s_birthday2);
_query += "生日(訖):" + s_birthday2.Value.Trim() + "\n";
}
qry = qry.OrderByDescending(o => o.num);
return qry.ToList();
}
if (!isStrNull(s_birthday2.Value) && isDate(s_birthday2.Value))
{
qry = qry.Where(o => o.birthday < Convert.ToDateTime(s_birthday2.Value).AddDays(1));
_query += "生日(訖):" + s_birthday2.Value.Trim() + "\n";
}
qry = qry.OrderByDescending(o => o.num);
return qry.ToList();
}
+1
View File
@@ -8,6 +8,7 @@
<span>信眾姓名:</span><asp:Literal runat="server" ID="username"></asp:Literal>
</div>
</div>
<a href="index.aspx" class="btn btn-outline-secondary">返回</a>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<div class="container-fluid">
+5 -4
View File
@@ -4,10 +4,11 @@
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
<div class="scroll-nav nav nav-tabs mb-2 mb-sm-0 d-none d-sm-flex">
<div class="ms-3">
<span>信眾姓名:</span><asp:Literal runat="server" ID="username"></asp:Literal>
</div>
</div>
<div class="ms-3">
<span>信眾姓名:</span><asp:Literal runat="server" ID="username"></asp:Literal>
</div>
</div>
<a href="order_record.aspx?userid=<%=Request["userid"] %>" class="btn btn-outline-secondary">返回</a>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<div class="container-fluid">
+17 -3
View File
@@ -3,6 +3,7 @@ using DocumentFormat.OpenXml.Vml.Office;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Information;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.UI;
@@ -26,7 +27,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
//紀錄匯出條件
string _query = "";
var qry = _db.followers.AsQueryable();
// ❌ 錯誤寫法: qry = qry.Where(o => o.f_number.Contains(Request["f_number"].Trim()));
// LINQ to Entities 無法轉換 Request[] 方法,必須先轉換為變數再使用
string fNumberParam = Request["f_number"]?.Trim();
@@ -60,7 +61,8 @@ public partial class admin_follower_print_ : System.Web.UI.Page
if (!string.IsNullOrEmpty(Request["birthday2"]))
{
DateTime birthday2Param = Convert.ToDateTime(Request["birthday2"].Trim());
qry = qry.Where(o => o.birthday < birthday2Param.AddDays(1));
var tmpBirthday2Param = birthday2Param.AddDays(1);
qry = qry.Where(o => o.birthday < tmpBirthday2Param);
_query += "生日(訖):" + birthday2Param.ToString("yyyy/MM/dd") + "\n";
}
// ❌ 錯誤寫法: _db.countries.Where(x => x.ID == Request["country"].ToString())
@@ -85,7 +87,19 @@ public partial class admin_follower_print_ : System.Web.UI.Page
}
_query += "國家:" + (_db.countries.Where(x => x.ID == country2Id).Select(x => x.name_zh).FirstOrDefault() ?? "") + "\n";
}
string phone_ipcode = Request["phone_idcode"]?.ToString();
if (!string.IsNullOrEmpty(phone_ipcode) && GlobalVariables.UseSearchKeywords)
{
MyWeb.encrypt encrypt = new MyWeb.encrypt();
string hexSearch = encrypt.ConvertToHex(phone_ipcode.Trim());
if (!string.IsNullOrEmpty(hexSearch))
{
qry = qry.Where(o => o.search_keywords != null && o.search_keywords.Contains(hexSearch));
_query += "電話/證號:" + phone_ipcode.Trim() + "\n";
}
}
//管理報表
if (!string.IsNullOrEmpty(Request["year"]))
{
+55 -35
View File
@@ -190,7 +190,7 @@
text_prop: 'name_zh',
value_prop: 'id',
keys: [
{ id: 'keyword', title: '關鍵字' },
{ id: 'keyword', title: '關鍵字' },
],
api_url: HTTP_HOST + 'api/country/GetList',
columns: [
@@ -208,11 +208,11 @@
title: '稱謂',
text_prop: 'title',
value_prop: 'num',
keys: [
keys: [
],
api_url: HTTP_HOST + 'api/appellation/GetList',
columns: [
{ id: 'title', title: '稱謂' },
{ id: 'title', title: '稱謂' },
],
selected: {},
select(item, index, t) {
@@ -230,7 +230,7 @@
{ id: 'f_number', title: '編號' },
{ id: 'u_name', title: '姓名' },
{ id: 'address', title: '地址' },
{ id: 'onlyfamily', title: '只查親屬'},
{ id: 'onlyfamily', title: '只查親屬' },
],
api_url: HTTP_HOST + 'api/follower/GetList',
columns: [
@@ -241,7 +241,7 @@
selected: {},
select(item, index, t) {
console.log("select search5", t);
}
},
},
@@ -314,10 +314,10 @@
birthday: '',
phoneDes: '',
demo: '',
appellation_id_selected :
appellation_id_selected:
{
text : '',
val : 0,
text: '',
val: 0,
},
},
@@ -372,33 +372,33 @@
],
tabletsDetail: { multiSort: false },
tabletTable: {
Loading:true,
Loading: true,
disableButton: true,
searchDetail: '',
headersDetail: [
{ text: '超渡/陽上', value: 'type', sortable: false, width: "100px" },
{ text: '* 牌位標題', value: 'title', sortable: true },
{ text: '超渡/陽上', value: 'type', sortable: false, width: "100px" },
{ text: '* 牌位標題', value: 'title', sortable: true },
{ text: '', value: 'actions', sortable: false, width: "100px" },
],
],
desserts: [],
desserts_count: 0,
editedIndex: -1,
editedItem: {
id: 0,
num: 0,
f_num: 0,
title: '',
num: 0,
f_num: 0,
title: '',
},
defaultItem: {
id: 0,
num: 0,
f_num: 0,
num: 0,
f_num: 0,
type: this.selectedTabletType,
title:'',
title: '',
},
},
//新:家人
family:{
family: {
dialog: false,
isEditing: false,
is_tw: true,
@@ -420,8 +420,8 @@
chinese_year: "",
zodiac: "",
birth_time: "",
city:"",
area:"",
city: "",
area: "",
address: "",
phone: "",
mobile: "",
@@ -440,8 +440,8 @@
chinese_year: "",
zodiac: "",
birth_time: "",
city:"",
area:"",
city: "",
area: "",
address: "",
phone: "",
mobile: "",
@@ -458,11 +458,11 @@
areaOptions: {}, // 區域選項
//天干地支:甲子, 乙丑...
chineseYears: [
'甲子', '乙丑', '丙寅', '丁卯', '戊辰', '己巳', '庚午', '辛未', '壬申', '癸酉',
'甲戌', '乙亥', '丙子', '丁丑', '戊寅', '己卯', '庚辰', '辛巳', '壬午', '癸未',
'甲申', '乙酉', '丙戌', '丁亥', '戊子', '己丑', '庚寅', '辛卯', '壬辰', '癸巳',
'甲午', '乙未', '丙申', '丁酉', '戊戌', '己亥', '庚子', '辛丑', '壬寅', '癸卯',
'甲辰', '乙巳', '丙午', '丁未', '戊申', '己酉', '庚戌', '辛亥', '壬子', '癸丑',
'甲子', '乙丑', '丙寅', '丁卯', '戊辰', '己巳', '庚午', '辛未', '壬申', '癸酉',
'甲戌', '乙亥', '丙子', '丁丑', '戊寅', '己卯', '庚辰', '辛巳', '壬午', '癸未',
'甲申', '乙酉', '丙戌', '丁亥', '戊子', '己丑', '庚寅', '辛卯', '壬辰', '癸巳',
'甲午', '乙未', '丙申', '丁酉', '戊戌', '己亥', '庚子', '辛丑', '壬寅', '癸卯',
'甲辰', '乙巳', '丙午', '丁未', '戊申', '己酉', '庚戌', '辛亥', '壬子', '癸丑',
'甲寅', '乙卯', '丙辰', '丁巳', '戊午', '己未', '庚申', '辛酉', '壬戌', '癸亥'
],
//生肖
@@ -585,7 +585,7 @@
this.search_dialog.list = response.data.list
this.search_dialog.count = response.data.count
this.search_dialog.loading = false
console.log(this.search_dialog.list)
})
.catch(error => {
console.log(error)
@@ -1318,13 +1318,32 @@
$('.tab-pane,.edit_Click').removeClass('pe-none'); // 移除 pe-none 類,允許編輯
}
});
let isComposing = false;
const cellphoneInput = document.querySelector('[id$="cellphone"]');
if (cellphoneInput) {
cellphoneInput.addEventListener('compositionstart', () => {
isComposing = true;
});
cellphoneInput.addEventListener('compositionend', (e) => {
isComposing = false;
formatCellphone(e.target);
});
cellphoneInput.addEventListener('input', (e) => {
if (!isComposing) {
formatCellphone(e.target);
}
});
}
</script>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="page_nav" runat="Server">
<div class="scroll-nav nav nav-tabs mb-2 mb-sm-0 d-none d-sm-flex">
<template v-if="follower_id !='' "> {{titleword()}} </template>
</div>
<div class="d-flex align-items-center">
<div class="">
<div class="form-check me-3 d-none" id="editCheckboxContainer">
<input class="form-check-input" type="checkbox" id="editCheckbox">
<label class="form-check-label" for="editCheckbox">
@@ -1332,8 +1351,8 @@
</label>
</div>
<asp:Button ID="add" runat="server" Text="送出" OnClick="add_Click" CssClass="btn btn-primary edit_Click noedit" />
<asp:Button ID="edit" runat="server" Text="修改" Visible="false" OnClick="edit_Click" CssClass="btn btn-primary edit_Click noedit" />
<asp:Button ID="goback" runat="server" Text="回列表" Visible="false" CausesValidation="false" OnClick="goback_Click" CssClass="btn btn-outline-secondary" />
<asp:Button ID="edit" runat="server" Text="儲存" Visible="false" OnClick="edit_Click" CssClass="btn btn-primary edit_Click noedit" />
<asp:Button ID="goback" runat="server" Text="取消" Visible="true" CausesValidation="false" OnClick="goback_Click" CssClass="btn btn-outline-secondary" />
</div>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
@@ -1412,7 +1431,7 @@
</div>
<label class="col-sm-2 col-lg-1 col-form-label">手機號碼<asp:Literal ID="cellphoneReqStar" runat="server" Text=" *"></asp:Literal></label>
<div class="col-sm-10 col-lg-3">
<asp:TextBox ID="cellphone" MaxLength="12" runat="server" CssClass="form-control" data-encrypt="Y" placeholder="聯絡電話與手機號碼請至少填寫一項" oninput="formatCellphone(this)"></asp:TextBox>
<asp:TextBox ID="cellphone" MaxLength="12" runat="server" CssClass="form-control" data-encrypt="Y" placeholder="聯絡電話與手機號碼請至少填寫一項"></asp:TextBox>
<asp:RegularExpressionValidator ControlToValidate="cellphone" Display="Dynamic" ErrorMessage="格式有誤" ID="RegularExpressionValidator2" runat="server" SetFocusOnError="true" ValidationExpression="^09\d{2}-?\d{3}-?\d{3}$" />
</div>
</div>
@@ -1578,12 +1597,12 @@
<asp:TextBox ID="demo" runat="server" Rows="5" TextMode="MultiLine" CssClass="form-control" placeholder="請輸入備註"></asp:TextBox>
</div>
</div>
<div class="row mb-1">
<%-- <div class="row mb-1">
<label class="col-form-label">自定義欄位預設值</label>
<div class="">
<asp:TextBox ID="customize_data" runat="server" Rows="3" TextMode="MultiLine" CssClass="form-control" placeholder="一行輸入一個定義欄位預設值,如:&#10;$姓名:王OO&#10;$聯絡電話:0987654321" ToolTip="一行輸入一個定義欄位預設值,格式: $變數名稱:值"></asp:TextBox>
</div>
</div>
</div>--%>
<div class="row mb-1">
<asp:Panel ID="timePanel1" runat="server" CssClass="col-12 text-right text-primary" Visible="false">建檔時間:<asp:Literal ID="reg_time" runat="server"></asp:Literal></asp:Panel>
<asp:Panel ID="timePanel2" runat="server" CssClass="col-12 text-right text-primary" Visible="false">最後修改時間:<asp:Literal ID="modify_time" runat="server"></asp:Literal></asp:Panel>
@@ -2155,4 +2174,5 @@
</template>
</v-snackbar>
</div>
</asp:Content>
+94 -23
View File
@@ -1,34 +1,44 @@
using System;
using Model;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Entity;
using System.Data.OleDb;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Globalization;
using Model;
public partial class admin_follower_reg : MyWeb.config
{
private Model.ezEntities _db = new Model.ezEntities();
public ArrayList _tmp = new ArrayList();
public bool isDataChanged = false;
public bool isAutoNumbering = ConfigurationManager.AppSettings["IsAutoNumbering"].ToString() == "true" ? true : false;
protected void Page_Load(object sender, EventArgs e)
{
CallAjax();
if (!IsPostBack)
{
{
InitEnumsOptions<Model.follower.type>(identity_type); //身分別
//var qry = _db.followers.AsEnumerable();
var qry = _db.followers.AsQueryable();
var qry = _db.followers.AsQueryable();
if (isStrNull(Request["num"]))
{
if (!isAutoNumbering)
{
f_number.ReadOnly = false;
}
if (!isStrNull(Request["leader"]))
{
int _num = Val(Request["leader"]);
@@ -50,9 +60,14 @@ public partial class admin_follower_reg : MyWeb.config
//預設國籍
country.Value = "158";
country_txt.Value = "中華民國(台灣)";
// 預設加入日期
join_date.Text = DateTime.Now.ToString("yyyy-MM-dd");
}
else
{
f_number.ReadOnly = true;
int _num = Val(Request["num"]);
var prod = qry.Where(q => q.num == _num).FirstOrDefault();
if (prod != null)
@@ -206,8 +221,11 @@ public partial class admin_follower_reg : MyWeb.config
}
}
}
// 使用新的 generate_f_number 方法,已內建重號檢查和重試機制
followers.f_number = follower.generate_f_number(sex.SelectedValue);
if (isAutoNumbering)
{
// 使用新的 generate_f_number 方法,已內建重號檢查和重試機制
followers.f_number = follower.generate_f_number(sex.SelectedValue);
}
followers.identity_type = Val(identity_type.SelectedValue);
if(!isStrNull(leader.Value)) followers.leader = Val(leader.Value);
if (!isStrNull(country.Value)) followers.country = country.Value;
@@ -226,8 +244,21 @@ public partial class admin_follower_reg : MyWeb.config
_db.followers.Add(followers);
_db.SaveChanges();
}
int _id = followers.num;
//應該要自動將此人設為第一個親友資料,不然牌位燈打時又要再做一次
family_members fm=new family_members();
fm.follower_num = _id;
fm.fam_name = followers.u_name;
fm.fam_gender=followers.sex;
fm.deceased = false;
fm.birthdate = followers.birthday;
_db.family_members.Add(fm);
_db.SaveChanges();
if (_id > 0)
{
// 如果啟用 search_keywords 功能,生成並更新 search_keywords
@@ -239,7 +270,9 @@ public partial class admin_follower_reg : MyWeb.config
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Follower, (int)Model.admin_log.Status.Insert, f_number.Text + u_name.Text);
Session["LastAddedID"] = followers.f_number;
Response.Redirect("index.aspx");
}
else
@@ -282,7 +315,7 @@ public partial class admin_follower_reg : MyWeb.config
try
{
foreach (Control obj in cardBodyPanel.Controls)
{
{
if (obj is TextBox)
{
var ObjValue = followers.GetType().GetProperty(obj.ID);
@@ -300,7 +333,7 @@ public partial class admin_follower_reg : MyWeb.config
ObjValue.SetValue(followers, null);
}
}
@@ -310,22 +343,60 @@ public partial class admin_follower_reg : MyWeb.config
followers.sex = sex.SelectedValue;
followers.blood = blood.SelectedValue;
followers.tab = tab.Value.Trim(',');
followers.admin_log = admin.info.u_id + " " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
//followers.admin_log = admin.info.u_id + " " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
followers.follower_hash = encrypt.followerHash(followers.phone, followers.id_code);
// 如果啟用 search_keywords 功能,生成並更新 search_keywords
if (GlobalVariables.UseSearchKeywords)
{
followers.search_keywords = encrypt.GenerateSearchKeywords(followers);
}
_db.SaveChanges();
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Follower, (int)Model.admin_log.Status.Update, f_number.Text + u_name.Text);
// 檢查是否有修改資料
var entry = _db.Entry(followers);
this.isDataChanged = entry.CurrentValues.PropertyNames.Any(name =>
{
if (name == "admin_log" || name == "follower_hash")
return false;
Response.Redirect("index.aspx?page=" + Convert.ToString(Request["page"]));
var originalVal = entry.OriginalValues[name]?.ToString();
var currentVal = entry.CurrentValues[name]?.ToString();
// 針對加密欄位進行特殊處理
bool isEncryptedField = (name == "phone" || name == "id_code");
if (isEncryptedField)
{
string originalPlain = !string.IsNullOrEmpty(originalVal) ? encrypt.DecryptAutoKey(originalVal) : "";
string currentPlain = !string.IsNullOrEmpty(currentVal) ? encrypt.DecryptAutoKey(currentVal) : "";
return originalPlain.Trim() != currentPlain.Trim();
}
return !object.Equals(originalVal, currentVal);
});
if (this.isDataChanged)
{
followers.admin_log = admin.info.u_id + " " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
}
else
{
entry.State = EntityState.Unchanged;
}
int isDataSaved = _db.SaveChanges();
if (isDataSaved > 0)
{
//L_msg.Type = alert_type.success;
//L_msg.Text = "修改成功";
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Follower, (int)Model.admin_log.Status.Update, f_number.Text + u_name.Text);
Response.Redirect("index.aspx?dirty=1&page=" + Convert.ToString(Request["page"]));
}
else
{
Response.Redirect("index.aspx?page=" + Convert.ToString(Request["page"]));
}
}
catch (Exception ex)
{
@@ -339,7 +410,7 @@ public partial class admin_follower_reg : MyWeb.config
L_msg.Type = alert_type.danger;
L_msg.Text = "查無資料";
}
/*
/*
if (chk_pro_num(f_number.Text, Val(Request["num"])))
{
}
@@ -349,7 +420,7 @@ public partial class admin_follower_reg : MyWeb.config
L_msg.Text = "信眾編號重複";
}
*/
}
}
}
#endregion
+32 -1
View File
@@ -85,7 +85,7 @@
<label for="u_gauth"><i class="mdi mdi-key-variant"></i> Google Authenticator 驗證碼</label>
</div>
<div class="mt-4 mb-0 d-grid gap-2">
<asp:LinkButton ID="Button1" runat="server" OnClick="Button1_Click"
<asp:LinkButton ID="Button1" runat="server" OnClick="Button1_Click"
CssClass="btn btn-primary rounded-pill">
<i class="mdi mdi-login"></i> 登入</asp:LinkButton>
<asp:LinkButton ID="DesignModeButton" runat="server" OnClick="DesignModeButton_Click"
@@ -105,5 +105,36 @@
</div>
</div>
<!-- /.content_box -->
<script>
document.addEventListener('DOMContentLoaded', function () {
var accountInput = document.getElementById('<%= u_id.ClientID %>');
var passwordInput = document.getElementById('<%= u_password.ClientID %>');
var chkInput = document.getElementById('<%= chknum.ClientID %>');
var btn = document.getElementById('<%= Button1.ClientID %>');
accountInput.addEventListener('keypress', function (e){
if (e.keyCode === 13) {
$("#<%= u_password.ClientID %>").focus();
return false;
}
})
passwordInput.addEventListener('keypress', function (e) {
if (e.keyCode === 13) {
$("#<%= chknum.ClientID %>").focus();
return false;
}
})
var triggerLogin = function (e) {
if (e.keyCode === 13) {
btn.click();
e.preventDefault();
return false;
}
};
if (chkInput) chkInput.addEventListener('keypress', triggerLogin);
});
</script>
</asp:Content>
+31 -15
View File
@@ -93,14 +93,14 @@
</select>
<label for="printSize" style="color: black">列印尺寸</label>
</div>
<div class="form-floating mb-3">
<%--<div class="form-floating mb-3">
<select class="form-select form-select-sm mb-2 " onchange="Designer.changePrintMode()" id="printMode">
<option value="">請選擇</option>
<option value="combine">合併</option>
<option value="origin">原尺寸</option>
</select>
<label for="printMode" style="color: black">列印模式</label>
</div>
</div>--%>
<%-- <div class="form-floating mb-3">
<input type="number" id="rosterLimit" onchange="Designer.changeRosterLimit()" class="form-control form-control-sm mb-2">
<label class="small" for="rosterLimit" style="color: black;">正名上限</label>
@@ -114,7 +114,7 @@
<span class="btn btn-sm btn-outline-info w-100 mb-2" onclick="Designer.saveStyle()">存檔</span>
</div>
<div class="col-6">
<span class="btn btn-sm btn-outline-info w-100 mb-2" onclick="Designer.print()">預覽列印</span>
<%--<span class="btn btn-sm btn-outline-info w-100 mb-2" onclick="Designer.print()">預覽列印</span>--%>
</div>
</div>
</div>
@@ -237,10 +237,10 @@
allSize: [
],
bg: [
{ name: "1", path: "../../admin/print/html/tablet-1_new.svg" },
{ name: "2", path: "../../admin/print/html/tablet-1B_new.svg" },
{ name: "1", path: "../../admin/print/html/tablet-2.svg" },
{ name: "2", path: "../../admin/print/html/tablet-2B.svg" }
{ name: "超薦1", path: "../../admin/print/html/tablet-1_new.svg" },
{ name: "超薦2", path: "../../admin/print/html/tablet-1B_new.svg" },
{ name: "消災1", path: "../../admin/print/html/tablet-2.svg" },
{ name: "消災2", path: "../../admin/print/html/tablet-2B.svg" }
],
allStyle: [
],
@@ -603,7 +603,8 @@
if (paperSize == "newsize") {
$("#sizeDiv").attr("style", "display:");
} else {
// console.log("paperSize:", paperSize);
// console.log("paperSize:", paperSize);
$("#sizeDiv").attr("style", "display:none;");
let size = this.allSize.find(x => x.id == paperSize)
$(".tablet-paper").css({
"background-color": "white",
@@ -641,6 +642,7 @@
bindEvents() {
// 文字變更連動渲染
//$('#rosterLimit').on('input', (e) =>this.)
console.log("OOQ");
$('#inp-text').on('input', (e) => this.updateActive('text', $(e.target).val()));
$('#inp-size').on('input', (e) => this.updateActive('fontSize', $(e.target).val()));
$('#width').on('input', (e) => this.updateActive('width', $(e.target).val()))
@@ -671,12 +673,12 @@
render() {
$(".tablet-paper").empty();
//const $canvas = $('#canvas').empty();
const roster = this.elements.find(e => e.type === 'roster');
const names = roster ? roster.text.split('\n').filter(s => s.trim()) : [];
const pages = Math.max(1, Math.ceil(names.length / this.rosterLimit));
//const roster = this.elements.find(e => e.type === 'roster');
let style = this.allStyle.find(x => x.styleID == this.styleID);
let size = this.allSize.find(x => x.id == style.paperSize);
console.log(this.paper, size);
//console.log(this.paper, size);
//for (let p = 0; p < pages; p++) {
// const $paper = $('<div class="tablet-paper"></div>')
// .css({ "background-color": "red", width: this.paper.width + 'mm', height: this.paper.height + 'mm' });
@@ -687,9 +689,14 @@
position: "absolute"
});
//const slice = names.slice(p * this.rosterLimit, (p + 1) * this.rosterLimit);
const slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit);
//const roster = this.elements.find(e => e.id === this.styleID);
let self = this;
this.elements.forEach(el => {
$paper.append(this.createEl(el, slice));
const names = el.text.split('\n').filter(s => s.trim());
const pages = Math.max(1, Math.ceil(names.length / self.rosterLimit));
const slice = names.slice(0 * self.rosterLimit, (0 + 1) * self.rosterLimit);
$paper.append(self.createEl(el, slice));
});
//$canvas.append($paper);
//}
@@ -722,6 +729,7 @@
html = this.renderNameList(slice, el);
}
else if (el.id === 'alive') {
console.log("createEl:",slice,el)
html = this.renderLiveList(slice, el);
}
else {
@@ -772,6 +780,8 @@
});
},
renderLiveList(names, el) {
console.log("renderLiveList:",names);
//$("#imp-text").updateActive("text",names)
let $namelist = $(`<div class='liveList'></div>`).css({
"writing-mode": "vertical rl",
display: "flex",
@@ -795,6 +805,8 @@
return $namelist;
},
renderLiveSpan(name, el) {
console.log("renderLiveSpan:", name, el
);
return $(`<span>${name}</span>`).css({
display: "block",
"min-height": `${el.textHeight}px`,
@@ -869,8 +881,11 @@
},
select(id) {
console.log("QQ:",id);
this.activeId = id;
console.log("QQ:",this.elements);
const el = this.elements.find(x => x.id === id);
const el1 = this.elements.find(x => x.id === "titletriangle");
$('#attr-box').removeClass('d-none');
$('#inp-text').val(el.text);
$('#inp-size').val(el.style.fontSize);
@@ -889,6 +904,7 @@
},
updateActive(key, val) {
console.log("updateActive:",key, val)
const el = this.elements.find(x => x.id === this.activeId);
if (key === 'fontSize') el.style.fontSize = parseFloat(val);
@@ -1049,7 +1065,7 @@
let master = {
styleID: this.styleID, styleName: $("#styleName").val(), paperSize: $("#paperSize").val(),
backendImg: $("#backendInp").val(), printSize: $("#printSize").val(), printMode: $("#printMode").val(),
orientation: $("#paperOrientation").val(), printPageCount: $("#perpage").val(), rosterLimit: $("#rosterLimit").val(),
orientation: $("#paperOrientation").val(), printPageCount: $("#perpage").val(), rosterLimit: 8,
detail: detail
}
console.log(master);
+1 -1
View File
@@ -8,7 +8,7 @@
<ul class="collapsible_item">
<asp:Repeater ID="Repeater2" runat="server" OnItemDataBound="Repeater2_ItemDataBound">
<ItemTemplate>
<li><a class="menu" href="<%#ResolveUrl(ValString(Eval("url"))) %>"
<li><a class="menu" href="<%#ResolveUrl(ValString(Eval("url"))) %>"
target="<%#(ValString(Eval("target"))=="B"?"_blank":"_self") %>"><%#Eval("title") %></a></li>
</ItemTemplate>
</asp:Repeater>
+288
View File
@@ -0,0 +1,288 @@
<template>
<div class="container-fluid">
<v-expansion-panels accordion>
<v-expansion-panel v-for="(item,i) in 1"
:key="i">
<v-expansion-panel-header>查詢條件</v-expansion-panel-header>
<v-expansion-panel-content>
<v-row>
<!-- <v-col cols="2" md="2">-->
<!--<v-text-field label="法會分類">
</v-text-field>-->
<!--</v-col>-->
<v-col cols="2" md="2">
<v-text-field label="法會名稱" v-model="search.subject">
</v-text-field>
</v-col>
<v-col cols="2" md="2">
<!--<v-text-field label="開始日期" v-model="search.uptime1">
</v-text-field>-->
<v-menu v-model="startmenu"
:close-on-content-click="false"
:nudge-right="40"
transition="scale-transition"
offset-y
min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="search.up_time1"
label="法會日期"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"></v-text-field>
</template>
<v-date-picker v-model="search.up_time1"
@input="startmenu = false"></v-date-picker>
</v-menu>
</v-col>
<v-col cols="2" md="2">
<v-btn @click.prevent="getDefault()">查詢</v-btn>
<v-btn @click.prevent="clearSearch()">清空條件</v-btn>
</v-col>
</v-row>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<v-data-table v-model="data_table.selected"
:items="data_table.list"
item-key="order_no"
:options.sync="options"
:headers="data_table.header"
:footer-props="data_table.footer"
:server-items-length="data_table.count"
:loading="data_table.loading"
:single-select="data_table.singleSelect"
hide-default-footer
:page.sync="data_table.page"
:items-per-page.sync="data_table.pageSize"
class="elevation-1">
<template #item.up_time="{ item }">
{{ item.up_time|timeString('YYYY/MM/DD') }}
</template>
<template #item.u_name="{ item }">
<a v-if="item.f_num != null && item.activity_num != null" @click="checkInMsg(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-account-check"></i>報到</a>
{{ item.u_name }}
</template>
<template #item.slot_btn="{ item }">
<a @click="gotoSignup(item)" class="btn btn-outline-secondary btn-sm">報名</a>
<a @click="gotoBatchSignup(item)" class="btn btn-outline-secondary btn-sm">批次報名</a>
<a @click="gotoSignupList(item)" class="btn btn-outline-secondary btn-sm">明細</a>
</template>
</v-data-table>
<v-container class="container-fluid">
<v-row class="align-baseline" wrap>
<v-col cols="12" md="9">
<v-pagination v-model="data_table.page"
:length="pageCount">
</v-pagination>
</v-col>
<v-col class="text-truncate text-right" cols="12" md="2">
{{ data_table.count }} , 頁數:
</v-col>
<v-col cols="6" md="1">
<v-text-field v-model="data_table.page"
type="number"
hide-details
dense
min="1"
:max="pageCount"
@input="data_table.page = parseInt($event, 10)"></v-text-field>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
module.exports = {
props: ['formData'],
data() {
return {
this_act:'',
options: { multiSort: false },
search_options: { multiSort: false },
startmenu: false,
endmenu: false,
itemKindList:[],
data_table: {
loading: true,
list: [],
selected: [],
singleSelect: false,
count: 0,
page: 1,
pageSize: 10,
header: [
{ text: '活動分類', value: 'kindsTxt' },
{ text: '活動名稱', value: 'subject' },
{ text: '開始日期', value: 'startDate_solar' },
{ text: '結束日期', value: 'endDate_solar', align: 'start' },
{ text: '報名人數', value: 'orderCounts' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' }
],
footer: {
showFirstLastPage: true,
itemsPerPageOptions: [5, 10, 20, 30],
},
},
select_act_list: [],
select_items: {
month: [{
text: "請選擇",
val: 0
},],
season: [{
text: "請選擇",
val: 0
},],
},
search: {
keyin1: '',
order_no: '',
subject: '',
u_name: '',
up_time1: '',
up_time2: '',
actItemTxt: '',
introducerTxt: '',
activity_num: '',
country: '',
country2: '',
hasPrice: '',
},
keyin1_items: [//
//{
//text: "",
//val: 0
//},
],
};
},
mounted() {
const navEntries = performance.getEntriesByType("navigation");
const isReload = navEntries.length > 0 && navEntries[0].type === "reload";
if (isReload) {
sessionStorage.removeItem("orderpage");
}
else {
const savedPage = parseInt(sessionStorage.getItem('orderpage'));
if (savedPage) {
this.options.page = savedPage;
}
}
this.initKindList()
},
watch: {
options: {
handler() {
this.getDefault()
},
deep: true,
},
search_options: {
handler() {
this.search_get()
},
deep: true,
},
},
methods: {
initKindList() {
axios
//.get(HTTP_HOST + 'api/activity_kind')
.get(HTTP_HOST + 'api/actItem_kind/GetAll')
.then(response => {
this.itemKindList = response.data
})
.catch(error => console.log(error))
},
clearSearch() {
this.search.subject = ""
this.search.up_time1=""
},
gotoBatchSignup(item) {
this.$emit('custom-event', { action: 'batchSign', item: item });
},
gotoSignup(item) {
this.$emit('custom-event', { action: 'add', item: item });
},
gotoSignupList(item) {
this.$emit('custom-event', { action:'detail', item:item });
},
getDetail(clearpage = false) {
//console.log("test");
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: sortBy[0], sortDesc: sortDesc[0],
page: clearpage ? '1' : page, pageSize: itemsPerPage
};
this.detail_table.loading = true
sessionStorage.setItem('orderpage', clearpage ? '1' : page);
axios
//.post(HTTP_HOST + 'api/order/GetList', this.search, { params: params })
.post(HTTP_HOST + 'api/order/GetList', this.search, { params: params })
.then(response => {
this.detail_table.list = response.data.list
this.detail_table.count = response.data.count;
this.detail_table.loading = false
})
.catch(error => console.log(error))
},
getDefault(clearpage = false) {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: sortBy[0], sortDesc: sortDesc[0],
page: clearpage ? '1' : page, pageSize: itemsPerPage
};
this.data_table.loading = true
sessionStorage.setItem('orderpage', clearpage ? '1' : page);
axios
//.post(HTTP_HOST + 'api/order/GetList', this.search, { params: params })
.post(HTTP_HOST + 'api/activity/GetListNew', this.search, { params: params })
.then(response => {
this.data_table.list = response.data.list
this.data_table.count = response.data.count;
this.data_table.loading = false
})
.catch(error => console.log(error))
},
editItem(item) {
console.log("edit", item);
},
btn_search() {
this.this_act = '';
this.search.activity_num = '';
this.getDefault(true)
},
btn_all() {
this.this_act = '';
this.search.activity_num = '';
clearObjProps(this.search);
this.btn_search()
},
initActivity() {
axios.get(HTTP_HOST + 'api/activity')
.then(response => {
this.select_act_list = response.data
})
.catch(error => {
console.log(error)
})
}
},
computed: {
pageCount() {
return Math.ceil(this.data_table.count / this.data_table.pageSize)
},
}
};
</script>
+191
View File
@@ -0,0 +1,191 @@
<template>
<div class="container-fluid">
<v-card>
<v-card-title class="bg-primary white--text text-center">
<v-expansion-panels accordion>
<v-expansion-panel v-for="(item,i) in 1"
:key="i">
<v-expansion-panel-header color="#006fc9"
class="white--text">
<v-row>
<v-col cols="10" md="10">
<label>查詢條件</label>
</v-col>
<v-col cols="2" md="2">
<v-btn @click.prevent='fast_signup' class="ms-auto">快速報名</v-btn>
<v-btn @click.prevent="back01()" class="ms-auto">返回</v-btn>
</v-col>
</v-row>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-row>
<v-col cols="2" md="2">
<v-text-field label="姓名" v-model="search.u_name">
</v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field label="電話" v-model="search.phone">
</v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field label="行動電話" v-model="search.cellphone">
</v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field label="身分證號" v-model="search.id_code">
</v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-btn @click.prevent="getDetail()">查詢</v-btn>
<v-btn @click.prevent="clearSearch()">清空條件</v-btn>
</v-col>
</v-row>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</v-card-title>
<v-card-text>
<v-data-table v-model="detail_table.selected"
:items="detail_table.list"
:search-props="search"
item-key="order_no"
:options.sync="options"
:headers="detail_table.header"
:footer-props="detail_table.footer"
:server-items-length="detail_table.count"
:loading="detail_table.loading"
:single-select="detail_table.singleSelect"
hide-default-footer
:page.sync="detail_table.page"
:items-per-page.sync="detail_table.pageSize"
class="elevation-1">
<template #item.slot_btn="{ item }">
<!--<a :href="'reg.aspx?order_no='+item.order_no" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-pencil-box-outline"></i>修改</a>
<a @click="$root.currentView='step-three';$root.selected_order=item.order_no" class="btn btn-outline-secondary btn-sm">明細</a>
<a @click="deleteItem(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-trash-can"></i>刪除</a>-->
</template>
</v-data-table>
<v-container>
<v-row class="align-baseline" wrap>
<v-col cols="12" md="9">
<v-pagination v-model="detail_table.page"
:length="pageCount">
</v-pagination>
</v-col>
<v-col class="text-truncate text-right" cols="12" md="2">
{{ detail_table.count }} , 頁數:
</v-col>
<v-col cols="6" md="1">
<v-text-field v-model="detail_table.page"
type="number"
hide-details
dense
min="1"
:max="pageCount"
@input="detail_table.page = parseInt($event, 10)"></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
</div>
</template>
<script>
module.exports = {
props: {
num: {
type: Number }
},
activated() {
this.getDetail();
},
mounted() {
this.getDetail();
},
data() {
return {
options: { multiSort: false },
search_options: { multiSort: false },
detail_table: {
loading: true,
list: [],
selected: [],
singleSelect: false,
count: 0,
page: 1,
pageSize: 10,
header: [
{ text: '單號', value: 'order_no' },
{ text: '姓名', value: 'u_name' },
{ text: '報名日期', value: 'up_time' },
{ text: '單據狀態', value: 'keyin1_txt', align: 'start' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' }
],
footer: {
showFirstLastPage: true,
itemsPerPageOptions: [5, 10, 20, 30],
},
},
search: {
u_name: '',
phone: '',
cellphone: '',
id_code:''
}
};
},
watch: {
options: {
handler() {
this.getDetail()
},
deep: true,
},
},
methods: {
back01() {
this.$emit('custom-event', { action: 'signup1', item: this.num });
},
fast_signup() {
this.$emit('custom-event', { action: 'add', item: this.num });
},
clearSearch() {
},
getDetail(clearpage=false) {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: sortBy[0], sortDesc: sortDesc[0],
page: clearpage ? '1' : page, pageSize: itemsPerPage
};
let newsearch = {
activity_num: this.num, f_user: {
u_name: this.search.u_name, phone: this.search.phone,
cellphone: this.search.cellphone, id_code: this.search.id_code
}
}
this.detail_table.loading = true
sessionStorage.setItem('orderpage', '1');// clearpage ? '1' : page
axios
.post(HTTP_HOST + 'api/order/GetList', newsearch, { params: params })
.then(response => {
this.detail_table.list = response.data.list
this.detail_table.count = response.data.count;
this.detail_table.loading = false
})
.catch(error => console.log(error))
},
},
computed: {
pageCount() {
return Math.ceil(this.detail_table.count / this.detail_table.pageSize)
},
}
}
</script>
+495
View File
@@ -0,0 +1,495 @@
<template>
<div class="container-fluid">
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h5 class="mb-0">快速報名</h5>
<div class="ms-auto">
<v-btn @click.prevent="saveOrder()" v-if="follower.f_number!=undefined&&follower.f_number!=''">儲存</v-btn>
<v-btn @click.prevent="back01()">返回</v-btn>
</div>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" md="2">
<v-text-field v-model="name"
:counter="20"
label="姓名"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="idNo"
:counter="20"
label="身分證號"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="phoneNum"
:counter="20"
label="行動電話/市話"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-btn @click.prevent="getFollower">查詢</v-btn>
</v-col>
</v-row>
<v-divider></v-divider>
<v-row v-if="follower.f_number!=undefined&&follower.f_number!=''">
<v-col cols="12" md="2">
<v-text-field v-model="follower.f_number" readonly
label="信眾編號"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="follower.u_name" readonly
label="信眾姓名"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="follower.phoneDes" readonly
label="電話"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="follower.cellphoneDes" readonly
label="行動電話"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="follower.id_code" readonly
label="身分證號"></v-text-field>
</v-col>
<v-col cols="12" md="2">
<v-text-field v-model="follower.passport" readonly
label="護照號碼"></v-text-field>
</v-col>
</v-row>
<v-row v-if="follower.f_number!=undefined&&follower.f_number!=''">
<v-col cols="12" md="12">
<v-text-field v-model="follower.address" readonly
label="地址"></v-text-field>
</v-col>
</v-row>
<hr class="hr-text" data-content="前次法會功德" v-if="follower.f_number!=undefined&&follower.f_number!=''">
<v-row v-show="tablet_show">
<v-select :items="orders"
label="選擇法會"
item-text="subject" @change="changeOrderDetail()"
item-value="order_no" v-model="order_no">
</v-select>
</v-row>
<v-row v-show="tablet_show">
<v-col cols="12" sm="12">
<v-data-iterator :items="tablet_list" :items-per-page="-1"
hide-default-footer>
<template v-slot:default="{ items }">
<v-row>
<v-col cols="4" md="4" v-for="(item, i) in items" :key="i">
<v-card-title>
<v-row>
<v-col col="12" md="12">
<v-checkbox v-model="item.actitem_num_checked"
:disabled="item.disabled" class="custom-checkbox">
<template v-slot:label>
<h5 :class="renderDisabled(item)">{{ item.actitem_num_selected.text }}</h5>
</template>
</v-checkbox>
</v-col>
</v-row>
<v-row>
<v-col cols="3" md="3">
<v-btn class="ms-auto" @click.prevent="copytablet(item)">複製為</v-btn>
</v-col>
<v-col cols="9" md="9">
<v-select :items="act_items"
label="選擇功德項目"
item-text="act_item_selected.text"
item-value="act_item_selected.val" v-model="item.changeActitem">
</v-select>
</v-col>
</v-row>
</v-card-title>
<v-card-text>
<div v-html="renderTabletContent(item)" style="font-size:20px;"></div>
<v-divider></v-divider>
</v-card-text>
</v-col>
</v-row>
</template>
<template v-slot:no-data>
<v-alert :value="value" color="error" icon="warning">
無資料可顯示可能此信眾未曾參加過任何法會
</v-alert>
</template>
</v-data-iterator>
</v-col>
</v-row>
</v-card-text>
</v-card>
<v-dialog v-model="message_dialog.show" class="dialog_width">
<template>
<v-card>
<v-card-title>
{{ message_dialog.message }}
</v-card-title>
<v-card-text>
<v-btn @click.prevent="message_dialog.show=false" v-if="!message_dialog.show_confirm">關閉</v-btn>
<v-btn @click.prevent="success_confirm()" v-if="message_dialog.show_confirm">確認</v-btn>
</v-card-text>
</v-card>
</template>
</v-dialog>
<v-dialog v-model="none_dialog.show" class="dialog_width">
<template>
<v-card>
<v-card-title>
{{ none_dialog.message }}
</v-card-title>
<v-card-text>
<v-btn @click.prevent="none_dialog.show=false">關閉</v-btn>
<v-btn @click.prevent="gotoOldSign()">前往傳統報名</v-btn>
</v-card-text>
</v-card>
</template>
</v-dialog>
<v-dialog v-model="transfer_dialog.show" class="dialog_width">
<template>
<v-card>
<v-card-title>
{{ transfer_dialog.message }}
</v-card-title>
<v-card-text>
<v-btn @click.prevent="transfer_dialog.show=false">關閉</v-btn>
<v-btn @click.prevent="gotoSignUp4()">報名並將資料回寫信眾資料</v-btn>
</v-card-text>
</v-card>
</template>
</v-dialog>
</div>
</template>
<script>
module.exports = {
props: {
num: {
type: Number // num
}
},
data() {
return {
transfer_dialog: { show: false, message: "" },
message_dialog: { show: false, show_confirm: false, message: "" },
none_dialog: { show: false, message: "" },
idNo: "",
phoneNum: "",
name: "",
follower: {},
orders: [],
tablet_list: [],
act_items: [],
search: {},
value:false,
query: {
id_code: { type: String }, cellphone: { type: String }
},
search_options: { multiSort: false },
tablet_show: false,
activityes: [],
order_no: "",
copy_item: {}
}
},
activated() {
this.tablet_show = false
this.value=true
//this.getFollower()
//
this.follower = {}
this.orders = []
this.tablet_list = []
this.idNo = ""
this.phoneNum = ""
this.name=""
this.getActItem()
},
methods: {
gotoOldSign() {
this.$emit('custom-event', { action: 'oldSign', item: this.num });
},
back01() {
this.$emit('custom-event', { action: 'signup1', item: this.num });
},
gotoSignUp4() {
this.$emit('custom-event', { action: 'signup4', item: this.num });
},
renderDisabled(item) {
if (item.disabled) {
return "disabled-checkbox"
} else {
return "enabled-checkbox"
}
},
copytablet(item) {
let t = this.act_items.find(x => x.act_item_selected.val == item.changeActitem)
let newTablet = structuredClone(item)
newTablet.id = this.tablet_list.length + 1
//console.log("copytablet:",newTablet.nu, newTablet.actitem_num_selected, item.changeActitem);
newTablet.actitem_num_selected.val = t.act_item_selected.val
newTablet.actitem_num_selected.text = t.act_item_selected.text
newTablet.changeActitem = item.changeActitem
newTablet.disabled=false
this.tablet_list.push(newTablet)
},
getActItem() {
this.act_items.length = 0
this.search = { num: this.num }
let params = { sortBy: 'num', sortDesc: false, page: 1, pageSize: -1 };
axios
.post(HTTP_HOST + 'api/activity/GetActRelaList', this.search, { params: params })
.then(response => {
if (response.data.list) {
this.act_items = response.data.list
//if (this.act_items && this.act_items.length > 0) {
// this.act_items = this.act_items.map(x => ({
// ...x,selected:""
// }))
//}
//console.log("QOO:",this.act_items)
}
else
console.log("查無資料");
})
.catch(error => console.log(error))
},
getFollower() {
this.tablet_show = false
this.tablet_list.length = 0
this.follower = {}
this.search = { id_code: this.idNo, u_name: this.name, phone_idcode: this.phoneNum }
//
axios
.post(HTTP_HOST + 'api/follower/GetFollowerNew', this.search)
.then(response => {
if (response.data.list) {
//console.log(response.data.list)
if (response.data.list.length > 1) {
//alert("")
this.message_dialog.show = true
this.message_dialog.message = "相同條件不只一人,請增加查詢條件"
} else if (response.data.list.length==0) {
this.transfer_dialog.show = true
this.transfer_dialog.message = "查無信眾資料"
} else {
this.tablet_show = true
this.follower = response.data.list[0]
this.getOrder()
}
}
else {
this.message_dialog.show = true
this.message_dialog.message = response.message
}
//alter(response.message);
// console.log("");
})
.catch(error => console.log(error))
},
getOrder() {
let params = { page: 1, pageSize: 10, sortBy: 'reg_time', sortDesc: true };
this.search = { f_num: this.follower.num }
axios
.post(HTTP_HOST + 'api/order/GetList', this.search, { params: params })
.then(response => {
if (response.data.list && response.data.list.length > 0) {
this.orders = response.data.list
//
let o = this.orders.find(x => x.activity_num === this.num)
if (o != null && o != undefined) {
this.message_dialog.show = true
this.message_dialog.message = "此人已有報名資料,請勿重複報名"
return
}
this.orders = this.orders.filter(x => x.activity_num !== this.num)
//console.log("orders",this.orders)
if (this.orders && this.orders.length > 0) {
this.getOrderDetail()
}
}
else {
this.none_dialog.show = true
this.none_dialog.message = "尚未參加任何法會活動"
console.log("getOrder 查無資料");
}
})
.catch(error => console.log(error))
},
changeOrderDetail() {
this.tablet_show = true
this.tablet_list.length = 0
let params = { sortBy: 'num', sortDesc: true, page: 1, pageSize: -1 };
let search1 = { order_no: this.order_no }
axios
.post(HTTP_HOST + 'api/order/GetItemList', search1, { params: params })
.then(response => {
if (response.data.list) {
this.tablet_list = response.data.list
this.tablet_list.forEach(x => {
let act_item = this.act_items.filter(y => y.act_item_selected.val === x.actitem_num_selected.val)
if (act_item && act_item.length > 0) {
x.disabled = false
} else {
x.disabled = true
}
x["changeActitem"] = ""
})
}
else
console.log("changeOrderDetail 查無資料");
})
.catch(error => console.log(error))
},
getOrderDetail() {
this.tablet_show = true
let params = { sortBy: 'num', sortDesc: true, page: 1, pageSize: -1 };
let search1 = { order_no: this.orders[0].order_no }
axios
.post(HTTP_HOST + 'api/order/GetItemList', search1, { params: params })
.then(response => {
if (response.data.list) {
this.tablet_list = response.data.list
this.tablet_list.forEach(x => {
let act_item = this.act_items.filter(y => y.act_item_selected.val === x.actitem_num_selected.val)
if (act_item && act_item.length > 0) {
x.disabled = false
} else {
x.disabled = true
}
x["changeActitem"]=""
})
}
else
console.log("getOrderDetail 查無資料");
})
.catch(error => console.log(error))
},
renderTabletContent(item) {
let data = JSON.parse(item.f_num_tablet)
let left = data.left_items
let mid = data.mid_items
let mid_right = data.mid_right
let mid_left=data.mid_left
let returnVal = ""
if (left) {
returnVal = "陽上:" + left.map(x => x.fam_name).join("、")
}
if (mid) {
returnVal = returnVal + "<br>牌位正名:" + mid.map(x => x.fam_name).join("、")
}
if (mid_right) {
returnVal = returnVal + "<br>右正名:" + mid_right.map(x => x.fam_name).join("、")
}
if (mid_left) {
returnVal = returnVal + "<br>左正名:" + mid_left.map(x => x.fam_name).join("、")
}
return returnVal //item.actitem_num_selected.val+":" +
},
success_confirm() {
this.message_dialog.show = false
this.message_dialog.show_confirm=false
this.follower = {}
this.tablet_list.length = 0
this.tablet_show=false
},
saveOrder() {
let detail = []
////
this.tablet_list.forEach(x => {
if (x.actitem_num_checked && x.actitem_num_checked == true) {
//console.log("OOQ:", x.num)
let actItem = this.act_items.find(y => y.act_item_selected.val == x.actitem_num_selected.val)
detail.push({
num: x.num, order_no: x.order_no, change_item_num: x.changeActitem,
price: actItem.price, qty: 1, style: actItem.act_item_selected.style, printed_files: actItem.files[0].num
})
// console.log("saveOrder:", x, x.num, x.order_no)
}
})
//
let main = {
activity_num: this.num, f_num: this.follower.num,
details: detail
}
//console.log("saveOrderDetail:",detail);
axios
.post(HTTP_HOST + 'api/order/SaveWithDetails', main)
.then(response => {
//console.log(":",response)
if (response.data.result && response.data.result === "Y") {
//console.log("shit");
}
//console.log(":", response)
this.message_dialog.show_confirm = true
this.message_dialog.show = true
this.message_dialog.message = response.data.message
}).catch(error => {
this.message_dialog.show_confirm = false
this.message_dialog.show = true
this.message_dialog.message =error
console.log(error)
});
}
}
}
</script>
<style>
.hr-text {
display: flex;
align-items: center;
border: 0;
}
.hr-text::before,
.hr-text::after {
content: "";
flex: 1;
border-top: 1px solid #ccc; /* 分隔線樣式 */
margin: 0 10px;
}
.hr-text::before {
content: attr(data-content);
padding: 0 50px;
color: black;
font-size: 28px;
}
/* 当 checkbox 被禁用时,改变鼠标状态 */
input[type="checkbox"]:disabled {
cursor: not-allowed;
opacity: 0.4; /* 控制反灰程度 */
color: lightgray;
}
.disabled-checkbox {
cursor: not-allowed;
opacity: 0.8; /* 控制反灰程度 */
color: lightgray;
}
.enabled-checkbox {
}
.v-dialog{
width:500px !important;
}
</style>
+432
View File
@@ -0,0 +1,432 @@
<template>
<div class="container-fluid">
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h4>快速報名</h4>
<div class="ms-auto">
<v-btn @click.prevent="addOrderWithDetail()">儲存</v-btn>
<v-btn @click.prevent="back01()">返回</v-btn>
</div>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="2" md="2">
<v-text-field v-model="follower.u_name" :rules="requiredrules"
label="*姓名"></v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field v-model="follower.phone" :rules="phonerules"
label="*電話"></v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field v-model="follower.cellphone" :rules="cellrules"
label="*行動電話"></v-text-field>
</v-col>
<v-col cols="2" md="2">
e <v-select :items="countrys" :rules="requiredrules"
label="*國籍"
item-text="name_zh"
item-value="id" v-model="follower.country">
</v-select>
<!--<v-text-field v-model="follower.country" :rules="requiredrules"
label="*國籍"></v-text-field>-->
</v-col>
<v-col cols="2" md="2">
<v-text-field v-model="follower.id_code"
label="身分證號碼"></v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-text-field v-model="follower.passport"
label="旅行證件號碼"></v-text-field>
</v-col>
<v-col cols="2" md="2">
<v-select :items="sex_items" :rules="requiredrules"
label="*性別"
item-text="text"
item-value="val" v-model="follower.sex">
</v-select>
<!--<v-text-field v-model="follower.sex" :rules="[rules.required]"
label="*性別"></v-text-field>-->
</v-col>
<v-col cols="2" md="2">
<v-select :items="identity_items" :rules="requiredrules"
label="*身分別"
item-text="text"
item-value="val" v-model="follower.identity_type">
</v-select>
<!--<v-text-field v-model="follower.identity_type" :rules="[rules.required]"
label="*身分別"></v-text-field>-->
</v-col>
<v-col cols="2" md="2">
<v-menu v-model="menu"
:close-on-content-click="false"
:nudge-right="40"
transition="scale-transition"
offset-y
min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="follower.birthday"
label="生日"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"></v-text-field>
</template>
<v-date-picker v-model="follower.birthday"
@input="menu = false"></v-date-picker>
</v-menu>
<!--<v-date-picker v-model="follower.birthday"
label="生日"></v-date-picker>-->
<!--<v-text-field v-model="follower.birthday"
label="生日"></v-text-field>-->
</v-col>
<v-col cols="2" md="2">
<v-checkbox v-model="follower.send_receipt"
label="寄送收據"></v-checkbox>
</v-col>
<v-col cols="4" md="4">
<v-text-field v-model="follower.receipt_title"
label="收據抬頭"></v-text-field>
</v-col>
<v-col cols="12" md="12">
<v-text-field v-model="follower.address"
label="地址"></v-text-field>
</v-col>
</v-row>
<hr class="hr-text" data-content="功德項目">
<v-row>
<v-col cols="3" md="3">
<v-select :items="act_items"
label="功德項目"
item-text="act_item_selected.text"
item-value="act_item_selected.val" v-model="select_act_item">
</v-select>
</v-col>
<v-col cols="2" md="2">
<v-btn @click.prevent="newTablet()">新增</v-btn>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="12">
<v-expansion-panels>
<v-expansion-panel v-for="(item,i) in tablets"
:key="i">
<v-expansion-panel-header>
<h2>{{ item.actItem_subject }}</h2>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-container>
<v-row>
<v-col col="12" md="1">
標題
</v-col>
<v-col col="12" md="11">
<v-row>
<v-col>
<v-text-field v-model="title_new.name"
label="姓名"></v-text-field>
</v-col>
<v-col>
<v-select :items="live_select"
label="存/歿"
item-text="text"
item-value="val" v-model="title_new.islive">
</v-select>
<!--<v-text-field v-model="title_new.islive"
label="存/歿"></v-text-field>-->
</v-col>
<v-col>
<v-btn @click.prevent="addTitle(item)">加入</v-btn>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="12">
{{ renderContent( item.tablet.title) }}
</v-col>
</v-row>
</v-col>
</v-row>
<v-row v-if="checkType(item)">
<v-col col="12" md="1">
陽上
</v-col>
<v-col col="12" md="11">
<v-row>
<v-col>
<v-text-field v-model="alive_new.name"
label="姓名"></v-text-field>
</v-col>
<v-col>
<v-select :items="live_select"
label="存/歿"
item-text="text"
item-value="val" v-model="alive_new.islive">
</v-select>
<!--<v-text-field v-model="alive_new.islive"
label="存/歿"></v-text-field>-->
</v-col>
<v-col>
<v-btn @click.prevent="addLive(item)">加入</v-btn>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="12">
{{ renderContent( item.tablet.alive) }}
</v-col>
</v-row>
</v-col>
</v-row>
</v-container>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</v-col>
</v-row>
</v-card-text>
</v-card>
</div>
</template>
<script>
module.exports = {
props: {
num: {
type: Number
}
},
data() {
return {
follower: {
u_name: "", sex: "", identity_type: "", phone: "",
cellphone: "", country: "", address: "",
birthday: "", send_recript: "", receipt_title: "",
id_code:"",passport:""
},
act_items: [],
search: {},
date: new Date().toISOString().substr(0, 10),
menu: false,
tablets: [],
select_act_item: {},
title_new: { name: "", islive: true },
alive_new: { name: "", islive: true },
live_select: [
{ val: true, text: '存' },
{ val: false, text: '歿' },
],
requiredrules: [
v=>!!v||'必須填寫',
],
cellrules: [
v => (!!v||!!this.follower.phone) || '行動電話或電話為必須擇一填寫',
v => /^09\d{2}-?\d{3}-?\d{3}$/.test(v) || '行動電話必須為09xx-xxx-xxx',
],
phonerules: [
v => (!!v || !!this.follower.cellphone) || '行動電話或電話為必須擇一填寫',
],
sex_items: [
{ text: "男眾", val: "男眾" },
{ text: "女眾", val: "女眾" },
],
identity_items: [
{ text: "出家眾", val: "1" },
{ text: "個人", val: "2" },
{ text: "法人", val: "3" },
{ text: "往生菩薩", val: "4" },
{ text: "蓮友", val: "10" },
],
countrys:[]
}
},
activated() {
this.getActItem();
this.getCountrys();
},
methods: {
back01() {
this.$emit('custom-event', { action: 'signup1', item: this.num });
},
addOrderWithDetail() {
//
let details = []
this.tablets.forEach(x => {
let mid_items = []
let left_items = []
if (x.tablet.title) {
x.tablet.title.forEach(y => {
mid_items.push(
{
num: 0,
follower_num: 0,
fam_name: y.name,
deceased: !y.islive,
}
)
})
}
if (x.tablet.alive) {
x.tablet.alive.forEach(y => {
left_items.push(
{
num: 0,
follower_num: 0,
fam_name: y.name,
deceased: !y.islive,
}
)
})
}
//let f_num_tablet = {
// mid_items: mid_items,
// left_items:left_items
//}//歿num
details.push({
num: 0,
actItem_num: x.actItem_num,
price: x.price,
qty: x.qty,
printed_files: x.files.val,
style: x.style,
mid_items: mid_items,
left_items: left_items,
})
})
let order = {
order_no: "",
f_num: 0,
u_name: "",
activity_num: this.num,
phone: this.follower.phone,
address: this.follower.address,
send_recript: this.follower.send_recript,
receipt_title: this.follower.receipt_title,
f_user: this.follower,
details:details
}
axios
.post(HTTP_HOST + 'api/order/AddOrderWithDetail', order)
.then(response => {
console.log(response);
})
},
renderContent(titles) {
return titles.map(title => title.name).join('、');
//return title.join("")
},
checkType(item) {
if (item.actItem_subject.startsWith("超")) {
return true
}
return false
},
addTitle(item) {
item.tablet.title.push({
name: this.title_new.name, islive: this.title_new.islive
});
this.title_new = { name: "", islive: this.title_new.islive }
},
addLive(item) {
item.tablet.alive.push({
name: this.alive_new.name, islive: this.alive_new.islive
});
this.alive_new = { name: "", islive: this.alive_new.islive }
},
newTablet() {
console.log(this.act_items);
console.log(this.select_act_item);
let item = this.act_items.find(x => x.act_item_selected.val == this.select_act_item);
this.tablets.push({
actItem_num: item.act_item_selected.val,
actItem_subject: item.act_item_selected.text,
f_num_tablet: [],
num: this.tablets.length,
qty: 1,
price: item.price,
files: item.files,
style: item.act_item_selected.style,
tablet: {
title: [],
alive: []
}
});
},
getCountrys() {
let search = {}
let params = { sortBy: 'ID', sortDesc: false, page: 1, pageSize: -1 };
axios
.post(HTTP_HOST + 'api/country/GetList', this.search, { params: params })
.then(response => {
if (response.data.list) {
this.countrys = response.data.list
}
else
console.log("查無資料");
})
.catch(error => console.log(error))
},
getActItem() {//
this.act_items.length = 0
this.search = { num: this.num }
let params = { sortBy: 'num', sortDesc: false, page: 1, pageSize: -1 };
axios
.post(HTTP_HOST + 'api/activity/GetActRelaList', this.search, { params: params })
.then(response => {
if (response.data.list) {
this.act_items = response.data.list
console.log(this.act_items);
}
else
console.log("查無資料");
})
.catch(error => console.log(error))
},
}
}
</script>
<style>
.hr-text {
display: flex;
align-items: center;
border: 0;
}
.hr-text::before,
.hr-text::after {
content: "";
flex: 1;
border-top: 1px solid #ccc; /* 分隔線樣式 */
margin: 0 10px;
}
.hr-text::before {
content: attr(data-content);
padding: 0 50px;
color: black;
font-size: 28px;
}
/* 当 checkbox 被禁用时,改变鼠标状态 */
input[type="checkbox"]:disabled {
cursor: not-allowed;
opacity: 0.4; /* 控制反灰程度 */
color: lightgray;
}
.disabled-checkbox {
cursor: not-allowed;
opacity: 0.8; /* 控制反灰程度 */
color: lightgray;
}
.enabled-checkbox {
}
</style>
+187
View File
@@ -0,0 +1,187 @@
<template>
<div class="container-fluid">
<v-card>
<v-card-title class="bg-primary white--text text-center">
<h4>批次報名</h4>
<div class="ms-auto">
<v-btn @click.prevent="batchSave()">儲存</v-btn>
<v-btn @click.prevent="back01()">返回</v-btn>
</div>
</v-card-title>
<v-card-text>
<v-select :items="data_table.list"
label="選擇法會"
item-text="subject"
item-value="num" v-model="selected">
</v-select>
<v-data-table v-model="detail_table.selected"
:items="detail_table.list"
:headers="detail_table.header"
:footer-props="detail_table.footer"
:server-items-length="detail_table.count"
:loading="detail_table.loading"
show-select
item-key="order_no"
hide-default-footer
:page.sync="detail_table.page"
:items-per-page.sync="detail_table.pageSize"
class="elevation-1">
<template #item.slot_btn="{ item }">
</template>
</v-data-table>
</v-card-text>
</v-card>
<v-dialog v-model="message_dialog.show" class="dialog_width">
<template>
<v-card>
<v-card-title>
{{ message_dialog.message }}
</v-card-title>
<v-card-text>
<v-btn @click.prevent="message_dialog.show=false" v-if="!message_dialog.show_confirm">關閉</v-btn>
</v-card-text>
</v-card>
</template>
</v-dialog>
</div>
</template>
<script>
module.exports = {
props: {
num: {
type: Number
}
},
data() {
return {
options: { multiSort: false },
search_options: { multiSort: false },
message_dialog: { show: false, show_confirm: false, message: "" },
data_table: {
loading: true,
list: [],
selected: [],
singleSelect: false,
count: 0,
page: 1,
pageSize: -1,
},
selected: null,
detail_table: {
loading: false,
list: [],
selected: [],
singleSelect: false,
count: 0,
page: 1,
pageSize: -1,
header: [
{ text: '單號', value: 'order_no' },
{ text: '信眾號碼', value: 'f_num' },
{ text: '姓名', value: 'u_name' },
{ text: '報名日期', value: 'up_time' },
{ text: '單據狀態', value: 'keyin1_txt', align: 'start' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' }
],
footer: {
showFirstLastPage: true,
itemsPerPageOptions: [5, 10, 20, 30],
},
},
search: {subject:""}
}
},
activated() {
this.getActivity()
},
watch: {
selected: {
handler() {
this.getProOrder()
},
deep: true,
},
},
methods: {
batchSave() {//
const orders = []
this.detail_table.selected.forEach(x => {
orders.push(x.order_no)
})
const po = {
old_activity_num: this.selected,
activity_num: this.num,
orders: orders
}
axios
.post(HTTP_HOST + 'api/order/BatchSave', po)
.then(response => {
this.message_dialog.show = true;
if (response.data.result == "Y") {
let rp = response.data.data;
let other = ""
if (rp && rp.length>0) {
other = rp.join(",")
other = ",以下信眾號碼已經報過名:" + other;
}
this.message_dialog.message = response.data.message + other;
if (rp.length > 0) {
} else {
this.$emit('custom-event', { action: 'signup1', item: this.num });
}
} else {
this.message_dialog.message = response.data.message
}
}).catch(error => console.log(error))
},
back01() {//
this.$emit('custom-event', { action: 'signup1', item: this.num });
},
getActivity() {//
//const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: "startDate_solar", sortDesc: true,
page: 1, pageSize: -1
};
this.data_table.loading = true
const self = this
axios
.post(HTTP_HOST + 'api/activity/GetListNew', this.search, { params: params })
.then(response => {
this.data_table.list = response.data.list
this.data_table.list=this.data_table.list.filter(x=>x.num!=self.num)
this.data_table.count = response.data.count;
this.data_table.loading = false
})
.catch(error => console.log(error))
},
getProOrder() {//
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
sortBy: "order_no", sortDesc: true,
page: 1, pageSize: -1
};
let newsearch = {
activity_num: this.selected
}
this.detail_table.loading = true
// sessionStorage.setItem('orderpage', '1');// clearpage ? '1' : page
axios
.post(HTTP_HOST + 'api/order/GetList', newsearch, { params: params })
.then(response => {
this.detail_table.list = response.data.list
this.detail_table.count = response.data.count;
this.detail_table.loading = false
})
.catch(error => console.log(error))
}
}
}
</script>
<style>
.v-dialog{
width:80% !important;
}
</style>
+233 -61
View File
@@ -14,17 +14,18 @@
<a @click="print_dialog.show=true" class="btn btn-outline-primary btn-print" target="_blank">
<i class="mdi mdi-printer"></i>列印管理報表
</a>
<div class="dropdown d-inline-block">
<a class="btn btn-outline-primary btn-print dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false"><i class="mdi mdi-printer"></i>列印報名資料</a>
<ul class="dropdown-menu ps-0 w-100" aria-labelledby="dropdownPrintLink">
<li><a @click="search.hasPrice='Y';goPrint()" class="dropdown-item"><i class="mdi mdi-printer me-1"></i>有金額</a></li>
<li><a @click="search.hasPrice='N';goPrint()" class="dropdown-item"><i class="mdi mdi-printer me-1"></i>無金額</a></li>
</ul>
<div :style="data_table.list.length === 0 ? 'pointer-events: none; opacity: 0.5;' : ''" style="display:inline-block;">
<div class="dropdown d-inline-block">
<a class="btn btn-outline-primary btn-print dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false"><i class="mdi mdi-printer"></i>列印查詢資料</a>
<ul class="dropdown-menu ps-0 w-100" aria-labelledby="dropdownPrintLink">
<li><a @click="search.hasPrice='Y';goPrint()" class="dropdown-item"><i class="mdi mdi-printer me-1"></i>有金額</a></li>
<li><a @click="search.hasPrice='N';goPrint()" class="dropdown-item"><i class="mdi mdi-printer me-1"></i>無金額</a></li>
</ul>
</div>
</div>
<div :style="data_table.list.length === 0 ? 'pointer-events: none; opacity: 0.5;' : ''" style="display:inline-block;">
<asp:LinkButton ID="excel" runat="server" CssClass="btn btn-outline-success" OnClick="excel_Click"><span class="fa-solid fa-file-excel"></span> 匯出查詢資料(Excel</asp:LinkButton>
</div>
<asp:LinkButton ID="excel" runat="server" CssClass="btn btn-outline-success" OnClick="excel_Click"><span class="fa-solid fa-file-excel"></span> 匯出Excel</asp:LinkButton>
</div>
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" runat="Server">
@@ -39,6 +40,8 @@
vuetify: new Vuetify(vuetify_options),
data() {
return {
print_error_msg: "",
isSearched: false,
this_act : '<%= Request["act_id"]%>',
options: { multiSort: false },
search_options: { multiSort: false },
@@ -56,6 +59,7 @@
{ text: '報名日期', value: 'up_time' },
{ text: '報名活動', value: 'subject', align: 'start' },
{ text: '單據狀態', value: 'keyin1_txt' },
{ text: '報到狀態', value: 'status' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' }
],
footer:{
@@ -203,6 +207,10 @@
}
},
mounted() {
const printResult = document.getElementById('<%= hid_err_msg.ClientID %>').value;
document.getElementById('<%= hid_err_msg.ClientID %>').value = '';
window._printResult = printResult
this.detalKeyinArray();
this.search_dialog.current = this.search_dialog.controls.search1 ///default
if (this.this_act != '')
@@ -211,20 +219,63 @@
this.initActivity();
const navEntries = performance.getEntriesByType("navigation");
const isReload = navEntries.length > 0 && navEntries[0].type === "reload";
if (isReload) {
sessionStorage.removeItem("orderpage");
sessionStorage.removeItem("order_list_cache");
sessionStorage.removeItem("order_query_params");
}
else if ("<%=lastAddedNo%>" !== "") {
const newQuery = { order_no: '<%=lastAddedNo%>' };
sessionStorage.setItem('order_query_params', JSON.stringify(newQuery));
this.search = newQuery;
this.isSearched = true;
}
else {
const savedPage = parseInt(sessionStorage.getItem('orderpage'));
const savedData = sessionStorage.getItem("order_list_cache");
const savedQuery = JSON.parse(sessionStorage.getItem("order_query_params"));
if (savedQuery) {
this.search = savedQuery;
this.isSearched = true;
}
if (savedPage) {
this.options.page = savedPage;
}
}
if (savedData && savedData !== "undefined") {
this.data_table = JSON.parse(savedData);
this.isSearched = true;
}
}
if (printResult === 'nodata' || printResult === 'success') {
this.$nextTick(() => {
this.print_dialog.show = true;
if (printResult === 'nodata') {
this.print_error_msg = "查無資料,請重新選擇區間";
}
});
}
this.$nextTick(() => {
setTimeout(() => {
// 清空 URL
const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
window.history.replaceState({}, '', cleanUrl);
}, 100);
});
},
watch: {
options: {
handler() {
this.getDefault()
if (this.isSearched) {
this.getDefault()
}
else {
this.data_table.loading = false;
}
},
deep: true,
},
@@ -236,6 +287,57 @@
},
},
methods: {
triggerManagementExport(mode) {
this.print_error_msg = "";
if (this.print_search.year == '') {
msgbox('請輸入年份');
return;
}
if (!this.print_search.chk_noact && !this.print_search.chk_hasact) {
msgbox('活動/非活動至少勾選一項');
return;
}
// 將 Vue 狀態同步至 ASP.NET HiddenField,供後端 PostBack 讀取參數
document.getElementById('<%= hid_print_mode.ClientID %>').value = this.print_conditions;
document.getElementById('<%= hid_print_year.ClientID %>').value = this.print_search.year;
if (this.print_conditions == 'mm')
document.getElementById('<%= hid_print_month.ClientID %>').value = this.print_search.month;
else if (this.print_conditions == 'ss')
document.getElementById('<%= hid_print_season.ClientID %>').value = this.print_search.season;
document.getElementById('<%= hid_select_act.ClientID %>').value = this.print_search.select_act;
document.getElementById('<%= hid_select_actitem.ClientID %>').value = this.print_search.select_actitem;
document.getElementById('<%= hid_chk_hasact.ClientID %>').value = this.print_search.chk_hasact;
document.getElementById('<%= hid_chk_noact.ClientID %>').value = this.print_search.chk_noact;
let qry = "";
Object.keys(this.print_search).forEach(key => {
if (this.print_search[key] != undefined && this.print_search[key] != null && this.print_search[key] != '') {
if (key == 'month' ) {
if (this.print_conditions == 'mm') {
qry += "&month=" + this.print_search.month;
}
} else if ( key == 'season')
{
if (this.print_conditions == 'ss') {
qry += "&season=" + this.print_search.season;
}
}
else {
qry += (qry != '' ? '&' : '?') + (key + '=' + this.print_search[key]);
}
}
});
document.getElementById('<%= hid_qry.ClientID %>').value = qry;
if (mode === 'print') {
document.getElementById('<%= print_management.ClientID %>').click();
}
else if (mode === "excel") {
document.getElementById('<%= excel_management.ClientID %>').click();
}
},
search_show(curr) {
//console.log("btn_click:", curr, curr.api_url);
this.search_dialog.current = curr;
@@ -321,19 +423,24 @@
this.data_table.list = response.data.list
this.data_table.count = response.data.count;
this.data_table.loading = false
const dataToStore = JSON.stringify(this.data_table);
sessionStorage.setItem("order_list_cache", dataToStore);
})
.catch(error => console.log(error))
},
detalKeyinArray() {
var getArray = <%=Newtonsoft.Json.JsonConvert.SerializeObject(_keyin1Item, Newtonsoft.Json.Formatting.Indented) %>;
var keys = Object.keys(getArray);
for (let i = 0; i < keys.length; i++) {
//console.log(`${keys[i]}:${getArray[keys[i]]}`); //value : text
var _tmp = {
text: getArray[keys[i]],
val: parseInt(keys[i]),
if (getArray !== null) {
var keys = Object.keys(getArray);
for (let i = 0; i < keys.length; i++) {
//console.log(`${keys[i]}:${getArray[keys[i]]}`); //value : text
var _tmp = {
text: getArray[keys[i]],
val: parseInt(keys[i]),
}
this.keyin1_items.push(_tmp);
}
this.keyin1_items.push(_tmp);
}
},
@@ -368,22 +475,28 @@
//}
//this.data_table.selected = [];
//this.data_table.count = this.data_table.list.length
location.reload();
//location.reload();
this.getDefault();
})
.catch(error => console.log(error))
}
},
btn_search() {
this.isSearched = true;
sessionStorage.setItem("order_query_params", JSON.stringify(this.search));
this.this_act = '';
this.search.activity_num = '';
this.getDefault(true)
bootstrap.Offcanvas.getInstance(document.getElementById("offcanvasRight")).hide()
},
btn_all() {
this.isSearched = false;
this.this_act = '';
this.search.activity_num = '';
clearObjProps(this.search);
this.btn_search()
sessionStorage.setItem("order_query_params", JSON.stringify(this.search));
//this.btn_search()
},
checkInMsg(item) {
this.check_data.f_num = item.f_num;
@@ -392,39 +505,59 @@
this.check_data.activity_name = item.subject;
this.check_dialog.show = true;
},
checkIn() {
if (this.check_data.qty > 0 && this.check_data.status.val > 0) {
var chechdata =
{
f_num: this.check_data.f_num,
activity_num: this.check_data.activity_num,
status: this.check_data.status.val,
qty: this.check_data.qty,
}
axios
.post(HTTP_HOST + 'api/activity/OrderCheckIn', chechdata)
.then(response => {
//清空
this.check_data.f_num = 0;
this.check_data.u_name = '';
this.check_data.activity_num = 0;
this.check_data.activity_name = '';
this.check_data.qty = 1;
this.check_data.status.text = '';
this.check_data.status.val = 1;
this.check_dialog.show = false;
msgtop('簽到成功')
})
.catch(
error => {
console.log(error)
msgtop('簽到失敗', 'error')
}
)
} else {
msgbox('報到資訊請填寫完整');
checkIn(item) {
var checkdata =
{
f_num: item.f_num,
activity_num: item.activity_num,
status: 1,
qty: 1,
}
console.log(checkdata)
axios
.post(HTTP_HOST + 'api/activity/OrderCheckIn', checkdata)
.then(response => {
msgtop('簽到成功');
this.getDefault();
})
.catch(
error => {
console.log(error)
msgtop('簽到失敗', 'error')
}
)
//if (this.check_data.qty > 0 && this.check_data.status.val > 0) {
// var chechdata =
// {
// f_num: this.check_data.f_num,
// activity_num: this.check_data.activity_num,
// status: this.check_data.status.val,
// qty: this.check_data.qty,
// }
// axios
// .post(HTTP_HOST + 'api/activity/OrderCheckIn', chechdata)
// .then(response => {
// //清空
// this.check_data.f_num = 0;
// this.check_data.u_name = '';
// this.check_data.activity_num = 0;
// this.check_data.activity_name = '';
// this.check_data.qty = 1;
// this.check_data.status.text = '';
// this.check_data.status.val = 1;
// this.check_dialog.show = false;
// msgtop('簽到成功')
// })
// .catch(
// error => {
// console.log(error)
// msgtop('簽到失敗', 'error')
// }
// )
//} else {
// msgbox('報到資訊請填寫完整');
//}
}, goPrint() {
let _qry = "";
Object.keys(this.search).forEach(key => {
@@ -442,6 +575,7 @@
//列印管理報表
print_close() {
this.print_dialog.show = false;
this.print_error_msg = "";
}
,
initPrintSearch() {
@@ -504,9 +638,9 @@
}
}
});
console.log(_qry);
this.print_dialog.show = false;
window.open("print.aspx" + _qry, '_blank');
//window.open("print.aspx" + _qry, '_blank');
} else {
msgbox('活動/非活動至少勾選一項');
}
@@ -537,7 +671,7 @@
initActivity() {
axios.get(HTTP_HOST + 'api/activity')
.then(response => {
this.select_act_list = response.data
this.select_act_list = response.data
})
.catch(error => {
console.log(error)
@@ -556,10 +690,35 @@
$('#country2').val('');
VueApp.search.country2 = '';
});
$(document).ready(function () {
// 判斷是否彈出 search dialog
let hasSearchResult = sessionStorage.getItem("order_list_cache") !== null;
if (!hasSearchResult && window._printResult === '') {
let $btn = $("a[data-bs-target='#offcanvasRight'][href='#search_panel']");
$btn.click();
let el = document.getElementById('offcanvasRight');
let offcanvas = bootstrap.Offcanvas.getOrCreateInstance(el);
offcanvas.show();
}
});
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<uc1:alert runat="server" ID="L_msg" Text="" />
<asp:HiddenField ID="hid_err_msg" runat="server" />
<asp:HiddenField ID="hid_print_year" runat="server" />
<asp:HiddenField ID="hid_print_month" runat="server" />
<asp:HiddenField ID="hid_print_season" runat="server" />
<asp:HiddenField ID="hid_print_mode" runat="server" />
<asp:HiddenField ID="hid_chk_hasact" runat="server" />
<asp:HiddenField ID="hid_chk_noact" runat="server" />
<asp:HiddenField ID="hid_select_act" runat="server" />
<asp:HiddenField ID="hid_select_actitem" runat="server" />
<asp:HiddenField ID="hid_qry" runat="server" />
<asp:LinkButton ID="print_management" runat="server" OnClick="export_Click" style="display:none;" />
<asp:LinkButton ID="excel_management" runat="server" OnClick="export_Click" style="display:none;" />
<div id="content" class="container-fluid">
<v-data-table
v-model="data_table.selected"
@@ -580,10 +739,13 @@
<template #item.up_time="{ item }" >
{{ item.up_time|timeString('YYYY/MM/DD') }}
</template>
<template #item.u_name="{ item }" >
<a v-if="item.f_num != null && item.activity_num != null" @click="checkInMsg(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-account-check"></i>報到</a>
<template #item.u_name="{ item }" >
{{ item.u_name }}
</template>
<template #item.status="{ item }" >
<a v-if="item.f_num != null && item.activity_num != null && item.status == 0" @click="checkIn(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-account"></i>報到</a>
<a v-if="item.f_num != null && item.activity_num != null && item.status == 1" class="btn btn-outline-secondary btn-sm opacity-50" style="pointer-events: none" ><i class="mdi mdi-account-check"></i>已報到</a>
</template>
<template #item.slot_btn="{ item }">
<a :href="'reg.aspx?order_no='+item.order_no" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-pencil-box-outline"></i>修改</a>
<a @click="deleteItem(item)" class="btn btn-outline-secondary btn-sm"><i class="mdi mdi-trash-can"></i>刪除</a>
@@ -692,7 +854,8 @@
item-text="text"
item-value="val"
v-model="print_search.month"
:items="select_items.month"
:items="select_items.month"
eager
></v-select>
</v-col>
<v-col :cols="2" class="pt-5" v-if="print_conditions=='ss' ">
@@ -703,7 +866,8 @@
item-text="text"
item-value="val"
v-model="print_search.season"
:items="select_items.season"
:items="select_items.season"
eager
></v-select>
</v-col>
@@ -764,9 +928,17 @@
</v-row>
</v-col>--%>
</v-row>
<v-row>
<v-col>
<div v-if="print_error_msg" class="red--text mt-2 text-center" style="font-weight: bold;">
{{ print_error_msg }}
</div>
</v-col>
</v-row>
<v-row densee class="pt-3" >
<v-col :cols="12" class="pt-3 text-center" >
<v-btn class="ma-2" color="primary" dark @click="goPrint2" > 列印 </v-btn>
<v-btn class="ma-2" color="primary" dark @click="triggerManagementExport('print')" > 列印 </v-btn>
<v-btn class="ma-2" color="primary" dark @click="triggerManagementExport('excel')"> 匯出 Excel </v-btn>
<v-btn class="ma-2" color="green" dark @click="print_close" > 取消 </v-btn>
</v-col>
</v-row>
+565 -26
View File
@@ -1,22 +1,28 @@
using System;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Linq;
using static Model.admin_log;
using static regionController;
public partial class admin_order_index : MyWeb.config
{
private Model.ezEntities _db = new Model.ezEntities();
public Dictionary<int, string> _keyin1Item = null;
protected string lastAddedNo;
protected void Page_Load(object sender, EventArgs e)
{
@@ -33,6 +39,13 @@ public partial class admin_order_index : MyWeb.config
}
_keyin1Item = publicFun.enum_desc<Model.activity_check.keyin1>(); //狀態
if (Session["LastAddedNo"] != null)
{
lastAddedNo = Session["LastAddedNo"].ToString();
Session.Remove("LastAddedNo");
}
BuildKind();
}
@@ -58,6 +71,452 @@ public partial class admin_order_index : MyWeb.config
#endregion
#region Excel
protected void export_Click(object sender, EventArgs e)
{
LinkButton btn = sender as LinkButton;
if (btn == null) return;
bool isPrintMode = (btn.ID == "print_management");
bool isExcelMode = (btn.ID == "excel_management" || btn.ID == "excel");
//查詢要匯出的資料
string _query = ""; // 紀錄匯出條件
//var list = searchData(ref _query, isManagementMode);
if (isExcelMode)
{
int selYear = !string.IsNullOrEmpty(hid_print_year.Value) ? int.Parse(hid_print_year.Value) : 0;
int selMonth = !string.IsNullOrEmpty(hid_print_month.Value) ? int.Parse(hid_print_month.Value) : 0;
int selSeason = !string.IsNullOrEmpty(hid_print_season.Value) ? int.Parse(hid_print_season.Value) : 0;
string selMode = !string.IsNullOrEmpty(hid_print_mode.Value) ? hid_print_mode.Value : "";
bool chkHasAct = !string.IsNullOrEmpty(hid_chk_hasact.Value) && Convert.ToBoolean(hid_chk_hasact.Value);
bool chkNoAct = !string.IsNullOrEmpty(hid_chk_noact.Value) && Convert.ToBoolean(hid_chk_noact.Value);
int selAct = !string.IsNullOrEmpty(hid_select_act.Value) ? int.Parse(hid_select_act.Value) : 0;
int selActItem = !string.IsNullOrEmpty(hid_select_actitem.Value) ? int.Parse(hid_select_actitem.Value) : 0;
var qry = _db.pro_order.AsQueryable();
if (selYear > 0)
{
qry = qry.Where(o => o.up_time.HasValue && o.up_time.Value.Year == selYear);
_query += "年份:" + selYear + "\n";
}
if (selMode == "mm" && selMonth > 0)
{
qry = qry.Where(o => o.up_time.HasValue && o.up_time.Value.Month == selMonth);
_query += "月份:" + selMonth + "\n";
}
if (selMode == "ss" && selSeason > 0)
{
if (selSeason == 1)
{
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 1 || o.up_time.Value.Month == 2 || o.up_time.Value.Month == 3);
}
else if (selSeason == 2)
{
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 4 || o.up_time.Value.Month == 5 || o.up_time.Value.Month == 6);
}
else if (selSeason == 3)
{
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 7 || o.up_time.Value.Month == 8 || o.up_time.Value.Month == 9);
}
else if (selSeason == 4)
{
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 10 || o.up_time.Value.Month == 11 || o.up_time.Value.Month == 12);
}
_query += "季度:" + selSeason + "\n";
}
if (chkHasAct)
{
if(selAct > 0)
{
var actSubject = _db.activities.Where(a => a.num == selAct).Select(a => a.subject).FirstOrDefault();
_query += $"活動報名: {actSubject}\n";
}
else
{
_query += $"活動報名\n";
}
if (chkNoAct)
{
_query += "非活動報名\n";
if (selAct > 0)
qry = qry.Where(o => o.activity_num.HasValue && o.activity_num.Value == selAct);
}
else
{
qry = qry.Where(o => o.activity_num.HasValue);
if (selAct > 0)
qry = qry.Where(o => o.activity_num.Value == selAct);
}
}
else
{
if (chkNoAct)
{
qry = qry.Where(o => o.activity_num == null);
_query += "非活動報名\n";
}
}
if (selActItem > 0)
qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.actItem_num.HasValue && f2.actItem_num.Value == selActItem).Count() > 0);
if (selYear > 0)
qry = qry.OrderByDescending(o => o.activity != null ? o.activity.startDate_solar : null).ThenByDescending(o => o.up_time).ThenByDescending(o => o.order_no);
else
qry = qry.OrderByDescending(o => o.order_no);
MyWeb.encrypt encrypt = new MyWeb.encrypt();
var tdesc = publicFun.enum_desc<Model.pro_order.detailKeyin1>();
var bedDt = _db.bed_order_detail.AsQueryable();//掛單明細
//紀錄匯出條件
//var list = qry.ToList();
var list = qry.GroupJoin(
_db.pro_order_detail, o => o.order_no, p => p.order_no, (o, c) =>
new
{
//訂單資料
order_no = o.order_no,
up_time = o.up_time,
keyin1 = o.keyin1,
f_num = o.follower != null ? o.follower.u_name : "", //姓名/名稱
phone = o.phone,
activity_num = o.activity_num.HasValue ? o.activity.subject : "",
address = o.address,
demo = o.demo,
c
}).SelectMany(o => o.c.DefaultIfEmpty(), (o, d) => //SelectMany 展開
new
{
//訂單資料
order_no = o.order_no,
up_time = o.up_time,
keyin1 = o.keyin1,
f_num = o.f_num, //姓名/名稱
phone = o.phone,
activity_num = o.activity_num,
address = o.address,
demo = o.demo,
//訂單明細
//使用DefaultIfEmpty 因匿名型別無法輸出NULL(無法轉換成強型別),需特別注意Null的處理
d_actItem_num = d == null ? "" : (d.actItem_num.HasValue ? d.actItem.subject : ""), //項目名稱
d_category = d == null ? "" : (d.actItem_num.HasValue ? d.actItem.category.ToString() : ""),
d_f_num = d == null ? "" : (d.f_num.HasValue ? d.follower.u_name : ""), //姓名
d_address = d == null ? "" : d.address,
d_from_id = d == null ? "" : (d.from_id.HasValue ? d.follower1.u_name : ""), //陽上/報恩者
d_f_num_tablet = d == null ? "" : d.f_num_tablet,
d_start_date = d == null ? (DateTime?)null : d.start_date, //開始日期
d_due_date = d == null ? (DateTime?)null : d.due_date, //期滿日期
d_extend_date = d == null ? (DateTime?)null : d.extend_date, //應續約日
d_price = d == null ? (float?)null : d.price, //預設金額
d_qty = d == null ? "" : (d.qty.HasValue ? d.qty.Value.ToString() : "0"), //數量
d_writeBedQty = d == null ? 0 : bedDt.Where(b => (b.bed_order.o_detail_id.Value == d.num) && b.checkIn_date.HasValue && b.bed_kind_detail_id.HasValue).Count(), //已劃數量
d_notBedQty = d == null ? 0 : bedDt.Where(b => b.bed_order.o_detail_id.Value == d.num && (!b.checkIn_date.HasValue || !b.bed_kind_detail_id.HasValue)).Count(), //未劃數量
d_pay = d == null ? "" : (d.pay.HasValue ? d.pay.Value.ToString() : "0"), //已收金額
d_pay_date = d == null ? (DateTime?)null : d.start_date, //付款期限
d_keyin1 = d == null ? (int?)null : d.keyin1,
d_demo = d == null ? "" : d.demo, //狀態備註
}).ToList();
var memoryStream = new MemoryStream();
if (list.Count > 0)
{
using (var doc = SpreadsheetDocument.Create(memoryStream, SpreadsheetDocumentType.Workbook))
{
var wb = doc.AddWorkbookPart();
wb.Workbook = new Workbook();
var sheets = wb.Workbook.AppendChild(new Sheets());
//建立第一個頁籤
var ws = wb.AddNewPart<WorksheetPart>();
ws.Worksheet = new Worksheet();
sheets.Append(new Sheet()
{
Id = wb.GetIdOfPart(ws),
SheetId = 1,
Name = "報名"
});
//設定欄寬
var cu = new Columns();
cu.Append(
new Column { Min = 1, Max = 1, Width = 15, CustomWidth = true },
new Column { Min = 2, Max = 4, Width = 10, CustomWidth = true },
new Column { Min = 5, Max = 5, Width = 15, CustomWidth = true },
new Column { Min = 6, Max = 8, Width = 25, CustomWidth = true },
new Column { Min = 9, Max = 9, Width = 15, CustomWidth = true },
new Column { Min = 10, Max = 10, Width = 10, CustomWidth = true },
new Column { Min = 11, Max = 11, Width = 25, CustomWidth = true },
new Column { Min = 12, Max = 16, Width = 10, CustomWidth = true },
new Column { Min = 17, Max = 20, Width = 10, CustomWidth = true }
);
ws.Worksheet.Append(cu);
//建立資料頁
var sd = new SheetData();
ws.Worksheet.AppendChild(sd);
//第一列資料
var tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue("單號"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("報名日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("單據狀態"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("姓名/名稱"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("聯絡電話"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("報名活動"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("收件地址"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("備註"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("項目名稱"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("姓名"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("代表地址"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("標題"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("陽上/報恩者"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("開始日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("期滿日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("應續約日"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("劃位狀態"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("預設金額"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("數量"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("小計"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("已收金額"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("未收金額"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("付款期限"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("報名狀態"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("狀態備註"), DataType = CellValues.String }
);
sd.AppendChild(tr);
foreach (var item in list)
{
string midNamesResult = "";
string leftNamesResult = "";
string jsonString = item.d_f_num_tablet?.ToString() ?? "";
try
{
var jo = JObject.Parse(jsonString);
// 標題
var midList = jo["mid_items"]?
.Select(i => (string)i["fam_name"])
.Where(name => !string.IsNullOrEmpty(name))
.ToList();
if (midList != null && midList.Any())
{
midNamesResult = string.Join(", ", midList);
}
// 陽上
var leftList = jo["left_items"]?
.Select(i => (string)i["fam_name"])
.Where(name => !string.IsNullOrEmpty(name))
.ToList();
if (leftList != null && leftList.Any())
{
leftNamesResult = string.Join(", ", leftList);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"JSON 解析失敗 (訂單:{item.order_no}): {ex.Message}");
}
//新增資料列
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(item.order_no), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.up_time?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(Model.pro_order.keyin1_value_to_text(item.keyin1)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.f_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.phone)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.activity_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.address), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.demo), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_actItem_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_f_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_address), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(midNamesResult), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(leftNamesResult), DataType = CellValues.String },
//new Cell() { CellValue = new CellValue(item.d_from_id), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_start_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_due_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_extend_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue((Val(item.d_category) == (int)Model.activity.category.Order) ? (item.d_notBedQty + "/" + item.d_writeBedQty) : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(item.d_price)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_qty), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(ValFloat(item.d_price) * Val(item.d_qty))), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(item.d_pay)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(ValFloat(item.d_price) * Val(item.d_qty) - ValFloat(item.d_pay))), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_pay_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_keyin1.HasValue ? tdesc[item.d_keyin1.Value] : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_demo), DataType = CellValues.String }
);
sd.AppendChild(tr);
}
//空一列
tr = new Row();
sd.AppendChild(tr);
//匯出資訊
string _data = "匯出時間 : " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
_data += " " + admin.info.u_id;
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
_data = "匯出條件 : " + (!isStrNull(_query) ? _query : "-");
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Order, (int)Model.admin_log.Status.Excel, admin_log.LogViewBtn(list.Select(x => x.order_no).ToList()));
}
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=報名.xlsx");
HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
HttpContext.Current.Response.BinaryWrite(memoryStream.ToArray());
HttpContext.Current.Response.End();
hid_err_msg.Value = "success";
}
else
{
hid_err_msg.Value = "nodata";
}
}
else if (isPrintMode)
{
int selYear = !string.IsNullOrEmpty(hid_print_year.Value) ? int.Parse(hid_print_year.Value) : 0;
int selMonth = !string.IsNullOrEmpty(hid_print_month.Value) ? int.Parse(hid_print_month.Value) : 0;
int selSeason = !string.IsNullOrEmpty(hid_print_season.Value) ? int.Parse(hid_print_season.Value) : 0;
string selMode = !string.IsNullOrEmpty(hid_print_mode.Value) ? hid_print_mode.Value : "";
bool chkHasAct = !string.IsNullOrEmpty(hid_chk_hasact.Value) && Convert.ToBoolean(hid_chk_hasact.Value);
bool chkNoAct = !string.IsNullOrEmpty(hid_chk_noact.Value) && Convert.ToBoolean(hid_chk_noact.Value);
int selAct = !string.IsNullOrEmpty(hid_select_act.Value) ? int.Parse(hid_select_act.Value) : -1;
int selActItem = !string.IsNullOrEmpty(hid_select_actitem.Value) ? int.Parse(hid_select_actitem.Value) : -1;
string urlParams = !string.IsNullOrEmpty(hid_qry.Value) ? hid_qry.Value : "";
var qry = _db.pro_order.AsQueryable();
if (selYear > 0)
qry = qry.Where(o => o.up_time.HasValue && o.up_time.Value.Year == selYear);
if (selMode == "mm" && selMonth > 0)
qry = qry.Where(o => o.up_time.HasValue && o.up_time.Value.Month == selMonth);
else if (selMode == "ss" && selSeason > 0)
{
if (selSeason == 1)
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 1 || o.up_time.Value.Month == 2 || o.up_time.Value.Month == 3);
else if (selSeason == 2)
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 4 || o.up_time.Value.Month == 5 || o.up_time.Value.Month == 6);
else if (selSeason == 3)
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 7 || o.up_time.Value.Month == 8 || o.up_time.Value.Month == 9);
else if (selSeason == 4)
qry = qry.Where(o => o.up_time.HasValue)
.Where(o => o.up_time.Value.Month == 10 || o.up_time.Value.Month == 11 || o.up_time.Value.Month == 12);
}
if (chkHasAct)
{
if (chkNoAct)
{
if (selAct >= 0)
qry = qry.Where(o => o.activity_num.HasValue && o.activity_num.Value == selAct);
}
else
{
qry = qry.Where(o => o.activity_num.HasValue);
if (selAct >= 0)
qry = qry.Where(o => o.activity_num.Value == selAct);
}
}
else
{
if (chkNoAct)
qry = qry.Where(o => o.activity_num == null);
}
if (selActItem >= 0)
{
qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.actItem_num.HasValue && f2.actItem_num.Value == selActItem).Count() > 0);
}
var count = qry.Count();
if (count > 0)
{
hid_err_msg.Value = "success";
string script = $@"var otherWin = window.open('print.aspx{urlParams}', '列印報名資料', 'noopener,noreferrer');";
ScriptManager.RegisterStartupScript(this, GetType(), "ExecutePrint", script, true);
}
else
{
hid_err_msg.Value = "nodata";
}
//var qry = _db.followers.AsQueryable();
//if (selYear > 0)
//{
// urlParams += "&year=" + selYear;
//}
//if (selMode == "mm" && selMonth > 0)
//{
// urlParams += "&month=" + selMonth;
//}
//else if (selMode == "ss" && selSeason > 0)
//{
// urlParams += "&season=" + selSeason;
//}
//if (list.Count > 0)
//{
// hid_err_msg.Value = "success";
// hid_print_year.Value = selYear.ToString();
// hid_print_month.Value = selMonth.ToString();
// hid_print_season.Value = selSeason.ToString();
// hid_print_mode.Value = selMode;
// string script = $@"window.open('print.aspx?{urlParams}&mode={selMode}', '列印信眾資料');";
// ScriptManager.RegisterStartupScript(this, GetType(), "ExecutePrint", script, true);
//}
//else
//{
// hid_err_msg.Value = "nodata";
// string script = $@"
// var win = window.open('', '列印信眾資料');
// if (win) win.close()";
// ScriptManager.RegisterStartupScript(this, GetType(), "CancelPrint", script, true);
//}
}
}
protected void excel_Click(object sender, EventArgs e)
{
@@ -111,6 +570,7 @@ public partial class admin_order_index : MyWeb.config
new Cell() { CellValue = new CellValue("項目名稱"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("姓名"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("代表地址"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("標題"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("陽上/報恩者"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("開始日期"), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("期滿日期"), DataType = CellValues.String },
@@ -132,32 +592,57 @@ public partial class admin_order_index : MyWeb.config
// 改為整數陣列,避免後續查詢中使用 .ToString()
var aIDt = _db.actItems.Where(f => f.subject.Contains(s_actItemTxt.Value.Trim())).Select(f => f.num).ToArray();//品項
var qry = _db.pro_order.AsQueryable();
string _query = ""; // 紀錄匯出條件
if (!isStrNull(s_order_no.Value))
{
_query += "單號:" + s_order_no.Value + "\n";
qry = qry.Where(o => o.order_no.Contains(s_order_no.Value.Trim()));
}
if (!isStrNull(s_u_name.Value))
{
_query += "姓名/名稱:" + s_u_name.Value + "\n";
qry = qry.Where(o => o.f_num.HasValue && o.follower.u_name.Contains(s_u_name.Value.Trim()));
}
if (!isStrNull(s_introducerTxt.Value))
{
_query += "介紹人:" + s_introducerTxt.Value + "\n";
qry = qry.Where(o => o.f_num.HasValue && o.follower1.u_name.Contains(s_introducerTxt.Value.Trim()));
}
if (!isStrNull(s_subject.Value))
{
_query += "報名活動:" + s_subject.Value + "\n";
qry = qry.Where(o => o.activity_num.HasValue && o.activity.subject.Contains(s_subject.Value.Trim()));
}
if (!isStrNull(s_actItemTxt.Value))
{
// ❌ 錯誤寫法: qry = qry.Where(o => o.pro_order_detail.Where(f2 => f2.order_no == o.order_no && aIDt.ToArray().Contains(f2.actItem_num.ToString())).Count() > 0);
// ✅ 實際比較:僅在 actItem_num 有值時才與整數陣列比對
_query += "品項:" + s_actItemTxt.Value + "\n";
qry = qry.Where(o => o.pro_order_detail.Any(f2 =>
f2.order_no == o.order_no &&
f2.actItem_num.HasValue &&
aIDt.Contains(f2.actItem_num.Value)));
}
if (!isStrNull(s_keyin1.SelectedValue))
{
_query += $"單據狀態:{Model.pro_order.keyin1_value_to_text(s_keyin1.SelectedValue)}\n";
qry = qry.Where(o => o.keyin1 == s_keyin1.SelectedValue);
}
if (!isStrNull(s_up_time1.Value) && isDate(s_up_time1.Value))
qry = qry.Where(o => o.up_time >= ValDate(s_up_time1.Value));
{
_query += "報名日期(起):" + s_up_time1.Value.Trim() + "\n";
var tmp_s_up_time1 = ValDate(s_up_time1.Value);
qry = qry.Where(o => o.up_time >= tmp_s_up_time1);
}
if (!isStrNull(s_up_time2.Value) && isDate(s_up_time2.Value))
qry = qry.Where(o => o.up_time < Convert.ToDateTime(s_up_time2.Value).AddDays(1));
{
_query += "報名日期(訖):" + s_up_time2.Value.Trim() + "\n";
var tmp_s_up_time2 = Convert.ToDateTime(s_up_time2.Value).AddDays(1);
qry = qry.Where(o => o.up_time < tmp_s_up_time2);
}
qry = qry.OrderByDescending(o => o.reg_time);
MyWeb.encrypt encrypt = new MyWeb.encrypt();
@@ -198,20 +683,21 @@ public partial class admin_order_index : MyWeb.config
//訂單明細
//使用DefaultIfEmpty 因匿名型別無法輸出NULL(無法轉換成強型別),需特別注意Null的處理
d_actItem_num = d == null ? "" : (d.actItem_num.HasValue ? d.actItem.subject : ""), //項目名稱
d_category = d == null ? "" : (d.actItem_num.HasValue ? d.actItem.category.ToString() : ""),
d_category = d == null ? "" : (d.actItem_num.HasValue ? d.actItem.category.ToString() : ""),
d_f_num = d == null ? "" : (d.f_num.HasValue ? d.follower.u_name : ""), //姓名
d_address = d == null ? "" : d.address,
d_from_id = d == null ? "" : (d.from_id.HasValue ? d.follower1.u_name : ""), //陽上/報恩者
d_start_date = d == null ? "" : (d.start_date.HasValue ? d.start_date.Value.ToString("yyyy/MM/dd") : ""), //開始日期
d_due_date = d == null ? "" : (d.due_date.HasValue ? d.due_date.Value.ToString("yyyy/MM/dd") : ""), //期滿日期
d_extend_date = d == null ? "" : (d.extend_date.HasValue ? d.extend_date.Value.ToString("yyyy/MM/dd") : ""), //應續約日
d_price = d == null ? "" : (d.price.HasValue ? d.price.Value.ToString() : "0"), //預設金額
d_f_num_tablet = d == null ? "" : d.f_num_tablet,
d_start_date = d == null ? (DateTime?)null : d.start_date, //開始日期
d_due_date = d == null ? (DateTime?)null : d.due_date, //期滿日期
d_extend_date = d == null ? (DateTime?)null : d.extend_date, //應續約日
d_price = d == null ? (float?)null : d.price, //預設金額
d_qty = d == null ? "" : (d.qty.HasValue ? d.qty.Value.ToString() : "0"), //數量
d_writeBedQty = d == null ? 0 : bedDt.Where(b =>( b.bed_order.o_detail_id.Value == d.num) && b.checkIn_date.HasValue && b.bed_kind_detail_id.HasValue).Count(), //已劃數量
d_writeBedQty = d == null ? 0 : bedDt.Where(b => (b.bed_order.o_detail_id.Value == d.num) && b.checkIn_date.HasValue && b.bed_kind_detail_id.HasValue).Count(), //已劃數量
d_notBedQty = d == null ? 0 : bedDt.Where(b => b.bed_order.o_detail_id.Value == d.num && (!b.checkIn_date.HasValue || !b.bed_kind_detail_id.HasValue)).Count(), //未劃數量
d_pay = d == null ? "" : (d.pay.HasValue ? d.pay.Value.ToString() : "0"), //已收金額
d_pay_date = d == null ? "" : (d.pay_date.HasValue ? d.pay_date.Value.ToString("yyyy/MM/dd") : ""), //付款期限
d_keyin1 = d == null ? "" : (d.keyin1.HasValue&& d.keyin1.Value>0 ? tdesc[d.keyin1 ?? 1] : ""), //報名狀態
d_pay_date = d == null ? (DateTime?)null : d.start_date, //付款期限
d_keyin1 = d == null ? (int?)null : d.keyin1,
d_demo = d == null ? "" : d.demo, //狀態備註
}).ToList();
@@ -259,11 +745,43 @@ public partial class admin_order_index : MyWeb.config
{
foreach (var item in list)
{
string midNamesResult = "";
string leftNamesResult = "";
string jsonString = item.d_f_num_tablet?.ToString() ?? "";
try
{
var jo = JObject.Parse(jsonString);
// 標題
var midList = jo["mid_items"]?
.Select(i => (string)i["fam_name"])
.Where(name => !string.IsNullOrEmpty(name))
.ToList();
if (midList != null && midList.Any())
{
midNamesResult = string.Join(", ", midList);
}
// 陽上
var leftList = jo["left_items"]?
.Select(i => (string)i["fam_name"])
.Where(name => !string.IsNullOrEmpty(name))
.ToList();
if (leftList != null && leftList.Any())
{
leftNamesResult = string.Join(", ", leftList);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"JSON 解析失敗 (訂單:{item.order_no}): {ex.Message}");
}
//新增資料列
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(item.order_no), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.up_time.Value.ToString("yyyy/MM/dd")), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.up_time?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(Model.pro_order.keyin1_value_to_text(item.keyin1)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.f_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(encrypt.DecryptAutoKey(item.phone)), DataType = CellValues.String },
@@ -274,23 +792,44 @@ public partial class admin_order_index : MyWeb.config
new Cell() { CellValue = new CellValue(item.d_actItem_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_f_num), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_address), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_from_id), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_start_date), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_due_date), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_extend_date), DataType = CellValues.String },
new Cell() { CellValue = new CellValue((Val(item.d_category) ==(int)Model.activity.category.Order) ?( item.d_notBedQty +"/" + item.d_writeBedQty):""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(midNamesResult), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(leftNamesResult), DataType = CellValues.String },
//new Cell() { CellValue = new CellValue(item.d_from_id), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_start_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_due_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_extend_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue((Val(item.d_category) == (int)Model.activity.category.Order) ? (item.d_notBedQty + "/" + item.d_writeBedQty) : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(item.d_price)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_qty), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(ValFloat(item.d_price) * Val(item.d_qty))), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(item.d_pay)), DataType = CellValues.String },
new Cell() { CellValue = new CellValue("$" + ValMoney(ValFloat(item.d_price) * Val(item.d_qty) - ValFloat(item.d_pay))), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_pay_date), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_keyin1), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_pay_date?.ToString("yyyy/MM/dd") ?? ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_keyin1.HasValue ? tdesc[item.d_keyin1.Value] : ""), DataType = CellValues.String },
new Cell() { CellValue = new CellValue(item.d_demo), DataType = CellValues.String }
);
sd.AppendChild(tr);
}
//空一列
tr = new Row();
sd.AppendChild(tr);
//匯出資訊
string _data = "匯出時間 : " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
_data += " " + admin.info.u_id;
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
_data = "匯出條件 : " + (!isStrNull(_query) ? _query : "-");
tr = new Row();
tr.Append(
new Cell() { CellValue = new CellValue(_data), DataType = CellValues.String }
);
sd.AppendChild(tr);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Order, (int)Model.admin_log.Status.Excel, admin_log.LogViewBtn(list.Select(x => x.order_no).ToList()));
+2 -2
View File
@@ -41,7 +41,7 @@
{ text: '活動名稱​', value: 'subject', },
{ text: '報到日期​', value: 'reg_time_date', },
{ text: '報到時間​', value: 'reg_time_time' },
{ text: '姓名(人數)', value: 'qty' },
{ text: '姓名', value: 'qty' },
{ text: '狀態', value: 'statusTxt' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' }
@@ -199,7 +199,7 @@
{{item.reg_time |timeString('HH:mm:ss') }}
</template>
<template #item.qty="{ item }" >
{{item.u_name }}({{item.qty }})
{{item.u_name }}
</template>
+42 -10
View File
@@ -1,12 +1,21 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index3.aspx.cs" Inherits="admin_order_index3" %>
<%@ Register Src="~/admin/_uc/alert.ascx" TagPrefix="uc1" TagName="alert" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
<style>
.modify_modal{
width:calc(100vw-20%);
background-color:white;
opacity:1;
z-index:1046 !important;
}
</style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" runat="Server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<uc1:alert runat="server" ID="L_msg" Text="" />
<keep-alive>
<component
@@ -16,8 +25,24 @@
</keep-alive>
<div id="print_data">
</div>
<div id="print_data">
</div>
<%-- <v-container>
<v-card>
<v-card-text>
<v-dialog v-model="modify_dialog.show" class="modify_modal">
<template>
<keep-alive>
<component :is="modifyView" @close-dialog="modify_dialog.show=false"></component>
</keep-alive>
</template>
</v-dialog>
</v-card-text>
</v-card>
</v-container>--%>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" runat="Server">
@@ -625,6 +650,9 @@
Vue.component('step-two', {
template: `
<div class="container-fluid">
<template>
<button class='btn btn-plus' @click.prevent='fast_signup'>快速報名</button>
</template>
<v-data-table
v-model="detail_table.selected"
:items="detail_table.list"
@@ -728,6 +756,10 @@
},
methods: {
fast_signup() {
//this.$root.modify_dialog.show = true
this.$root.currentView = "step-three"
},
getDetail() {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
@@ -765,13 +797,8 @@
let VueApp = new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
currentView: 'step-one',
selected_act: '',
selected_order:'',
};
},activated() {
activated() {
console.log("yes ,go go");
this.getDetail();
},
@@ -779,6 +806,8 @@
return {
options: { multiSort: false },
search_options: { multiSort: false },
selected_act: '',
selected_order: '',
sign_table: {
loading: true,
list: [],
@@ -812,7 +841,10 @@
country: '',
country2: '',
hasPrice: '',
}
},
modifyView: '',
modify_dialog: { show: false },
currentView: 'step-one',
};
},
watch: {
+160
View File
@@ -0,0 +1,160 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index4.aspx.cs" Inherits="admin_order_index4" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
<link rel="stylesheet" href="../../js/_bootstrap-icons-1.8.1/bootstrap-icons.css">
<style>
.function-icon {
font-size: 2em;
line-height: 1;
align-content: center;
}
.external-link-icon {
font-size: 0.8em;
}
.modify_modal {
width: calc(100vw-20%);
background-color: white;
opacity: 0;
}
</style>
<script src="/js/httpVueLoader.js"></script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<%-- <v-container class="container-fluid">--%>
<div id="content" class="container-fluid py-4">
<keep-alive>
<component :is="currentView" :form-data="$data"
:num="twoData"
@custom-event="changeView">
</component>
</keep-alive>
</div>
<%-- </v-container>--%>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
<script>
let VueApp = new Vue({
el: '#app',
vuetify: new Vuetify(),
mounted() {
this.getDetail();
},
data() {
return {
options: { multiSort: false },
search_options: { multiSort: false },
selected_act: '',
selected_order: '',
sign_table: {
loading: true,
list: [],
selected: [],
singleSelect: false,
count: 0,
page: 1,
pageSize: 10,
header: [
{ text: '單號', value: 'order_no' },
{ text: '姓名', value: 'u_name' },
{ text: '報名日期', value: 'up_time' },
{ text: '單據狀態', value: 'keyin1_txt', align: 'start' },
{ text: '', value: 'slot_btn', sortable: false, align: 'end' }
],
footer: {
showFirstLastPage: true,
itemsPerPageOptions: [5, 10, 20, 30],
},
},
search: {
keyin1: '',
order_no: '',
subject: '',
u_name: '',
up_time1: '',
up_time2: '',
actItemTxt: '',
introducerTxt: '',
activity_num: '',
country: '',
country2: '',
hasPrice: '',
},
currentView: 'fastSignUp-component',
twoData: {},
detail_table:[]
};
},
components: {
'fastSignUp-component': httpVueLoader('./fastSignUp.vue'),
'fastSignUp2-component': httpVueLoader('./fastSignUp2.vue'),
'fastSignUp3-component': httpVueLoader('./fastSignUp3.vue'),
'fastSignUp4-component': httpVueLoader('./fastSignUp4.vue'),
'fastSignUp5-component': httpVueLoader('./fastSignUp5.vue'),
},
watch: {
options: {
handler() {
this.getDetail()
},
deep: true,
},
},
methods: {
changeView(item) {
if (item.action === "detail") {
this.twoData = item.item.num
this.currentView = "fastSignUp2-component"
} else if (item.action === "add") {
this.twoData = item.item.num
this.currentView = "fastSignUp3-component"
} else if (item.action === "signup4") {
this.twoData = item.item
this.currentView = "fastSignUp4-component"
} else if (item.action === "signup1") {
this.twoData = item.item
this.currentView = "fastSignUp-component"
} else if (item.action === "oldSign") {
location.href="/admin/order/index.aspx"
} else if (item.action === "batchSign") {
this.twoData = item.item.num
this.currentView = "fastSignUp5-component"
}
//console.log("item.action:",this.twoData)
},
getDetail() {
const { sortBy, sortDesc, page, itemsPerPage } = this.options
const params = {
page: '1', pageSize: 10,
};
this.search = { activity_num: this.$root.selected_act }
this.detail_table.loading = true
sessionStorage.setItem('orderpage', '1');// clearpage ? '1' : page
axios
.post(HTTP_HOST + 'api/order/GetList', this.search, { params: params })
.then(response => {
this.detail_table.list = response.data.list
this.detail_table.count = response.data.count;
this.detail_table.loading = false
})
.catch(error => console.log(error))
},
},
computed: {
pageCount() {
return Math.ceil(this.detail_table.count / this.detail_table.pageSize)
},
}
});
</script>
</asp:Content>
+14
View File
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class admin_order_index4 : MyWeb.config
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
+6 -13
View File
@@ -144,13 +144,10 @@
<tbody>
<tr>
<th>項目名稱</th>
<th class="nowarp">姓名</th>
<th>代表地址</th>
<th class="nowarp">陽上/報恩</th>
<th class="nowarp">陽上</th>
<th class="nowarp">標題</th>
<th class="fit">開始日</th>
<th class="fit">期滿日</th>
<th class="fit">延續日</th>
<th class="fit">劃位</th>
<asp:PlaceHolder ID="PlaceHolder2" runat="server">
<th class="fit">功德金</th>
<th class="fit">數量</th>
@@ -167,18 +164,14 @@
<td>
<asp:Literal ID="actitem_numTxt" runat="server"></asp:Literal></td>
<td class="nowarp">
<asp:Literal ID="f_numTxt" runat="server"></asp:Literal></td>
<td><%# Eval("address") %></td>
<asp:Literal ID="left_nameTxt" runat="server"></asp:Literal></td>
<td class="nowarp">
<asp:Literal ID="from_idTxt" runat="server"></asp:Literal></td>
<asp:Literal ID="mid_nameTxt" runat="server"></asp:Literal></td>
<td class="fit"><%# Eval("start_date") != null? Convert.ToDateTime( Eval("start_date")).ToString("yyyy/MM/dd") : "" %></td>
<td class="fit">
<asp:Literal ID="due_date" runat="server"></asp:Literal></td>
<td class="fit">
<asp:Literal ID="extend_date" runat="server"></asp:Literal></td>
<td class="fit">
<asp:Literal ID="BedQty" runat="server"></asp:Literal></td>
<asp:PlaceHolder ID="PlaceHolder2" runat="server">
<td class="fit text-end">
<asp:Literal ID="item_price" runat="server" Text='<%# Eval("price") %>'></asp:Literal></td>
+83 -25
View File
@@ -1,9 +1,15 @@
using DocumentFormat.OpenXml.Drawing.Charts;
using DocumentFormat.OpenXml.Vml.Office;
using Microsoft.Ajax.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.Helpers;
using System.Web.UI;
using System.Web.UI.WebControls;
@@ -182,8 +188,20 @@ public partial class admin_follower_print_ : System.Web.UI.Page
if (!string.IsNullOrEmpty(_chkHasAct) && Convert.ToBoolean(_chkHasAct))
{
_query += "活動報名\n";
if (!string.IsNullOrEmpty(_selectAct))
{
int selectAct = Convert.ToInt32(_selectAct);
if (selectAct > 0)
{
var actSubject = _db.activities.Where(a => a.num == selectAct).Select(a => a.subject).FirstOrDefault();
_query += $"活動報名: {actSubject}\n";
}
}
else
{
_query += $"活動報名\n";
}
if (!string.IsNullOrEmpty(_chkNoAct) && Convert.ToBoolean(_chkNoAct))
{
_query += "非活動報名\n";
@@ -200,7 +218,7 @@ public partial class admin_follower_print_ : System.Web.UI.Page
else
{
qry = qry.Where(o => o.activity_num.HasValue);
if (!string.IsNullOrEmpty(_selectAct))
{
int selectAct = Convert.ToInt32(_selectAct);
@@ -404,16 +422,56 @@ public partial class admin_follower_print_ : System.Web.UI.Page
actitem_numTxt.Text = row.actItem_num.HasValue && row.actItem != null ? row.actItem.subject : "";
}
Literal fNumTxtLit = (Literal)e.Item.FindControl("f_numTxt");
if (fNumTxtLit != null)
{
fNumTxtLit.Text = row.f_num.HasValue && row.follower != null ? row.follower.u_name : "";
}
//Literal fNumTxtLit = (Literal)e.Item.FindControl("f_numTxt");
//if (fNumTxtLit != null)
//{
// fNumTxtLit.Text = row.f_num.HasValue && row.follower != null ? row.follower.u_name : "";
//}
Literal fromIdTxtLit = (Literal)e.Item.FindControl("from_idTxt");
if (fromIdTxtLit != null)
string jsonString = row.f_num_tablet?.ToString() ?? "";
if (!string.IsNullOrWhiteSpace(jsonString))
{
fromIdTxtLit.Text = row.from_id.HasValue && row.follower1 != null ? row.follower1.u_name : "";
var jo = JObject.Parse(jsonString);
List<string> allMidNames = new List<string>();
List<string> allLeftNames = new List<string>();
// 標題
var midNames = jo["mid_items"]?
.Select(item => (string)item["fam_name"])
.ToList();
if (midNames != null && midNames.Any())
{
allMidNames.AddRange(midNames);
}
if (allMidNames.Any())
{
Literal midNameTextLit = (Literal)e.Item.FindControl("mid_nameTxt");
if (midNameTextLit != null)
{
midNameTextLit.Text = string.Join(", ", allMidNames);
}
}
// 陽上
var leftNames = jo["left_items"]?
.Select(item => (string)item["fam_name"])
.ToList();
if (leftNames != null && leftNames.Any())
{
allLeftNames.AddRange(leftNames);
}
if (allLeftNames.Any())
{
Literal leftNameTextLit = (Literal)e.Item.FindControl("left_nameTxt");
if (leftNameTextLit != null)
{
leftNameTextLit.Text = string.Join(", ", allLeftNames);
}
}
}
Literal dueDateLit = (Literal)e.Item.FindControl("due_date");
@@ -422,21 +480,21 @@ public partial class admin_follower_print_ : System.Web.UI.Page
dueDateLit.Text = row.due_date.HasValue ? row.due_date.Value.ToString("yyyy-MM-dd") : "";
}
Literal extendDateLit = (Literal)e.Item.FindControl("extend_date");
if (extendDateLit != null)
{
extendDateLit.Text = row.extend_date.HasValue ? row.extend_date.Value.ToString("yyyy-MM-dd") : "";
}
//Literal extendDateLit = (Literal)e.Item.FindControl("extend_date");
//if (extendDateLit != null)
//{
// extendDateLit.Text = row.extend_date.HasValue ? row.extend_date.Value.ToString("yyyy-MM-dd") : "";
//}
//劃位狀態
int writeBedQty = _bedDt.Where(b => b.bed_order != null && b.bed_order.o_detail_id == row.num && b.checkIn_date.HasValue && b.bed_kind_detail_id.HasValue).Count();
int notBedQty = _bedDt.Where(b => b.bed_order != null && b.bed_order.o_detail_id == row.num && (!b.checkIn_date.HasValue || !b.bed_kind_detail_id.HasValue)).Count();
Literal bedQtyLit = (Literal)e.Item.FindControl("BedQty");
if (bedQtyLit != null)
{
bedQtyLit.Text = (row.actItem != null && row.actItem.category.HasValue && Convert.ToInt32(row.actItem.category.Value) == (int)Model.activity.category.Order) ? (notBedQty + "/" + writeBedQty) : "";
}
////劃位狀態
//int writeBedQty = _bedDt.Where(b => b.bed_order != null && b.bed_order.o_detail_id == row.num && b.checkIn_date.HasValue && b.bed_kind_detail_id.HasValue).Count();
//int notBedQty = _bedDt.Where(b => b.bed_order != null && b.bed_order.o_detail_id == row.num && (!b.checkIn_date.HasValue || !b.bed_kind_detail_id.HasValue)).Count();
//Literal bedQtyLit = (Literal)e.Item.FindControl("BedQty");
//if (bedQtyLit != null)
//{
// bedQtyLit.Text = (row.actItem != null && row.actItem.category.HasValue && Convert.ToInt32(row.actItem.category.Value) == (int)Model.activity.category.Order) ? (notBedQty + "/" + writeBedQty) : "";
//}
Literal keyin1Lit = (Literal)e.Item.FindControl("keyin1");
if (keyin1Lit != null)
+7 -4
View File
@@ -755,6 +755,7 @@
curr.select(this.editedItem, row);
}
this.search_dialog.show = false;
this.search_dialog.page = 1;
//console.log(row, row["u_name"], row["f_number"], curr.id, target);
},
//報名詳細資料
@@ -859,7 +860,7 @@
isValidDate = Date.parse(item.pay_date);
if (!isNaN(isValidDate))
item.pay_date = new Date(item.pay_date).format("yyyy-MM-dd")
console.log(item);
this.editedItem = $.extend(true, {}, item);
this.data_dialog.show = true;
this.data_dialog.isAddNew = false;
@@ -1100,9 +1101,11 @@
/* (this.editedItem.category=="1"? this.editedItem.from_id_selected.val != 0 : true) &&*/
this.requireData(this.editedItem.qty, (this.editedItem.num == 0 ? false : true)) ) /* qty為0視為不需要此項目,不儲存此筆資料*/
{
console.log(this.editedItem);
//check price
if (this.editedItem.pay <= this.editedItem.price * this.editedItem.qty) {
console.log(this.editedItem.f_num_selected.val);
console.log(this.editedItem.from_id_selected.val);
//check qty
//數量不可小於掛單明細的數量
if (this.editedItem.qty >= this.editedItem.writeBedQty + this.editedItem.notBedQty) {
@@ -1857,7 +1860,7 @@
<div class="">
<asp:Button ID="add" runat="server" Text="送出" OnClick="add_Click" CssClass="btn btn-primary" />
<asp:Button ID="edit" runat="server" Text="修改" Visible="false" OnClick="edit_Click" CssClass="btn btn-primary" />
<asp:Button ID="goback" runat="server" Text="回列表" Visible="false" CausesValidation="false" OnClick="goback_Click" CssClass="btn btn-outline-secondary" />
<asp:Button ID="goback" runat="server" Text="取消" CausesValidation="false" OnClick="goback_Click" CssClass="btn btn-outline-secondary" />
</div>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
@@ -2486,7 +2489,7 @@
<v-card>
<v-card-title class="justify-space-between grey lighten-2">
查詢:{{search_dialog.current.title}}
<v-btn icon @click="search_dialog.show=false"><v-icon>mdi-close</v-icon></v-btn>
<v-btn icon @click="search_dialog.show=false; search_dialog.page = 1;"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<v-card-text >
<v-row>
+26 -12
View File
@@ -1,15 +1,16 @@
using System;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Data.Entity.Infrastructure;
public partial class admin_order_reg : MyWeb.config
{
@@ -187,16 +188,29 @@ public partial class admin_order_reg : MyWeb.config
{
if (!isStrNull(pro_order.order_no))
{
_db.pro_order.Add(pro_order);
_db.SaveChanges();
bool isRegistered = _db.pro_order.Any(x => x.f_num == pro_order.f_num && x.activity_num == pro_order.activity_num);
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Order, (int)Model.admin_log.Status.Insert, pro_order.order_no);
if (isRegistered) // 重複報名
{
L_msg.Type = alert_type.warning;
L_msg.Text = "此信眾已報名過本活動";
}
else
{
_db.pro_order.Add(pro_order);
_db.SaveChanges();
Model.admin_log admin_log = new Model.admin_log();
admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Order, (int)Model.admin_log.Status.Insert, pro_order.order_no);
string url = "index.aspx";
url = "reg.aspx?order_no=" + pro_order.order_no;
Response.Redirect(url);
string url = "index.aspx";
url = "reg.aspx?order_no=" + pro_order.order_no;
Session["LastAddedNo"] = pro_order.order_no;
Response.Redirect(url);
}
}
else
{
+62 -13
View File
@@ -44,7 +44,8 @@
<div class="row mt-2">
<div class="col-3">
版型設定:
<select id="styleSel" onchange="designer.changeStyle()">
<select id="styleSel" onchange="designer.changeStyle()" class="form-select">
<option value="">請選擇版型</option>
</select>
</div>
@@ -211,10 +212,10 @@
toast: null,
modal: null,
bg: [
{ name: "1", path: "../../../admin/print/html/tablet-1_new.svg" },
{ name: "2", path: "../../../admin/print/html/tablet-1B_new.svg" },
{ name: "1", path: "../../../admin/print/html/tablet-2.svg" },
{ name: "2", path: "../../../admin/print/html/tablet-2B.svg" }
{ name: "超薦1", path: "../../../admin/print/html/tablet-1_new.svg" },
{ name: "超薦2", path: "../../../admin/print/html/tablet-1B_new.svg" },
{ name: "消災1", path: "../../../admin/print/html/tablet-2.svg" },
{ name: "消災2", path: "../../../admin/print/html/tablet-2B.svg" }
],
async init() {
// 載入常用片語
@@ -303,7 +304,7 @@
let data = response.data;
if (data.data) {
data.data.forEach(x => {
if (x.styleID !== "00001") {
if (x.styleID !== "000001") {
this.allStyle.push(x);
$("#styleSel").append(`<option value="${x.styleID}">${x.name}</option`);
}
@@ -853,11 +854,28 @@
self.bindEvent();
},
async addNewItem() {
console.log("addNewItem:",$("#custom_txt").val())
if ($("#custom_txt").val()=="") {
alert("名稱不可空白")
return false
}
let self = this;
let target = $(self.selectedElement).children().first();
let error=""
//let fam = self.familyMembers.find(x => x.fam_name == target.text());
if (target.hasClass("liveList")) {
self.alive.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.alive.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
@@ -871,6 +889,15 @@
}
self.family_deceased_Y_selected.push(obj);
} else if (target.hasClass("nameList") || target.hasClass("rosterList")) {
self.properTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.properTitle.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
@@ -884,6 +911,16 @@
}
self.family_deceased_N_selected.push(obj);
} else if (target.hasClass("ancestor-wrapper")) {
//再來判斷是否已有這個名稱
self.properTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
if (self.properTitle.length >= 2) {
alert("多姓氏合併必須第一個名稱為姓,第二個名稱為氏歷代祖先之類,且最多兩個名稱");
} else {
@@ -901,9 +938,15 @@
self.family_deceased_N_selected.push(obj);
}
} else if (target.hasClass("lefttitle")) {
//if (self.leftProperTitle.length >= 1) {
// alert("左正名");
//} else {
self.leftProperTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.leftProperTitle.length = 0;
self.family_left_title.length = 0;
self.leftProperTitle.push($("#custom_txt").val());
@@ -921,9 +964,15 @@
//self.family_deceased_N_selected.push(obj);
//}
} else if (target.hasClass("righttitle")) {
//if (self.rightProperTitle.length >= 1) {
// alert("右正名");
//} else {
self.rightProperTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.rightProperTitle.length = 0;
self.family_right_title.length = 0;
self.rightProperTitle.push($("#custom_txt").val());
+29 -2
View File
@@ -105,6 +105,23 @@
></v-text-field>
</div>
</template>
<template v-slot:item.kind="{ item }">
<div>
<div class="mb-2">
<span class="badge bg-primary me-1" title="支付方式">收支項目</span>
</div>
</div>
<div>
<v-select
:items="accountingKinds"
v-model="item.kind"
dense
outlined
hide-details
style="max-width: 200px"
></v-select>
</div>
</template>
<template v-slot:item.check_memo="{ item }">
<div class="d-flex align-center my-2" style="min-width: 300px">
<v-text-field
@@ -159,6 +176,7 @@ new Vue({
{ text: '*入帳銀行/帳戶 | 支付資訊/帳號後5碼', value: 'acc_num' },
{ text: '*入帳日期', value: 'check_date' },
{ text: '*入帳金額', value: 'check_amount' },
{ text: '*收支項目', value: 'kind' },
{ text: '備註/狀態 | 核對記錄', value: 'check_memo' }
],
items: [],
@@ -172,7 +190,8 @@ new Vue({
{ text: '其他問題', value: '4' },
{ text: '作廢', value: '5' }
],
bankOptions: [],
bankOptions: [],
accountingKinds:[],
payTypeText: {
1: '現金',
2: '匯款',
@@ -212,6 +231,13 @@ new Vue({
text: x.kind + (x.bank_name ? ' - ' + x.bank_name : '') + (x.bank_id ? ' (' + x.bank_id + ')' : ''),
value: x.num
}));
const kindRes = await axios.post('../../api/accounting/GetTitleKindList', {}, { params: { page: 1, pageSize: 1000 } });
this.accountingKinds = kindRes.data.list.map(x => ({
text: x.kind ,
value: x.num
}));
this.loading = false;
},
async submitData() {
@@ -246,7 +272,8 @@ new Vue({
acc_num: item.acc_num,
check_date: item.check_date,
check_amount: item.check_amount,
check_memo: item.check_memo
check_memo: item.check_memo,
kind: item.kind
}));
try {
const res = await axios.post('../../api/transfer_register/batch_update', updateList);
@@ -65,6 +65,7 @@
</template>
<template v-slot:item.transfer_info="{ item }">
<div>
<div class="font-weight-bold text-primary">應收總額:{{ item.price_totals | currency }}</div>
<div class="font-weight-bold text-primary">入帳金額:{{ item.transfer_check_amount | currency }}</div>
<div class="text-success">沖帳日期:{{ item.transfer_check_date | date }}</div>
<div v-if="item.transfer_remain_amount > 0" class="text-warning">
@@ -202,6 +203,7 @@
<div class="mb-2"><span class="font-weight-bold">入帳帳戶:</span>{{ dialog.selected.acc_name }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳日期:</span>{{ dialog.selected.transfer_check_date | date }}</div>
<div class="mb-2"><span class="font-weight-bold">入帳金額:</span>{{ dialog.selected.transfer_check_amount | currency }}</div>
<div class="mb-2"><span class="font-weight-bold">應收總額:</span>{{ dialog.selected.price_totals | currency }}</div>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
+478
View File
@@ -0,0 +1,478 @@
(function umd(root,factory){
if(typeof module==='object' && typeof exports === 'object' )
module.exports=factory()
else if(typeof define==='function' && define.amd)
define([],factory)
else
root.httpVueLoader=factory()
})(this,function factory() {
'use strict';
var scopeIndex = 0;
StyleContext.prototype = {
withBase: function(callback) {
var tmpBaseElt;
if ( this.component.baseURI ) {
// firefox and chrome need the <base> to be set while inserting or modifying <style> in a document.
tmpBaseElt = document.createElement('base');
tmpBaseElt.href = this.component.baseURI;
var headElt = this.component.getHead();
headElt.insertBefore(tmpBaseElt, headElt.firstChild);
}
callback.call(this);
if ( tmpBaseElt )
this.component.getHead().removeChild(tmpBaseElt);
},
scopeStyles: function(styleElt, scopeName) {
function process() {
var sheet = styleElt.sheet;
var rules = sheet.cssRules;
for ( var i = 0; i < rules.length; ++i ) {
var rule = rules[i];
if ( rule.type !== 1 )
continue;
var scopedSelectors = [];
rule.selectorText.split(/\s*,\s*/).forEach(function(sel) {
scopedSelectors.push(scopeName+' '+sel);
var segments = sel.match(/([^ :]+)(.+)?/);
scopedSelectors.push(segments[1] + scopeName + (segments[2]||''));
});
var scopedRule = scopedSelectors.join(',') + rule.cssText.substr(rule.selectorText.length);
sheet.deleteRule(i);
sheet.insertRule(scopedRule, i);
}
}
try {
// firefox may fail sheet.cssRules with InvalidAccessError
process();
} catch (ex) {
if ( ex instanceof DOMException && ex.code === DOMException.INVALID_ACCESS_ERR ) {
styleElt.sheet.disabled = true;
styleElt.addEventListener('load', function onStyleLoaded() {
styleElt.removeEventListener('load', onStyleLoaded);
// firefox need this timeout otherwise we have to use document.importNode(style, true)
setTimeout(function() {
process();
styleElt.sheet.disabled = false;
});
});
return;
}
throw ex;
}
},
compile: function() {
var hasTemplate = this.template !== null;
var scoped = this.elt.hasAttribute('scoped');
if ( scoped ) {
// no template, no scopable style needed
if ( !hasTemplate )
return;
// firefox does not tolerate this attribute
this.elt.removeAttribute('scoped');
}
this.withBase(function() {
this.component.getHead().appendChild(this.elt);
});
if ( scoped )
this.scopeStyles(this.elt, '['+this.component.getScopeId()+']');
return Promise.resolve();
},
getContent: function() {
return this.elt.textContent;
},
setContent: function(content) {
this.withBase(function() {
this.elt.textContent = content;
});
}
};
function StyleContext(component, elt) {
this.component = component;
this.elt = elt;
}
ScriptContext.prototype = {
getContent: function() {
return this.elt.textContent;
},
setContent: function(content) {
this.elt.textContent = content;
},
compile: function(module) {
var childModuleRequire = function(childURL) {
return httpVueLoader.require(resolveURL(this.component.baseURI, childURL));
}.bind(this);
var childLoader = function(childURL, childName) {
return httpVueLoader(resolveURL(this.component.baseURI, childURL), childName);
}.bind(this);
try {
Function('exports', 'require', 'httpVueLoader', 'module', this.getContent()).call(this.module.exports, this.module.exports, childModuleRequire, childLoader, this.module);
} catch(ex) {
if ( !('lineNumber' in ex) ) {
return Promise.reject(ex);
}
var vueFileData = responseText.replace(/\r?\n/g, '\n');
var lineNumber = vueFileData.substr(0, vueFileData.indexOf(script)).split('\n').length + ex.lineNumber - 1;
throw new (ex.constructor)(ex.message, url, lineNumber);
}
return Promise.resolve(this.module.exports)
.then(httpVueLoader.scriptExportsHandler.bind(this))
.then(function(exports) {
this.module.exports = exports;
}.bind(this));
}
};
function ScriptContext(component, elt) {
this.component = component;
this.elt = elt;
this.module = { exports:{} };
}
TemplateContext.prototype = {
getContent: function() {
return this.elt.innerHTML;
},
setContent: function(content) {
this.elt.innerHTML = content;
},
getRootElt: function() {
var tplElt = this.elt.content || this.elt;
if ( 'firstElementChild' in tplElt )
return tplElt.firstElementChild;
for ( tplElt = tplElt.firstChild; tplElt !== null; tplElt = tplElt.nextSibling )
if ( tplElt.nodeType === Node.ELEMENT_NODE )
return tplElt;
return null;
},
compile: function() {
return Promise.resolve();
}
};
function TemplateContext(component, elt) {
this.component = component;
this.elt = elt;
}
Component.prototype = {
getHead: function() {
return document.head || document.getElementsByTagName('head')[0];
},
getScopeId: function() {
if ( this._scopeId === '' ) {
this._scopeId = 'data-s-' + (scopeIndex++).toString(36);
this.template.getRootElt().setAttribute(this._scopeId, '');
}
return this._scopeId;
},
load: function(componentURL) {
return httpVueLoader.httpRequest(componentURL)
.then(function(responseText) {
this.baseURI = componentURL.substr(0, componentURL.lastIndexOf('/')+1);
var doc = document.implementation.createHTMLDocument('');
// IE requires the <base> to come with <style>
doc.body.innerHTML = (this.baseURI ? '<base href="'+this.baseURI+'">' : '') + responseText;
for ( var it = doc.body.firstChild; it; it = it.nextSibling ) {
switch ( it.nodeName ) {
case 'TEMPLATE':
this.template = new TemplateContext(this, it);
break;
case 'SCRIPT':
this.script = new ScriptContext(this, it);
break;
case 'STYLE':
this.styles.push(new StyleContext(this, it));
break;
}
}
return this;
}.bind(this));
},
_normalizeSection: function(eltCx) {
var p;
if ( eltCx === null || !eltCx.elt.hasAttribute('src') ) {
p = Promise.resolve(null);
} else {
p = httpVueLoader.httpRequest(eltCx.elt.getAttribute('src'))
.then(function(content) {
eltCx.elt.removeAttribute('src');
return content;
});
}
return p
.then(function(content) {
if ( eltCx !== null && eltCx.elt.hasAttribute('lang') ) {
var lang = eltCx.elt.getAttribute('lang');
eltCx.elt.removeAttribute('lang');
return httpVueLoader.langProcessor[lang.toLowerCase()].call(this, content === null ? eltCx.getContent() : content);
}
return content;
}.bind(this))
.then(function(content) {
if ( content !== null )
eltCx.setContent(content);
});
},
normalize: function() {
return Promise.all(Array.prototype.concat(
this._normalizeSection(this.template),
this._normalizeSection(this.script),
this.styles.map(this._normalizeSection)
))
.then(function() {
return this;
}.bind(this));
},
compile: function() {
return Promise.all(Array.prototype.concat(
this.template && this.template.compile(),
this.script && this.script.compile(),
this.styles.map(function(style) { return style.compile(); })
))
.then(function() {
return this;
}.bind(this));
}
};
function Component(name) {
this.name = name;
this.template = null;
this.script = null;
this.styles = [];
this._scopeId = '';
}
function identity(value) {
return value;
}
function parseComponentURL(url) {
var comp = url.match(/(.*?)([^/]+?)\/?(\.vue)?(\?.*|#.*|$)/);
return {
name: comp[2],
url: comp[1] + comp[2] + (comp[3] === undefined ? '/index.vue' : comp[3]) + comp[4]
};
}
function resolveURL(baseURL, url) {
if (url.substr(0, 2) === './' || url.substr(0, 3) === '../') {
return baseURL + url;
}
return url;
}
httpVueLoader.load = function(url, name) {
return function() {
return new Component(name).load(url)
.then(function(component) {
return component.normalize();
})
.then(function(component) {
return component.compile();
})
.then(function(component) {
var exports = component.script !== null ? component.script.module.exports : {};
if ( component.template !== null )
exports.template = component.template.getContent();
if ( exports.name === undefined )
if ( component.name !== undefined )
exports.name = component.name;
exports._baseURI = component.baseURI;
return exports;
});
};
};
httpVueLoader.register = function(Vue, url) {
var comp = parseComponentURL(url);
Vue.component(comp.name, httpVueLoader.load(comp.url));
};
httpVueLoader.install = function(Vue) {
Vue.mixin({
beforeCreate: function () {
var components = this.$options.components;
for ( var componentName in components ) {
if ( typeof(components[componentName]) === 'string' && components[componentName].substr(0, 4) === 'url:' ) {
var comp = parseComponentURL(components[componentName].substr(4));
var componentURL = ('_baseURI' in this.$options) ? resolveURL(this.$options._baseURI, comp.url) : comp.url;
if ( isNaN(componentName) )
components[componentName] = httpVueLoader.load(componentURL, componentName);
else
components[componentName] = Vue.component(comp.name, httpVueLoader.load(componentURL, comp.name));
}
}
}
});
};
httpVueLoader.require = function(moduleName) {
return window[moduleName];
};
httpVueLoader.httpRequest = function(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'text';
xhr.onreadystatechange = function() {
if ( xhr.readyState === 4 ) {
if ( xhr.status >= 200 && xhr.status < 300 )
resolve(xhr.responseText);
else
reject(xhr.status);
}
};
xhr.send(null);
});
};
httpVueLoader.langProcessor = {
html: identity,
js: identity,
css: identity
};
httpVueLoader.scriptExportsHandler = identity;
function httpVueLoader(url, name) {
var comp = parseComponentURL(url);
return httpVueLoader.load(comp.url, name);
}
return httpVueLoader;
});
+7 -4
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
@@ -42,12 +42,14 @@
<add key="UseSearchKeywords" value="true" />
<!--網站的簡稱,用來區別session和cookie-->
<add key="SC" value="erp17168" />
<add key="LogPath" value="C:\\log\\" />
<add key="LogPath" value="D:\\log\\" />
<!--是否啟用信眾自動編號 (true=啟用, false=停用)-->
<add key="IsAutoNumbering" value="true" />
</appSettings>
<connectionStrings>
<!--SQL用-->
<add name="shopConn" providerName="System.Data.SqlClient" connectionString="Data Source=192.168.5.20;Initial Catalog=17168erp_c;User ID=sa;Password=linux@mssql1234;Encrypt=False;TrustServerCertificate=True;Provider=SQLOLEDB;Connection Timeout=10;" />
<add name="ezEntities" providerName="System.Data.EntityClient" connectionString="metadata=res://*/App_Code.Model.Model.csdl|res://*/App_Code.Model.Model.ssdl|res://*/App_Code.Model.Model.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=192.168.5.20;Initial Catalog=17168erp_c;User ID=sa;Password=linux@mssql1234;Encrypt=False;TrustServerCertificate=True;Connection Timeout=10;&quot;" />
<add name="shopConn" providerName="System.Data.SqlClient" connectionString="Data Source=192.168.5.225;Initial Catalog=17168erp_c;User ID=sa;Password=linux@mssql1234;Encrypt=False;TrustServerCertificate=True;Provider=SQLOLEDB;Connection Timeout=10;" />
<add name="ezEntities" providerName="System.Data.EntityClient" connectionString="metadata=res://*/App_Code.Model.Model.csdl|res://*/App_Code.Model.Model.ssdl|res://*/App_Code.Model.Model.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=192.168.5.225;Initial Catalog=17168erp_c;User ID=sa;Password=linux@mssql1234;Encrypt=False;TrustServerCertificate=True;Connection Timeout=10;&quot;" />
<!--
-->
<!--SQL用-->
@@ -223,6 +225,7 @@
<urlCompression doStaticCompression="true" doDynamicCompression="true" />
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="777.00:00:00" />
<mimeMap fileExtension=".vue" mimeType="application/javascript"/>
</staticContent>
</system.webServer>
<runtime>
+8
View File
@@ -0,0 +1,8 @@
FK_act_bom_actItem1 刪除規則設為 cascade
FK_pro_order_followers 刪除規則設為 cascade
FK_pro_order_activity 刪除規則設為 cascade
FK_pro_order_detail_pro_order 刪除規則設為 cascade
FK_pro_order_detail_followers 刪除規則設為 沒有動作
FK_activity_relating_activity 刪除規則設為 cascade
actitem 加上 sort_order(int) 欄位