UpdateMediaDialog.razor 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. @using AI.Platform.Page.Pages.Media.Model
  2. @using System.Threading.Tasks
  3. @using System.Text.Json
  4. @using System.Net.Http.Headers
  5. @if (IsVisible && Model != null)
  6. {
  7. <div class="modal-overlay">
  8. <div class="modal-content filter_box">
  9. @if (Model?.Type == 1 || Model?.Type == 2)
  10. {
  11. <EditForm Model="@Model" style="width:100%" Context="editContext" @key="Model">
  12. <DataAnnotationsValidator />
  13. <div class="filter_row">
  14. <div class="filter_colume">
  15. <div class="filter_row_around">
  16. <span>文件名</span><Input Placeholder="文件名" @bind-Value="Model.FileName" Style="width:80%" Disabled />
  17. </div>
  18. <ValidationMessage For="@(() => Model.FileName)" style="color:red"/>
  19. </div>
  20. <div class="filter_colume">
  21. <div class="filter_row_around">
  22. <span>操作人</span><Input Placeholder="操作人" @bind-Value="Model.MediaUploader" Style="width:80%" Disabled />
  23. </div>
  24. <ValidationMessage For="@(() => Model.MediaUploader)" style="color:red" />
  25. </div>
  26. </div>
  27. <div class="filter_row">
  28. <div class="filter_colume">
  29. <div class="filter_row_around">
  30. <span>广告状态</span>
  31. <SimpleSelect DefaultValue="@Model.MediaState.ToString()" OnSelectedItemChanged="@(data => OnSelectItemChange(1, data, editContext))" Style="width:80%">
  32. <SelectOptions>
  33. <SimpleSelectOption Value="0" Label="禁用" style="width:80%" />
  34. <SimpleSelectOption Value="1" Label="可用" style="width:80%" />
  35. </SelectOptions>
  36. </SimpleSelect>
  37. </div>
  38. <ValidationMessage For="@(() => Model.MediaState)" style="color:red" />
  39. </div>
  40. <div class="filter_colume">
  41. <div class="filter_row_around">
  42. <span>油机状态</span>
  43. <SimpleSelect DefaultValues="@Model.getMachineStateList()" OnSelectedItemsChanged="@(data => OnSelectItemsChange(data, editContext))" Mode="SelectMode.Multiple" style="width:80%">
  44. <SelectOptions>
  45. <SimpleSelectOption Value="idle" Label="空闲" />
  46. <SimpleSelectOption Value="lock" Label="锁枪" />
  47. <SimpleSelectOption Value="offline" Label="脱机" />
  48. <SimpleSelectOption Value="lift" Label="提枪" />
  49. <SimpleSelectOption Value="authorised" Label="授权" />
  50. <SimpleSelectOption Value="start" Label="开始" />
  51. <SimpleSelectOption Value="fueling" Label="加油中" />
  52. </SelectOptions>
  53. </SimpleSelect>
  54. </div>
  55. <ValidationMessage For="@(() => Model.MachineStateList)" style="color:red" />
  56. </div>
  57. </div>
  58. <div class="filter_row">
  59. <div class="filter_colume">
  60. <div class="filter_row_around">
  61. <span>有效时间段</span><RangePicker TValue="DateTime?[]" Value="EffecitiveTime" OnChange="@(date => onDateChage(date, 1))" />
  62. </div>
  63. <ValidationMessage For="@(() => Model.EffecitiveTime)" style="color:red" />
  64. </div>
  65. <div class="filter_colume">
  66. <div class="filter_row_around">
  67. <span style="margin-right:5%">播放时段</span><RangePicker TValue="DateTime?[]" Value="playRange" Picker="DatePickerType.Time" Format="@("HH")" OnChange="@(date => onDateChage(date, 2))" />
  68. </div>
  69. <ValidationMessage For="@(() => Model.StartTime)" style="color:red" />
  70. </div>
  71. </div>
  72. <div class="filter_row">
  73. <div class="filter_colume">
  74. <div class="filter_row_around">
  75. <span>下发油站</span>
  76. <SimpleSelect DefaultValue="@Model.BusinessUnitID.ToString()" Disabled="@(Model.Type == 2)" OnSelectedItemChanged="@(data => OnSelectItemChange(2, data, editContext))" style="width:80%">
  77. <SelectOptions>
  78. @foreach (var department in Model.siteInfos)
  79. {
  80. <SimpleSelectOption Value="@department.Id.ToString()" Label="@department.Name" style="width:80%" />
  81. }
  82. </SelectOptions>
  83. </SimpleSelect>
  84. </div>
  85. <ValidationMessage For="@(() => Model.BusinessUnitID)" style="color:red" />
  86. </div>
  87. <div class="filter_colume">
  88. <div class="filter_row_around">
  89. <span>备注</span><Input Placeholder="备注" @bind-Value="Model.Remark" style="width:80%" />
  90. </div>
  91. <ValidationMessage For="@(() => Model.Remark)" style="color:red" />
  92. </div>
  93. </div>
  94. @if (Model?.Type == 1)
  95. {
  96. <div class="upload_box">
  97. <div class="upload-container">
  98. <div class="custom-upload-content">
  99. <Icon Type="upload" />
  100. <div>点击上传文件</div>
  101. @if (selectedFile != null)
  102. {
  103. <div class="file-name">@selectedFile.Name</div>
  104. }
  105. </div>
  106. <InputFile OnChange="OnFileSelected"
  107. Class="transparent-input-file" />
  108. </div>
  109. </div>
  110. }
  111. <div class="filter_row" style="justify-content:end;margin-top:1%;">
  112. <button Icon="plus" type="button" @onclick="() => HandleSubmit(editContext)" style="margin-right:2%">确定</button>
  113. <button Icon="reload" @onclick="Close" style="margin-right:2%">取消</button>
  114. </div>
  115. </EditForm>
  116. }
  117. @if (isUploading)
  118. {
  119. <div style="position: absolute; inset: 0; background: rgba(255,255,255,0.6); z-index: 100;">
  120. <Spin Tip="上传中..."
  121. Style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
  122. </div>
  123. }
  124. @if(Model?.Type == 3)
  125. {
  126. <h3>是否删除广告?@(Model.FileName)</h3>
  127. <div class="filter_row" style="justify-content:end;margin-top:5%;">
  128. <Button Icon="plus" OnClick="onSure" Style="margin-right:2%">确定</Button>
  129. <Button Icon="reload" OnClick="Close" Style="margin-right:2%">取消</Button>
  130. </div>
  131. }
  132. </div>
  133. </div>
  134. }
  135. @code {
  136. /// <summary>
  137. /// 信息回调
  138. /// </summary>
  139. [Parameter] public EventCallback<MediaDialogModel> ModelChanged { get; set; }
  140. /// <summary>
  141. /// 打开/关闭窗口回调
  142. /// </summary>
  143. [Parameter] public EventCallback<bool> IsVisibleChanged { get; set; }
  144. /// <summary>
  145. /// 信息回调
  146. /// </summary>
  147. [Parameter] public EventCallback<MediaDialogModel> onCallback { get; set; }
  148. /// <summary>
  149. /// 打开/关闭窗口回调
  150. /// </summary>
  151. [Parameter] public EventCallback<bool> onVisibleCallback { get; set; }
  152. /// <summary>
  153. /// 数据
  154. /// </summary>
  155. [Parameter] public MediaDialogModel Model { get; set; }
  156. /// <summary>
  157. /// 配置是否弹窗
  158. /// </summary>
  159. [Parameter] public bool IsVisible { get; set; }
  160. /// <summary>
  161. /// 上传文件
  162. /// </summary>
  163. private List<UploadFileItem> fileList = new List<UploadFileItem>();
  164. /// <summary>
  165. /// 用于显示当前有效时间范围
  166. /// </summary>
  167. private DateTime?[] EffecitiveTime = new DateTime?[2];
  168. /// <summary>
  169. /// 用于显示当前播放时段
  170. /// </summary>
  171. private DateTime?[] playRange = new DateTime?[2];
  172. private Dictionary<string, string> uploadFileHeader = new Dictionary<string, string>();
  173. [Inject] public HttpClient Http { get; set; } = default!;
  174. [Inject] public MessageService Message { get; set; } = default!;
  175. private ElementReference inputFileRef;
  176. private IBrowserFile? selectedFile;
  177. private bool isUploading = false;
  178. protected override void OnParametersSet()
  179. {
  180. fileList.Clear();
  181. selectedFile = null;
  182. uploadFileHeader["Authorization"] = Global.GetToken();
  183. EffecitiveTime = new DateTime?[]
  184. {
  185. Model?.EffecitiveTime,
  186. Model?.FailureTime
  187. };
  188. int startTimeInt = Math.Max(0, Math.Min(23, Model?.StartTime ?? 0));
  189. int endTimeInt = Math.Max(0, Math.Min(23, Model?.EndTime ?? 0));
  190. DateTime startTime = DateTime.Today.AddHours(startTimeInt);
  191. DateTime endTime = DateTime.Today.AddHours(endTimeInt);
  192. playRange = new DateTime?[]
  193. {
  194. startTime,endTime
  195. };
  196. }
  197. /// <summary>
  198. /// 关闭弹窗
  199. /// </summary>
  200. public async Task Close()
  201. {
  202. await OnlyClose();
  203. if(Model.SavePath.IsNotNullOrEmpty()) File.Delete(Model.SavePath);
  204. }
  205. /// <summary>
  206. /// 仅关闭弹窗
  207. /// </summary>
  208. /// <returns></returns>
  209. private async Task OnlyClose()
  210. {
  211. if (IsVisibleChanged.HasDelegate)
  212. {
  213. await IsVisibleChanged.InvokeAsync(false);
  214. }
  215. if (onVisibleCallback.HasDelegate)
  216. {
  217. await onVisibleCallback.InvokeAsync(false);
  218. }
  219. }
  220. private void OnFileSelected(InputFileChangeEventArgs e)
  221. {
  222. // if (e.Value is not IBrowserFile file) return;
  223. var siteinfo = Model.siteInfos.Find(it => it.Id == Model.BusinessUnitID);
  224. if (siteinfo != null && e.File.Size > siteinfo.MaxSize * 1024 * 1024)
  225. {
  226. Message.Error("上传文件超过限制大小");
  227. return;
  228. }
  229. selectedFile = e.File;
  230. Model.FileName = e.File.Name;
  231. }
  232. private async Task UploadFile()
  233. {
  234. if (isUploading || selectedFile == null) return;
  235. isUploading = true;
  236. StateHasChanged();
  237. try
  238. {
  239. const long maxFileSize = 300 * 1024 * 1024; // 300 MB
  240. using var stream = selectedFile.OpenReadStream(maxFileSize);
  241. using var content = new MultipartFormDataContent();
  242. content.Add(new StreamContent(stream), "file", selectedFile.Name);
  243. content.Add(new StringContent(Model.BusinessUnitID.ToString()), "siteId");
  244. var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5076/api/File/UploadMedia")
  245. {
  246. Content = content
  247. };
  248. request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Global.Token);
  249. // 发送请求
  250. var httpResponse = await Http.SendAsync(request);
  251. httpResponse.EnsureSuccessStatusCode();
  252. var responseJson = await httpResponse.Content.ReadAsStringAsync();
  253. var response = JsonSerializer.Deserialize<Service.Output.Response<Service.Output.MediaFileUploadOutput>>(responseJson);
  254. Model.FileName = response?.data.fileName ?? "";
  255. Model.SavePath = response?.data.savePath ?? "";
  256. Model.GuidFileName = response?.data.guidName ?? "";
  257. Model.FileExtension = response?.data.extension ?? "";
  258. // Message.Success("上传成功!");
  259. // selectedFile = null; // 清空
  260. }
  261. catch (Exception ex)
  262. {
  263. Message.Error( $"上传失败: {ex.Message}");
  264. Model.FileName = "";
  265. }
  266. finally
  267. {
  268. isUploading = false;
  269. StateHasChanged();
  270. }
  271. }
  272. /// <summary>
  273. /// 上传文件完毕
  274. /// </summary>
  275. /// <param name="info"></param>
  276. private void OnUploadCompleted(UploadInfo info)
  277. {
  278. // _upload.StartUpload()
  279. string responseJson = info.File.Response;
  280. var response = JsonSerializer.Deserialize<Service.Output.Response<Service.Output.MediaFileUploadOutput>>(responseJson);
  281. Model.FileName = response?.data.fileName ?? "";
  282. Model.SavePath = response?.data.savePath ?? "";
  283. Model.GuidFileName = response?.data.guidName ?? "";
  284. Model.FileExtension = response?.data.extension ?? "";
  285. }
  286. /// <summary>
  287. /// 文件上传组件——删除事件
  288. /// </summary>
  289. /// <param name="fileItem"></param>
  290. /// <returns></returns>
  291. private async Task<bool> OnRemove(UploadFileItem fileItem)
  292. {
  293. Console.WriteLine(fileItem);
  294. if (Model.SavePath.IsNotNullOrEmpty()) File.Delete(Model.SavePath);
  295. return true;
  296. }
  297. /// <summary>
  298. /// 单选选择器选择事件
  299. /// </summary>
  300. /// <param name="type">1:油机状态;2:站点</param>
  301. /// <param name="value">选择的值</param>
  302. private void OnSelectItemChange(int type, string value, EditContext editContext)
  303. {
  304. Console.WriteLine(value);
  305. switch (type)
  306. {
  307. case 1:
  308. int state = -1;
  309. int.TryParse(value, out state);
  310. Model.MediaState = state;
  311. editContext.NotifyFieldChanged(FieldIdentifier.Create(() => Model.MediaState));
  312. break;
  313. case 2:
  314. var siteinfo = Model.siteInfos.Find(it => it.Id.ToString().Equals(value));
  315. if (siteinfo != null)
  316. {
  317. if (selectedFile != null && selectedFile.Size > siteinfo.MaxSize * 1024 * 1024)
  318. {
  319. Message.Error("上传文件超过限制大小");
  320. selectedFile = null;
  321. Model.FileName = "";
  322. }
  323. Model.GroupID = siteinfo.ParentId;
  324. Model.BusinessUnitID = siteinfo.Id;
  325. editContext.NotifyFieldChanged(FieldIdentifier.Create(() => Model.BusinessUnitID));
  326. }
  327. break;
  328. }
  329. }
  330. /// <summary>
  331. /// 多选选择器选择事件
  332. /// </summary>
  333. /// <param name="values">选择的值</param>
  334. private void OnSelectItemsChange(IEnumerable<string> values ,EditContext editContext)
  335. {
  336. Console.WriteLine(values);
  337. if (values.Count() <= 0)
  338. {
  339. Model.MachineStateList = Model.getAllMachineStateList();
  340. } else
  341. {
  342. Model.MachineStateList = values.ToList();
  343. }
  344. editContext.NotifyFieldChanged(FieldIdentifier.Create(() => Model.MachineStateList));
  345. }
  346. /// <summary>
  347. /// 日期选择器选择回调
  348. /// </summary>
  349. /// <param name="value">选择的日期</param>
  350. /// <param name="type">1:有效时间段;2:播放时段</param>
  351. private void onDateChage(DateRangeChangedEventArgs<DateTime?[]> value, int type)
  352. {
  353. switch (type)
  354. {
  355. case 1:
  356. Model.EffecitiveTime = value.Dates[0];
  357. Model.FailureTime = value.Dates[1];
  358. EffecitiveTime = new DateTime?[]
  359. {
  360. value.Dates[0],value.Dates[1]
  361. };
  362. break;
  363. case 2:
  364. DateTime? startTime = value.Dates[0];
  365. DateTime? endTime = value.Dates[1];
  366. Model.StartTime = startTime?.Hour;
  367. Model.EndTime = endTime?.Hour;
  368. playRange = new DateTime?[]
  369. {
  370. value.Dates[0], value.Dates[1]
  371. };
  372. break;
  373. }
  374. }
  375. private async Task HandleSubmit(EditContext editContext)
  376. {
  377. if(editContext.Validate())
  378. {
  379. if(Model.BusinessUnitID <= 0)
  380. {
  381. Message.Error("请绑定油站");
  382. return;
  383. }
  384. await UploadFile();
  385. await onSure();
  386. }
  387. }
  388. /// <summary>
  389. /// 确定按钮事件
  390. /// </summary>
  391. /// <returns></returns>
  392. private async Task onSure()
  393. {
  394. if (ModelChanged.HasDelegate)
  395. {
  396. await ModelChanged.InvokeAsync(Model);
  397. }
  398. if (onCallback.HasDelegate)
  399. {
  400. await onCallback.InvokeAsync(Model);
  401. }
  402. await OnlyClose();
  403. }
  404. }
  405. <style>
  406. /* 遮罩层:全屏、半透明 */
  407. .modal-overlay {
  408. position: fixed;
  409. top: 0;
  410. left: 0;
  411. width: 100vw;
  412. height: 100vh;
  413. background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色遮罩 */
  414. display: flex;
  415. justify-content: center;
  416. align-items: center;
  417. z-index: 1000;
  418. }
  419. /* 弹窗内容:白色卡片,居中由父容器控制 */
  420. .modal-content {
  421. background: white;
  422. border-radius: 8px;
  423. width: 80%;
  424. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  425. /* 注意:不要设 height: 100vh,否则会拉满全屏 */
  426. }
  427. .filter_box {
  428. display: flex;
  429. flex-direction: column;
  430. align-items: center;
  431. background: #ffffff;
  432. padding: 2%;
  433. }
  434. .filter_row {
  435. display: flex;
  436. flex-direction: row;
  437. justify-content: space-between;
  438. align-items: center;
  439. width: 100%;
  440. margin-top: 2%;
  441. }
  442. .filter_row_around {
  443. display: flex;
  444. flex-direction: row;
  445. justify-content: space-around;
  446. align-items: center;
  447. width: 100%;
  448. margin-top: 2%;
  449. }
  450. .filter_colume {
  451. display: flex;
  452. flex-direction: column;
  453. justify-content: center;
  454. align-items: end;
  455. width: 100%;
  456. margin-top: 2%;
  457. }
  458. .upload_box{
  459. display:flex;
  460. flex-direction:column;
  461. justify-content:center;
  462. align-items:center;
  463. margin-top:1%;
  464. }
  465. .upload-container {
  466. position: relative;
  467. display: inline-block;
  468. width: 50%;
  469. height:160px;
  470. padding:3%;
  471. }
  472. .custom-upload-content {
  473. width: 100%;
  474. height: 100%;
  475. border: 2px dashed #d9d9d9;
  476. border-radius: 8px;
  477. display: flex;
  478. flex-direction: column;
  479. align-items: center;
  480. justify-content: center;
  481. cursor: pointer;
  482. background-color: #fafafa;
  483. z-index: 1;
  484. position: relative;
  485. }
  486. .transparent-input-file {
  487. position: absolute;
  488. top: 0;
  489. left: 0;
  490. width: 100%;
  491. height: 100%;
  492. opacity: 0;
  493. cursor: pointer;
  494. z-index: 2;
  495. }
  496. </style>