Skip to content

Commit 3b02805

Browse files
committed
Merge branch 'templates'
2 parents 34030b2 + 584b44b commit 3b02805

File tree

6 files changed

+324
-5
lines changed

6 files changed

+324
-5
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66
* Открывает текстовые модули 1С (*.bsl) через контекстное меню у соответствующих элементов.
77
* У метаданных для которых есть возможность создания предопределенных элементов можно открыть панель существующих в конфигурации элементов.
88

9-
![Скриншот дерева метаданных](/resources/screenshot.png)
9+
![Скриншот дерева метаданных](/resources/screenshot_0.png)
10+
11+
**ВНИМАНИЕ! Следующий функционал находится в статусе 'beta'! Предоставлен исключительно в ознакомительных целях.**
12+
13+
* Открывает табличные документы в режиме просмотра.
14+
15+
![Скриншот табличного документа](/resources/screenshot_1.png)
1016

1117
## Метаданные и модули
1218

File renamed without changes.

resources/screenshot_1.png

75.2 KB
Loading

src/metadataView.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import * as xml2js from 'xml2js';
55
import { posix } from 'path';
66
import * as path from 'path';
77
import { MetadataFile, ObjectMetadata, ObjectParams, VersionMetadata } from './metadataInterfaces';
8+
import { TemplatePanel } from './templatePanel';
9+
import { TemplateFile } from './templatInterfaces';
810
import { PredefinedDataFile } from './predefinedDataInterfaces';
911
import { PredefinedDataPanel } from './predefinedDataPanel';
1012

@@ -58,6 +60,7 @@ interface TreeItemParams {
5860
context?: string,
5961
command?: string,
6062
commandTitle?: string,
63+
commandArguments?: any[],
6164
path?: string,
6265
children?: TreeItem[],
6366
}
@@ -98,7 +101,31 @@ export class MetadataView {
98101
view.reveal(tree[0]);
99102
});
100103

104+
vscode.commands.registerCommand('metadataViewer.showTemplate', (template) => this.openTemplate(context, template));
101105
vscode.commands.registerCommand('metadataViewer.openPredefinedData', (item) => this.openPredefinedData(context, item));
106+
}
107+
108+
private openTemplate(context: vscode.ExtensionContext, template: string): void {
109+
const rootPath = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0))
110+
? vscode.workspace.workspaceFolders[0].uri : undefined;
111+
112+
if (rootPath) {
113+
const fileName = posix.join(template, 'Ext/Template.xml');
114+
if (!fs.existsSync(fileName)) {
115+
return;
116+
}
117+
vscode.workspace.fs.readFile(rootPath.with({ path: fileName }))
118+
.then(configXml => {
119+
xml2js.parseString(configXml, (err, result) => {
120+
const typedResult = result as TemplateFile;
121+
if (!typedResult.document) {
122+
// Это макет, но другого типа. Для него нужно писать свою панель
123+
return;
124+
}
125+
TemplatePanel.show(context.extensionUri, typedResult.document);
126+
});
127+
});
128+
}
102129
}
103130

