Создание надстройки Project, использующей REST с локальной службой OData Project Server
В этой статье описывается создание надстройки области задач для Project профессиональный, которая сравнивает данные о затратах и трудоемках в активном проекте со средними значениями для всех проектов в текущем экземпляре Project Web App. Надстройка использует REST с библиотекой jQuery для доступа к службе отчетов OData ProjectData в Project Server.
Код в данной статье основан на примере, разработанном Саурабхом Сангхви (Saurabh Sanghvi) и Эрвиндом Лаиром (Arvind Iyer), сотрудниками корпорации Майкрософт.
Предварительные условия
Ниже приведены предварительные требования для создания надстройки области задач Project, которая считывает службу ProjectData экземпляра Project Web App в локальной установке Project Server.
Project профессиональный требуется для подключения к Project Web App. На компьютере разработки должен быть установлен Project профессиональный, чтобы включить отладку F5 с помощью Visual Studio.
Примечание.
Project стандартный также могут размещать надстройки области задач, но не могут выполнять вход в Project Web App.
Visual Studio 2015 со средствами разработчика Office для Visual Studio включает шаблоны для создания надстроек Office и SharePoint. Убедитесь, что установлена последняя версия средств разработчика Office. См. раздел Сервис надстройки Office и загрузки SharePoint.
Процедуры и примеры кода в этой статье обращаются к службе ProjectData Project Server в локальном домене. Методы jQuery, описанные в этой статье, не работают с Project в Интернете.
Убедитесь, что служба ProjectData доступна с компьютера разработки.
Процедура 1. Убедитесь, что служба ProjectData доступна
Чтобы разрешить браузеру напрямую отображать XML-данные из запроса REST, отключите вид чтения канала. Сведения о том, как это сделать в Обозреватель Интернета, см. в статье Процедура 1, шаг 4 статьи Запрос веб-каналов OData для данных отчетов Project.
Запросите службу ProjectData с помощью браузера со следующим URL-адресом:
http://ServerName /ProjectServerName /_api/ProjectData
. Например, если экземпляром Project Web App является , в браузере отображаютсяhttp://MyServer/pwa
следующие результаты.<?xml version="1.0" encoding="utf-8"?> <service xml:base="http://myserver/pwa/_api/ProjectData/" xmlns="https://www.w3.org/2007/app" xmlns:atom="https://www.w3.org/2005/Atom"> <workspace> <atom:title>Default</atom:title> <collection href="Projects"> <atom:title>Projects</atom:title> </collection> <collection href="ProjectBaselines"> <atom:title>ProjectBaselines</atom:title> </collection> <!-- ... and 33 more collection elements --> </workspace> </service>
You may have to provide your network credentials to see the results. If the browser shows "Error 403, Access Denied," either you do not have logon permission for that Project Web App instance, or there is a network problem that requires administrative help.
Создание надстройки области задач для Project с помощью Visual Studio
Инструменты разработчика Office для Visual Studio включают шаблон для надстроек области задач для Project. При создании решения HelloProjectOData оно содержит следующие два проекта Visual Studio:
Проект надстройки получает имя решения. Он включает только файл манифеста надстройки для надстройки и предназначен для платформа .NET Framework 4.5. В процедуре 3 показаны шаги по изменению манифеста надстройки HelloProjectOData.
Веб-проект называется HelloProjectODataWeb. Оно содержит файлы JavaScript веб-страниц, файлы CSS, рисунки, ссылки и файлы конфигурации для веб-контента в области задач. Веб-проект настраивается на конечную платформу .NET Framework 4. В процедуре 4 и процедуре 5 показано, как изменить эти файлы в веб-проекте, чтобы создать функциональность надстройки HelloProjectOData.
Процедура 2. Создание надстройки HelloProjectOData для Project
Запустите Visual Studio 2015 от имени администратора и выберите Создать проект на начальной странице.
В диалоговом окне Новый проект разверните узлы Шаблоны, Visual C# и Office/SharePoint, а затем выберите Надстройки Office. Выберите платформа .NET Framework 4.5.2 в раскрывающемся списке целевая платформа в верхней части центральной области, а затем выберите Надстройка Office (см. следующий снимок экрана).
Чтобы разместить оба проекта Visual Studio в одной папке, выберите Создать каталог для решения и найдите требуемое расположение.
В поле Имя введитеHelloProjectOData и нажмите кнопку ОК.
Рис. 1. Создание надстройки Office
В диалоговом окне Выбор типа надстройки выберите пункт Надстройка области задач и нажмите кнопку Далее (см. следующий снимок экрана).
Рис. 2. Выбор типа создаваемой надстройки
В диалоговом окне Выбор ведущих приложений снимите все флажки, кроме флажка Project (см. следующий снимок экрана), а затем нажмите кнопку Готово.
Рис. 3. Выбор ведущего приложения
Visual Studio создает проекты HelloProjectOdata и HelloProjectODataWeb .
Папка AddIn (см. следующий снимок экрана) содержит файл App.css для пользовательских стилей CSS. Во вложенной папке Home находится файл Home.html, содержащий ссылки на CSS-файлы и файлы JavaScript, используемые надстройкой, а также содержимое HTML5 для этой надстройки. Также в ней располагается файл Home.js, предназначенный для настраиваемого кода JavaScript. Папка Scripts содержит файлы библиотеки jQuery. Во вложенной папке Office находятся библиотеки JavaScript, например office.js и project-15.js, а также языковые библиотеки для стандартных строк в надстройках Office. В папке Content находится файл Office.css, содержащий стили по умолчанию для всех надстроек Office.
Рис. 4. Просмотр файлов веб-проекта по умолчанию в Обозреватель решений
Манифест проекта HelloProjectOData — это файл HelloProjectOData.xml. Его можно изменить при необходимости, чтобы добавить описание надстройки, ссылку на значок, сведения о дополнительных языках и другие параметры. В процедуре 3 изменяется только отображаемое имя надстройки и описание и добавляется значок.
Дополнительные сведения о манифесте см. в разделах Манифест надстроек Office и Справочник по схеме манифестов надстроек Office.
Процедура 3. Изменение манифеста надстройки
Откройте файл HelloProjectOData.xml в Visual Studio.
Отображаемое имя по умолчанию — это имя проекта Visual Studio ("HelloProjectOData"). Например, измените значение по умолчанию элемента <DisplayName> на "Hello ProjectData".
Описание по умолчанию — "HelloProjectOData". Например, измените значение по умолчанию элемента Description на "Test REST queries of the ProjectData service" (тестирование запросов REST службы ProjectData).
Добавьте значок для отображения в раскрывающемся списке Надстройки Office на вкладке PROJECT ленты. Можно добавить файл значка в решении Visual Studio или использовать URL-адрес значка.
Ниже показано, как добавить файл значка в решение Visual Studio.
В Обозреватель решений перейдите в папку с именем Images.
Чтобы отображаться в раскрывающемся списке Надстройки Office, значок должен иметь размер 32 x 32 пикселя. Используйте собственный значок 32 x 32; Или скопируйте приведенное ниже изображение в файл с именемNewIcon.png, а затем добавьте этот файл в папку
HelloProjectODataWeb\Images
.В HelloProjectOData.xml добавьте <элемент IconUrl> под элементом <Description> , где значение URL-адреса значка является относительным путем к файлу значка 32x32. Например, добавьте следующую строку:
<IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" />
. Файл HelloProjectOData.xml теперь содержит следующее ( <значение идентификатора> будет отличаться):<?xml version="1.0" encoding="UTF-8"?> <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="TaskPaneApp"> <!--IMPORTANT! Id must be unique for each add-in. If you copy this manifest ensure that you change this id to your own GUID. --> <Id>c512df8d-a1c5-4d74-8a34-d30f6bbcbd82</Id> <Version>1.0</Version> <ProviderName> [Provider name]</ProviderName> <DefaultLocale>en-US</DefaultLocale> <DisplayName DefaultValue="Hello ProjectData" /> <Description DefaultValue="Test REST queries of the ProjectData service"/> <IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" /> <SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" /> <Hosts> <Host Name="Project" /> </Hosts> <DefaultSettings> <SourceLocation DefaultValue="~remoteAppUrl/AddIn/Home/Home.html" /> </DefaultSettings> <Permissions>ReadWriteDocument</Permissions> </OfficeApp>
Создание HTML-содержимого для надстройки HelloProjectOData
Надстройка HelloProjectOData — это пример, включающий отладку и вывод ошибок. он не предназначен для использования в рабочей среде. Прежде чем приступить к написанию HTML-содержимого, спроектируйте пользовательский интерфейс и пользовательский интерфейс для надстройки, а также нарисуйте функции JavaScript, взаимодействующие с HTML-кодом. Дополнительные сведения см. в статье Руководство по проектированию надстроек Office.
В верхней части области задач отображается отображаемое имя надстройки, которое является значением <элемента DisplayName> в манифесте. Элемент body в файле HelloProjectOData.html содержит другие элементы пользовательского интерфейса:
Подзаголовок, указывающий на общую функциональность или тип работы, например: ODATA REST QUERY.
Кнопка Получить конечную точку
setOdataUrl
ProjectData вызывает функцию, чтобы получить конечную точку службы ProjectData и отобразить ее в текстовом поле. Если Project не подключен к Project Web App, надстройка вызывает обработчик ошибок для отображения всплывающего сообщения об ошибке.Кнопка Compare All Projects отключена до тех пор, пока надстройка не получит действительную конечную точку OData. При нажатии кнопки она вызывает
retrieveOData
функцию, которая использует запрос REST для получения затрат и рабочих данных проекта из службы ProjectData .Таблица отображает средние значения затрат проекта, фактических затрат, трудозатрат и процент выполнения. В таблице также сравниваются значения текущего активного проекта со средними. Если текущее значение больше среднего по всем проектам, значение отображается красным цветом. Если текущее значение меньше среднего, оно отображается зеленым цветом. Если текущее значение недоступно, в таблице отображается синее значение NA.
Функция
retrieveOData
вызывает функциюparseODataResult
, которая вычисляет и отображает значения для таблицы.Примечание.
В данном примере данные о материальных и трудовых затратах по активному проекту извлекаются из опубликованных значений. Если изменить значения в Project, служба ProjectData не будет знать об изменениях до тех пор, пока проект не опубликован.
Процедура 4. Создание html-содержимого
В элементе head файла Home.html добавьте любые дополнительные элементы link для CSS-файлов, используемых в надстройке. Шаблон проекта Visual Studio содержит ссылку на файл App.css, который можно использовать для настраиваемых стилей CSS.
Добавьте все дополнительные элементы скрипта для библиотек JavaScript, которые использует надстройка. Шаблон проекта содержит ссылки на файлы jQuery- [version].js, office.js и MicrosoftAjax.js в папке Scripts .
Примечание.
Перед развертыванием надстройки измените ссылку office.js и ссылку jQuery на ссылку сети доставки содержимого (CDN). Ссылка CDN предоставляет самую последнюю версию и обеспечивает оптимальную производительность.
Надстройка HelloProjectOData также использует файлSurfaceErrors.js , в котором отображаются ошибки во всплывающем сообщении. Скопируйте код из раздела файлаSurfaceErrors.js этой статьи в папку Scripts\Office проекта HelloProjectODataWeb в виде нового файла с именемSurfaceErrors.js.
Ниже приведен обновленный HTML-код для элемента head с дополнительной строкой для файлаSurfaceErrors.js .
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> <title>Test ProjectData Service</title> <link rel="stylesheet" type="text/css" href="../Content/Office.css" /> <!-- Add your CSS styles to the following file. --> <link rel="stylesheet" type="text/css" href="../Content/App.css" /> <!-- Use the CDN reference to the mini-version of jQuery when deploying your add-in. --> <!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> --> <script src="../Scripts/jquery-1.7.1.js"></script> <!-- Use the CDN reference to office.js when deploying your add-in. --> <!--<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>--> <!-- Use the local script references for Office.js to enable offline debugging --> <script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script> <script src="../Scripts/Office/1.0/Office.js"></script> <!-- Add your JavaScript to the following files. --> <script src="../Scripts/HelloProjectOData.js"></script> <script src="../Scripts/SurfaceErrors.js"></script> </head> <body> <!-- See the code in Step 3. --> </body> </html>
В элементе body удалите существующий код из шаблона, а затем добавьте код для пользовательского интерфейса. Если элемент должен заполняться данными или изменяться оператором jQuery, элемент должен содержать уникальный атрибут id. В следующем коде атрибуты идентификатора для элементов button, span и td (определение ячейки таблицы), которые используют функции jQuery, выделены полужирным шрифтом.
Следующий HTML-код добавляет графическое изображение (NewLogo.png), которое может быть логотипом компании.
<body> <div id="SectionContent"> <div id="odataQueries"> ODATA REST QUERY </div> <div id="odataInfo"> <button class="button-wide" onclick="setOdataUrl()">Get ProjectData Endpoint</button> <br /><br /> <span class="rest" id="projectDataEndPoint">Endpoint of the <strong>ProjectData</strong> service</span> <br /> </div> <div id="compareProjectData"> <button class="button-wide" disabled="disabled" id="compareProjects" onclick="retrieveOData()">Compare All Projects</button> <br /> </div> </div> <div id="corpInfo"> <table class="infoTable" aria-readonly="True" style="width: 100%;"> <tr> <td class="heading_leftCol"></td> <td class="heading_midCol"><strong>Average</strong></td> <td class="heading_rightCol"><strong>Current</strong></td> </tr> <tr> <td class="row_leftCol"><strong>Project Cost</strong></td> <td class="row_midCol" id="AverageProjectCost">&nbsp;</td> <td class="row_rightCol" id="CurrentProjectCost">&nbsp;</td> </tr> <tr> <td class="row_leftCol"><strong>Project Actual Cost</strong></td> <td class="row_midCol" id="AverageProjectActualCost">&nbsp;</td> <td class="row_rightCol" id="CurrentProjectActualCost">&nbsp;</td> </tr> <tr> <td class="row_leftCol"><strong>Project Work</strong></td> <td class="row_midCol" id="AverageProjectWork">&nbsp;</td> <td class="row_rightCol" id="CurrentProjectWork">&nbsp;</td> </tr> <tr> <td class="row_leftCol"><strong>Project % Complete</strong></td> <td class="row_midCol" id="AverageProjectPercentComplete">&nbsp;</td> <td class="row_rightCol" id="CurrentProjectPercentComplete">&nbsp;</td> </tr> </table> </div> <img alt="Corporation" class="logo" src="../../images/NewLogo.png" /> <br /> <textarea id="odataText" rows="12" cols="40"></textarea> </body>
Создание кода JavaScript для надстройки
Шаблон надстройки области задач Project содержит код инициализации по умолчанию, предназначенный для демонстрации базовых действий по получении и настройке данных в документе для надстройки Office, которая использует общие API. Так как Project не поддерживает действия, которые записываются в активный проект, а надстройка HelloProjectOData не использует getSelectedDataAsync
метод, можно удалить скрипт в Office.initialize
функции, а функцию и setData
getData
функцию в файле по умолчанию HelloProjectOData.js.
В JavaScript содержатся глобальные константы для запроса REST и глобальные переменные, используемые в нескольких функциях. Кнопка Получить конечную точкуsetOdataUrl
ProjectData вызывает функцию, которая инициализирует глобальные переменные и определяет, связан ли Project с Project Web App.
Оставшаяся часть файла HelloProjectOData.js включает две функции: retrieveOData
функция вызывается, когда пользователь выбирает сравнить все проекты, а parseODataResult
функция вычисляет средние значения, а затем заполняет таблицу сравнения значениями, отформатированными для цветов и единиц.
Процедура 5. Создание кода JavaScript
Удалите весь код в файле HelloProjectOData.js по умолчанию, а затем добавьте глобальные переменные и
Office.initialize
функцию. Имена переменных, которые являются прописными буквами, подразумевают, что они являются константами; Позже они используются с переменной_pwa
для создания запроса REST в этом примере.let PROJDATA = "/_api/ProjectData"; let PROJQUERY = "/Projects?"; let QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work Items'"; let QUERY_SELECT1 = "&$select=ProjectId, ProjectName"; let QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost"; let _pwa; // URL of Project Web App. let _projectUid; // GUID of the active project. let _docUrl; // Path of the project document. let _odataUrl = ""; // URL of the OData service: http[s]://ServerName /ProjectServerName /_api/ProjectData // Ensure the Office.js library is loaded. Office.onReady(function() { // Office is ready. $(document).ready(function () { // The document is ready. }); });
Добавление
setOdataUrl
и связанных функций. ФункцияsetOdataUrl
вызываетgetProjectGuid
и для инициализацииgetDocumentUrl
глобальных переменных. В методе getProjectFieldAsync анонимная функция для параметра обратного вызова включает кнопку Сравнить все проекты с помощьюremoveAttr
метода в библиотеке jQuery, а затем отображает URL-адрес службы ProjectData . Если Project не связан с Project Web App, функция выдает ошибку, которая отображает всплывающее сообщение об ошибке. Файл SurfaceErrors.js содержит функциюthrowError
.Примечание.
Если вы запускаете Visual Studio на компьютере Project Server, чтобы использовать отладку F5 , раскомментируйте код после строки, которая инициализирует глобальную
_pwa
переменную. Чтобы включить использование метода jQueryajax
при отладке на компьютере Project Server, необходимо задатьlocalhost
значение URL-адреса PWA. Если вы запускаете Visual Studio на удаленном компьютере,localhost
URL-адрес не требуется. Перед развертыванием надстройки закомментируйте этот код.function setOdataUrl() { Office.context.document.getProjectFieldAsync( Office.ProjectProjectFields.ProjectServerUrl, function (asyncResult) { if (asyncResult.status == Office.AsyncResultStatus.Succeeded) { _pwa = String(asyncResult.value.fieldValue); // If you debug with Visual Studio on a local Project Server computer, // uncomment the following lines to use the localhost URL. //let localhost = location.host.split(":", 1); //let pwaStartPosition = _pwa.lastIndexOf("/"); //let pwaLength = _pwa.length - pwaStartPosition; //let pwaName = _pwa.substr(pwaStartPosition, pwaLength); //_pwa = location.protocol + "//" + localhost + pwaName; if (_pwa.substring(0, 4) == "http") { _odataUrl = _pwa + PROJDATA; $("#compareProjects").removeAttr("disabled"); getProjectGuid(); } else { _odataUrl = "No connection!"; throwError(_odataUrl, "You are not connected to Project Web App."); } getDocumentUrl(); $("#projectDataEndPoint").text(_odataUrl); } else { throwError(asyncResult.error.name, asyncResult.error.message); } } ); } // Get the GUID of the active project. function getProjectGuid() { Office.context.document.getProjectFieldAsync( Office.ProjectProjectFields.GUID, function (asyncResult) { if (asyncResult.status == Office.AsyncResultStatus.Succeeded) { _projectUid = asyncResult.value.fieldValue; } else { throwError(asyncResult.error.name, asyncResult.error.message); } } ); } // Get the path of the project in Project web app, which is in the form <>\ProjectName . function getDocumentUrl() { _docUrl = "Document path:\r\n" + Office.context.document.url; }
Добавьте функцию
retrieveOData
, которая объединяет значения для запроса REST, а затем вызываетajax
функцию в jQuery для получения запрошенных данных из службы ProjectData . Переменнаяsupport.cors
включает общий доступ к ресурсам между источникамиajax
(CORS) с помощью функции .support.cors
Если оператор отсутствует или имеет значениеfalse
,ajax
функция возвращает ошибку No transport.Примечание.
Следующий код работает с локальной установкой Project Server. Для Project в Интернете можно использовать OAuth для проверки подлинности на основе маркеров. For more information, see Addressing same-origin policy limitations in Office Add-ins.
В вызове
ajax
можно использовать параметр headers или параметр beforeSend . Полный параметр является анонимной функцией, поэтому он находится в том же область, что и переменные вretrieveOData
. Функция для полного параметра отображает результаты в элементеodataText
управления, а также вызываетparseODataResult
метод для анализа и отображения ответа JSON. Параметр error указывает именованнуюgetProjectDataErrorHandler
функцию, которая записывает сообщение об ошибке вodataText
элемент управления, а также использует функциюthrowError
для отображения всплывающего сообщения.// Functions to get and parse the Project Server reporting data./ // Get data about all projects on Project Server, // by using a REST query with the ajax method in jQuery. function retrieveOData() { let restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 + QUERY_SELECT2; let accept = "application/json; odata=verbose"; accept.toLocaleLowerCase(); // Enable cross-origin scripting (required by jQuery 1.5 and later). // This does not work with Project on the web. $.support.cors = true; $.ajax({ url: restUrl, type: "GET", contentType: "application/json", data: "", // Empty string for the optional data. //headers: { "Accept": accept }, beforeSend: function (xhr) { xhr.setRequestHeader("ACCEPT", accept); }, complete: function (xhr, textStatus) { // Create a message to display in the text box. let message = "\r\ntextStatus: " + textStatus + "\r\nContentType: " + xhr.getResponseHeader("Content-Type") + "\r\nStatus: " + xhr.status + "\r\nResponseText:\r\n" + xhr.responseText; // xhr.responseText is the result from an XmlHttpRequest, which // contains the JSON response from the OData service. parseODataResult(xhr.responseText, _projectUid); // Write the document name, response header, status, and JSON to the odataText control. $("#odataText").text(_docUrl); $("#odataText").append("\r\nREST query:\r\n" + restUrl); $("#odataText").append(message); if (xhr.status != 200 && xhr.status != 1223 && xhr.status != 201) { $("#odataInfo").append("<div>" + htmlEncode(restUrl) + "</div>"); } }, error: getProjectDataErrorHandler }); } function getProjectDataErrorHandler(data, errorCode, errorMessage) { $("#odataText").text("Error code: " + errorCode + "\r\nError message: \r\n" + errorMessage); throwError(errorCode, errorMessage); }
Добавьте функцию
parseODataResult
, которая десериализирует и обрабатывает ответ JSON от службы OData. ФункцияparseODataResult
вычисляет средние значения данных о затратах и работах с точностью до одного или двух десятичных разрядов, форматирует значения с правильным цветом и добавляет единицу измерения ( $, чс или %), а затем отображает значения в указанных ячейках таблицы.Если ИДЕНТИФИКАТОР GUID активного проекта соответствует значению
ProjectId
,myProjectIndex
переменная задается в индекс проекта. ЕслиmyProjectIndex
указывает, что активный проект опубликован в Project Server,parseODataResult
метод форматирует и отображает данные о затратах и рабочих работах для этого проекта. Если активный проект не опубликован, значения активного проекта отображаются в виде синего na.// Calculate the average values of actual cost, cost, work, and percent complete // for all projects, and compare with the values for the current project. function parseODataResult(oDataResult, currentProjectGuid) { // Deserialize the JSON string into a JavaScript object. let res = Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult); let len = res.d.results.length; let projActualCost = 0; let projCost = 0; let projWork = 0; let projPercentCompleted = 0; let myProjectIndex = -1; for (i = 0; i < len; i++) { // If the current project GUID matches the GUID from the OData query, // store the project index. if (currentProjectGuid.toLocaleLowerCase() == res.d.results[i].ProjectId) { myProjectIndex = i; } projCost += Number(res.d.results[i].ProjectCost); projWork += Number(res.d.results[i].ProjectWork); projActualCost += Number(res.d.results[i].ProjectActualCost); projPercentCompleted += Number(res.d.results[i].ProjectPercentCompleted); } let avgProjCost = projCost / len; let avgProjWork = projWork / len; let avgProjActualCost = projActualCost / len; let avgProjPercentCompleted = projPercentCompleted / len; // Round off cost to two decimal places, and round off other values to one decimal place. avgProjCost = avgProjCost.toFixed(2); avgProjWork = avgProjWork.toFixed(1); avgProjActualCost = avgProjActualCost.toFixed(2); avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1); // Display averages in the table, with the correct units. document.getElementById("AverageProjectCost").innerHTML = "$" + avgProjCost; document.getElementById("AverageProjectActualCost").innerHTML = "$" + avgProjActualCost; document.getElementById("AverageProjectWork").innerHTML = avgProjWork + " hrs"; document.getElementById("AverageProjectPercentComplete").innerHTML = avgProjPercentCompleted + "%"; // Calculate and display values for the current project. if (myProjectIndex != -1) { let myProjCost = Number(res.d.results[myProjectIndex].ProjectCost); let myProjWork = Number(res.d.results[myProjectIndex].ProjectWork); let myProjActualCost = Number(res.d.results[myProjectIndex].ProjectActualCost); let myProjPercentCompleted = Number(res.d.results[myProjectIndex].ProjectPercentCompleted); myProjCost = myProjCost.toFixed(2); myProjWork = myProjWork.toFixed(1); myProjActualCost = myProjActualCost.toFixed(2); myProjPercentCompleted = myProjPercentCompleted.toFixed(1); document.getElementById("CurrentProjectCost").innerHTML = "$" + myProjCost; if (Number(myProjCost) <= Number(avgProjCost)) { document.getElementById("CurrentProjectCost").style.color = "green" } else { document.getElementById("CurrentProjectCost").style.color = "red" } document.getElementById("CurrentProjectActualCost").innerHTML = "$" + myProjActualCost; if (Number(myProjActualCost) <= Number(avgProjActualCost)) { document.getElementById("CurrentProjectActualCost").style.color = "green" } else { document.getElementById("CurrentProjectActualCost").style.color = "red" } document.getElementById("CurrentProjectWork").innerHTML = myProjWork + " hrs"; if (Number(myProjWork) <= Number(avgProjWork)) { document.getElementById("CurrentProjectWork").style.color = "red" } else { document.getElementById("CurrentProjectWork").style.color = "green" } document.getElementById("CurrentProjectPercentComplete").innerHTML = myProjPercentCompleted + "%"; if (Number(myProjPercentCompleted) <= Number(avgProjPercentCompleted)) { document.getElementById("CurrentProjectPercentComplete").style.color = "red" } else { document.getElementById("CurrentProjectPercentComplete").style.color = "green" } } else { document.getElementById("CurrentProjectCost").innerHTML = "NA"; document.getElementById("CurrentProjectCost").style.color = "blue" document.getElementById("CurrentProjectActualCost").innerHTML = "NA"; document.getElementById("CurrentProjectActualCost").style.color = "blue" document.getElementById("CurrentProjectWork").innerHTML = "NA"; document.getElementById("CurrentProjectWork").style.color = "blue" document.getElementById("CurrentProjectPercentComplete").innerHTML = "NA"; document.getElementById("CurrentProjectPercentComplete").style.color = "blue" } }
Тестирование надстройки HelloProjectOData
Для тестирования и отладки надстройки HelloProjectOData в Visual Studio на компьютере разработки необходимо установить Project профессиональный. Для работы с различными тестовыми сценариями убедитесь, что можно выбрать открытие файлов Project на локальном компьютере или подключение к Project Web App. Ниже приведены примеры шагов.
На вкладке Файл выберите вкладку Сведения в представлении Backstage и выберите Управление учетными записями.
В диалоговом окне Учетные записи project web app список Доступные учетные записи может содержать несколько учетных записей Project Web App в дополнение к учетной записи локального компьютера. В разделе Во время запуска выберите Выбрать учетную запись.
Закройте Project, чтобы среда Visual Studio могла запустить его для отладки надстройки.
Базовые тесты должны быть следующие:
Запустите приложение в Visual Studio и откройте опубликованный проект из Project Web App, содержащего данные о материальных и трудовых затратах. Убедитесь, что надстройка отображает конечную точку ProjectData и правильно отображает данные о затратах и рабочих работах в таблице. Можно использовать выходные данные в элементе управления odataText для проверки запроса REST и других сведений.
Запустите надстройку еще раз и выберите профиль локального компьютера с помощью диалогового окна Вход во время запуска Project. Откройте локальный MPP-файл и протестируйте надстройку. Убедитесь, что она отображает сообщение об ошибке при попытке получить конечную точку ProjectData.
Запустите надстройку еще раз и создайте проект, содержащий задачи с данными о материальных и трудовых затратах. Этот проект можно сохранить в Project Web App, но не публиковать. Убедитесь, что надстройка отображает данные с Project Server, но показывает NA для текущего проекта.
Процедура 6. Тестирование надстройки
Запустите Project профессиональный, подключитесь к Project Web App, а затем создайте тестовый проект. Назначьте задачи локальным ресурсам или ресурсам предприятия, настройте различные значения процента выполнения для некоторых задач и затем опубликуйте проект. Закройте Project, что позволит Visual Studio запустить Project для отладки надстройки.
В Visual Studio нажмите клавишу F5. Войдите в Project Web App и затем откройте проект, созданный на предыдущем шаге. Проект можно открыть в режиме чтения или в режиме редактирования.
На вкладке ПРОЕКТ ленты в раскрывающемся списке Надстройки Office выберите Hello ProjectData (см. рис. 5). Кнопка Сравнить все проекты должна быть отключена.
Рис. 5. Запуск надстройки HelloProjectOData
В области задач Hello ProjectData выберите Получить конечную точку ProjectData. В строке projectDataEndPoint должен отображаться URL-адрес службы ProjectData , а кнопка Сравнить все проекты должна быть включена (см. рис. 6).
Нажмите кнопку Compare All Projects. Надстройка может приостановить работу на время получения данных из службы ProjectData, а затем она должна отобразить отформатированные средние и текущие значения в таблице.
Рисунок 6. Просмотр результатов запроса REST
Проверьте выходные данные в текстовом поле. В нем должен отображаться путь к документу, запрос REST, сведения о состоянии и результаты JSON из вызовов
ajax
иparseODataResult
. Выходные данные помогают понять, создать и отладить код вparseODataResult
функции,projCost += Number(res.d.results[i].ProjectCost);
например .Ниже приведен пример выходных данных с разрывами строк и пробелами, добавленными в текст для ясности для трех проектов в экземпляре Project Web App.
Document path: <>\WinProj test1 REST query: http://sphvm-37189/pwa/_api/ProjectData/Projects?$filter=ProjectName ne 'Timesheet Administrative Work Items' &$select=ProjectId, ProjectName, ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost textStatus: success ContentType: application/json;odata=verbose;charset=utf-8 Status: 200 ResponseText: {"d":{"results":[ {"__metadata": {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-00155d157123')", "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-00155d157123')", "type":"ReportingData.Project"}, "ProjectId":"ce3d0d65-3904-e211-96cd-00155d157123", "ProjectActualCost":"0.000000", "ProjectCost":"0.000000", "ProjectName":"Task list created in PWA", "ProjectPercentCompleted":0, "ProjectWork":"16.000000"}, {"__metadata": {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-3c075433b7bd')", "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-3c075433b7bd')", "type":"ReportingData.Project"}, "ProjectId":"c31023fc-1404-e211-86b2-3c075433b7bd", "ProjectActualCost":"700.000000", "ProjectCost":"2400.000000", "ProjectName":"WinProj test 2", "ProjectPercentCompleted":29, "ProjectWork":"48.000000"}, {"__metadata": {"id":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-3c075433b7bd')", "uri":"http://sphvm-37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-3c075433b7bd')", "type":"ReportingData.Project"}, "ProjectId":"dc81fbb2-b801-e211-9d2a-3c075433b7bd", "ProjectActualCost":"1900.000000", "ProjectCost":"5200.000000", "ProjectName":"WinProj test1", "ProjectPercentCompleted":37, "ProjectWork":"104.000000"} ]}}
Остановите отладку (нажмите клавишу SHIFT+F5), а затем снова нажмите клавишу F5 , чтобы запустить новый экземпляр Project. В диалоговом окне Вход выберите локальный профиль компьютера, а не Project Web App. Создайте или откройте локальный проект .mpp файл, откройте область задач Hello ProjectData, а затем выберите Получить конечную точку ProjectData. В надстройке должна появиться ошибка Нет подключения! (см. рис. 7), а кнопка Сравнить все проекты должна оставаться отключенной.
Рис. 7. Использование надстройки без подключения к веб-приложению Project
Остановите отладку и нажмите клавишу F5 снова. Войдите в Project Web App и создайте проект, содержащий данные о материальных и трудовых затратах. Проект можно сохранить, но не публикуйте его.
В области задач Hello ProjectData при выборе параметра Сравнить все проекты вы увидите синюю НС для полей в текущем столбце (см. рис. 8).
Рис. 8. Сравнение неопубликованного проекта с другими проектами
Даже если ваша надстройка работала правильно в предыдущих тестах, есть другие тесты, которые необходимо выполнить. Например:
Откройте в Project Web App проект, который не содержит данных о материальных и трудовых затратах для задач. В полях столбца Current (текущий) должны отображаться нули.
Протестируйте проект, не содержащий задачи.
Если вы измените надстройку и опубликуете ее, необходимо запустить аналогичные тесты снова с опубликованной надстройкой. Другие рекомендации см. в разделе Дальнейшие действия.
Примечание.
Имеются ограничения на объем данных, который может быть возвращен в одном запросе службы ProjectData; этот объем данных меняется для разных сущностей. Например, Projects
набор сущностей имеет ограничение по умолчанию в 100 проектов на запрос, но Risks
набор сущностей имеет ограничение по умолчанию 200. Для установки в рабочей среде код в примере HelloProjectOData необходимо изменить для поддержки запросов, содержащих более 100 проектов. Дополнительные сведения см. в разделах Дальнейшие действия и Запрос веб-каналов OData для данных отчетов Project.
Пример кода для надстройки HelloProjectOData
Файл HelloProjectOData.html
Приведенный ниже код находится в файле Pages\HelloProjectOData.html
проекта HelloProjectODataWeb.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<title>Test ProjectData Service</title>
<link rel="stylesheet" type="text/css" href="../Content/Office.css" />
<!-- Add your CSS styles to the following file. -->
<link rel="stylesheet" type="text/css" href="../Content/App.css" />
<!-- Use the CDN reference to the mini-version of jQuery when deploying your add-in. -->
<!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> -->
<script src="../Scripts/jquery-1.7.1.js"></script>
<!-- Use the CDN reference to Office.js when deploying your add-in -->
<!--<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>-->
<!-- Use the local script references for Office.js to enable offline debugging -->
<script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
<script src="../Scripts/Office/1.0/Office.js"></script>
<!-- Add your JavaScript to the following files. -->
<script src="../Scripts/HelloProjectOData.js"></script>
<script src="../Scripts/SurfaceErrors.js"></script>
</head>
<body>
<div id="SectionContent">
<div id="odataQueries">
ODATA REST QUERY
</div>
<div id="odataInfo">
<button class="button-wide" onclick="setOdataUrl()">Get ProjectData Endpoint</button>
<br />
<br />
<span class="rest" id="projectDataEndPoint">Endpoint of the
<strong>ProjectData</strong> service</span>
<br />
</div>
<div id="compareProjectData">
<button class="button-wide" disabled="disabled" id="compareProjects"
onclick="retrieveOData()">
Compare All Projects</button>
<br />
</div>
</div>
<div id="corpInfo">
<table class="infoTable" aria-readonly="True" style="width: 100%;">
<tr>
<td class="heading_leftCol"></td>
<td class="heading_midCol"><strong>Average</strong></td>
<td class="heading_rightCol"><strong>Current</strong></td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Cost</strong></td>
<td class="row_midCol" id="AverageProjectCost">&nbsp;</td>
<td class="row_rightCol" id="CurrentProjectCost">&nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Actual Cost</strong></td>
<td class="row_midCol" id="AverageProjectActualCost">&nbsp;</td>
<td class="row_rightCol" id="CurrentProjectActualCost">&nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Work</strong></td>
<td class="row_midCol" id="AverageProjectWork">&nbsp;</td>
<td class="row_rightCol" id="CurrentProjectWork">&nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project % Complete</strong></td>
<td class="row_midCol" id="AverageProjectPercentComplete">&nbsp;</td>
<td class="row_rightCol" id="CurrentProjectPercentComplete">&nbsp;</td>
</tr>
</table>
</div>
<img alt="Corporation" class="logo" src="../../images/NewLogo.png" />
<br />
<textarea id="odataText" rows="12" cols="40"></textarea>
</body>
</html>
Файл HelloProjectOData.js
Приведенный ниже код находится в файле Scripts\Office\HelloProjectOData.js
проекта HelloProjectODataWeb.
/* File: HelloProjectOData.js
* JavaScript functions for the HelloProjectOData example task pane app.
* October 2, 2012
*/
let PROJDATA = "/_api/ProjectData";
let PROJQUERY = "/Projects?";
let QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work Items'";
let QUERY_SELECT1 = "&$select=ProjectId, ProjectName";
let QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted, ProjectActualCost";
let _pwa; // URL of Project Web App.
let _projectUid; // GUID of the active project.
let _docUrl; // Path of the project document.
let _odataUrl = ""; // URL of the OData service: http[s]://ServerName /ProjectServerName /_api/ProjectData
// The initialize function is required for all add-ins.
Office.initialize = function (reason) {
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
});
}
// Set the global variables, enable the Compare All Projects button,
// and display the URL of the ProjectData service.
// Display an error if Project isn't connected with Project Web App.
function setOdataUrl() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.ProjectServerUrl,
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
_pwa = String(asyncResult.value.fieldValue);
// If you debug with Visual Studio on a local Project Server computer,
// uncomment the following lines to use the localhost URL.
//let localhost = location.host.split(":", 1);
//let pwaStartPosition = _pwa.lastIndexOf("/");
//let pwaLength = _pwa.length - pwaStartPosition;
//let pwaName = _pwa.substr(pwaStartPosition, pwaLength);
//_pwa = location.protocol + "//" + localhost + pwaName;
if (_pwa.substring(0, 4) == "http") {
_odataUrl = _pwa + PROJDATA;
$("#compareProjects").removeAttr("disabled");
getProjectGuid();
}
else {
_odataUrl = "No connection!";
throwError(_odataUrl, "You are not connected to Project Web App.");
}
getDocumentUrl();
$("#projectDataEndPoint").text(_odataUrl);
}
else {
throwError(asyncResult.error.name, asyncResult.error.message);
}
}
);
}
// Get the GUID of the active project.
function getProjectGuid() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.GUID,
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
_projectUid = asyncResult.value.fieldValue;
}
else {
throwError(asyncResult.error.name, asyncResult.error.message);
}
}
);
}
// Get the path of the project in Project web app, which is in the form <>\ProjectName .
function getDocumentUrl() {
_docUrl = "Document path:\r\n" + Office.context.document.url;
}
// Functions to get and parse the Project Server reporting data./
// Get data about all projects on Project Server,
// by using a REST query with the ajax method in jQuery.
function retrieveOData() {
let restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 + QUERY_SELECT2;
let accept = "application/json; odata=verbose";
accept.toLocaleLowerCase();
// Enable cross-origin scripting (required by jQuery 1.5 and later).
// This does not work with Project on the web.
$.support.cors = true;
$.ajax({
url: restUrl,
type: "GET",
contentType: "application/json",
data: "", // Empty string for the optional data.
//headers: { "Accept": accept },
beforeSend: function (xhr) {
xhr.setRequestHeader("ACCEPT", accept);
},
complete: function (xhr, textStatus) {
// Create a message to display in the text box.
let message = "\r\ntextStatus: " + textStatus +
"\r\nContentType: " + xhr.getResponseHeader("Content-Type") +
"\r\nStatus: " + xhr.status +
"\r\nResponseText:\r\n" + xhr.responseText;
// xhr.responseText is the result from an XmlHttpRequest, which
// contains the JSON response from the OData service.
parseODataResult(xhr.responseText, _projectUid);
// Write the document name, response header, status, and JSON to the odataText control.
$("#odataText").text(_docUrl);
$("#odataText").append("\r\nREST query:\r\n" + restUrl);
$("#odataText").append(message);
if (xhr.status != 200 && xhr.status != 1223 && xhr.status != 201) {
$("#odataInfo").append("<div>" + htmlEncode(restUrl) + "</div>");
}
},
error: getProjectDataErrorHandler
});
}
function getProjectDataErrorHandler(data, errorCode, errorMessage) {
$("#odataText").text("Error code: " + errorCode + "\r\nError message: \r\n"
+ errorMessage);
throwError(errorCode, errorMessage);
}
// Calculate the average values of actual cost, cost, work, and percent complete
// for all projects, and compare with the values for the current project.
function parseODataResult(oDataResult, currentProjectGuid) {
// Deserialize the JSON string into a JavaScript object.
let res = Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
let len = res.d.results.length;
let projActualCost = 0;
let projCost = 0;
let projWork = 0;
let projPercentCompleted = 0;
let myProjectIndex = -1;
for (i = 0; i < len; i++) {
// If the current project GUID matches the GUID from the OData query,
// then store the project index.
if (currentProjectGuid.toLocaleLowerCase() == res.d.results[i].ProjectId) {
myProjectIndex = i;
}
projCost += Number(res.d.results[i].ProjectCost);
projWork += Number(res.d.results[i].ProjectWork);
projActualCost += Number(res.d.results[i].ProjectActualCost);
projPercentCompleted += Number(res.d.results[i].ProjectPercentCompleted);
}
let avgProjCost = projCost / len;
let avgProjWork = projWork / len;
let avgProjActualCost = projActualCost / len;
let avgProjPercentCompleted = projPercentCompleted / len;
// Round off cost to two decimal places, and round off other values to one decimal place.
avgProjCost = avgProjCost.toFixed(2);
avgProjWork = avgProjWork.toFixed(1);
avgProjActualCost = avgProjActualCost.toFixed(2);
avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);
// Display averages in the table, with the correct units.
document.getElementById("AverageProjectCost").innerHTML = "$"
+ avgProjCost;
document.getElementById("AverageProjectActualCost").innerHTML
= "$" + avgProjActualCost;
document.getElementById("AverageProjectWork").innerHTML
= avgProjWork + " hrs";
document.getElementById("AverageProjectPercentComplete").innerHTML
= avgProjPercentCompleted + "%";
// Calculate and display values for the current project.
if (myProjectIndex != -1) {
let myProjCost = Number(res.d.results[myProjectIndex].ProjectCost);
let myProjWork = Number(res.d.results[myProjectIndex].ProjectWork);
let myProjActualCost = Number(res.d.results[myProjectIndex].ProjectActualCost);
let myProjPercentCompleted = Number(res.d.results[myProjectIndex].ProjectPercentCompleted);
myProjCost = myProjCost.toFixed(2);
myProjWork = myProjWork.toFixed(1);
myProjActualCost = myProjActualCost.toFixed(2);
myProjPercentCompleted = myProjPercentCompleted.toFixed(1);
document.getElementById("CurrentProjectCost").innerHTML = "$" + myProjCost;
if (Number(myProjCost) <= Number(avgProjCost)) {
document.getElementById("CurrentProjectCost").style.color = "green"
}
else {
document.getElementById("CurrentProjectCost").style.color = "red"
}
document.getElementById("CurrentProjectActualCost").innerHTML = "$" + myProjActualCost;
if (Number(myProjActualCost) <= Number(avgProjActualCost)) {
document.getElementById("CurrentProjectActualCost").style.color = "green"
}
else {
document.getElementById("CurrentProjectActualCost").style.color = "red"
}
document.getElementById("CurrentProjectWork").innerHTML = myProjWork + " hrs";
if (Number(myProjWork) <= Number(avgProjWork)) {
document.getElementById("CurrentProjectWork").style.color = "red"
}
else {
document.getElementById("CurrentProjectWork").style.color = "green"
}
document.getElementById("CurrentProjectPercentComplete").innerHTML = myProjPercentCompleted + "%";
if (Number(myProjPercentCompleted) <= Number(avgProjPercentCompleted)) {
document.getElementById("CurrentProjectPercentComplete").style.color = "red"
}
else {
document.getElementById("CurrentProjectPercentComplete").style.color = "green"
}
}
else { // The current project isn't published.
document.getElementById("CurrentProjectCost").innerHTML = "NA";
document.getElementById("CurrentProjectCost").style.color = "blue"
document.getElementById("CurrentProjectActualCost").innerHTML = "NA";
document.getElementById("CurrentProjectActualCost").style.color = "blue"
document.getElementById("CurrentProjectWork").innerHTML = "NA";
document.getElementById("CurrentProjectWork").style.color = "blue"
document.getElementById("CurrentProjectPercentComplete").innerHTML = "NA";
document.getElementById("CurrentProjectPercentComplete").style.color = "blue"
}
}
Файл App.css
Приведенный ниже код находится в файле Content\App.css
проекта HelloProjectODataWeb.
/*
* File: App.css for the HelloProjectOData app.
* Updated: 10/2/2012
*/
body
{
font-size: 11pt;
}
h1
{
font-size: 22pt;
}
h2
{
font-size: 16pt;
}
/******************************************************************
Code label class
******************************************************************/
.rest
{
font-family: 'Courier New';
font-size: 0.9em;
}
/******************************************************************
Button classes
******************************************************************/
.button-wide {
width: 210px;
margin-top: 2px;
}
.button-narrow
{
width: 80px;
margin-top: 2px;
}
/******************************************************************
Table styles
******************************************************************/
.infoTable
{
text-align: center;
vertical-align: middle
}
.heading_leftCol
{
width: 20px;
height: 20px;
}
.heading_midCol
{
width: 100px;
height: 20px;
font-size: medium;
font-weight: bold;
}
.heading_rightCol
{
width: 101px;
height: 20px;
font-size: medium;
font-weight: bold;
}
.row_leftCol
{
width: 20px;
font-size: small;
font-weight: bold;
}
.row_midCol
{
width: 100px;
}
.row_rightCol
{
width: 101px;
}
.logo
{
width: 135px;
height: 53px;
}
Файл SurfaceErrors.js
Следующий код включает функцию throwError
, которая создает Toast
объект .
/*
* Show error messages in a "toast" notification.
*/
// Throws a custom defined error.
function throwError(errTitle, errMessage) {
try {
// Define and throw a custom error.
let customError = { name: errTitle, message: errMessage }
throw customError;
}
catch (err) {
// Catch the error and display it to the user.
Toast.showToast(err.name, err.message);
}
}
// Add a dynamically-created div "toast" for displaying errors to the user.
let Toast = {
Toast: "divToast",
Close: "btnClose",
Notice: "lblNotice",
Output: "lblOutput",
// Show the toast with the specified information.
showToast: function (title, message) {
if (document.getElementById(this.Toast) == null) {
this.createToast();
}
document.getElementById(this.Notice).innerText = title;
document.getElementById(this.Output).innerText = message;
$("#" + this.Toast).hide();
$("#" + this.Toast).show("slow");
},
// Create the display for the toast.
createToast: function () {
let divToast;
let lblClose;
let btnClose;
let divOutput;
let lblOutput;
let lblNotice;
// Create the container div.
divToast = document.createElement("div");
let toastStyle = "background-color:rgba(220, 220, 128, 0.80);" +
"position:absolute;" +
"bottom:0px;" +
"width:90%;" +
"text-align:center;" +
"font-size:11pt;";
divToast.setAttribute("style", toastStyle);
divToast.setAttribute("id", this.Toast);
// Create the close button.
lblClose = document.createElement("div");
lblClose.setAttribute("id", this.Close);
let btnStyle = "text-align:right;" +
"padding-right:10px;" +
"font-size:10pt;" +
"cursor:default";
lblClose.setAttribute("style", btnStyle);
lblClose.appendChild(document.createTextNode("CLOSE "));
btnClose = document.createElement("span");
btnClose.setAttribute("style", "cursor:pointer;");
btnClose.setAttribute("onclick", "Toast.close()");
btnClose.innerText = "X";
lblClose.appendChild(btnClose);
// Create the div to contain the toast title and message.
divOutput = document.createElement("div");
divOutput.setAttribute("id", "divOutput");
let outputStyle = "margin-top:0px;";
divOutput.setAttribute("style", outputStyle);
lblNotice = document.createElement("span");
lblNotice.setAttribute("id", this.Notice);
let labelStyle = "font-weight:bold;margin-top:0px;";
lblNotice.setAttribute("style", labelStyle);
lblOutput = document.createElement("span");
lblOutput.setAttribute("id", this.Output);
// Add the child nodes to the toast div.
divOutput.appendChild(lblNotice);
divOutput.appendChild(document.createElement("br"));
divOutput.appendChild(lblOutput);
divToast.appendChild(lblClose);
divToast.appendChild(divOutput);
// Add the toast div to the document body.
document.body.appendChild(divToast);
},
// Close the toast.
close: function () {
$("#" + this.Toast).hide("slow");
}
}
Дальнейшие действия
Если helloProjectOData была рабочей надстройкой, продаваемой в AppSource или распространяемой в каталоге приложений SharePoint, она была бы разработана по-другому. Например, здесь не было бы отладочных выходных данных в текстовом поле и, вероятно, не было бы кнопки для получения конечной точки ProjectData. Вам также придется переписать функцию retrieveOData
для обработки Project Web App экземпляров с более чем 100 проектами.
Надстройка должна содержать дополнительные проверки ошибок, а также логику для записи, объяснения или демонстрации пограничных случаев. Например, если экземпляр Project Web App содержит 1000 проектов со средней продолжительностью в пять дней и средними затратами в $2400, а активный проект является единственным с продолжительностью более 20 дней, то сравнение материальных и трудовых затрат может быть перекошено. Это может быть показано с помощью частотной диаграммы. Вам необходимо добавить команды для отображения продолжительности, сравнения проектов с одинаковой продолжительностью или сравнения проектов из одного или разных отделов. Либо добавить возможность пользователю выбирать из списка полей, которые требуется отобразить.
Для других запросов службы ProjectData имеются ограничения на длину строки запроса, что влияет на число шагов, которые запрос может предпринять для выборки из родительской коллекции в объект в дочерней коллекции. Например, двухшаговый запрос Projects в Tasks для получения элементов задач работает, но трехшаговый запрос, такой как Projects в Tasks в Assignments, для получения элемента назначения может превысить максимальную длину URL-адреса по умолчанию. Дополнительные сведения см. в разделе Запрос веб-каналов OData для данных отчетов Project.
Если вы измените надстройку HelloProjectOData для использования в рабочей среде, выполните следующие действия.
В файле HelloProjectOData.html для лучшей производительности измените ссылку office.js из локального проекта на ссылку CDN:
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
Перепишите функцию
retrieveOData
, чтобы включить запросы для более чем 100 проектов. Например, можно получить число проектов с помощью запроса~/ProjectData/Projects()/$count
и использовать оператор $skip и оператор $top в запросе REST для получения данных проекта. Запустите несколько запросов в цикле и затем усредните данные из всех запросов. Каждый запрос к данным проекта будет иметь следующий вид:~/ProjectData/Projects()?skip= [numSkipped]&$top=100&$filter=[filter]&$select=[field1,field2, ???????]
Дополнительные сведения см. в статье Параметры системных запросов OData с помощью конечной точки REST. You can also use the Set-SPProjectOdataConfiguration command in Windows PowerShell to override the default page size for a query of the Projects entity set (or any of the 33 entity sets). See ProjectData - Project OData service reference.
Сведения о развертывании надстройки см. в статье Публикация надстройки Office.
См. также
Office Add-ins