@mx-sose-front/mx-sose-graph 1.1.9 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +362 -2
- package/dist/index.esm.js +1090 -688
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Matrix/Matrix.vue +45 -65
- package/src/components/Table/Table.vue +849 -626
- package/src/constants/index.ts +23 -9
- package/src/statics/icons/childIcons/AV-1/346/246/202/350/277/260/344/270/216/346/221/230/350/246/201/344/277/241/346/201/257/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/AV-1/346/246/202/350/277/260/346/221/230/350/246/201/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/AV-2/351/233/206/346/210/220/345/255/227/345/205/270/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/AV-2/351/233/206/346/210/220/345/255/227/345/205/270/350/241/250@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-1/346/204/277/346/231/257/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-1/346/204/277/346/231/257/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-2/350/203/275/345/212/233/345/210/206/347/261/273/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-2/350/203/275/345/212/233/345/210/206/347/261/273/345/233/276@3x-2.png +0 -0
- package/src/statics/icons/childIcons/CV-2/350/203/275/345/212/233/345/210/206/347/261/273/345/233/276@3x-3.png +0 -0
- package/src/statics/icons/childIcons/CV-2/350/203/275/345/212/233/345/210/206/347/261/273/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-3/350/203/275/345/212/233/345/210/206/346/256/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-3/350/203/275/345/212/233/351/230/266/346/256/265/347/224/230/347/211/271/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-4/350/203/275/345/212/233/344/276/235/350/265/226/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-4/350/203/275/345/212/233/344/276/235/350/265/226/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-5/350/203/275/345/212/233/345/210/260/347/273/204/347/273/207/347/232/204/345/274/200/345/217/221/346/230/240/345/260/204/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-5/350/203/275/345/212/233/345/210/260/347/273/204/347/273/207/347/232/204/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-6/350/203/275/345/212/233/345/210/260/344/270/232/345/212/241/346/264/273/345/212/250/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-6/350/203/275/345/212/233/345/210/260/344/275/234/346/210/230/346/264/273/345/212/250/347/232/204/346/230/240/345/260/204/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-7/350/203/275/345/212/233/345/210/260/346/234/215/345/212/241/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/CV-7/350/203/275/345/212/233/345/210/260/346/234/215/345/212/241/347/232/204/346/230/240/345/260/204/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/DIV-1/346/246/202/345/277/265/346/225/260/346/215/256/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/DIV-1/346/246/202/345/277/265/346/225/260/346/215/256/346/250/241/345/236/213/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/DIV-2/351/200/273/350/276/221/346/225/260/346/215/256/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/DIV-2/351/200/273/350/276/221/346/225/260/346/215/256/346/250/241/345/236/213/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/DIV-3/347/211/251/347/220/206/346/225/260/346/215/256/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-1/350/207/252/347/224/261/345/210/206/347/261/273/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-1/351/253/230/347/272/247/344/275/234/346/210/230/346/246/202/345/277/265/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-1/351/253/230/347/272/247/344/275/234/346/210/230/346/246/202/345/277/265/345/233/276/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-2/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/345/206/205/351/203/250/346/217/217/350/277/260/345/233/276@3x-2.png +0 -0
- package/src/statics/icons/childIcons/OV-2/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/345/206/205/351/203/250/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-2/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-2/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-3/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-3/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-3/345/237/272/347/241/200/350/247/222/350/211/262/344/275/234/346/210/230/350/265/204/346/272/220/346/265/201/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-4/347/273/204/347/273/207/345/205/263/347/263/273/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-4/347/273/204/347/273/207/345/205/263/347/263/273/345/233/276/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-5a/344/275/234/346/210/230/346/264/273/345/212/250/345/210/206/350/247/243/346/240/221/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-5a/344/275/234/346/210/230/346/264/273/345/212/250/345/210/206/350/247/243/346/240/221/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-5b/344/275/234/346/210/230/346/264/273/345/212/250/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-5b/344/275/234/346/210/230/346/264/273/345/212/250/346/250/241/345/236/213/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-6a/344/275/234/346/210/230/350/247/204/345/210/231/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-6a/344/275/234/346/210/230/350/265/204/346/272/220/345/217/202/346/225/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-6b/347/212/266/346/200/201/350/275/254/346/215/242/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-6b/347/212/266/346/200/201/350/275/254/346/215/242/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-6c/344/272/213/344/273/266/350/277/275/350/270/252/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/OV-6c/344/275/234/346/210/230/344/272/213/344/273/266/350/267/237/350/270/252/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/PV-1/351/241/271/347/233/256/347/273/204/345/220/210/345/205/263/347/263/273/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/PV-1/351/241/271/347/233/256/347/273/204/345/220/210/345/205/263/347/263/273/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/PV-2/351/241/271/347/233/256/346/227/266/351/227/264/350/277/233/345/272/246/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/PV-2/351/241/271/347/233/256/346/227/266/351/227/264/350/277/233/345/272/246/347/224/230/347/211/271/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/PV-3/351/241/271/347/233/256/344/270/216/350/203/275/345/212/233/346/230/240/345/260/204/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/PV-3/351/241/271/347/233/256/344/270/216/350/203/275/345/212/233/347/232/204/346/230/240/345/260/204/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-1 /347/263/273/347/273/237/346/216/245/345/217/243/345/206/205/351/203/250/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-1 /347/263/273/347/273/237/346/216/245/345/217/243/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10a /347/263/273/347/273/237/345/217/202/346/225/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10a /347/263/273/347/273/237/350/247/204/345/210/231/346/250/241/345/236/213/350/241/250@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10a/347/263/273/347/273/237/350/247/204/345/210/231/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10b /347/263/273/347/273/237/347/212/266/346/200/201/350/275/254/346/215/242/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10b/347/263/273/347/273/237/347/212/266/346/200/201/350/275/254/346/215/242/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10c /347/263/273/347/273/237/344/272/213/344/273/266/350/267/237/350/270/252/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-10c/347/263/273/347/273/237/344/272/213/344/273/266-/350/277/275/350/270/252/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-1/347/263/273/347/273/237/346/216/245/345/217/243/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-2 /347/263/273/347/273/237/345/206/205/351/203/250/350/265/204/346/272/220/346/265/201/345/212/250/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-2 /347/263/273/347/273/237/350/265/204/346/272/220/346/265/201/345/212/250/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-2/347/263/273/347/273/237/350/265/204/346/272/220/346/265/201/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-3 /347/263/273/347/273/237 - /347/263/273/347/273/237/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-3/347/263/273/347/273/237-/347/263/273/347/273/237/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-4 /347/263/273/347/273/237/345/212/237/350/203/275/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-4 /347/263/273/347/273/237/345/212/237/350/203/275/346/265/201/347/250/213/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-4/347/263/273/347/273/237/345/212/237/350/203/275/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-5a /344/270/232/345/212/241/346/264/273/345/212/250/345/210/260/347/263/273/347/273/237/345/212/237/350/203/275/347/232/204/345/217/257/350/277/275/346/272/257/346/200/247/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-5a/344/275/234/346/210/230/346/264/273/345/212/250/345/210/260/347/263/273/347/273/237/345/212/237/350/203/275/345/217/257/350/277/275/350/270/252/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-5b /344/270/232/345/212/241/346/264/273/345/212/250/345/210/260/347/263/273/347/273/237/347/232/204/345/217/257/350/277/275/346/272/257/346/200/247/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-5b/344/275/234/346/210/230/346/264/273/345/212/250/345/210/260/347/263/273/347/273/237/345/217/257/350/277/275/350/270/252/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-6 /347/263/273/347/273/237/350/265/204/346/272/220/346/265/201/345/212/250/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-6/347/263/273/347/273/237/350/265/204/346/272/220/346/265/201/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-7 /347/263/273/347/273/237/345/256/236/351/231/205/345/272/246/351/207/217/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-7/347/263/273/347/273/237/345/272/246/351/207/217/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-8 /347/263/273/347/273/237/346/274/224/350/277/233/346/217/217/350/277/260/347/224/230/347/211/271/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-8/347/263/273/347/273/237/346/274/224/350/277/233/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-9 /347/263/273/347/273/237/346/212/200/346/234/257/344/270/216/346/212/200/350/203/275/351/242/204/346/265/213/350/241/250@3x.png +0 -0
- package/src/statics/icons/childIcons/SV-9/347/263/273/347/273/237/346/212/200/346/234/257/344/270/216/346/212/200/350/203/275/351/242/204/346/265/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/StdV-1 /346/240/207/345/207/206/351/205/215/347/275/256/350/241/250@3x.png +0 -0
- package/src/statics/icons/childIcons/StdV-1/346/240/207/345/207/206/351/205/215/347/275/256/346/226/207/344/273/266/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/StdV-2 /346/240/207/345/207/206/351/242/204/346/265/213/350/241/250@3x.png +0 -0
- package/src/statics/icons/childIcons/StdV-2/346/240/207/345/207/206/351/242/204/346/265/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-10a /346/234/215/345/212/241/345/217/202/346/225/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-10a/346/234/215/345/212/241/350/247/204/345/210/231/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-10b /346/234/215/345/212/241/347/212/266/346/200/201/350/275/254/346/215/242/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-10b/346/234/215/345/212/241/347/212/266/346/200/201/350/275/254/346/215/242/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-10c /346/234/215/345/212/241/344/272/213/344/273/266/350/267/237/350/270/252/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-10c/346/234/215/345/212/241/344/272/213/344/273/266-/350/277/275/350/270/252/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-1/346/234/215/345/212/241/344/270/212/344/270/213/346/226/207/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-1/346/234/215/345/212/241/350/203/214/346/231/257/346/250/241/345/236/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-2 /346/234/215/345/212/241/350/265/204/346/272/220/346/265/201/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-2/346/234/215/345/212/241/350/265/204/346/272/220/346/265/201/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-3a /347/263/273/347/273/237 -/346/234/215/345/212/241/347/237/251/351/230/265@3x-2.png +0 -0
- package/src/statics/icons/childIcons/SvcV-3a /347/263/273/347/273/237 -/346/234/215/345/212/241/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-3a/347/263/273/347/273/237-/346/234/215/345/212/241/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-3b/346/234/215/345/212/241-/346/234/215/345/212/241/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-4 /346/234/215/345/212/241/345/212/237/350/203/275/346/217/217/350/277/260/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-4 /346/234/215/345/212/241/345/212/237/350/203/275/346/265/201/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-4/346/234/215/345/212/241/345/212/237/350/203/275/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-5/344/275/234/346/210/230/346/264/273/345/212/250/345/210/260/346/234/215/345/212/241/345/217/257/350/277/275/350/270/252/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-6 /346/234/215/345/212/241/350/265/204/346/272/220/346/265/201/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-6/346/234/215/345/212/241/350/265/204/346/272/220/346/265/201/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-7 /346/234/215/345/212/241/345/205/270/345/236/213/345/272/246/351/207/217/347/237/251/351/230/265@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-7/346/234/215/345/212/241/345/272/246/351/207/217/347/237/251/351/230/265/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-8 /346/234/215/345/212/241/346/274/224/350/277/233/347/224/230/347/211/271/345/233/276@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-8/346/234/215/345/212/241/346/274/224/350/277/233/346/217/217/350/277/260/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-9 /346/234/215/345/212/241/346/212/200/346/234/257/344/270/216/346/212/200/350/203/275/351/242/204/346/265/213/350/241/250@3x.png +0 -0
- package/src/statics/icons/childIcons/SvcV-9/346/234/215/345/212/241/346/212/200/346/234/257/344/270/216/346/212/200/350/203/275/351/242/204/346/265/213/345/214/205@3x.png +0 -0
- package/src/statics/icons/childIcons//344/275/223/347/263/273/346/236/266/346/236/204@3x.png +0 -0
- package/src/statics/icons/childIcons//344/275/234/346/210/230/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//345/205/250/346/231/257/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//345/205/254/345/217/270@3x.png +0 -0
- package/src/statics/icons/childIcons//345/215/217/350/256/256@3x.png +0 -0
- package/src/statics/icons/childIcons//345/215/217/350/256/256/345/261/202@3x.png +0 -0
- package/src/statics/icons/childIcons//345/215/217/350/256/256/346/240/210@3x.png +0 -0
- package/src/statics/icons/childIcons//346/224/257/346/214/201@3x.png +0 -0
- package/src/statics/icons/childIcons//346/225/260/346/215/256/344/277/241/346/201/257/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//346/234/215/345/212/241/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//346/240/207/345/207/206@3x.png +0 -0
- package/src/statics/icons/childIcons//346/240/207/345/207/206/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//347/263/273/347/273/237/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//350/203/275/345/212/233/350/247/206/350/247/222@3x.png +0 -0
- package/src/statics/icons/childIcons//351/241/271/347/233/256/350/247/206/350/247/222@3x.png +0 -0
- package/src/store/graphStore.ts +21 -1
- package/src/types/index.ts +1 -0
|
@@ -1,216 +1,305 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
2
|
+
<div class="gantt-container" @click="onContainerClick">
|
|
3
|
+
<!-- 左侧表格 -->
|
|
4
|
+
<div
|
|
5
|
+
class="gantt-left"
|
|
6
|
+
:class="{ 'with-search-bar': showSearchBar }"
|
|
7
|
+
ref="tableContainerRef"
|
|
8
|
+
@scroll="() => syncVirtualViewportState()"
|
|
9
|
+
>
|
|
10
|
+
<table class="gantt-table" :style="{ width: tableWidth + 'px' }">
|
|
11
|
+
<!-- 用 colgroup 统一控制整列宽度 -->
|
|
12
|
+
<colgroup>
|
|
13
|
+
<col
|
|
14
|
+
v-for="col in displayColumns"
|
|
15
|
+
:key="col.configId"
|
|
16
|
+
:style="{ width: getColumnWidth(col.configId) + 'px' }"
|
|
17
|
+
/>
|
|
18
|
+
</colgroup>
|
|
19
|
+
<thead>
|
|
20
|
+
<tr class="header-row-2">
|
|
21
|
+
<th v-for="col in displayColumns" :key="col.id" class="col-date">
|
|
22
|
+
<div class="th-content">
|
|
23
|
+
<div v-if="col.configId == 'index'" style="text-align: center">
|
|
24
|
+
{{ col.name }}
|
|
25
|
+
</div>
|
|
26
|
+
<div v-else class="th-label">{{ col.name }}</div>
|
|
27
|
+
<div
|
|
28
|
+
v-if="col.configId !== 'index'"
|
|
29
|
+
class="col-resizer"
|
|
30
|
+
@mousedown="startColumnResize(col.configId, $event)"
|
|
31
|
+
></div>
|
|
32
|
+
</div>
|
|
33
|
+
</th>
|
|
34
|
+
</tr>
|
|
35
|
+
</thead>
|
|
36
|
+
|
|
37
|
+
<tbody>
|
|
38
|
+
<!-- 上方占位空间 -->
|
|
39
|
+
<tr
|
|
40
|
+
v-if="virtualTopSpacerHeight > 0"
|
|
41
|
+
class="table-spacer-row"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
>
|
|
44
|
+
<td :colspan="visibleColumnSpan">
|
|
45
|
+
<div
|
|
46
|
+
class="table-spacer"
|
|
47
|
+
:style="{ height: virtualTopSpacerHeight + 'px' }"
|
|
48
|
+
></div>
|
|
49
|
+
</td>
|
|
50
|
+
</tr>
|
|
51
|
+
|
|
52
|
+
<!-- 虚拟行 -->
|
|
53
|
+
<tr
|
|
54
|
+
v-for="row in virtualRows"
|
|
55
|
+
:key="row.item.id"
|
|
56
|
+
class="gantt-row"
|
|
57
|
+
:class="{ 'row-selected': selectedRowIds.has(row.item.id) }"
|
|
58
|
+
@click.stop="selectRow(row.item.id, $event)"
|
|
59
|
+
@contextmenu="handleContextMenu(row.item, $event)"
|
|
60
|
+
>
|
|
61
|
+
<td
|
|
62
|
+
v-for="col in displayColumns"
|
|
63
|
+
:key="col.configId"
|
|
64
|
+
class="col-date"
|
|
65
|
+
:title="getDisplayCellValue(row.item, col)"
|
|
66
|
+
@dblclick.stop="
|
|
67
|
+
col.configId !== 'index' && openCellEditor(row.item, col)
|
|
68
|
+
"
|
|
69
|
+
>
|
|
70
|
+
<!-- 编辑态:根据列的 valueType 渲染不同编辑控件(Element Plus) -->
|
|
71
|
+
<template v-if="isCellEditing(row.item.id, col.configId)">
|
|
72
|
+
<!-- 布尔:单选(与属性面板保持一致) -->
|
|
73
|
+
<el-radio-group
|
|
74
|
+
v-if="editingTextCell?.editorType === 'Boolean'"
|
|
75
|
+
v-model="editingTextCell!.value"
|
|
76
|
+
class="cell-boolean-group"
|
|
77
|
+
@click.stop
|
|
78
|
+
@change="saveTextEdit(row.item, col.configId)"
|
|
79
|
+
>
|
|
80
|
+
<el-radio :label="true" size="small">是</el-radio>
|
|
81
|
+
<el-radio :label="false" size="small">否</el-radio>
|
|
82
|
+
</el-radio-group>
|
|
83
|
+
<!-- 日期:日期时间选择器 -->
|
|
84
|
+
<el-date-picker
|
|
85
|
+
v-else-if="editingTextCell?.editorType === 'Date'"
|
|
86
|
+
:ref="setEditingInputRef"
|
|
87
|
+
v-model="editingTextCell!.value"
|
|
88
|
+
type="datetime"
|
|
89
|
+
size="small"
|
|
90
|
+
class="cell-date"
|
|
91
|
+
format="YYYY-MM-DD HH:mm:ss"
|
|
92
|
+
value-format="YYYY-MM-DD HH:mm:ss"
|
|
93
|
+
:teleported="false"
|
|
94
|
+
:clearable="true"
|
|
95
|
+
:editable="false"
|
|
96
|
+
@click.stop
|
|
97
|
+
@change="saveTextEdit(row.item, col.configId)"
|
|
98
|
+
@blur="saveTextEdit(row.item, col.configId)"
|
|
99
|
+
@keydown.esc.prevent="closeCellEditor"
|
|
100
|
+
/>
|
|
101
|
+
<!-- 枚举:下拉选择,选项来自后端 literals -->
|
|
102
|
+
<el-select
|
|
103
|
+
v-else-if="
|
|
104
|
+
editingTextCell?.editorType === 'Enumeration' &&
|
|
105
|
+
col.literals &&
|
|
106
|
+
col.literals.length
|
|
107
|
+
"
|
|
108
|
+
v-model="editingTextCell!.value"
|
|
109
|
+
size="small"
|
|
110
|
+
class="cell-select"
|
|
111
|
+
@click.stop
|
|
112
|
+
@change="saveTextEdit(row.item, col.configId)"
|
|
113
|
+
@blur="saveTextEdit(row.item, col.configId)"
|
|
114
|
+
>
|
|
115
|
+
<el-option
|
|
116
|
+
v-for="opt in col.literals"
|
|
117
|
+
:key="opt.value"
|
|
118
|
+
:label="opt.key"
|
|
119
|
+
:value="opt.value"
|
|
120
|
+
/>
|
|
121
|
+
</el-select>
|
|
122
|
+
<!-- 富文本:先用 textarea(属性面板是弹窗富文本,这里做轻量版) -->
|
|
123
|
+
<el-input
|
|
124
|
+
v-else-if="editingTextCell?.editorType === 'Html'"
|
|
125
|
+
:ref="setEditingInputRef"
|
|
126
|
+
v-model="editingTextCell!.value"
|
|
127
|
+
type="textarea"
|
|
128
|
+
autosize
|
|
129
|
+
size="small"
|
|
130
|
+
class="cell-textarea"
|
|
131
|
+
@click.stop
|
|
132
|
+
@blur="saveTextEdit(row.item, col.configId)"
|
|
133
|
+
@keydown.esc.prevent="closeCellEditor"
|
|
134
|
+
/>
|
|
135
|
+
<!-- 其他(String、Integer 等):文本/数字输入框 -->
|
|
136
|
+
<el-input
|
|
137
|
+
v-else
|
|
138
|
+
:ref="setEditingInputRef"
|
|
139
|
+
v-model="editingTextCell!.value"
|
|
140
|
+
:type="
|
|
141
|
+
editingTextCell?.editorType === 'Integer'
|
|
142
|
+
? 'number'
|
|
143
|
+
: 'text'
|
|
144
|
+
"
|
|
145
|
+
size="small"
|
|
146
|
+
class="cell-input"
|
|
147
|
+
@click.stop
|
|
148
|
+
@blur="saveTextEdit(row.item, col.configId)"
|
|
149
|
+
@keydown.enter.prevent="saveTextEdit(row.item, col.configId)"
|
|
150
|
+
@keydown.esc.prevent="closeCellEditor"
|
|
151
|
+
/>
|
|
152
|
+
</template>
|
|
153
|
+
|
|
154
|
+
<!-- 显示态 -->
|
|
155
|
+
<template v-else>
|
|
156
|
+
<div
|
|
157
|
+
v-if="col.configId === 'index'"
|
|
158
|
+
class="item-index"
|
|
159
|
+
style="text-align: center"
|
|
160
|
+
>
|
|
161
|
+
{{ row.index + 1 }}
|
|
162
|
+
</div>
|
|
163
|
+
<div
|
|
164
|
+
v-else-if="
|
|
165
|
+
col.valueType === 'Boolean' &&
|
|
166
|
+
hasBooleanCellValue(row.item, col.configId)
|
|
167
|
+
"
|
|
168
|
+
class="item-name cell-boolean-display"
|
|
169
|
+
>
|
|
170
|
+
<el-radio-group
|
|
171
|
+
:model-value="normalizeBooleanValue((row.item as any)[col.configId])"
|
|
172
|
+
class="cell-boolean-group"
|
|
173
|
+
:disabled="col.isReadonly === true"
|
|
174
|
+
@click.stop
|
|
175
|
+
@change="handleBooleanCellChange(row.item, col.configId, $event)"
|
|
176
|
+
>
|
|
177
|
+
<el-radio :label="true" size="small">是</el-radio>
|
|
178
|
+
<el-radio :label="false" size="small">否</el-radio>
|
|
179
|
+
</el-radio-group>
|
|
180
|
+
</div>
|
|
181
|
+
<div v-else-if="col.valueType === 'Boolean'" class="item-name"></div>
|
|
182
|
+
<div v-else class="item-name">
|
|
183
|
+
<div
|
|
184
|
+
v-if="getDisplayCellIcon(row.item, col)"
|
|
185
|
+
class="expand-icon"
|
|
186
|
+
>
|
|
187
|
+
<img
|
|
188
|
+
:src="
|
|
189
|
+
getIcon('childIcons', getDisplayCellIcon(row.item, col))
|
|
190
|
+
"
|
|
191
|
+
alt=""
|
|
192
|
+
style="width: 16px; height: 16px; display: block"
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
195
|
+
<span class="cell-text">{{
|
|
196
|
+
getDisplayCellValue(row.item, col)
|
|
197
|
+
}}</span>
|
|
198
|
+
</div>
|
|
199
|
+
</template>
|
|
200
|
+
</td>
|
|
201
|
+
</tr>
|
|
202
|
+
|
|
203
|
+
<!-- 下方占位空间 -->
|
|
204
|
+
<tr
|
|
205
|
+
v-if="virtualBottomSpacerHeight > 0"
|
|
206
|
+
class="table-spacer-row"
|
|
207
|
+
aria-hidden="true"
|
|
208
|
+
>
|
|
209
|
+
<td :colspan="visibleColumnSpan">
|
|
210
|
+
<div
|
|
211
|
+
class="table-spacer"
|
|
212
|
+
:style="{ height: virtualBottomSpacerHeight + 'px' }"
|
|
213
|
+
></div>
|
|
214
|
+
</td>
|
|
215
|
+
</tr>
|
|
216
|
+
</tbody>
|
|
217
|
+
</table>
|
|
143
218
|
</div>
|
|
219
|
+
<!-- 右键菜单 -->
|
|
220
|
+
<GanttContextMenu
|
|
221
|
+
v-model:visible="showContextMenu"
|
|
222
|
+
:position="contextMenuPosition"
|
|
223
|
+
:can-move-up="canMoveUp"
|
|
224
|
+
:can-move-down="canMoveDown"
|
|
225
|
+
@property-config="handlePropertyConfig"
|
|
226
|
+
@tree-highlight="handleTreeHighlight"
|
|
227
|
+
@move-up="handleMoveUpFromMenu"
|
|
228
|
+
@move-down="handleMoveDownFromMenu"
|
|
229
|
+
@delete="handleDeleteFromMenu"
|
|
230
|
+
@remove="handleRemoveFromMenu"
|
|
231
|
+
/>
|
|
232
|
+
</div>
|
|
144
233
|
</template>
|
|
145
234
|
|
|
146
235
|
<script setup lang="ts">
|
|
147
|
-
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from
|
|
148
|
-
import type { Shape, GanttData, GanttColumn } from
|
|
149
|
-
import { eventBus } from
|
|
150
|
-
import GanttContextMenu from
|
|
151
|
-
import { getIcon } from
|
|
152
|
-
import { useChartRowSelection, useVirtualScroll } from
|
|
236
|
+
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
|
237
|
+
import type { Shape, GanttData, GanttColumn } from "../../types";
|
|
238
|
+
import { eventBus } from "../../store/eventBus";
|
|
239
|
+
import GanttContextMenu from "../GanttContextMenu/GanttContextMenu.vue";
|
|
240
|
+
import { getIcon } from "../../utils/iconLoader";
|
|
241
|
+
import { useChartRowSelection, useVirtualScroll } from "../../hooks";
|
|
153
242
|
/**
|
|
154
243
|
* 组件入参
|
|
155
244
|
*/
|
|
156
245
|
interface Props {
|
|
157
|
-
|
|
246
|
+
shape: Shape;
|
|
158
247
|
}
|
|
159
248
|
|
|
160
|
-
const props = defineProps<Props>()
|
|
249
|
+
const props = defineProps<Props>();
|
|
161
250
|
|
|
162
251
|
/**
|
|
163
252
|
* 前端内置固定列:序号列
|
|
164
253
|
*/
|
|
165
254
|
const indexColumn = {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
255
|
+
configId: "index",
|
|
256
|
+
name: "序号",
|
|
257
|
+
isShow: true,
|
|
258
|
+
isReadonly: false,
|
|
259
|
+
};
|
|
171
260
|
/**
|
|
172
261
|
* 当前组件内部使用的行数据类型
|
|
173
262
|
* 允许动态字段,便于适配后端返回的任意列
|
|
174
263
|
*/
|
|
175
264
|
// 列宽度状态(动态调整,使用 configId 作为 key)
|
|
176
|
-
const columnWidths = ref<Record<string, number>>({})
|
|
265
|
+
const columnWidths = ref<Record<string, number>>({});
|
|
177
266
|
/**
|
|
178
267
|
* 搜索输入框 ref
|
|
179
268
|
*/
|
|
180
|
-
const searchInputRef = ref<HTMLInputElement | null>(null)
|
|
269
|
+
const searchInputRef = ref<HTMLInputElement | null>(null);
|
|
181
270
|
|
|
182
271
|
/**
|
|
183
272
|
* 表格容器 ref(用于虚拟滚动)
|
|
184
273
|
*/
|
|
185
|
-
const tableContainerRef = ref<HTMLDivElement | null>(null)
|
|
274
|
+
const tableContainerRef = ref<HTMLDivElement | null>(null);
|
|
186
275
|
|
|
187
276
|
/**
|
|
188
277
|
* 当前编辑中的输入框 ref
|
|
189
278
|
*/
|
|
190
|
-
const editingInputRef = ref<HTMLInputElement | null>(null)
|
|
279
|
+
const editingInputRef = ref<HTMLInputElement | null>(null);
|
|
191
280
|
|
|
192
281
|
/**
|
|
193
282
|
* 搜索相关状态
|
|
194
283
|
*/
|
|
195
|
-
const showSearchBar = ref(false)
|
|
196
|
-
const searchKeyword = ref(
|
|
197
|
-
const searchMatchedIds = ref<string[]>([])
|
|
198
|
-
const currentMatchIndex = ref(-1)
|
|
284
|
+
const showSearchBar = ref(false);
|
|
285
|
+
const searchKeyword = ref("");
|
|
286
|
+
const searchMatchedIds = ref<string[]>([]);
|
|
287
|
+
const currentMatchIndex = ref(-1);
|
|
199
288
|
/**
|
|
200
289
|
* 默认列配置
|
|
201
290
|
* 没有后端列配置时,使用这组默认列
|
|
202
291
|
*/
|
|
203
|
-
const defaultColumns = ref<GanttColumn[]>([])
|
|
292
|
+
const defaultColumns = ref<GanttColumn[]>([]);
|
|
204
293
|
|
|
205
294
|
// 甘特图数据:使用后端数据
|
|
206
|
-
type GanttItem = GanttData & { hasChildren: boolean }
|
|
207
|
-
const ganttItems = ref<GanttItem[]>([])
|
|
295
|
+
type GanttItem = GanttData & { hasChildren: boolean };
|
|
296
|
+
const ganttItems = ref<GanttItem[]>([]);
|
|
208
297
|
/**
|
|
209
298
|
* 除序号列之外的第一列 configId(displayColumns[1])
|
|
210
299
|
*/
|
|
211
300
|
const firstDataColId = computed(() => {
|
|
212
|
-
|
|
213
|
-
})
|
|
301
|
+
return displayColumns.value[1]?.configId || "";
|
|
302
|
+
});
|
|
214
303
|
/**
|
|
215
304
|
* 实际表格数据
|
|
216
305
|
* 优先使用后端数据,没有则使用 mock 数据
|
|
@@ -218,753 +307,887 @@ const firstDataColId = computed(() => {
|
|
|
218
307
|
// const ganttItems = ref<GanttItem[]>([...mockData.value])
|
|
219
308
|
|
|
220
309
|
// 列配置:优先使用后端数据
|
|
221
|
-
const columnConfig = ref<any[]>([...defaultColumns.value])
|
|
310
|
+
const columnConfig = ref<any[]>([...defaultColumns.value]);
|
|
222
311
|
|
|
223
312
|
/**
|
|
224
313
|
* 当前可见列
|
|
225
314
|
*/
|
|
226
315
|
const visibleColumns = computed(() => {
|
|
227
|
-
|
|
228
|
-
})
|
|
316
|
+
return columnConfig.value.filter((col) => col.isShow);
|
|
317
|
+
});
|
|
229
318
|
/**
|
|
230
319
|
* 最终渲染列
|
|
231
320
|
* 序号列固定在最左侧
|
|
232
321
|
*/
|
|
233
322
|
const displayColumns = computed(() => {
|
|
234
|
-
|
|
235
|
-
})
|
|
323
|
+
return [indexColumn, ...visibleColumns.value];
|
|
324
|
+
});
|
|
236
325
|
|
|
237
326
|
/**
|
|
238
327
|
* 过滤后的数据(用于搜索)
|
|
239
328
|
* 如果没有搜索关键字,返回所有数据
|
|
240
329
|
*/
|
|
241
330
|
const filteredGanttItems = computed(() => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
331
|
+
const keyword = searchKeyword.value.trim().toLowerCase();
|
|
332
|
+
if (!keyword) {
|
|
333
|
+
return ganttItems.value;
|
|
334
|
+
}
|
|
246
335
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
336
|
+
if (searchMatchedIds.value.length === 0) {
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
250
339
|
|
|
251
|
-
|
|
252
|
-
|
|
340
|
+
return ganttItems.value.filter((item) =>
|
|
341
|
+
searchMatchedIds.value.includes(item.id),
|
|
342
|
+
);
|
|
343
|
+
});
|
|
253
344
|
|
|
254
345
|
// ========== 虚拟滚动 ==========
|
|
255
|
-
const TABLE_HEADER_HEIGHT = 28
|
|
256
|
-
const TABLE_ROW_HEIGHT = 32
|
|
257
|
-
const TABLE_VIRTUAL_OVERSCAN = 8
|
|
346
|
+
const TABLE_HEADER_HEIGHT = 28;
|
|
347
|
+
const TABLE_ROW_HEIGHT = 32;
|
|
348
|
+
const TABLE_VIRTUAL_OVERSCAN = 8;
|
|
258
349
|
|
|
259
350
|
const {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
351
|
+
virtualRows,
|
|
352
|
+
virtualTopSpacerHeight,
|
|
353
|
+
virtualBottomSpacerHeight,
|
|
354
|
+
virtualStartIndex,
|
|
355
|
+
virtualEndIndex,
|
|
356
|
+
syncVirtualViewportState,
|
|
357
|
+
visibleColumnSpan,
|
|
267
358
|
} = useVirtualScroll(filteredGanttItems, {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
})
|
|
359
|
+
itemHeight: TABLE_ROW_HEIGHT,
|
|
360
|
+
headerHeight: TABLE_HEADER_HEIGHT,
|
|
361
|
+
overscan: TABLE_VIRTUAL_OVERSCAN,
|
|
362
|
+
containerRef: tableContainerRef,
|
|
363
|
+
columnCount: computed(() => displayColumns.value.length),
|
|
364
|
+
});
|
|
274
365
|
|
|
275
366
|
/**
|
|
276
367
|
* 监听虚拟索引变化,自动关闭不可见行的编辑状态
|
|
277
368
|
*/
|
|
278
369
|
watch([virtualStartIndex, virtualEndIndex], () => {
|
|
279
|
-
|
|
370
|
+
if (!editingTextCell.value) return;
|
|
280
371
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
})
|
|
372
|
+
const renderedIds = new Set(virtualRows.value.map(({ item }) => item.id));
|
|
373
|
+
if (!renderedIds.has(editingTextCell.value.id)) {
|
|
374
|
+
closeCellEditor();
|
|
375
|
+
}
|
|
376
|
+
});
|
|
286
377
|
|
|
287
378
|
/**
|
|
288
379
|
* 行交互状态
|
|
289
380
|
* 统一复用表格/甘特图共享的行选择与右键菜单逻辑
|
|
290
381
|
*/
|
|
291
382
|
const {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
383
|
+
canMoveDown,
|
|
384
|
+
canMoveUp,
|
|
385
|
+
clearSelection,
|
|
386
|
+
contextMenuPosition,
|
|
387
|
+
deleteRows,
|
|
388
|
+
handleContextMenu,
|
|
389
|
+
handleDeleteFromMenu,
|
|
390
|
+
handleMoveDownFromMenu,
|
|
391
|
+
handleMoveUpFromMenu,
|
|
392
|
+
handlePropertyConfig,
|
|
393
|
+
handleRemoveFromMenu,
|
|
394
|
+
handleTreeHighlight,
|
|
395
|
+
moveDown,
|
|
396
|
+
moveUp,
|
|
397
|
+
onRemoveSuccess,
|
|
398
|
+
removeRows,
|
|
399
|
+
selectLastRow,
|
|
400
|
+
selectRow,
|
|
401
|
+
selectedRowIds,
|
|
402
|
+
showContextMenu,
|
|
312
403
|
} = useChartRowSelection<GanttItem>({
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 当前编辑中的单元格
|
|
328
|
-
* 增加 editorType / column 信息,便于根据 valueType 渲染不同编辑控件
|
|
329
|
-
*/
|
|
404
|
+
items: ganttItems,
|
|
405
|
+
rowSelectedEvent: "table-row-selected",
|
|
406
|
+
onClearSelection: () => {
|
|
407
|
+
closeCellEditor();
|
|
408
|
+
},
|
|
409
|
+
onDeleteRows: () => {
|
|
410
|
+
closeCellEditor();
|
|
411
|
+
},
|
|
412
|
+
onAfterRemoveSuccess: () => {
|
|
413
|
+
closeCellEditor();
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* 当前编辑中的单元格
|
|
419
|
+
* 增加 editorType / column 信息,便于根据 valueType 渲染不同编辑控件
|
|
420
|
+
*/
|
|
330
421
|
const editingTextCell = ref<{
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
} | null>(null)
|
|
422
|
+
id: string;
|
|
423
|
+
field: string;
|
|
424
|
+
value: any;
|
|
425
|
+
editorType?: string;
|
|
426
|
+
column?: any;
|
|
427
|
+
} | null>(null);
|
|
337
428
|
|
|
338
429
|
// 根据 configId 获取默认列宽
|
|
339
430
|
function getDefaultColumnWidth(configId: string): number {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
431
|
+
if (configId === "index") {
|
|
432
|
+
return 50;
|
|
433
|
+
}
|
|
434
|
+
// 其他列默认中等宽度
|
|
435
|
+
return 400;
|
|
345
436
|
}
|
|
346
437
|
function getColumnWidth(configId: string): number {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
438
|
+
if (configId === "index") {
|
|
439
|
+
return 50;
|
|
440
|
+
}
|
|
441
|
+
return columnWidths.value[configId] || getDefaultColumnWidth(configId);
|
|
351
442
|
}
|
|
352
443
|
|
|
353
444
|
/**
|
|
354
445
|
* 点击空白区域,清空选中并关闭编辑态
|
|
355
446
|
*/
|
|
356
447
|
function onContainerClick() {
|
|
357
|
-
|
|
448
|
+
clearSelection();
|
|
358
449
|
}
|
|
359
450
|
/**
|
|
360
451
|
* 新增行
|
|
361
452
|
*/
|
|
362
453
|
function addRow() {
|
|
363
|
-
|
|
454
|
+
eventBus.emit("table-add-row");
|
|
364
455
|
}
|
|
365
456
|
/**
|
|
366
457
|
* 搜索结果显示文本
|
|
367
458
|
*/
|
|
368
459
|
const searchResultText = computed(() => {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
})
|
|
460
|
+
if (!searchKeyword.value.trim()) return "";
|
|
461
|
+
if (searchMatchedIds.value.length === 0) return "0 of 0";
|
|
462
|
+
return `${currentMatchIndex.value + 1} of ${searchMatchedIds.value.length}`;
|
|
463
|
+
});
|
|
373
464
|
|
|
374
465
|
/**
|
|
375
466
|
* 上一条 / 下一条按钮状态
|
|
376
467
|
*/
|
|
377
468
|
const canGoPrev = computed(() => {
|
|
378
|
-
|
|
379
|
-
})
|
|
469
|
+
return searchMatchedIds.value.length > 0 && currentMatchIndex.value > 0;
|
|
470
|
+
});
|
|
380
471
|
|
|
381
472
|
const canGoNext = computed(() => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
})
|
|
473
|
+
return (
|
|
474
|
+
searchMatchedIds.value.length > 0 &&
|
|
475
|
+
currentMatchIndex.value < searchMatchedIds.value.length - 1
|
|
476
|
+
);
|
|
477
|
+
});
|
|
387
478
|
|
|
388
479
|
/**
|
|
389
480
|
* 执行搜索
|
|
390
481
|
* 搜索范围为当前可见列
|
|
391
482
|
*/
|
|
392
483
|
function runSearch() {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
484
|
+
const keyword = searchKeyword.value.trim().toLowerCase();
|
|
485
|
+
if (!keyword) {
|
|
486
|
+
searchMatchedIds.value = [];
|
|
487
|
+
currentMatchIndex.value = -1;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// 模糊匹配:搜索所有可见列的值
|
|
492
|
+
const matched = ganttItems.value.filter((item) => {
|
|
493
|
+
return visibleColumns.value.some((col) => {
|
|
494
|
+
const value = getDisplayCellValue(item, col);
|
|
495
|
+
return value.toLowerCase().includes(keyword);
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
searchMatchedIds.value = matched.map((item) => item.id);
|
|
500
|
+
currentMatchIndex.value = searchMatchedIds.value.length > 0 ? 0 : -1;
|
|
501
|
+
|
|
502
|
+
// 自动选中第一个匹配项
|
|
503
|
+
if (currentMatchIndex.value >= 0) {
|
|
504
|
+
selectRow(searchMatchedIds.value[0]);
|
|
505
|
+
}
|
|
415
506
|
}
|
|
416
507
|
|
|
417
508
|
/**
|
|
418
509
|
* 搜索输入事件
|
|
419
510
|
*/
|
|
420
511
|
function onSearchInput() {
|
|
421
|
-
|
|
512
|
+
runSearch();
|
|
422
513
|
}
|
|
423
514
|
|
|
424
515
|
/**
|
|
425
516
|
* 跳转上一条匹配
|
|
426
517
|
*/
|
|
427
518
|
function gotoPrevMatch() {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
519
|
+
if (!canGoPrev.value) return;
|
|
520
|
+
currentMatchIndex.value--;
|
|
521
|
+
selectRow(searchMatchedIds.value[currentMatchIndex.value]);
|
|
431
522
|
}
|
|
432
523
|
|
|
433
524
|
/**
|
|
434
525
|
* 跳转下一条匹配
|
|
435
526
|
*/
|
|
436
527
|
function gotoNextMatch() {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
528
|
+
if (!canGoNext.value) return;
|
|
529
|
+
currentMatchIndex.value++;
|
|
530
|
+
selectRow(searchMatchedIds.value[currentMatchIndex.value]);
|
|
440
531
|
}
|
|
441
532
|
|
|
442
533
|
/**
|
|
443
534
|
* 打开搜索栏
|
|
444
535
|
*/
|
|
445
536
|
function openSearch() {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
537
|
+
showSearchBar.value = true;
|
|
538
|
+
nextTick(() => {
|
|
539
|
+
searchInputRef.value?.focus();
|
|
540
|
+
});
|
|
450
541
|
}
|
|
451
542
|
|
|
452
543
|
/**
|
|
453
544
|
* 关闭搜索栏
|
|
454
545
|
*/
|
|
455
546
|
function closeSearch() {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
547
|
+
showSearchBar.value = false;
|
|
548
|
+
searchKeyword.value = "";
|
|
549
|
+
searchMatchedIds.value = [];
|
|
550
|
+
currentMatchIndex.value = -1;
|
|
460
551
|
}
|
|
461
552
|
|
|
462
553
|
/**
|
|
463
554
|
* 工具栏指令监听
|
|
464
555
|
*/
|
|
465
556
|
function onGanttToolbarAction(action: string) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
557
|
+
const actionMap: Record<string, () => void> = {
|
|
558
|
+
chartNewModel: addRow,
|
|
559
|
+
chartDelete: deleteRows,
|
|
560
|
+
chartRemove: removeRows,
|
|
561
|
+
chartMoveUp: moveUp,
|
|
562
|
+
chartMoveDown: moveDown,
|
|
563
|
+
chartSearch: openSearch,
|
|
564
|
+
};
|
|
565
|
+
const fn = actionMap[action];
|
|
566
|
+
if (!fn) return;
|
|
567
|
+
fn();
|
|
477
568
|
}
|
|
478
569
|
/**
|
|
479
570
|
* 判断当前单元格是否正在编辑
|
|
480
571
|
*/
|
|
481
572
|
function isCellEditing(rowId: string, field: string) {
|
|
482
|
-
|
|
573
|
+
return (
|
|
574
|
+
editingTextCell.value?.id === rowId &&
|
|
575
|
+
editingTextCell.value?.field === field
|
|
576
|
+
);
|
|
483
577
|
}
|
|
484
578
|
|
|
485
579
|
/**
|
|
486
580
|
* 设置当前编辑输入框 ref
|
|
487
581
|
*/
|
|
488
582
|
function setEditingInputRef(el: HTMLInputElement | null | any) {
|
|
489
|
-
|
|
583
|
+
editingInputRef.value = el;
|
|
490
584
|
}
|
|
491
585
|
|
|
492
586
|
/**
|
|
493
|
-
* 打开单元格编辑
|
|
494
|
-
* 根据列的 valueType 选择合适的编辑控件
|
|
495
|
-
*/
|
|
587
|
+
* 打开单元格编辑
|
|
588
|
+
* 根据列的 valueType 选择合适的编辑控件
|
|
589
|
+
*/
|
|
496
590
|
function openCellEditor(item: GanttItem, col: any) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
591
|
+
if (col.isReadonly === true) return;
|
|
592
|
+
if (!hasCellProperty(item, col.configId)) return;
|
|
593
|
+
|
|
594
|
+
// 原始值直接从数据行中读取,避免使用展示值(例如枚举的 label)
|
|
595
|
+
const rawValue = (item as any)[col.configId];
|
|
596
|
+
|
|
597
|
+
console.log("[graph-table-openCellEditor] valueType:", col.valueType, {
|
|
598
|
+
configId: col.configId,
|
|
599
|
+
column: col,
|
|
600
|
+
value: rawValue ?? null,
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
if (col.valueType === "Boolean") {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// 复杂类型:交给宿主应用使用“属性面板同款编辑器”处理
|
|
608
|
+
// 这样 graph 包保持轻量,不强依赖业务侧的弹窗/选择器实现
|
|
609
|
+
// 复杂值类型不走表格内联编辑,统一交给宿主侧弹出属性面板同款编辑器。
|
|
610
|
+
// Transfer 在业务上对应“选择用例”穿梭框,双击单元格时也要走这条链路。
|
|
611
|
+
const complexTypes = new Set([
|
|
612
|
+
"Element",
|
|
613
|
+
"Element_List",
|
|
614
|
+
"Relations_End",
|
|
615
|
+
"ValueSpecification",
|
|
616
|
+
"NoString",
|
|
617
|
+
"Transfer",
|
|
618
|
+
"Html",
|
|
619
|
+
"Htmld",
|
|
620
|
+
]);
|
|
621
|
+
if (complexTypes.has(col.valueType)) {
|
|
622
|
+
eventBus.emit("table-cell-open-editor", {
|
|
623
|
+
item,
|
|
624
|
+
column: col,
|
|
625
|
+
configId: col.configId,
|
|
626
|
+
valueType: col.valueType,
|
|
627
|
+
value: rawValue ?? null,
|
|
628
|
+
});
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
editingTextCell.value = {
|
|
633
|
+
id: item.id,
|
|
634
|
+
field: col.configId,
|
|
635
|
+
value: rawValue ?? "",
|
|
636
|
+
editorType: col.valueType,
|
|
637
|
+
column: col,
|
|
638
|
+
};
|
|
639
|
+
nextTick(() => {
|
|
640
|
+
const inst = editingInputRef.value as any;
|
|
641
|
+
inst?.focus?.();
|
|
642
|
+
inst?.select?.();
|
|
643
|
+
});
|
|
537
644
|
}
|
|
538
645
|
|
|
539
646
|
function getRawCellValue(item: any, configId: string) {
|
|
540
|
-
|
|
541
|
-
|
|
647
|
+
const v = item?.[configId];
|
|
648
|
+
return v;
|
|
542
649
|
}
|
|
543
650
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
function saveTextEdit(item: GanttItem, field: string) {
|
|
651
|
+
function hasCellProperty(item: any, configId: string): boolean {
|
|
652
|
+
return !!item && Object.prototype.hasOwnProperty.call(item, configId);
|
|
653
|
+
}
|
|
548
654
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
655
|
+
function normalizeBooleanValue(value: any): boolean | undefined {
|
|
656
|
+
if (value === null || value === undefined || value === "") {
|
|
657
|
+
return undefined;
|
|
658
|
+
}
|
|
553
659
|
|
|
554
|
-
|
|
555
|
-
|
|
660
|
+
if (typeof value === "boolean") {
|
|
661
|
+
return value;
|
|
662
|
+
}
|
|
556
663
|
|
|
557
|
-
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
664
|
+
if (typeof value === "string") {
|
|
665
|
+
if (value === "true") return true;
|
|
666
|
+
if (value === "false") return false;
|
|
667
|
+
if (value === "1") return true;
|
|
668
|
+
if (value === "0") return false;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (typeof value === "number") {
|
|
672
|
+
if (value === 1) return true;
|
|
673
|
+
if (value === 0) return false;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return undefined;
|
|
677
|
+
}
|
|
562
678
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
oldValue,
|
|
568
|
-
newValue
|
|
569
|
-
})
|
|
679
|
+
function hasBooleanCellValue(item: any, configId: string): boolean {
|
|
680
|
+
if (!hasCellProperty(item, configId)) {
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
570
683
|
|
|
571
|
-
|
|
572
|
-
|
|
684
|
+
return normalizeBooleanValue(item[configId]) !== undefined;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function stripHtmlTags(value: unknown): string {
|
|
688
|
+
return String(value ?? "")
|
|
689
|
+
.replace(/<[^>]+>/g, "")
|
|
690
|
+
.replace(/ /gi, " ")
|
|
691
|
+
.replace(/\s+/g, " ")
|
|
692
|
+
.trim();
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function handleBooleanCellChange(
|
|
696
|
+
item: GanttItem,
|
|
697
|
+
field: string,
|
|
698
|
+
value: string | number | boolean | undefined,
|
|
699
|
+
) {
|
|
700
|
+
if (!hasBooleanCellValue(item, field)) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const oldValue = getRawCellValue(item as any, field);
|
|
705
|
+
const normalizedOldValue = normalizeBooleanValue(oldValue);
|
|
706
|
+
const normalizedNewValue = normalizeBooleanValue(value);
|
|
707
|
+
|
|
708
|
+
if (normalizedOldValue === normalizedNewValue) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
eventBus.emit("gantt-cell-edit", {
|
|
713
|
+
item,
|
|
714
|
+
configId: field,
|
|
715
|
+
oldValue,
|
|
716
|
+
newValue: normalizedNewValue,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* 保存单元格值
|
|
722
|
+
*/
|
|
723
|
+
function saveTextEdit(item: GanttItem, field: string) {
|
|
724
|
+
if (!editingTextCell.value || editingTextCell.value.value === undefined) {
|
|
725
|
+
editingTextCell.value = null;
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const oldValue = getRawCellValue(item as any, field);
|
|
730
|
+
const newValue = editingTextCell.value.value;
|
|
731
|
+
|
|
732
|
+
// 值未改变,直接关闭编辑
|
|
733
|
+
if (oldValue === newValue) {
|
|
734
|
+
editingTextCell.value = null;
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// 发送编辑事件到父组件(与 Gantt 使用相同的事件名)
|
|
739
|
+
eventBus.emit("gantt-cell-edit", {
|
|
740
|
+
item,
|
|
741
|
+
configId: field,
|
|
742
|
+
oldValue,
|
|
743
|
+
newValue,
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// 暂时关闭编辑状态(等待接口返回)
|
|
747
|
+
editingTextCell.value = null;
|
|
573
748
|
}
|
|
574
749
|
|
|
575
750
|
/**
|
|
576
751
|
* 关闭单元格编辑
|
|
577
752
|
*/
|
|
578
753
|
function closeCellEditor() {
|
|
579
|
-
|
|
580
|
-
|
|
754
|
+
editingTextCell.value = null;
|
|
755
|
+
editingInputRef.value = null;
|
|
581
756
|
}
|
|
582
757
|
//监听获取后端列配置
|
|
583
|
-
watch(
|
|
758
|
+
watch(
|
|
759
|
+
() => props.shape.attributeColumns,
|
|
760
|
+
(columns) => {
|
|
584
761
|
if (columns && columns.length > 0) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
762
|
+
columnConfig.value = columns;
|
|
763
|
+
// 初始化列宽(如果还没有设置)
|
|
764
|
+
columns.forEach((col: any) => {
|
|
765
|
+
if (col.configId !== "index" && !columnWidths.value[col.configId]) {
|
|
766
|
+
columnWidths.value[col.configId] = getDefaultColumnWidth(
|
|
767
|
+
col.configId,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
// 同步到工具栏菜单
|
|
772
|
+
eventBus.emit("chart-columns-from-backend", columns);
|
|
594
773
|
} else {
|
|
595
|
-
|
|
596
|
-
|
|
774
|
+
// 没有后端数据时使用默认配置
|
|
775
|
+
columnConfig.value = [...defaultColumns.value];
|
|
597
776
|
}
|
|
598
|
-
},
|
|
777
|
+
},
|
|
778
|
+
{ immediate: true },
|
|
779
|
+
);
|
|
599
780
|
// 统一提取对象/对象数组类型单元格的展示文本和图标,避免 Element_List 展示成 [object Object]。
|
|
600
781
|
function getCellDisplayMeta(value: any): { text: string; icon: string } {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
text: text || String(value ?? ''),
|
|
610
|
-
icon: objectItems.find((item: any) => item.icon)?.icon || '',
|
|
611
|
-
}
|
|
612
|
-
}
|
|
782
|
+
if (Array.isArray(value)) {
|
|
783
|
+
const objectItems = value.filter(
|
|
784
|
+
(item) => item && typeof item === "object",
|
|
785
|
+
);
|
|
786
|
+
const text = objectItems
|
|
787
|
+
.map((item: any) => item.nodeName || item.modelName || "")
|
|
788
|
+
.filter(Boolean)
|
|
789
|
+
.join("、");
|
|
613
790
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}
|
|
791
|
+
return {
|
|
792
|
+
text: text || String(value ?? ""),
|
|
793
|
+
icon: objectItems.find((item: any) => item.icon)?.icon || "",
|
|
794
|
+
};
|
|
795
|
+
}
|
|
620
796
|
|
|
797
|
+
if (value && typeof value === "object") {
|
|
621
798
|
return {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
799
|
+
text: value.nodeName || value.modelName || String(value ?? ""),
|
|
800
|
+
icon: value.icon || "",
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
return {
|
|
805
|
+
text: String(value ?? ""),
|
|
806
|
+
icon: "",
|
|
807
|
+
};
|
|
625
808
|
}
|
|
626
809
|
|
|
627
810
|
// 根据列配置获取“展示值”(用于单元格展示 / title)
|
|
628
|
-
function getDisplayCellValue(
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
811
|
+
function getDisplayCellValue(
|
|
812
|
+
item: GanttData & { hasChildren: boolean },
|
|
813
|
+
col: any,
|
|
814
|
+
): string {
|
|
815
|
+
const configId = col.configId;
|
|
816
|
+
const value = (item as any)[configId];
|
|
817
|
+
|
|
818
|
+
if (col.valueType === "Html" || col.valueType === "Htmld") {
|
|
819
|
+
return stripHtmlTags(value);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (col.valueType === "Boolean") {
|
|
823
|
+
const normalizedValue = normalizeBooleanValue(value);
|
|
824
|
+
if (normalizedValue === true) return "是";
|
|
825
|
+
if (normalizedValue === false) return "否";
|
|
826
|
+
return "";
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// 枚举类型:用 literals 中的 key 作为展示文案
|
|
830
|
+
if (
|
|
831
|
+
col.valueType === "Enumeration" &&
|
|
832
|
+
col.literals &&
|
|
833
|
+
Array.isArray(col.literals)
|
|
834
|
+
) {
|
|
835
|
+
const matched = col.literals.find((opt: any) => opt.value === value);
|
|
836
|
+
return matched?.key ?? String(value ?? "");
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return getCellDisplayMeta(value).text;
|
|
645
840
|
}
|
|
646
841
|
|
|
647
842
|
// 获取单元格应展示的图标。
|
|
648
843
|
// 第一列仍然优先展示行本身图标,其他对象/对象数组列则展示值里的 icon。
|
|
649
|
-
function getDisplayCellIcon(
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
return
|
|
844
|
+
function getDisplayCellIcon(
|
|
845
|
+
item: GanttData & { hasChildren: boolean },
|
|
846
|
+
col: any,
|
|
847
|
+
): string {
|
|
848
|
+
if (col.configId === firstDataColId.value && item.icon) {
|
|
849
|
+
return item.icon;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return getCellDisplayMeta((item as any)[col.configId]).icon;
|
|
655
853
|
}
|
|
656
854
|
// 根据 configId 获取“原始值”(用于比较 old/new、搜索等)
|
|
657
|
-
function getCellValue(
|
|
658
|
-
|
|
855
|
+
function getCellValue(
|
|
856
|
+
item: GanttData & { hasChildren: boolean },
|
|
857
|
+
configId: string,
|
|
858
|
+
): string {
|
|
859
|
+
return getCellDisplayMeta((item as any)[configId]).text;
|
|
659
860
|
}
|
|
660
861
|
// 列宽调整
|
|
661
862
|
function startColumnResize(columnKey: string, e: MouseEvent) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
863
|
+
if (columnKey === "index") return;
|
|
864
|
+
|
|
865
|
+
e.stopPropagation();
|
|
866
|
+
e.preventDefault();
|
|
867
|
+
|
|
868
|
+
const startX = e.clientX;
|
|
869
|
+
const startWidth = getColumnWidth(columnKey);
|
|
870
|
+
|
|
871
|
+
const onMove = (ev: MouseEvent) => {
|
|
872
|
+
const delta = ev.clientX - startX;
|
|
873
|
+
const nextWidth = Math.max(200, startWidth + delta);
|
|
874
|
+
columnWidths.value[columnKey] = nextWidth;
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
const onUp = () => {
|
|
878
|
+
document.removeEventListener("mousemove", onMove);
|
|
879
|
+
document.removeEventListener("mouseup", onUp);
|
|
880
|
+
document.body.style.cursor = "";
|
|
881
|
+
document.body.style.userSelect = "";
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
document.body.style.cursor = "col-resize";
|
|
885
|
+
document.body.style.userSelect = "none";
|
|
886
|
+
document.addEventListener("mousemove", onMove);
|
|
887
|
+
document.addEventListener("mouseup", onUp);
|
|
687
888
|
}
|
|
688
889
|
function handleCanvasColumnChange(columns: any[]) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
890
|
+
if (columns && columns.length > 0) {
|
|
891
|
+
columnConfig.value = columns;
|
|
892
|
+
|
|
893
|
+
// 初始化列宽(如果还没有设置)
|
|
894
|
+
columns.forEach((col: any) => {
|
|
895
|
+
if (col.configId !== "index" && !columnWidths.value[col.configId]) {
|
|
896
|
+
columnWidths.value[col.configId] = getDefaultColumnWidth(col.configId);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
// 同步到工具栏菜单
|
|
901
|
+
// eventBus.emit('chart-columns-from-backend', columns)
|
|
902
|
+
}
|
|
702
903
|
}
|
|
703
904
|
|
|
704
905
|
// 监听后端数据变化
|
|
705
906
|
function handleCanvasDataChange(data: any[]) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
907
|
+
if (data && data.length > 0) {
|
|
908
|
+
ganttItems.value = data.map((d) => ({
|
|
909
|
+
...d,
|
|
910
|
+
hasChildren: false,
|
|
911
|
+
})) as (GanttData & { hasChildren: boolean })[];
|
|
912
|
+
}
|
|
709
913
|
}
|
|
710
914
|
|
|
711
915
|
// 监听列配置变更(来自工具栏菜单)
|
|
712
916
|
function onColumnConfigChange(config: Array<any>) {
|
|
713
|
-
|
|
917
|
+
columnConfig.value = config;
|
|
714
918
|
}
|
|
715
919
|
const tableWidth = computed(() => {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
})
|
|
920
|
+
return displayColumns.value.reduce((total, col) => {
|
|
921
|
+
return total + getColumnWidth(col.configId);
|
|
922
|
+
}, 0);
|
|
923
|
+
});
|
|
720
924
|
/**
|
|
721
925
|
* 生命周期:注册事件
|
|
722
926
|
*/
|
|
723
927
|
onMounted(() => {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
})
|
|
928
|
+
eventBus.on("table-toolbar-action", onGanttToolbarAction);
|
|
929
|
+
eventBus.on("canvas-data-change", handleCanvasDataChange);
|
|
930
|
+
eventBus.on("chart-column-config", onColumnConfigChange);
|
|
931
|
+
eventBus.on("canvas-data-column", handleCanvasColumnChange);
|
|
932
|
+
//监听移除成功事件
|
|
933
|
+
eventBus.on("table-gantt-remove:success", onRemoveSuccess);
|
|
934
|
+
// 新增成功回执:直接选中最后一条
|
|
935
|
+
eventBus.on("table-gantt-add:success", selectLastRow);
|
|
936
|
+
});
|
|
733
937
|
|
|
734
938
|
/**
|
|
735
939
|
* 生命周期:卸载事件
|
|
736
940
|
*/
|
|
737
941
|
onUnmounted(() => {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
})
|
|
942
|
+
eventBus.off("table-toolbar-action", onGanttToolbarAction);
|
|
943
|
+
eventBus.off("canvas-data-change", handleCanvasDataChange);
|
|
944
|
+
eventBus.off("chart-column-config", onColumnConfigChange);
|
|
945
|
+
eventBus.off("canvas-data-column", handleCanvasColumnChange);
|
|
946
|
+
eventBus.off("table-gantt-remove:success", onRemoveSuccess);
|
|
947
|
+
eventBus.off("table-gantt-add:success", selectLastRow);
|
|
948
|
+
});
|
|
745
949
|
</script>
|
|
746
950
|
|
|
747
951
|
<style scoped lang="scss">
|
|
748
952
|
/* 甘特表格容器 */
|
|
749
953
|
.gantt-container {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
954
|
+
--header-row-h: 28px;
|
|
955
|
+
--data-row-h: 32px;
|
|
956
|
+
|
|
957
|
+
position: relative;
|
|
958
|
+
display: flex;
|
|
959
|
+
width: 100%;
|
|
960
|
+
height: 100%;
|
|
961
|
+
border: 1px solid #dcdfe6;
|
|
962
|
+
background: #fff;
|
|
963
|
+
overflow: hidden;
|
|
964
|
+
font-size: 12px;
|
|
965
|
+
font-family:
|
|
966
|
+
"Source Han Sans SC", "Microsoft YaHei", "PingFang SC", Arial, sans-serif;
|
|
762
967
|
}
|
|
763
968
|
/* 左侧表格区域 */
|
|
764
969
|
.gantt-left {
|
|
765
|
-
|
|
766
|
-
|
|
970
|
+
width: 100%;
|
|
971
|
+
overflow: auto;
|
|
767
972
|
}
|
|
768
973
|
|
|
769
974
|
.gantt-left.with-search-bar {
|
|
770
|
-
|
|
975
|
+
margin-top: 36px;
|
|
771
976
|
}
|
|
772
977
|
|
|
773
978
|
/* 虚拟滚动占位行 */
|
|
774
979
|
.table-spacer-row td {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
980
|
+
height: 0;
|
|
981
|
+
padding: 0;
|
|
982
|
+
border: none !important;
|
|
778
983
|
}
|
|
779
984
|
|
|
780
985
|
.table-spacer {
|
|
781
|
-
|
|
986
|
+
width: 1px;
|
|
782
987
|
}
|
|
783
988
|
|
|
784
989
|
/* 表格 */
|
|
785
990
|
.gantt-table {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
991
|
+
border-collapse: separate;
|
|
992
|
+
border-spacing: 0;
|
|
993
|
+
table-layout: fixed;
|
|
789
994
|
}
|
|
790
995
|
|
|
791
996
|
.th-content {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
997
|
+
position: relative;
|
|
998
|
+
display: flex;
|
|
999
|
+
align-items: center;
|
|
1000
|
+
width: 100%;
|
|
1001
|
+
height: 100%;
|
|
1002
|
+
min-width: 0;
|
|
1003
|
+
box-sizing: border-box;
|
|
1004
|
+
}
|
|
1005
|
+
.th-content > :nth-child(1) {
|
|
1006
|
+
margin-left: 4px;
|
|
800
1007
|
}
|
|
801
|
-
|
|
802
1008
|
.gantt-table th,
|
|
803
1009
|
.gantt-table td {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1010
|
+
box-sizing: border-box;
|
|
1011
|
+
padding: 0 8px;
|
|
1012
|
+
border: none;
|
|
1013
|
+
border-bottom: 1px solid #ebeef5;
|
|
1014
|
+
border-right: 1px solid #ebeef5;
|
|
1015
|
+
text-align: left;
|
|
1016
|
+
vertical-align: middle;
|
|
1017
|
+
white-space: nowrap;
|
|
1018
|
+
overflow: hidden;
|
|
1019
|
+
text-overflow: ellipsis;
|
|
814
1020
|
}
|
|
815
1021
|
|
|
816
1022
|
.gantt-table th:first-child,
|
|
817
1023
|
.gantt-table td:first-child {
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1024
|
+
border-left: 1px solid #ebeef5;
|
|
1025
|
+
width: 50px;
|
|
1026
|
+
min-width: 50px;
|
|
1027
|
+
max-width: 50px;
|
|
822
1028
|
}
|
|
823
1029
|
|
|
824
|
-
|
|
825
1030
|
/* 表头第二行 */
|
|
826
1031
|
.header-row-2 th {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
1032
|
+
position: sticky;
|
|
1033
|
+
z-index: 1;
|
|
1034
|
+
height: var(--header-row-h);
|
|
1035
|
+
background: #f5f7fa;
|
|
1036
|
+
border-bottom-color: #dcdfe6;
|
|
1037
|
+
font-weight: 500;
|
|
1038
|
+
color: #606266;
|
|
834
1039
|
}
|
|
835
1040
|
|
|
836
1041
|
/* 数据行 */
|
|
837
1042
|
.gantt-table td {
|
|
838
|
-
|
|
1043
|
+
height: var(--data-row-h);
|
|
839
1044
|
}
|
|
840
1045
|
|
|
841
1046
|
/* 列样式 */
|
|
842
1047
|
.col-name {
|
|
843
|
-
|
|
1048
|
+
text-align: left;
|
|
844
1049
|
}
|
|
845
1050
|
|
|
846
1051
|
.col-td {
|
|
847
|
-
|
|
1052
|
+
text-align: center;
|
|
848
1053
|
}
|
|
849
1054
|
|
|
850
1055
|
.col-index {
|
|
851
|
-
|
|
1056
|
+
width: 60px !important;
|
|
852
1057
|
}
|
|
853
1058
|
|
|
854
1059
|
.th-label {
|
|
855
|
-
|
|
1060
|
+
font-weight: 600;
|
|
856
1061
|
}
|
|
857
1062
|
.cell-text {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1063
|
+
flex: 1;
|
|
1064
|
+
min-width: 0;
|
|
1065
|
+
overflow: hidden;
|
|
1066
|
+
text-overflow: ellipsis;
|
|
1067
|
+
white-space: nowrap;
|
|
863
1068
|
}
|
|
864
1069
|
/* 展开图标 */
|
|
865
1070
|
.expand-icon {
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1071
|
+
display: inline-block;
|
|
1072
|
+
margin-right: 4px;
|
|
1073
|
+
cursor: pointer;
|
|
1074
|
+
user-select: none;
|
|
870
1075
|
}
|
|
871
1076
|
|
|
872
1077
|
/* 名称文本 */
|
|
873
1078
|
.item-name {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1079
|
+
display: inline-block;
|
|
1080
|
+
width: 100%;
|
|
1081
|
+
overflow: hidden;
|
|
1082
|
+
text-overflow: ellipsis;
|
|
1083
|
+
vertical-align: middle;
|
|
1084
|
+
display: flex;
|
|
1085
|
+
align-items: center;
|
|
1086
|
+
min-width: 0;
|
|
882
1087
|
}
|
|
883
1088
|
|
|
884
1089
|
.expand-icon {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1090
|
+
flex: 0 0 auto;
|
|
1091
|
+
display: inline-flex;
|
|
1092
|
+
align-items: center;
|
|
1093
|
+
justify-content: center;
|
|
1094
|
+
margin-right: 5px;
|
|
1095
|
+
cursor: pointer;
|
|
1096
|
+
user-select: none;
|
|
892
1097
|
}
|
|
893
1098
|
|
|
894
1099
|
.item-index {
|
|
895
|
-
|
|
1100
|
+
text-align: center;
|
|
896
1101
|
}
|
|
897
1102
|
|
|
898
1103
|
/* 单元格编辑输入框(Element Plus 包一层外壳,尽量贴合现有样式) */
|
|
899
1104
|
.cell-input {
|
|
900
|
-
|
|
1105
|
+
width: 100%;
|
|
901
1106
|
}
|
|
902
1107
|
|
|
903
1108
|
.cell-input :deep(.el-input__wrapper) {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1109
|
+
padding: 0 6px;
|
|
1110
|
+
height: 24px;
|
|
1111
|
+
box-shadow: 0 0 0 1px #409eff inset;
|
|
1112
|
+
border-radius: 3px;
|
|
908
1113
|
}
|
|
909
1114
|
|
|
910
1115
|
.cell-select {
|
|
911
|
-
|
|
1116
|
+
width: 100%;
|
|
912
1117
|
}
|
|
913
1118
|
|
|
914
1119
|
.cell-select :deep(.el-input__wrapper) {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1120
|
+
min-height: 24px;
|
|
1121
|
+
height: 24px;
|
|
1122
|
+
padding: 0 6px;
|
|
1123
|
+
border-radius: 3px;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
.cell-boolean-display {
|
|
1127
|
+
overflow: visible;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
.cell-boolean-group {
|
|
1131
|
+
display: inline-flex;
|
|
1132
|
+
align-items: center;
|
|
1133
|
+
height: 24px;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.cell-boolean-group :deep(.el-radio) {
|
|
1137
|
+
align-items: center;
|
|
1138
|
+
margin-right: 10px;
|
|
1139
|
+
margin-bottom: 0;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
.cell-boolean-group :deep(.el-radio:last-child) {
|
|
1143
|
+
margin-right: 0;
|
|
919
1144
|
}
|
|
920
1145
|
|
|
921
|
-
.cell-
|
|
922
|
-
|
|
923
|
-
align-items: center;
|
|
924
|
-
height: 24px;
|
|
1146
|
+
.cell-boolean-group :deep(.el-radio__label) {
|
|
1147
|
+
padding-left: 4px;
|
|
925
1148
|
}
|
|
926
1149
|
|
|
927
1150
|
.cell-date {
|
|
928
|
-
|
|
1151
|
+
width: 100%;
|
|
929
1152
|
}
|
|
930
1153
|
|
|
931
1154
|
.cell-date :deep(.el-input__wrapper) {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1155
|
+
min-height: 24px;
|
|
1156
|
+
height: 24px;
|
|
1157
|
+
padding: 0 6px;
|
|
1158
|
+
border-radius: 3px;
|
|
936
1159
|
}
|
|
937
1160
|
|
|
938
1161
|
.cell-textarea {
|
|
939
|
-
|
|
1162
|
+
width: 100%;
|
|
940
1163
|
}
|
|
941
1164
|
|
|
942
1165
|
.cell-textarea :deep(.el-textarea__inner) {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1166
|
+
font-size: 12px;
|
|
1167
|
+
line-height: 18px;
|
|
1168
|
+
padding: 4px 6px;
|
|
946
1169
|
}
|
|
947
1170
|
|
|
948
1171
|
.col-resizer {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1172
|
+
position: absolute;
|
|
1173
|
+
right: -8px;
|
|
1174
|
+
top: 0;
|
|
1175
|
+
bottom: 0;
|
|
1176
|
+
width: 5px;
|
|
1177
|
+
cursor: col-resize;
|
|
1178
|
+
user-select: none;
|
|
956
1179
|
}
|
|
957
1180
|
|
|
958
1181
|
.col-resizer:hover {
|
|
959
|
-
|
|
1182
|
+
background-color: rgba(64, 158, 255, 0.1);
|
|
960
1183
|
}
|
|
961
1184
|
|
|
962
1185
|
/* 选中行高亮 */
|
|
963
1186
|
.row-selected {
|
|
964
|
-
|
|
1187
|
+
background-color: #ecf5ff !important;
|
|
965
1188
|
}
|
|
966
1189
|
|
|
967
1190
|
.gantt-row.row-selected td {
|
|
968
|
-
|
|
1191
|
+
background-color: #ecf5ff;
|
|
969
1192
|
}
|
|
970
1193
|
</style>
|