|
@@ -3,7 +3,7 @@ import moment from 'moment';
|
|
* 甘特图
|
|
* 甘特图
|
|
* @param {} options
|
|
* @param {} options
|
|
*/
|
|
*/
|
|
-export function GanttChart(options) {
|
|
|
|
|
|
+export function GanttChartDay(options) {
|
|
// 任务列表
|
|
// 任务列表
|
|
this.tasks = options.tasks || [];
|
|
this.tasks = options.tasks || [];
|
|
// AntVG Canvas
|
|
// AntVG Canvas
|
|
@@ -55,6 +55,8 @@ export function GanttChart(options) {
|
|
this.todayTimeLineEl = null;
|
|
this.todayTimeLineEl = null;
|
|
// 点击回调事件
|
|
// 点击回调事件
|
|
this.callback = options.callback || null;
|
|
this.callback = options.callback || null;
|
|
|
|
+ // 翻页时间回调
|
|
|
|
+ this.pageToCallback = options.pageToCallback || null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -62,7 +64,7 @@ export function GanttChart(options) {
|
|
* 设置数据
|
|
* 设置数据
|
|
* @param {*} _tasks
|
|
* @param {*} _tasks
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.changeTasks = function(_tasks){
|
|
|
|
|
|
+GanttChartDay.prototype.changeTasks = function(_tasks){
|
|
this.tasks = _tasks
|
|
this.tasks = _tasks
|
|
}
|
|
}
|
|
|
|
|
|
@@ -70,7 +72,7 @@ GanttChart.prototype.changeTasks = function(_tasks){
|
|
* 打开关闭分组
|
|
* 打开关闭分组
|
|
*/
|
|
*/
|
|
|
|
|
|
-GanttChart.prototype.toggle = function(index) {
|
|
|
|
|
|
+GanttChartDay.prototype.toggle = function(index) {
|
|
if (this.tasks[index].open) {
|
|
if (this.tasks[index].open) {
|
|
this.tasks[index].open = false;
|
|
this.tasks[index].open = false;
|
|
} else {
|
|
} else {
|
|
@@ -83,7 +85,7 @@ GanttChart.prototype.toggle = function(index) {
|
|
/**
|
|
/**
|
|
* 预处理数据
|
|
* 预处理数据
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.processData = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.processData = function() {
|
|
for (let i = 0; i < this.tasks.length; i++) {
|
|
for (let i = 0; i < this.tasks.length; i++) {
|
|
let currentTopTask = this.tasks[i];
|
|
let currentTopTask = this.tasks[i];
|
|
let lastTopTask = null;
|
|
let lastTopTask = null;
|
|
@@ -112,7 +114,7 @@ GanttChart.prototype.processData = function() {
|
|
/**
|
|
/**
|
|
* 强制清空图像,重绘
|
|
* 强制清空图像,重绘
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.forceRefreshGraph = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.forceRefreshGraph = function() {
|
|
this.tasks.forEach((topTask) => {
|
|
this.tasks.forEach((topTask) => {
|
|
topTask.gGroup = null;
|
|
topTask.gGroup = null;
|
|
});
|
|
});
|
|
@@ -124,7 +126,7 @@ GanttChart.prototype.forceRefreshGraph = function() {
|
|
/**
|
|
/**
|
|
* 准备绘制,用于初始化和强制刷新
|
|
* 准备绘制,用于初始化和强制刷新
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.initDrawingReady = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.initDrawingReady = function() {
|
|
this.initCanvas();
|
|
this.initCanvas();
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
this.initDragHandler();
|
|
this.initDragHandler();
|
|
@@ -138,7 +140,7 @@ GanttChart.prototype.initDrawingReady = function() {
|
|
/**
|
|
/**
|
|
* 翻页
|
|
* 翻页
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.pageTo = function(dir = 'next') {
|
|
|
|
|
|
+GanttChartDay.prototype.pageTo = function(dir = 'next') {
|
|
this.draging = false;
|
|
this.draging = false;
|
|
this.endEvent = null;
|
|
this.endEvent = null;
|
|
if (dir == 'next') {
|
|
if (dir == 'next') {
|
|
@@ -151,6 +153,7 @@ GanttChart.prototype.pageTo = function(dir = 'next') {
|
|
this.offsetDis = 2*this.viewWidth
|
|
this.offsetDis = 2*this.viewWidth
|
|
}
|
|
}
|
|
this.endAt = moment(this.startAt).add(this.daysCount, 'days');
|
|
this.endAt = moment(this.startAt).add(this.daysCount, 'days');
|
|
|
|
+ this.pageToCallback({dir: dir, startAt: this.startAt.format('YYYY-MM-DD'), endAt: this.endAt.format('YYYY-MM-DD')})
|
|
console.log('已翻页', this.startAt.format('YYYY-MM-DD'),this.endAt.format('YYYY-MM-DD'), this.offsetDis);
|
|
console.log('已翻页', this.startAt.format('YYYY-MM-DD'),this.endAt.format('YYYY-MM-DD'), this.offsetDis);
|
|
// offsetDis = viewWidth;
|
|
// offsetDis = viewWidth;
|
|
this.forceRefreshGraph();
|
|
this.forceRefreshGraph();
|
|
@@ -162,7 +165,7 @@ let lastClickAt = null;
|
|
* 改变graphDiv 滚动距离
|
|
* 改变graphDiv 滚动距离
|
|
* 到达边界距离后,刷新页面
|
|
* 到达边界距离后,刷新页面
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.doDrag = function(sEvent, eEvent) {
|
|
|
|
|
|
+GanttChartDay.prototype.doDrag = function(sEvent, eEvent) {
|
|
if (sEvent == null) {
|
|
if (sEvent == null) {
|
|
sEvent = this.startEvent;
|
|
sEvent = this.startEvent;
|
|
}
|
|
}
|
|
@@ -192,7 +195,7 @@ GanttChart.prototype.doDrag = function(sEvent, eEvent) {
|
|
/**
|
|
/**
|
|
* 初始化抓取拖动事件
|
|
* 初始化抓取拖动事件
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.initDragHandler = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.initDragHandler = function() {
|
|
this.graphDiv.scrollLeft = this.offsetDis;
|
|
this.graphDiv.scrollLeft = this.offsetDis;
|
|
let _canvas = this._canvas
|
|
let _canvas = this._canvas
|
|
_canvas.addEventListener('mousedown', (ev) => {
|
|
_canvas.addEventListener('mousedown', (ev) => {
|
|
@@ -234,7 +237,7 @@ GanttChart.prototype.initDragHandler = function() {
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
|
|
|
|
-GanttChart.prototype.initCanvas = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.initCanvas = function() {
|
|
console.error('初始化画布...')
|
|
console.error('初始化画布...')
|
|
this.gCanvas = new G.Canvas({
|
|
this.gCanvas = new G.Canvas({
|
|
container: this.chartContainer,
|
|
container: this.chartContainer,
|
|
@@ -248,7 +251,7 @@ GanttChart.prototype.initCanvas = function() {
|
|
* 绘制时间区域
|
|
* 绘制时间区域
|
|
*/
|
|
*/
|
|
|
|
|
|
-GanttChart.prototype.drawTimeZone = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.drawTimeZone = function() {
|
|
console.log('时间段', this.startAt.format('YYYY-MM-DD'),this.endAt.format('YYYY-MM-DD'));
|
|
console.log('时间段', this.startAt.format('YYYY-MM-DD'),this.endAt.format('YYYY-MM-DD'));
|
|
let start = moment(this.startAt);
|
|
let start = moment(this.startAt);
|
|
let timeGroup = this.gCanvas.addGroup();
|
|
let timeGroup = this.gCanvas.addGroup();
|
|
@@ -331,7 +334,7 @@ GanttChart.prototype.drawTimeZone = function() {
|
|
/**
|
|
/**
|
|
* 处理点击
|
|
* 处理点击
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.handleClick = function(task, flag, ev) {
|
|
|
|
|
|
+GanttChartDay.prototype.handleClick = function(task, flag, ev) {
|
|
// let detailDiv = document.getElementById('detailDiv')
|
|
// let detailDiv = document.getElementById('detailDiv')
|
|
if(flag == 'enter'){
|
|
if(flag == 'enter'){
|
|
// detailDiv.style.display = 'block'
|
|
// detailDiv.style.display = 'block'
|
|
@@ -356,7 +359,7 @@ GanttChart.prototype.handleClick = function(task, flag, ev) {
|
|
* 根据任务状态区分颜色
|
|
* 根据任务状态区分颜色
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.statusColor =function(task) {
|
|
|
|
|
|
+GanttChartDay.prototype.statusColor =function(task) {
|
|
switch (task.status) {
|
|
switch (task.status) {
|
|
case '按时完成':
|
|
case '按时完成':
|
|
return 'aqua';
|
|
return 'aqua';
|
|
@@ -376,7 +379,7 @@ GanttChart.prototype.statusColor =function(task) {
|
|
* 判断任务是否在视图内
|
|
* 判断任务是否在视图内
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.isInView = function(task) {
|
|
|
|
|
|
+GanttChartDay.prototype.isInView = function(task) {
|
|
let isLessThanEndAt = (task.endDate <= this.startAt.format('YYYY-MM-DD'))
|
|
let isLessThanEndAt = (task.endDate <= this.startAt.format('YYYY-MM-DD'))
|
|
let isGreaterThanStartAt = task.startDate >= this.endAt.format('YYYY-MM-DD')
|
|
let isGreaterThanStartAt = task.startDate >= this.endAt.format('YYYY-MM-DD')
|
|
return !(isLessThanEndAt || isGreaterThanStartAt)
|
|
return !(isLessThanEndAt || isGreaterThanStartAt)
|
|
@@ -385,7 +388,7 @@ GanttChart.prototype.isInView = function(task) {
|
|
/**
|
|
/**
|
|
* 绘制完成之后的回调,用于某些工具缺陷的hack写法
|
|
* 绘制完成之后的回调,用于某些工具缺陷的hack写法
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.postDrawTasksCallBack = function(){
|
|
|
|
|
|
+GanttChartDay.prototype.postDrawTasksCallBack = function(){
|
|
// 画当前线条 TODO,放前面不行
|
|
// 画当前线条 TODO,放前面不行
|
|
if(true){
|
|
if(true){
|
|
let todayAt = new Date()
|
|
let todayAt = new Date()
|
|
@@ -426,110 +429,111 @@ GanttChart.prototype.postDrawTasksCallBack = function(){
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
|
|
|
|
-GanttChart.prototype.drawTasks = function() {
|
|
|
|
- if (this.graphGroup) {
|
|
|
|
- this.graphGroup.clear();
|
|
|
|
- } else {
|
|
|
|
- this.graphGroup = this.gCanvas.addGroup();
|
|
|
|
- this.graphGroup._tname = 'graphGroup';
|
|
|
|
- }
|
|
|
|
|
|
+GanttChartDay.prototype.drawTasks = function() {
|
|
|
|
+ this.graphGroup = this.gCanvas.addGroup();
|
|
|
|
+ this.graphGroup._tname = 'graphGroup';
|
|
|
|
+
|
|
// 第一层循环,用于分组,例如,维保--xxxx
|
|
// 第一层循环,用于分组,例如,维保--xxxx
|
|
this.tasks.forEach((topTask, topIndex) => {
|
|
this.tasks.forEach((topTask, topIndex) => {
|
|
- if (topTask.open) {
|
|
|
|
- let taskGroup = null;
|
|
|
|
- taskGroup = this.graphGroup.addGroup();
|
|
|
|
- taskGroup._tname = 'TaskGroup_' + topTask.id;
|
|
|
|
- topTask.gGroup = taskGroup;
|
|
|
|
-
|
|
|
|
- // 组名背景矩形
|
|
|
|
- let TopGroupRectEl = taskGroup.addShape('rect', {
|
|
|
|
- attrs: {
|
|
|
|
- x: 0,
|
|
|
|
- y: topTask.renderOptions.startY,
|
|
|
|
- width: this.cWidth,
|
|
|
|
- height: this.taskRowHeight,
|
|
|
|
- fill: '#F5F6F7',
|
|
|
|
- radius: [2, 4],
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
- TopGroupRectEl.setZIndex(-1)
|
|
|
|
- // 第二层循环,用于 区分具体多少任务,例如,维保-商管1/商管2...
|
|
|
|
- topTask.dataList.forEach((taskP, index) => {
|
|
|
|
- let taskPGroup = taskGroup.addGroup()
|
|
|
|
- taskGroup.addGroup(taskPGroup);
|
|
|
|
- // 任务背景矩形,主要用于Hover效果变更颜色
|
|
|
|
- if(true){
|
|
|
|
- let tempTaskContainerEl = taskPGroup.addShape('rect', {
|
|
|
|
- attrs: {
|
|
|
|
- x: 0,
|
|
|
|
- y: topTask.renderOptions.startY + ((index+2)* (this.taskRowHeight + this.rowSpanDis))-5,
|
|
|
|
- width: this.cWidth,
|
|
|
|
- height: this.taskRowHeight+10,
|
|
|
|
- fill: '#fff',
|
|
|
|
- // stroke: 'black',
|
|
|
|
- radius: [2, 4],
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
- tempTaskContainerEl.setZIndex(1)
|
|
|
|
- tempTaskContainerEl._pdata = taskP
|
|
|
|
- tempTaskContainerEl.on('mouseenter',(ev)=>{
|
|
|
|
- tempTaskContainerEl.attr({fill: '#F5F6F7'})
|
|
|
|
- tempTaskContainerEl._pdata.tasks.forEach(_tempTask=>{
|
|
|
|
- if(_tempTask._rectEl){
|
|
|
|
- _tempTask._rectEl.setZIndex(5)
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- })
|
|
|
|
- tempTaskContainerEl.on('mouseleave',(ev)=>{
|
|
|
|
- tempTaskContainerEl.attr({fill: '#fff'})
|
|
|
|
- tempTaskContainerEl._pdata.tasks.forEach(_tempTask=>{
|
|
|
|
- if(_tempTask._rectEl){
|
|
|
|
- _tempTask._rectEl.setZIndex(5)
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- })
|
|
|
|
- taskP._containerEl = tempTaskContainerEl
|
|
|
|
- }
|
|
|
|
- // 第三层循环,用户区分每个子任务的执行时间段,例如:维保-商管1-2020.05-2020.06 / 2020.08- 2020.09
|
|
|
|
- taskP.tasks.forEach((_taskItem, _index) => {
|
|
|
|
- let _isInView = this.isInView(_taskItem)
|
|
|
|
- // 在视图中才显示
|
|
|
|
- if(_isInView){
|
|
|
|
- let pos = this.calRectPos(_taskItem);
|
|
|
|
- // console.log('render rect:', _taskItem, pos, topTask.renderOptions.startY + index * taskRowHeight);
|
|
|
|
- let rectEl = taskPGroup.addShape('rect', {
|
|
|
|
|
|
+ try {
|
|
|
|
+ if (topTask.open) {
|
|
|
|
+ let taskGroup = null;
|
|
|
|
+ taskGroup = this.graphGroup.addGroup();
|
|
|
|
+ taskGroup._tname = 'TaskGroup_' + topTask.id;
|
|
|
|
+ topTask.gGroup = taskGroup;
|
|
|
|
+
|
|
|
|
+ // 组名背景矩形
|
|
|
|
+ let TopGroupRectEl = taskGroup.addShape('rect', {
|
|
|
|
+ attrs: {
|
|
|
|
+ x: 0,
|
|
|
|
+ y: topTask.renderOptions.startY,
|
|
|
|
+ width: this.cWidth,
|
|
|
|
+ height: this.taskRowHeight,
|
|
|
|
+ fill: '#F5F6F7',
|
|
|
|
+ radius: [2, 4],
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ TopGroupRectEl.setZIndex(-1)
|
|
|
|
+ // 第二层循环,用于 区分具体多少任务,例如,维保-商管1/商管2...
|
|
|
|
+ topTask.dataList.forEach((taskP, index) => {
|
|
|
|
+ let taskPGroup = taskGroup.addGroup()
|
|
|
|
+ taskGroup.addGroup(taskPGroup);
|
|
|
|
+ // 任务背景矩形,主要用于Hover效果变更颜色
|
|
|
|
+ if(true){
|
|
|
|
+ let tempTaskContainerEl = taskPGroup.addShape('rect', {
|
|
attrs: {
|
|
attrs: {
|
|
- x: pos.x,
|
|
|
|
- y: topTask.renderOptions.startY + ((index + 1)* (this.taskRowHeight + this.rowSpanDis)),
|
|
|
|
- width: pos.width,
|
|
|
|
- height: this.taskRowHeight,
|
|
|
|
- fill: this.statusColor(_taskItem),
|
|
|
|
- stroke: 'black',
|
|
|
|
|
|
+ x: 0,
|
|
|
|
+ y: topTask.renderOptions.startY + ((index+2)* (this.taskRowHeight + this.rowSpanDis))-5,
|
|
|
|
+ width: this.cWidth,
|
|
|
|
+ height: this.taskRowHeight+10,
|
|
|
|
+ fill: '#fff',
|
|
|
|
+ // stroke: 'black',
|
|
radius: [2, 4],
|
|
radius: [2, 4],
|
|
},
|
|
},
|
|
});
|
|
});
|
|
- rectEl.setZIndex(50)
|
|
|
|
- rectEl._pdata = _taskItem;
|
|
|
|
- _taskItem._rectEl = rectEl
|
|
|
|
- rectEl.on('mouseover', (ev) => {
|
|
|
|
- this.handleClick(ev.target, 'enter', ev);
|
|
|
|
- });
|
|
|
|
- rectEl.on('mouseleave', (ev) => {
|
|
|
|
- this.handleClick(ev.target, 'leave', ev);
|
|
|
|
- });
|
|
|
|
- rectEl.on('click', (ev) => {
|
|
|
|
- this.handleClick(ev.target, 'click', ev);
|
|
|
|
- });
|
|
|
|
|
|
+ tempTaskContainerEl.setZIndex(1)
|
|
|
|
+ tempTaskContainerEl._pdata = taskP
|
|
|
|
+ tempTaskContainerEl.on('mouseenter',(ev)=>{
|
|
|
|
+ tempTaskContainerEl.attr({fill: '#F5F6F7'})
|
|
|
|
+ tempTaskContainerEl._pdata.tasks.forEach(_tempTask=>{
|
|
|
|
+ if(_tempTask._rectEl){
|
|
|
|
+ _tempTask._rectEl.setZIndex(5)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ tempTaskContainerEl.on('mouseleave',(ev)=>{
|
|
|
|
+ tempTaskContainerEl.attr({fill: '#fff'})
|
|
|
|
+ tempTaskContainerEl._pdata.tasks.forEach(_tempTask=>{
|
|
|
|
+ if(_tempTask._rectEl){
|
|
|
|
+ _tempTask._rectEl.setZIndex(5)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+ taskP._containerEl = tempTaskContainerEl
|
|
}
|
|
}
|
|
|
|
+ // 第三层循环,用户区分每个子任务的执行时间段,例如:维保-商管1-2020.05-2020.06 / 2020.08- 2020.09
|
|
|
|
+ taskP.tasks.forEach((_taskItem, _index) => {
|
|
|
|
+ let _isInView = this.isInView(_taskItem)
|
|
|
|
+ // 在视图中才显示
|
|
|
|
+ if(_isInView){
|
|
|
|
+ let pos = this.calRectPos(_taskItem);
|
|
|
|
+ // console.log('render rect:', _taskItem, pos, topTask.renderOptions.startY + index * taskRowHeight);
|
|
|
|
+ let rectEl = taskPGroup.addShape('rect', {
|
|
|
|
+ attrs: {
|
|
|
|
+ x: pos.x,
|
|
|
|
+ y: topTask.renderOptions.startY + ((index + 1)* (this.taskRowHeight + this.rowSpanDis)),
|
|
|
|
+ width: pos.width,
|
|
|
|
+ height: this.taskRowHeight,
|
|
|
|
+ fill: this.statusColor(_taskItem),
|
|
|
|
+ stroke: 'black',
|
|
|
|
+ radius: [2, 4],
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ rectEl.setZIndex(50)
|
|
|
|
+ rectEl._pdata = _taskItem;
|
|
|
|
+ _taskItem._rectEl = rectEl
|
|
|
|
+ rectEl.on('mouseover', (ev) => {
|
|
|
|
+ this.handleClick(ev.target, 'enter', ev);
|
|
|
|
+ });
|
|
|
|
+ rectEl.on('mouseleave', (ev) => {
|
|
|
|
+ this.handleClick(ev.target, 'leave', ev);
|
|
|
|
+ });
|
|
|
|
+ rectEl.on('click', (ev) => {
|
|
|
|
+ this.handleClick(ev.target, 'click', ev);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
});
|
|
});
|
|
- });
|
|
|
|
-
|
|
|
|
- taskGroup.show();
|
|
|
|
- } else {
|
|
|
|
- if (topTask.gGroup) {
|
|
|
|
- // topTask.gGroup.hide()
|
|
|
|
- topTask.gGroup = null;
|
|
|
|
|
|
+
|
|
|
|
+ taskGroup.show();
|
|
|
|
+ } else {
|
|
|
|
+ if (topTask.gGroup) {
|
|
|
|
+ // topTask.gGroup.hide()
|
|
|
|
+ topTask.gGroup = null;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('drawTasks error:', error)
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
@@ -540,7 +544,7 @@ GanttChart.prototype.drawTasks = function() {
|
|
* 根据 Task 计算矩形位置
|
|
* 根据 Task 计算矩形位置
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
-GanttChart.prototype.calRectPos = function(taskItem) {
|
|
|
|
|
|
+GanttChartDay.prototype.calRectPos = function(taskItem) {
|
|
let duraStartAt = new Date(taskItem.startDate) - new Date(this.startAt.format('YYYY-MM-DD'));
|
|
let duraStartAt = new Date(taskItem.startDate) - new Date(this.startAt.format('YYYY-MM-DD'));
|
|
let secondsStartAt = duraStartAt/1000
|
|
let secondsStartAt = duraStartAt/1000
|
|
|
|
|
|
@@ -559,6 +563,6 @@ GanttChart.prototype.calRectPos = function(taskItem) {
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
|
|
|
|
-GanttChart.prototype.main = function() {
|
|
|
|
|
|
+GanttChartDay.prototype.main = function() {
|
|
this.initDrawingReady();
|
|
this.initDrawingReady();
|
|
}
|
|
}
|