Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -43,8 +43,9 @@ namespace Active_Client.Browser_Tools
|
||||
private static Dictionary<string, IntPtr> _editorOpened = new Dictionary<string, IntPtr>();
|
||||
private static EditorVar _currentEditorObject = new EditorVar();
|
||||
public static string RECENT_FOLDER_KEY = "RECENT";
|
||||
private const string THERMO_RECIPE_PATH = @"C:\CMS\Recipes";
|
||||
|
||||
private const string THERMO_RECIPE_PATH = @"C:\CMS\Recipes";
|
||||
private const string CMS_PATH = @"C:\CMS";
|
||||
|
||||
public static FileSystemWatcher watcher = null;
|
||||
public static DateTime _lastTimeFileWatcherEventRaised = DateTime.Now;
|
||||
|
||||
@@ -77,13 +78,15 @@ namespace Active_Client.Browser_Tools
|
||||
AddFunction("openOrStartProcess").Execute += openOrStartProcess;
|
||||
AddFunction("isVirtualKeybConfigured").Execute += isVirtualKeybConfigured;
|
||||
AddFunction("getOSdriveList").Execute += getOSdriveList;
|
||||
AddFunction("getAllRecipeDirectories").Execute += getAllRecipeDirectories;
|
||||
AddFunction("getFileList").Execute += getFileList;
|
||||
AddFunction("getProgramInfo").Execute += getProgramInfo;
|
||||
AddFunction("editProgram").Execute += editProgram;
|
||||
AddFunction("deleteFile").Execute += deleteFile;
|
||||
AddFunction("deleteFolder").Execute += deleteFolder;
|
||||
AddFunction("createFolder").Execute += createFolder;
|
||||
|
||||
AddFunction("duplicateRecipe").Execute += duplicateRecipe;
|
||||
|
||||
|
||||
|
||||
AddFunction("uploadAndActivateProgram").Execute += uploadAndActivateProgram;
|
||||
@@ -364,34 +367,6 @@ namespace Active_Client.Browser_Tools
|
||||
{
|
||||
List<Drive> drivelist = new List<Drive>();
|
||||
|
||||
// USB & HD Drives
|
||||
/*
|
||||
foreach (var drive in DriveInfo.GetDrives())
|
||||
{
|
||||
if (drive.IsReady)
|
||||
{
|
||||
//Filter NC Address
|
||||
if (drive.DriveType != DriveType.Network)
|
||||
{
|
||||
drivelist.Add(new Drive()
|
||||
{
|
||||
Name = ElaborateName(drive.VolumeLabel, drive.Name.TrimEnd('\\'), drive.DriveType),
|
||||
Path = drive.RootDirectory.ToString(),
|
||||
Type = ElaborateType(drive.DriveType)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Desktop folder
|
||||
drivelist.Add(new Drive()
|
||||
{
|
||||
Name = ElaborateName("Desktop", "", DriveType.Unknown),
|
||||
Path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\",
|
||||
Type = "SPFO"
|
||||
});
|
||||
*/
|
||||
|
||||
if (Directory.Exists(THERMO_RECIPE_PATH))
|
||||
{
|
||||
drivelist.Add(new Drive()
|
||||
@@ -401,32 +376,59 @@ namespace Active_Client.Browser_Tools
|
||||
Type = "SPFO"
|
||||
});
|
||||
}
|
||||
/*
|
||||
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(drivelist));
|
||||
}
|
||||
|
||||
public void getAllRecipeDirectories(object sender, CfrV8HandlerExecuteEventArgs e)
|
||||
{
|
||||
List<string> dirs = this.DirSearch(THERMO_RECIPE_PATH);
|
||||
for (int i = 0; i< dirs.Count; i++)
|
||||
{
|
||||
dirs[i] = dirs[i].Remove(0,CMS_PATH.Length +1);
|
||||
}
|
||||
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(dirs));
|
||||
}
|
||||
|
||||
|
||||
public void duplicateRecipe(object sender, CfrV8HandlerExecuteEventArgs e)
|
||||
{
|
||||
if (e.Arguments.Count() < 2)
|
||||
{
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("error_arguments_not_ok")));
|
||||
return;
|
||||
}
|
||||
|
||||
string oldFile = CMS_PATH + "\\" + e.Arguments[0].StringValue + ".rcp";
|
||||
string newFile = CMS_PATH + "\\" + e.Arguments[1].StringValue + ".rcp";
|
||||
string oldImage = CMS_PATH + "\\" + e.Arguments[0].StringValue + ".svg";
|
||||
string newImage = CMS_PATH + "\\" + e.Arguments[1].StringValue + ".svg";
|
||||
if (!File.Exists(oldFile))
|
||||
{
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("file_not_found")));
|
||||
return;
|
||||
}
|
||||
if (File.Exists(newFile))
|
||||
{
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("file_already_exists")));
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
// Network Folders
|
||||
var searcher = new ManagementObjectSearcher("select * from Win32_MappedLogicalDisk");
|
||||
foreach (ManagementObject queryObj in searcher.Get())
|
||||
File.Copy(oldFile, newFile, true);
|
||||
if (File.Exists(oldImage))
|
||||
{
|
||||
//Filter not CNC folder
|
||||
if (!queryObj["ProviderName"].ToString().Contains(Config.VendorHmiConfig.IpAddress))
|
||||
{
|
||||
drivelist.Add(new Drive()
|
||||
{
|
||||
Name = ElaborateName(queryObj["VolumeName"].ToString(), queryObj["Name"].ToString(), DriveType.Network),
|
||||
Path = queryObj["Name"].ToString(),
|
||||
Type = ElaborateType(DriveType.Network)
|
||||
});
|
||||
}
|
||||
File.Copy(oldImage, newImage, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("cannot_copy_file")));
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(drivelist));
|
||||
}
|
||||
|
||||
|
||||
// Read all files in directory
|
||||
public void getFileList(object sender, CfrV8HandlerExecuteEventArgs e)
|
||||
@@ -434,14 +436,14 @@ namespace Active_Client.Browser_Tools
|
||||
List<FileModel> filelist = new List<FileModel>();
|
||||
if (e.Arguments.Count() == 0)
|
||||
{
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new List<FileModel>()));
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("error_arguments_not_ok")));
|
||||
return;
|
||||
}
|
||||
|
||||
string p = e.Arguments[0].StringValue;
|
||||
if (p != RECENT_FOLDER_KEY && !Directory.Exists(p))
|
||||
{
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new List<FileModel>()));
|
||||
e.SetReturnValue(JsonConvert.SerializeObject(new ErrorContainer("file_not_exists")));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1069,6 +1071,26 @@ namespace Active_Client.Browser_Tools
|
||||
return retName;
|
||||
}
|
||||
|
||||
|
||||
private List<String> DirSearch(string sDir)
|
||||
{
|
||||
List<String> files = new List<String>();
|
||||
try
|
||||
{
|
||||
files.Add(sDir);
|
||||
foreach (string d in Directory.GetDirectories(sDir))
|
||||
{
|
||||
files.AddRange(DirSearch(d));
|
||||
}
|
||||
}
|
||||
catch (System.Exception excpt)
|
||||
{
|
||||
MessageBox.Show(excpt.Message);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#endregion FILESYSTEM_METHODS
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -362,6 +362,9 @@ namespace Thermo.Active.Controllers.WebApi
|
||||
currParams.Add(item.Key, item.Value.SetpointPLC);
|
||||
}
|
||||
|
||||
// salvo su disco ricetta corrente
|
||||
NcFileAdapter.SaveRecipe($"{NcAdapter.RecipeLiveData.RecipeDir}{NcAdapter.RecipeLiveData.RecipeName}", NcAdapter.RecipeLiveData);
|
||||
|
||||
// ora salvo ANCHE i dati live...
|
||||
SaveCurrentRecipeParams(currParams);
|
||||
|
||||
@@ -500,7 +503,7 @@ namespace Thermo.Active.Controllers.WebApi
|
||||
NcAdapter.RecipeLiveData.recipeNotes = recipeNotes.Trim();
|
||||
}
|
||||
// e salvo su disco
|
||||
NcFileAdapter.SaveRecipe(NcAdapter.RecipeLiveData.RecipeName, NcAdapter.RecipeLiveData);
|
||||
NcFileAdapter.SaveRecipe($"{NcAdapter.RecipeLiveData.RecipeDir}{NcAdapter.RecipeLiveData.RecipeName}", NcAdapter.RecipeLiveData);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -2388,7 +2388,7 @@
|
||||
line-height: 4px;
|
||||
|
||||
.title {
|
||||
min-width: 110px;
|
||||
min-width: 130px;
|
||||
text-align: right;
|
||||
color: @color-darkish-blue;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
// out: false, sourceMap: false, main: ../style.less
|
||||
.modal.save-as {
|
||||
width: 500px;
|
||||
height: 300px;
|
||||
top: calc(~'50% - 200px');
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
top: calc(~'50% - 250px');
|
||||
|
||||
section{
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
label{
|
||||
margin-top: 22px;
|
||||
font-size: 14px;
|
||||
color: #878787;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
article {
|
||||
|
||||
|
||||
input {
|
||||
padding: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #6d6d6d;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
border: none;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
padding: 0 4px;
|
||||
padding: 0 20px;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,25 @@
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.select_folder{
|
||||
height: 48px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: #6d6d6d;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
border: none;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
padding-right: 25px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.description_lbl{
|
||||
margin-top: 22px;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -19,6 +19,24 @@
|
||||
box-sizing: border-box;
|
||||
resize: none;
|
||||
}
|
||||
.setup .select_folder {
|
||||
height: 48px;
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: #6d6d6d;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
border: none;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
padding-right: 25px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.setup .description_lbl {
|
||||
margin-top: 22px;
|
||||
font-size: 14px;
|
||||
@@ -2546,7 +2564,7 @@ article .box .body {
|
||||
.modal.modal-add-element-queue .modal-load-program-body .selected-item .selected-item-header .subtitle .title,
|
||||
.modal.modal-load-program .modal-add-element-queue-body .selected-item .selected-item-header .subtitle .title,
|
||||
.modal.modal-add-element-queue .modal-add-element-queue-body .selected-item .selected-item-header .subtitle .title {
|
||||
min-width: 110px;
|
||||
min-width: 130px;
|
||||
text-align: right;
|
||||
color: #002680;
|
||||
}
|
||||
@@ -23851,23 +23869,31 @@ footer .container button.big:before {
|
||||
color: #4b4b4b;
|
||||
}
|
||||
.modal.save-as {
|
||||
width: 500px;
|
||||
height: 300px;
|
||||
top: calc(50% - 200px);
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
top: calc(50% - 250px);
|
||||
}
|
||||
.modal.save-as section {
|
||||
display: block !important;
|
||||
}
|
||||
.modal.save-as label {
|
||||
margin-top: 22px;
|
||||
font-size: 14px;
|
||||
color: #878787;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.modal.save-as article input {
|
||||
padding: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #6d6d6d;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.5);
|
||||
border: none;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
padding: 0 4px;
|
||||
padding: 0 20px;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import Vue from "vue";
|
||||
import Component from "vue-class-component";
|
||||
import modal from "@/components/modals/modal.vue";
|
||||
import { Deferred } from "@/services";
|
||||
import { Prop, Watch } from 'vue-property-decorator';
|
||||
import { messageService } from "@/_base";
|
||||
import { Modal, ModalHelper } from "@/components/modals";
|
||||
import * as iziToast from "izitoast";
|
||||
|
||||
declare var cmsClient: any;
|
||||
|
||||
@Component({ components: { modal: Modal } })
|
||||
export default class Duplicate extends Vue {
|
||||
|
||||
|
||||
@Prop()
|
||||
deferred: Deferred<string>;
|
||||
|
||||
@Prop()
|
||||
value: any;
|
||||
|
||||
folder = "";
|
||||
name = "";
|
||||
canDo = false;
|
||||
|
||||
folderlist = [];
|
||||
|
||||
|
||||
mounted(){
|
||||
if (typeof cmsClient != "undefined") {
|
||||
this.folderlist = JSON.parse(cmsClient.getAllRecipeDirectories());
|
||||
}
|
||||
if(this.value.folder){
|
||||
this.value.folder = this.value.folder.replace("C:\\CMS\\", "");
|
||||
this.folder = this.value.folder;
|
||||
}
|
||||
if(this.value.name){
|
||||
this.value.name = this.value.name.replace(".rcp", "");
|
||||
this.name = this.value.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Watch('folder',{deep:true})
|
||||
changedFolder(){
|
||||
if(this.value.folder && this.value.name){
|
||||
this.canDo = (this.value.folder != this.folder) || (this.value.name != this.name);
|
||||
}
|
||||
else
|
||||
this.canDo= true;
|
||||
}
|
||||
|
||||
@Watch('name',{deep:true})
|
||||
changedName(){
|
||||
if(this.value.folder && this.value.name){
|
||||
this.canDo = (this.value.folder != this.folder) || (this.value.name != this.name);
|
||||
}
|
||||
else
|
||||
this.canDo= true;
|
||||
}
|
||||
|
||||
save(value: string) {
|
||||
if (typeof cmsClient != "undefined") {
|
||||
var res = cmsClient.duplicateRecipe(this.value.folder + "\\" + this.value.name,this.folder + "\\" +this.name);
|
||||
if (res) {
|
||||
var obj = JSON.parse(res);
|
||||
if (obj.error) {
|
||||
(iziToast as any).error({
|
||||
title: "error",
|
||||
message: obj.error,
|
||||
theme: "dark",
|
||||
timeout: 10000,
|
||||
class: "t-error",
|
||||
transitionOut: "fadeOut",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
this.deferred.resolve(this.name)
|
||||
ModalHelper.HideModal();
|
||||
}
|
||||
|
||||
close() {
|
||||
messageService.deleteChannel("esc_pressed");
|
||||
ModalHelper.HideModal();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="setup">
|
||||
<modal type="save-as" :title="'modal_title_duplicate_recipe' | localize('Duplica Ricetta')">
|
||||
<div slot="header-buttons">
|
||||
<button class="modal-close" @click="close()">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
<section>
|
||||
<article>
|
||||
<label>{{'folder_name' | localize('Nuova Cartella')}}</label>
|
||||
<select v-model="folder" class="select_folder" :placeholder="'folder_name' | localize('Nuova Cartella')" >
|
||||
<option v-for="folder in folderlist">{{folder}}</option>
|
||||
</select>
|
||||
</article>
|
||||
<article>
|
||||
<label>{{'recipe_name' | localize('Nome della nuova ricetta')}}</label>
|
||||
<input v-model="name" :placeholder="'recipe_name' | localize('Nome della ricetta')" />
|
||||
</article>
|
||||
</section>
|
||||
<footer>
|
||||
<button class="btn" @click="close()">{{'cancel' | localize("Annulla")}}</button>
|
||||
<button class="btn btn-success" @click="save()" :disabled="!canDo">{{'confirm' | localize("Conferma")}}</button>
|
||||
</footer>
|
||||
</modal>
|
||||
</div>
|
||||
</template>
|
||||
<script src="./duplicate.ts" lang="ts"></script>
|
||||
@@ -16,6 +16,12 @@ export default class SaveAs extends Vue {
|
||||
value: string;
|
||||
|
||||
recipeName: string = null;
|
||||
|
||||
mounted() {
|
||||
if(this.value)
|
||||
this.value = this.value.replace(".rcp","");
|
||||
}
|
||||
|
||||
get RecipeName() {
|
||||
return this.recipeName || this.value;
|
||||
}
|
||||
@@ -24,7 +30,7 @@ export default class SaveAs extends Vue {
|
||||
}
|
||||
|
||||
save(value: string) {
|
||||
this.deferred.resolve(this.RecipeName)
|
||||
this.deferred.resolve(this.RecipeName + ".rcp")
|
||||
ModalHelper.HideModal();
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -28,9 +28,11 @@ export default class Resistance extends Vue {
|
||||
selectedColorTo: number[];
|
||||
|
||||
get currentStandardColor() {
|
||||
return [this.colorFrom[0] - (this.colorFrom[0] - this.colorTo[0]) * this.channel.setpointHMI / 100,
|
||||
this.colorFrom[1] - (this.colorFrom[1] - this.colorTo[1]) * this.channel.setpointHMI / 100,
|
||||
this.colorFrom[2] - (this.colorFrom[2] - this.colorTo[2]) * this.channel.setpointHMI / 100];
|
||||
//Not linear, but quadratic
|
||||
var x = 1/100 * this.channel.setpointHMI * this.channel.setpointHMI;
|
||||
return [this.colorFrom[0] - (this.colorFrom[0] - this.colorTo[0]) * x / 100,
|
||||
this.colorFrom[1] - (this.colorFrom[1] - this.colorTo[1]) * x / 100,
|
||||
this.colorFrom[2] - (this.colorFrom[2] - this.colorTo[2]) * x / 100];
|
||||
}
|
||||
|
||||
get currentColor() {
|
||||
@@ -39,8 +41,10 @@ export default class Resistance extends Vue {
|
||||
}
|
||||
|
||||
get currentSelectedColor() {
|
||||
return [this.selectedColorFrom[0] - (this.selectedColorFrom[0] - this.selectedColorTo[0]) * this.channel.setpointHMI / 100,
|
||||
this.selectedColorFrom[1] - (this.selectedColorFrom[1] - this.selectedColorTo[1]) * this.channel.setpointHMI / 100,
|
||||
this.selectedColorFrom[2] - (this.selectedColorFrom[2] - this.selectedColorTo[2]) * this.channel.setpointHMI / 100];
|
||||
//Not linear, but quadratic
|
||||
var x = 1/100 * this.channel.setpointHMI * this.channel.setpointHMI;
|
||||
return [this.selectedColorFrom[0] - (this.selectedColorFrom[0] - this.selectedColorTo[0]) * x / 100,
|
||||
this.selectedColorFrom[1] - (this.selectedColorFrom[1] - this.selectedColorTo[1]) * x / 100,
|
||||
this.selectedColorFrom[2] - (this.selectedColorFrom[2] - this.selectedColorTo[2]) * x / 100];
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -37,7 +37,7 @@
|
||||
:y="((parameters.warmerPlanSizeY- recipe.general_sizes_sheet_dim_y.setpointHMI)/2)"
|
||||
:width="recipe.general_sizes_sheet_dim_x.setpointHMI"
|
||||
:height="recipe.general_sizes_sheet_dim_y.setpointHMI"
|
||||
stroke="rgba(0,0,0,.3)"
|
||||
stroke="rgba(0,0,0,.8)"
|
||||
stroke-width="6"
|
||||
rx="3"
|
||||
ry="3"
|
||||
|
||||
@@ -8,6 +8,7 @@ import cardFolderPath from "./cards/card-folder-path.vue";
|
||||
import * as iziToast from "izitoast";
|
||||
import ZoomImage from './zoom-image.vue'
|
||||
import { recipeService } from "@/services/recipeService";
|
||||
import duplicateModal from "@/app_modules_thermo/save/duplicate.vue";
|
||||
|
||||
declare var cmsClient: any;
|
||||
|
||||
@@ -81,10 +82,16 @@ export default class ModalLoadProgram extends Vue {
|
||||
return this.$store.state.process.canLoadProgram;
|
||||
}
|
||||
|
||||
async duplicate(){
|
||||
let result = await ModalHelper.ShowModalAsync(duplicateModal,{name:this.selectedFile.Name,folder:this.currentPath});
|
||||
this.relaodFiles();
|
||||
}
|
||||
|
||||
async mounted() {
|
||||
if (typeof cmsClient != "undefined") {
|
||||
this.driveList = JSON.parse(cmsClient.getOSdriveList());
|
||||
this.recentPath = cmsClient.RECENT_FOLDER_KEY
|
||||
this.navigateTo(null,"C:\\CMS\\Recipes",this.isLocalNavigation,1,1);
|
||||
}
|
||||
|
||||
this.loadClicked = false
|
||||
|
||||
@@ -122,9 +122,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-button">
|
||||
<!--<button class="btn" @click="duplicateprogram">
|
||||
<button class="btn" @click="duplicate">
|
||||
<i class="fa fa-clone"></i>
|
||||
</button>-->
|
||||
</button>
|
||||
<button class="btn" :disabled="!selectedFile.CanEdit" @click="deleteprogram">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user