104131
private openPredefinedData(context: vscode.ExtensionContext, item: TreeItem): void {
@@ -230,12 +257,16 @@ function CreateTreeElements(element: TreeItem, metadataFile: MetadataFile) {
230257
if (!previous.template[objectName]) {
231258
previous.template[objectName] = [];
232259
}
260+
const path = `${element.id}/${CreatePath(objectName)}/Templates/${current.$.name.split('.').pop()}`;
233261
previous.template[objectName].push(GetTreeItem(
234262
treeItemIdSlash + current.$.id,
235263
current.$.name,
236264
{
237265
icon: 'template',
238-
path: `${element.id}/${CreatePath(objectName)}/Templates/${current.$.name.split('.').pop()}`,
266+
command: 'metadataViewer.showTemplate',
267+
commandTitle: 'Show template',
268+
commandArguments: [ path ],
269+
path: path,
239270
}));
240271
}
241272
return previous;
@@ -661,9 +692,9 @@ function GetTreeItem(id: string, name: string, params: TreeItemParams ): TreeIte
661692
if (params.context) {
662693
treeItem.contextValue = params.context;
663694
}
664-
treeItem.path = params.path; // ?? CreatePath(name.split('.').slice(0,2).join('.'));
695+
treeItem.path = params.path;
665696
if (params.command && params.commandTitle) {
666-
treeItem.command = { command: params.command, title: params.commandTitle };
697+
treeItem.command = { command: params.command, title: params.commandTitle, arguments: params.commandArguments };
667698
}
668699

669700
return treeItem;
@@ -721,7 +752,8 @@ function CreatePath(name: string): string {
721752
.replace('CalculationRegister.', 'CalculationRegisters/')
722753
.replace('BusinessProcess.', 'BusinessProcesses/')
723754
.replace('Task.', 'Tasks/')
724-
.replace('ExternalDataSource.', 'ExternalDataSources/');
755+
.replace('ExternalDataSource.', 'ExternalDataSources/')
756+
.replace('.Template.', '/Templates/');
725757
}
726758

727759
function NodeWithIdTreeDataProvider(): vscode.TreeDataProvider<TreeItem> {

src/templatInterfaces.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
export interface TemplateFile {
2+
document: TemplateDocument;
3+
}
4+
5+
export interface TemplateDocument {
6+
rowsItem: TemplateRow[];
7+
columns: TemplateColumns[];
8+
format: TemplateFormat[];
9+
merge: TemplateMergeCells[];
10+
font: [];
11+
}
12+
13+
interface TemplateColumns {
14+
id: string[];
15+
size: string[];
16+
columnsItem: ColumnsItem[];
17+
}
18+
19+
interface ColumnsItem {
20+
index: string[];
21+
column: ColumnsItemFormat[]
22+
}
23+
24+
interface ColumnsItemFormat {
25+
formatIndex: string[];
26+
}
27+
28+
interface TemplateFormat {
29+
width: string[];
30+
horizontalAlignment: string[];
31+
bySelectedColumns: string[];
32+
border: string[];
33+
leftBorder: string[];
34+
topBorder: string[];
35+
bottomBorder: string[];
36+
rightBorder: string[];
37+
textPlacement: string[];
38+
font: string[];
39+
}
40+
41+
export interface TemplateMergeCells {
42+
r: number[];
43+
c: number[];
44+
w: number[];
45+
h: number[];
46+
}
47+
48+
export interface TemplateRow {
49+
index: number;
50+
row: TemplateColumn[];
51+
}
52+
53+
export interface TemplateColumn {
54+
columnsID: string[];
55+
formatIndex: number;
56+
c: TemplateCell[];
57+
}
58+
59+
interface TemplateCell {
60+
i: string[];
61+
c: TemplateCellData[];
62+
}
63+
64+
interface TemplateCellData {
65+
f: TemplateFormat[];
66+
parameter: string[];
67+
tl: TemplateTextData[];
68+
}
69+
70+
interface TemplateTextData {
71+
[key: string]: TemplateTextData[]
72+
}

src/templatePanel.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import * as vscode from 'vscode';
2+
import { TemplateColumn, TemplateDocument, TemplateMergeCells, TemplateRow } from './templatInterfaces';
3+
4+
export class TemplatePanel {
5+
6+
public static readonly viewType = 'metadataViewer.templatePanel';
7+
8+
public static show(extensionUri: vscode.Uri, document: TemplateDocument) {
9+
const column = vscode.window.activeTextEditor
10+
? vscode.window.activeTextEditor.viewColumn
11+
: undefined;
12+
13+
const panel = vscode.window.createWebviewPanel(
14+
TemplatePanel.viewType,
15+
"Макет",
16+
column || vscode.ViewColumn.One
17+
);
18+
19+
panel.webview.html = this._getHtmlForWebview(panel.webview, extensionUri, document);
20+
}
21+
22+
private static _getHtmlForWebview(webview: vscode.Webview, extensionUri: vscode.Uri, document: TemplateDocument) {
23+
const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'template', 'styles.css'));
24+
let indexRow = 0;
25+
let hasColumndId = false;
26+
document.columns.forEach(columns => {
27+
if (columns.id) {
28+
hasColumndId = true;
29+
}
30+
});
31+
32+
return `<!DOCTYPE html>
33+
<html lang="en">
34+
<head>
35+
<meta charset="UTF-8">
36+
37+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
38+
<title></title>
39+
40+
<link href="${styleUri}" rel="stylesheet" />
41+
</style>
42+
</head>
43+
<body>
44+
${document.columns
45+
.filter(columns => columns.size[0] !== '0')
46+
.map(columns => { return `
47+
<table style="width: 800px; max-width: 800px; table-layout: fixed; white-space: nowrap; border-color: #333;" rules="all">
48+
<thead>
49+
<tr>
50+
<th style="max-width: 30px; width: 30px;"></th>
51+
${(columns
52+
.columnsItem ?? [])
53+
.map((c, index) => {
54+
const formatIndex = Number(c.column[0].formatIndex[0]) - 1;
55+
const columnWidth = document.format[formatIndex].width ? document.format[formatIndex].width[0] : '80';
56+
57+
return '<th' + (document.format[formatIndex] ?
58+
` style="max-width: ${columnWidth}px; width: ${columnWidth}px;"` :
59+
'') + `>${index + 1}</th>`;
60+
}).join('')}
61+
</tr>
62+
</thead>
63+
${(() => {
64+
return document.rowsItem
65+
.filter(ri => hasColumndId ? (ri.row[0].columnsID ? ri.row[0].columnsID[0] === columns.id[0] : false) : true)
66+
.map((_) => {
67+
let additionalRows = '';
68+
for(let i = indexRow; i < Number(_.index) - 1; i++) {
69+
// TODO: Высота из формата
70+
additionalRows += `<tr style="height: 20px;"><td style="text-align: center;">${++indexRow + 1}</td></tr>`;
71+
indexRow++;
72+
}
73+
indexRow = Number(_.index);
74+
return additionalRows + _getRow(_, document);
75+
}).join('');
76+
})()}
77+
</table>`;
78+
})
79+
}
80+
</body>
81+
</html>`;
82+
}
83+
}
84+
85+
function _getRow(templateRow: TemplateRow, document: TemplateDocument) {
86+
const indexRow = Number(templateRow.index);
87+
// TODO: Высота из формата
88+
return `<tr style="height: 20px;">
89+
<td style="text-align: center;">${indexRow + 1}</td>
90+
${(() => templateRow.row.map(column => _getColumn(column, indexRow, document)).join(''))()}
91+
</tr>`;
92+
}
93+
94+
function _getColumn(templatecolumn: TemplateColumn, indexRow: number, document: TemplateDocument) {
95+
if (templatecolumn.c) {
96+
let indexColumn = 0;
97+
let merge: TemplateMergeCells[] = [];
98+
let mergeSkipColumns = 0;
99+
100+
const rowMerge = FindRowMerge(indexRow, document.merge);
101+
102+
return templatecolumn.c.map((c) => {
103+
104+
let additionalColumns = '';
105+
106+
if (c.i) {
107+
let mergeSkipRows = 0;
108+
const mergeRows = rowMerge.filter(rm => Number(rm.c[0]) + 1 >= indexColumn && Number(rm.c[0]) + 1 <= Number(c.i[0]));
109+
if (mergeRows.length !== 0) {
110+
mergeSkipRows = mergeRows.reduce((previous, current) => {
111+
previous += Number(current.w[0]) + 1;
112+
return previous;
113+
}, 0);
114+
}
115+
for(let i = indexColumn + (HasMergeColumns(merge) ? Number(merge[0].w) : 0) + mergeSkipRows; i < Number(c.i[0]); i++){
116+
additionalColumns += '<td></td>';
117+
}
118+
indexColumn = Number(c.i[0]);
119+
} else if (mergeSkipColumns !== 0) {
120+
merge = [];
121+
mergeSkipColumns--;
122+
indexColumn++;
123+
return;
124+
}
125+
126+
merge = document.merge.filter(m => m.r[0] == indexRow && m.c[0] == indexColumn);
127+
128+
const hasMergeColumns = HasMergeColumns(merge);
129+
const hasMergeRows = HasMergeRows(merge);
130+
131+
mergeSkipColumns = (hasMergeColumns ? Number(merge[0].w) : 0);
132+
indexColumn++;
133+
134+
let style = '';
135+
const cellFormat = document.format[Number(c.c[0].f[0]) - 1];
136+
if (cellFormat) {
137+
if (cellFormat.horizontalAlignment) {
138+
if (cellFormat.horizontalAlignment[0].toLowerCase() === 'right' && !hasMergeColumns) {
139+
style += `float: right; text-align: right; border-top: 0; border-bottom: 0; border-left: 0;`;
140+
} else {
141+
style += `text-align: ${cellFormat.horizontalAlignment[0].toLowerCase()};`;
142+
}
143+
}
144+
145+
if (cellFormat.border && cellFormat.border[0] === '1') {
146+
style += ' border: 2px solid #fff;';
147+
}
148+
if (cellFormat.leftBorder && cellFormat.leftBorder[0] === '1') {
149+
style += ' left-border: 2px solid #fff;';
150+
}
151+
if (cellFormat.topBorder && cellFormat.topBorder[0] === '1') {
152+
style += ' top-border: 2px solid #fff;';
153+
}
154+
if (cellFormat.bottomBorder && cellFormat.bottomBorder[0] === '1') {
155+
style += ' bottom-border: 2px solid #fff;';
156+
}
157+
if (cellFormat.rightBorder && cellFormat.rightBorder[0] === '1') {
158+
style += ' right-border: 2px solid #fff;';
159+
}
160+
161+
if (cellFormat.textPlacement && cellFormat.textPlacement[0] === 'Wrap') {
162+
style += ' white-space: normal;';
163+
}
164+
165+
if (cellFormat.font && cellFormat.font[0] === '1') {
166+
// TODO:
167+
}
168+
}
169+
if (style) {
170+
style = ' style="' + style + '"';
171+
}
172+
173+
if (c.c.length !== 0 && c.c[0].parameter) {
174+
return `${additionalColumns}<td${hasMergeColumns ? ` colspan=${Number(merge[0].w[0]) + 1}` : ''}
175+
${hasMergeRows ? ` rowspan=${Number(merge[0].h[0]) + 1}` : ''}${style}>
176+
&lt;${c.c[0].parameter[0]}&gt;
177+
</td>`;
178+
} else if (c.c[0].tl) {
179+
const tl = c.c[0].tl[0];
180+
if (tl) {
181+
return `${additionalColumns}<td${hasMergeColumns ? ` colspan=${Number(merge[0].w[0]) + 1}` : ''}
182+
${hasMergeRows ? ` rowspan=${Number(merge[0].h[0]) + 1}` : ''}${style}>
183+
${tl['v8:item'][0]['v8:content'][0]}
184+
</td>`;
185+
}
186+
} else if (additionalColumns) {
187+
return `${additionalColumns}<td${hasMergeColumns ? ` colspan=${Number(merge[0].w[0]) + 1}` : ''}
188+
${hasMergeRows ? ` rowspan=${Number(merge[0].h[0]) + 1}` : ''}></td>`;
189+
} else {
190+
return `<td${hasMergeColumns ? ` colspan=${Number(merge[0].w[0]) + 1}` : ''}
191+
${hasMergeRows ? ` rowspan=${Number(merge[0].h[0]) + 1}` : ''}></td>`;
192+
}
193+
}).join('');
194+
}
195+
}
196+
197+
function FindRowMerge(indexRow: number, merge: TemplateMergeCells[]) {
198+
return merge
199+
.filter(m =>
200+
m.h && m.w &&
201+
(Number(m.r[0]) < indexRow && Number(m.r[0]) + Number(m.h[0]) >= indexRow));
202+
}
203+
204+
function HasMergeColumns(merge: TemplateMergeCells[]) {
205+
return merge && merge.length && merge[0].w;
206+
}
207+
function HasMergeRows(merge: TemplateMergeCells[]) {
208+
return merge && merge.length && merge[0].h;
209+
}

0 commit comments

Comments
 (0)