Warning: no document names in string for template #1300的解决办法

本文详细介绍了如何解决在使用文档模板时出现的错误,即模板名称未在字符串表中添加的问题。通过查看默认模板内容并进行相应修改,可以消除警告并顺利编译。

改错误是:使用了自己创建的文档模板,但是没有在字符串表中添加相关信息。

 

具体解决方法是在字符串表中添加相关内容。

可以首先看一下默认的文档模板的内容,如:\nMyTestSystem\nTestSystem\n\n\nMyTestSystem.Document\nTestSystem.Document

而我们只需要改最后一部分TestSystem.Document,为自己创建的文档模板添加的名字(名字可以随意)。

 

如:\nMyTestSystem\nTestSystem\n\n\nMyTestSystem.Document\nMyCreateSystem.Document

 

最后保存,编译即可消除该警告。

/** * MIT License * * Copyright (C) 2016-2025 WinToast v1.3.2 - Mohammed Boujemaoui <mohabouje@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "wintoastlib.h" #include <memory> #include <assert.h> #include <unordered_map> #include <array> #include <functional> #pragma comment(lib, "shlwapi") #pragma comment(lib, "user32") #define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" #define DEFAULT_LINK_FORMAT L".lnk" #define STATUS_SUCCESS (0x00000000) #ifdef NDEBUG static bool DebugOutputEnabled = false; #else static bool DebugOutputEnabled = true; #endif #define DEBUG_MSG(str) \ do { \ if (DebugOutputEnabled) { \ std::wcout << str << std::endl; \ } \ } while (false) // Quickstart: Handling toast activations from Win32 apps in Windows 10 // https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ using namespace WinToastLib; void WinToastLib::setDebugOutputEnabled(bool enabled) { DebugOutputEnabled = enabled; } namespace DllImporter { // Function load a function from library template <typename Function> HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function& func) { if (!library) { return E_INVALIDARG; } func = reinterpret_cast<Function>(GetProcAddress(library, name)); return (func != nullptr) ? S_OK : E_FAIL; } typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void** factory); typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER* hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); typedef PCWSTR(FAR STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; static f_PropVariantToString PropVariantToString; static f_RoGetActivationFactory RoGetActivationFactory; static f_WindowsCreateStringReference WindowsCreateStringReference; static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; static f_WindowsDeleteString WindowsDeleteString; template <class T> __inline _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); } template <typename T> inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef<T> factory) noexcept { return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); } inline HRESULT initialize() { HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); if (SUCCEEDED(hr)) { HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); if (SUCCEEDED(hr)) { HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); bool const succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); return succeded ? S_OK : E_FAIL; } } return hr; } } // namespace DllImporter class WinToastStringWrapper { public: WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) { HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); if (!SUCCEEDED(hr)) { RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), 0, 0, nullptr); } } WinToastStringWrapper(_In_ std::wstring const& stringRef) { HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast<UINT32>(stringRef.length()), &_header, &_hstring); if (FAILED(hr)) { RaiseException(static_cast<DWORD>(STATUS_INVALID_PARAMETER), 0, 0, nullptr); } } ~WinToastStringWrapper() { DllImporter::WindowsDeleteString(_hstring); } inline HSTRING Get() const noexcept { return _hstring; } private: HSTRING _hstring; HSTRING_HEADER _header; }; class InternalDateTime : public IReference<DateTime> { public: static INT64 Now() { FILETIME now; GetSystemTimeAsFileTime(&now); return ((((INT64) now.dwHighDateTime) << 32) | now.dwLowDateTime); } InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} InternalDateTime(INT64 millisecondsFromNow) { _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000; } virtual ~InternalDateTime() = default; operator INT64() { return _dateTime.UniversalTime; } HRESULT STDMETHODCALLTYPE get_Value(DateTime* dateTime) { *dateTime = _dateTime; return S_OK; } HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) { if (!ppvObject) { return E_POINTER; } if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference<DateTime>)) { *ppvObject = static_cast<IUnknown*>(static_cast<IReference<DateTime>*>(this)); return S_OK; } return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE Release() { return 1; } ULONG STDMETHODCALLTYPE AddRef() { return 2; } HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) { return E_NOTIMPL; } protected: DateTime _dateTime; }; namespace Util { typedef LONG NTSTATUS, *PNTSTATUS; typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); inline RTL_OSVERSIONINFOW getRealOSVersion() { HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); if (hMod) { RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); if (fxPtr != nullptr) { RTL_OSVERSIONINFOW rovi = {0}; rovi.dwOSVersionInfoSize = sizeof(rovi); if (STATUS_SUCCESS == fxPtr(&rovi)) { return rovi; } } } RTL_OSVERSIONINFOW rovi = {0}; return rovi; } inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); DEBUG_MSG("Default executable path: " << path); return (written > 0) ? S_OK : E_FAIL; } inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; if (SUCCEEDED(hr)) { errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); hr = (result == 0) ? S_OK : E_INVALIDARG; DEBUG_MSG("Default shell link path: " << path); } return hr; } inline HRESULT defaultShellLinkPath(_In_ std::wstring const& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { HRESULT hr = defaultShellLinksDirectory(path, nSize); if (SUCCEEDED(hr)) { std::wstring const appLink(appname + DEFAULT_LINK_FORMAT); errno_t result = wcscat_s(path, nSize, appLink.c_str()); hr = (result == 0) ? S_OK : E_INVALIDARG; DEBUG_MSG("Default shell link file path: " << path); } return hr; } inline std::wstring parentDirectory(WCHAR* path, DWORD size) { size_t lastSeparator = 0; for (size_t i = 0; i < size; i++) { if (path[i] == L&#39;\\&#39; || path[i] == L&#39;/&#39;) { lastSeparator = i; } } return {path, lastSeparator}; } inline PCWSTR AsString(_In_ ComPtr<IXmlDocument>& xmlDocument) { HSTRING xml; ComPtr<IXmlNodeSerializer> ser; HRESULT hr = xmlDocument.As<IXmlNodeSerializer>(&ser); hr = ser->GetXml(&xml); if (SUCCEEDED(hr)) { return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); } return nullptr; } inline PCWSTR AsString(_In_ HSTRING hstring) { return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); } inline HRESULT setNodeStringValue(_In_ std::wstring const& string, _Out_opt_ IXmlNode* node, _Out_ IXmlDocument* xml) { ComPtr<IXmlText> textNode; HRESULT hr = xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> stringNode; hr = textNode.As(&stringNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> appendedChild; hr = node->AppendChild(stringNode.Get(), &appendedChild); } } return hr; } template <typename FunctorT> inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr<IWinToastHandler> eventHandler, _In_ INT64 expirationTime, _Out_ EventRegistrationToken& activatedToken, _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, _In_ FunctorT&& markAsReadyForDeletionFunc) { HRESULT hr = notification->add_Activated( Callback<Implements<RuntimeClassFlags<ClassicCom>, ITypedEventHandler<ToastNotification*, IInspectable*>>>( [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IInspectable* inspectable) { ComPtr<IToastActivatedEventArgs> activatedEventArgs; HRESULT hr = inspectable->QueryInterface(activatedEventArgs.GetAddressOf()); if (SUCCEEDED(hr)) { HSTRING argumentsHandle; hr = activatedEventArgs->get_Arguments(&argumentsHandle); if (SUCCEEDED(hr)) { PCWSTR arguments = Util::AsString(argumentsHandle); if (wcscmp(arguments, L"action=reply") == 0) { ComPtr<IToastActivatedEventArgs2> inputBoxActivatedEventArgs; HRESULT hr2 = inspectable->QueryInterface(inputBoxActivatedEventArgs.GetAddressOf()); if (SUCCEEDED(hr2)) { ComPtr<Collections::IPropertySet> replyHandle; inputBoxActivatedEventArgs->get_UserInput(&replyHandle); ComPtr<__FIMap_2_HSTRING_IInspectable> replyMap; hr = replyHandle.As(&replyMap); if (SUCCEEDED(hr)) { IInspectable* propertySet; hr = replyMap.Get()->Lookup(WinToastStringWrapper(L"textBox").Get(), &propertySet); if (SUCCEEDED(hr)) { ComPtr<IPropertyValue> propertyValue; hr = propertySet->QueryInterface(IID_PPV_ARGS(&propertyValue)); if (SUCCEEDED(hr)) { // Successfully queried IPropertyValue, now extract the value HSTRING userInput; hr = propertyValue->GetString(&userInput); if (SUCCEEDED(hr)) { // Convert the HSTRING to a wide string PCWSTR strValue = Util::AsString(userInput); eventHandler->toastActivated(std::wstring(strValue)); DllImporter::WindowsDeleteString(userInput); return S_OK; } if (userInput != nullptr) { DllImporter::WindowsDeleteString(userInput); } } } } } } if (arguments && *arguments) { eventHandler->toastActivated(static_cast<long>(wcstol(arguments, nullptr, 10))); DllImporter::WindowsDeleteString(argumentsHandle); markAsReadyForDeletionFunc(); return S_OK; } DllImporter::WindowsDeleteString(argumentsHandle); } } eventHandler->toastActivated(); markAsReadyForDeletionFunc(); return S_OK; }) .Get(), &activatedToken); if (SUCCEEDED(hr)) { hr = notification->add_Dismissed( Callback<Implements<RuntimeClassFlags<ClassicCom>, ITypedEventHandler<ToastNotification*, ToastDismissedEventArgs*>>>( [eventHandler, expirationTime, markAsReadyForDeletionFunc](IToastNotification* notify, IToastDismissedEventArgs* e) { ToastDismissalReason reason; if (SUCCEEDED(e->get_Reason(&reason))) { if (reason == ToastDismissalReason_UserCanceled && expirationTime && InternalDateTime::Now() >= expirationTime) { reason = ToastDismissalReason_TimedOut; } eventHandler->toastDismissed(static_cast<IWinToastHandler::WinToastDismissalReason>(reason)); } markAsReadyForDeletionFunc(); return S_OK; }) .Get(), &dismissedToken); if (SUCCEEDED(hr)) { hr = notification->add_Failed( Callback<Implements<RuntimeClassFlags<ClassicCom>, ITypedEventHandler<ToastNotification*, ToastFailedEventArgs*>>>( [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IToastFailedEventArgs* e) { eventHandler->toastFailed(); markAsReadyForDeletionFunc(); return S_OK; }) .Get(), &failedToken); } } return hr; } inline HRESULT addAttribute(_In_ IXmlDocument* xml, std::wstring const& name, IXmlNamedNodeMap* attributeMap) { ComPtr<ABI::Windows::Data::Xml::Dom::IXmlAttribute> srcAttribute; HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> node; hr = srcAttribute.As(&node); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> pNode; hr = attributeMap->SetNamedItem(node.Get(), &pNode); } } return hr; } inline HRESULT createElement(_In_ IXmlDocument* xml, _In_ std::wstring const& root_node, _In_ std::wstring const& element_name, _In_ std::vector<std::wstring> const& attribute_names) { ComPtr<IXmlNodeList> rootList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> root; hr = rootList->Item(0, &root); if (SUCCEEDED(hr)) { ComPtr<ABI::Windows::Data::Xml::Dom::IXmlElement> audioElement; hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> audioNodeTmp; hr = audioElement.As(&audioNodeTmp); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> audioNode; hr = root->AppendChild(audioNodeTmp.Get(), &audioNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNamedNodeMap> attributes; hr = audioNode->get_Attributes(&attributes); if (SUCCEEDED(hr)) { for (auto const& it : attribute_names) { hr = addAttribute(xml, it, attributes.Get()); } } } } } } } return hr; } } // namespace Util WinToast* WinToast::instance() { thread_local static WinToast instance; return &instance; } WinToast::WinToast() : _isInitialized(false), _hasCoInitialized(false) { if (!isCompatible()) { DEBUG_MSG(L"Warning: Your system is not compatible with this library "); } } WinToast::~WinToast() { clear(); if (_hasCoInitialized) { CoUninitialize(); } } void WinToast::setAppName(_In_ std::wstring const& appName) { _appName = appName; } void WinToast::setAppUserModelId(_In_ std::wstring const& aumi) { _aumi = aumi; DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); } void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) { _shortcutPolicy = shortcutPolicy; } bool WinToast::isCompatible() { DllImporter::initialize(); return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || (DllImporter::PropVariantToString == nullptr) || (DllImporter::RoGetActivationFactory == nullptr) || (DllImporter::WindowsCreateStringReference == nullptr) || (DllImporter::WindowsDeleteString == nullptr)); } bool WinToastLib::WinToast::isSupportingModernFeatures() { constexpr auto MinimumSupportedVersion = 6; return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; } bool WinToastLib::WinToast::isWin10AnniversaryOrHigher() { return Util::getRealOSVersion().dwBuildNumber >= 14393; } std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, _In_ std::wstring const& subProduct, _In_ std::wstring const& versionInformation) { std::wstring aumi = companyName; aumi += L"." + productName; if (subProduct.length() > 0) { aumi += L"." + subProduct; if (versionInformation.length() > 0) { aumi += L"." + versionInformation; } } if (aumi.length() > SCHAR_MAX) { DEBUG_MSG("Error: max size allowed for AUMI: 128 characters."); } return aumi; } std::wstring const& WinToast::strerror(WinToastError error) { static std::unordered_map<WinToastError, std::wstring> const Labels = { {WinToastError::NoError, L"No error. The process was executed correctly" }, {WinToastError::NotInitialized, L"The library has not been initialized" }, {WinToastError::SystemNotSupported, L"The OS does not support WinToast" }, {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app" }, {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one" }, {WinToastError::InvalidParameters, L"Invalid parameters, please double-check the AUMI or App Name" }, {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, {WinToastError::UnknownError, L"Unknown error" } }; auto const iter = Labels.find(error); assert(iter != Labels.end()); return iter->second; } enum WinToast::ShortcutResult WinToast::createShortcut() { if (_aumi.empty() || _appName.empty()) { DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); return SHORTCUT_MISSING_PARAMETERS; } if (!isCompatible()) { DEBUG_MSG(L"Your OS is not compatible with this library! =("); return SHORTCUT_INCOMPATIBLE_OS; } if (!_hasCoInitialized) { HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); if (initHr != RPC_E_CHANGED_MODE) { if (FAILED(initHr) && initHr != S_FALSE) { DEBUG_MSG(L"Error on COM library initialization!"); return SHORTCUT_COM_INIT_FAILURE; } else { _hasCoInitialized = true; } } } bool wasChanged; HRESULT hr = validateShellLinkHelper(wasChanged); if (SUCCEEDED(hr)) { return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; } hr = createShellLinkHelper(); return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; } bool WinToast::initialize(_Out_opt_ WinToastError* error) { _isInitialized = false; setError(error, WinToastError::NoError); if (!isCompatible()) { setError(error, WinToastError::SystemNotSupported); DEBUG_MSG(L"Error: system not supported."); return false; } if (_aumi.empty() || _appName.empty()) { setError(error, WinToastError::InvalidParameters); DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); return false; } if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) { if (createShortcut() < 0) { setError(error, WinToastError::ShellLinkNotCreated); DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); return false; } } if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { setError(error, WinToastError::InvalidAppUserModelID); DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); return false; } _isInitialized = true; return _isInitialized; } bool WinToast::isInitialized() const { return _isInitialized; } std::wstring const& WinToast::appName() const { return _appName; } std::wstring const& WinToast::appUserModelId() const { return _aumi; } HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { WCHAR path[MAX_PATH] = {L&#39;\0&#39;}; Util::defaultShellLinkPath(_appName, path); // Check if the file exist DWORD attr = GetFileAttributesW(path); if (attr >= 0xFFFFFFF) { DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); return E_FAIL; } // Let&#39;s load the file as shell link to validate. // - Create a shell link // - Create a persistant file // - Load the path as data for the persistant file // - Read the property AUMI and validate with the current // - Review if AUMI is equal. ComPtr<IShellLink> shellLink; HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); if (SUCCEEDED(hr)) { ComPtr<IPersistFile> persistFile; hr = shellLink.As(&persistFile); if (SUCCEEDED(hr)) { hr = persistFile->Load(path, STGM_READWRITE); if (SUCCEEDED(hr)) { ComPtr<IPropertyStore> propertyStore; hr = shellLink.As(&propertyStore); if (SUCCEEDED(hr)) { PROPVARIANT appIdPropVar; hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); if (SUCCEEDED(hr)) { WCHAR AUMI[MAX_PATH]; hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); wasChanged = false; if (FAILED(hr) || _aumi != AUMI) { if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { // AUMI Changed for the same app, let&#39;s update the current value! =) wasChanged = true; PropVariantClear(&appIdPropVar); hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->Commit(); if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { hr = persistFile->Save(path, TRUE); } } } } else { // Not allowed to touch the shortcut to fix the AUMI hr = E_FAIL; } } PropVariantClear(&appIdPropVar); } } } } } return hr; } HRESULT WinToast::createShellLinkHelper() { if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { return E_FAIL; } WCHAR exePath[MAX_PATH]{L&#39;\0&#39;}; WCHAR slPath[MAX_PATH]{L&#39;\0&#39;}; Util::defaultShellLinkPath(_appName, slPath); Util::defaultExecutablePath(exePath); std::wstring exeDir = Util::parentDirectory(exePath, sizeof(exePath) / sizeof(exePath[0])); ComPtr<IShellLinkW> shellLink; HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); if (SUCCEEDED(hr)) { hr = shellLink->SetPath(exePath); if (SUCCEEDED(hr)) { hr = shellLink->SetArguments(L""); if (SUCCEEDED(hr)) { hr = shellLink->SetWorkingDirectory(exeDir.c_str()); if (SUCCEEDED(hr)) { ComPtr<IPropertyStore> propertyStore; hr = shellLink.As(&propertyStore); if (SUCCEEDED(hr)) { PROPVARIANT appIdPropVar; hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->Commit(); if (SUCCEEDED(hr)) { ComPtr<IPersistFile> persistFile; hr = shellLink.As(&persistFile); if (SUCCEEDED(hr)) { hr = persistFile->Save(slPath, TRUE); } } } PropVariantClear(&appIdPropVar); } } } } } } return hr; } INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error) { std::shared_ptr<IWinToastHandler> handler(eventHandler); setError(error, WinToastError::NoError); INT64 id = -1; if (!isInitialized()) { setError(error, WinToastError::NotInitialized); DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); return id; } if (!handler) { setError(error, WinToastError::InvalidHandler); DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr."); return id; } ComPtr<IToastNotificationManagerStatics> notificationManager; HRESULT hr = DllImporter::Wrap_GetActivationFactory( WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager); if (SUCCEEDED(hr)) { ComPtr<IToastNotifier> notifier; hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier); if (SUCCEEDED(hr)) { ComPtr<IToastNotificationFactory> notificationFactory; hr = DllImporter::Wrap_GetActivationFactory( WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &notificationFactory); if (SUCCEEDED(hr)) { ComPtr<IXmlDocument> xmlDocument; hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); if (SUCCEEDED(hr) && toast.isToastGeneric()) { hr = setBindToastGenericHelper(xmlDocument.Get()); } if (SUCCEEDED(hr)) { for (UINT32 i = 0, fieldsCount = static_cast<UINT32>(toast.textFieldsCount()); i < fieldsCount && SUCCEEDED(hr); i++) { hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); } // Modern feature are supported Windows > Windows 10 if (SUCCEEDED(hr) && isSupportingModernFeatures()) { // Note that we do this *after* using toast.textFieldsCount() to // iterate/fill the template&#39;s text fields, since we&#39;re adding yet another text field. if (SUCCEEDED(hr) && !toast.attributionText().empty()) { hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); } std::array<WCHAR, 12> buf; for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) { _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i); hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data()); } if (SUCCEEDED(hr)) { hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); } if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { hr = addDurationHelper(xmlDocument.Get(), (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); } if (SUCCEEDED(hr) && toast.isInput()) { hr = addInputHelper(xmlDocument.Get()); } if (SUCCEEDED(hr)) { hr = addScenarioHelper(xmlDocument.Get(), toast.scenario()); } } else { DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); } if (SUCCEEDED(hr)) { bool isWin10AnniversaryOrAbove = WinToast::isWin10AnniversaryOrHigher(); bool isCircleCropHint = isWin10AnniversaryOrAbove ? toast.isCropHintCircle() : false; hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath(), toast.isToastGeneric(), isCircleCropHint) : hr; if (SUCCEEDED(hr) && isWin10AnniversaryOrAbove && toast.hasHeroImage()) { hr = setHeroImageHelper(xmlDocument.Get(), toast.heroImagePath(), toast.isInlineHeroImage()); } if (SUCCEEDED(hr)) { ComPtr<IToastNotification> notification; hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), &notification); if (SUCCEEDED(hr)) { INT64 expiration = 0, relativeExpiration = toast.expiration(); if (relativeExpiration > 0) { InternalDateTime expirationDateTime(relativeExpiration); expiration = expirationDateTime; hr = notification->put_ExpirationTime(&expirationDateTime); } EventRegistrationToken activatedToken, dismissedToken, failedToken; GUID guid; HRESULT hrGuid = CoCreateGuid(&guid); id = guid.Data1; if (SUCCEEDED(hr) && SUCCEEDED(hrGuid)) { hr = Util::setEventHandlers(notification.Get(), handler, expiration, activatedToken, dismissedToken, failedToken, [this, id]() { markAsReadyForDeletion(id); }); if (FAILED(hr)) { setError(error, WinToastError::InvalidHandler); } } if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) { std::lock_guard<std::mutex> lock(m_mutex); _buffer.emplace(id, NotifyData(notification, activatedToken, dismissedToken, failedToken)); DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); hr = notifier->Show(notification.Get()); if (FAILED(hr)) { setError(error, WinToastError::NotDisplayed); } } } } } } } } } } return FAILED(hr) ? -1 : id; } ComPtr<IToastNotifier> WinToast::notifier(_In_ bool* succeded) const { ComPtr<IToastNotificationManagerStatics> notificationManager; ComPtr<IToastNotifier> notifier; HRESULT hr = DllImporter::Wrap_GetActivationFactory( WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &notificationManager); if (SUCCEEDED(hr)) { hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &notifier); } *succeded = SUCCEEDED(hr); return notifier; } void WinToast::markAsReadyForDeletion(_In_ INT64 id) { std::lock_guard<std::mutex> lock(m_mutex); // Flush the buffer by removing all the toasts that are ready for deletion for (auto it = _buffer.begin(); it != _buffer.end();) { if (it->second.isReadyForDeletion()) { it->second.RemoveTokens(); it = _buffer.erase(it); } else { ++it; } } // Mark the toast as ready for deletion (if it exists) so that it will be removed from the buffer in the next iteration auto const iter = _buffer.find(id); if (iter != _buffer.end()) { _buffer[id].markAsReadyForDeletion(); } } bool WinToast::hideToast(_In_ INT64 id) { std::lock_guard<std::mutex> lock(m_mutex); if (!isInitialized()) { DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); return false; } auto iter = _buffer.find(id); if (iter == _buffer.end()) { return false; } auto succeded = false; auto notify = notifier(&succeded); if (!succeded) { return false; } auto& notifyData = iter->second; auto result = notify->Hide(notifyData.notification()); if (FAILED(result)) { DEBUG_MSG("Error when hiding the toast. Error code: " << result); return false; } notifyData.RemoveTokens(); _buffer.erase(iter); return SUCCEEDED(result); } void WinToast::clear() { auto succeded = false; auto notify = notifier(&succeded); if (!succeded) { return; } std::lock_guard<std::mutex> lock(m_mutex); auto safeCopy = _buffer; for (auto& data : safeCopy) { auto& notifyData = data.second; notify->Hide(notifyData.notification()); notifyData.RemoveTokens(); } _buffer.clear(); } // // Available as of Windows 10 Anniversary Update // Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts // // NOTE: This will add a new text field, so be aware when iterating over // the toast&#39;s text fields or getting a count of them. // HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text) { Util::createElement(xml, L"binding", L"text", {L"placement"}); ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 nodeListLength; hr = nodeList->get_Length(&nodeListLength); if (SUCCEEDED(hr)) { for (UINT32 i = 0; i < nodeListLength; i++) { ComPtr<IXmlNode> textNode; hr = nodeList->Item(i, &textNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNamedNodeMap> attributes; hr = textNode->get_Attributes(&attributes); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> editedNode; if (SUCCEEDED(hr)) { hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode); if (FAILED(hr) || !editedNode) { continue; } hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml); if (SUCCEEDED(hr)) { return setTextFieldHelper(xml, text, i); } } } } } } } return hr; } HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 length; hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> toastNode; hr = nodeList->Item(0, &toastNode); if (SUCCEEDED(hr)) { ComPtr<IXmlElement> toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(duration).Get()); } } } } return hr; } HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 length; hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> toastNode; hr = nodeList->Item(0, &toastNode); if (SUCCEEDED(hr)) { ComPtr<IXmlElement> toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), WinToastStringWrapper(scenario).Get()); } } } } return hr; } HRESULT WinToast::addInputHelper(_In_ IXmlDocument* xml) { std::vector<std::wstring> attrbs; attrbs.push_back(L"id"); attrbs.push_back(L"type"); attrbs.push_back(L"placeHolderContent"); std::vector<std::wstring> attrbs2; attrbs2.push_back(L"content"); attrbs2.push_back(L"arguments"); Util::createElement(xml, L"toast", L"actions", {}); Util::createElement(xml, L"actions", L"input", attrbs); Util::createElement(xml, L"actions", L"action", attrbs2); ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"input").Get(), &nodeList); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> inputNode; hr = nodeList->Item(0, &inputNode); if (SUCCEEDED(hr)) { ComPtr<IXmlElement> toastElement; hr = inputNode.As(&toastElement); if (SUCCEEDED(hr)) { toastElement->SetAttribute(WinToastStringWrapper(L"id").Get(), WinToastStringWrapper(L"textBox").Get()); toastElement->SetAttribute(WinToastStringWrapper(L"type").Get(), WinToastStringWrapper(L"text").Get()); hr = toastElement->SetAttribute(WinToastStringWrapper(L"placeHolderContent").Get(), WinToastStringWrapper(L"...").Get()); } } } ComPtr<IXmlNodeList> nodeList2; hr = xml->GetElementsByTagName(WinToastStringWrapper(L"action").Get(), &nodeList2); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> actionNode; hr = nodeList2->Item(0, &actionNode); if (SUCCEEDED(hr)) { ComPtr<IXmlElement> actionElement; hr = actionNode.As(&actionElement); if (SUCCEEDED(hr)) { actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(L"Reply").Get()); actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(L"action=reply").Get()); actionElement->SetAttribute(WinToastStringWrapper(L"hint-inputId").Get(), WinToastStringWrapper(L"textBox").Get()); } } } return hr; } HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> node; hr = nodeList->Item(pos, &node); if (SUCCEEDED(hr)) { hr = Util::setNodeStringValue(text, node.Get(), xml); } } return hr; } HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 length; hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> toastNode; hr = nodeList->Item(0, &toastNode); if (SUCCEEDED(hr)) { ComPtr<IXmlElement> toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); } } } } return hr; } HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, _In_ bool isCropHintCircle) { assert(path.size() < MAX_PATH); wchar_t imagePath[MAX_PATH] = L"file:///"; HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); if (SUCCEEDED(hr)) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> node; hr = nodeList->Item(0, &node); ComPtr<IXmlElement> imageElement; HRESULT hrImage = node.As(&imageElement); if (SUCCEEDED(hr) && SUCCEEDED(hrImage) && isToastGeneric) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"appLogoOverride").Get()); if (SUCCEEDED(hr) && isCropHintCircle) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"hint-crop").Get(), WinToastStringWrapper(L"circle").Get()); } } if (SUCCEEDED(hr)) { ComPtr<IXmlNamedNodeMap> attributes; hr = node->get_Attributes(&attributes); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> editedNode; hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); if (SUCCEEDED(hr)) { Util::setNodeStringValue(imagePath, editedNode.Get(), xml); } } } } } return hr; } HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_opt_ WinToastTemplate::AudioOption option) { std::vector<std::wstring> attrs; if (!path.empty()) { attrs.push_back(L"src"); } if (option == WinToastTemplate::AudioOption::Loop) { attrs.push_back(L"loop"); } if (option == WinToastTemplate::AudioOption::Silent) { attrs.push_back(L"silent"); } Util::createElement(xml, L"toast", L"audio", attrs); ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> node; hr = nodeList->Item(0, &node); if (SUCCEEDED(hr)) { ComPtr<IXmlNamedNodeMap> attributes; hr = node->get_Attributes(&attributes); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> editedNode; if (!path.empty()) { if (SUCCEEDED(hr)) { hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); if (SUCCEEDED(hr)) { hr = Util::setNodeStringValue(path, editedNode.Get(), xml); } } } if (SUCCEEDED(hr)) { switch (option) { case WinToastTemplate::AudioOption::Loop: hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); if (SUCCEEDED(hr)) { hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); } break; case WinToastTemplate::AudioOption::Silent: hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); if (SUCCEEDED(hr)) { hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); } default: break; } } } } } return hr; } HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& content, _In_ std::wstring const& arguments) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 length; hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> actionsNode; if (length > 0) { hr = nodeList->Item(0, &actionsNode); } else { hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); if (SUCCEEDED(hr)) { hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> toastNode; hr = nodeList->Item(0, &toastNode); if (SUCCEEDED(hr)) { ComPtr<IXmlElement> toastElement; hr = toastNode.As(&toastElement); if (SUCCEEDED(hr)) { hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); } if (SUCCEEDED(hr)) { hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get()); } if (SUCCEEDED(hr)) { ComPtr<IXmlElement> actionsElement; hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); if (SUCCEEDED(hr)) { hr = actionsElement.As(&actionsNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> appendedChild; hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild); } } } } } } } if (SUCCEEDED(hr)) { ComPtr<IXmlElement> actionElement; hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); if (SUCCEEDED(hr)) { hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); } if (SUCCEEDED(hr)) { hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); } if (SUCCEEDED(hr)) { ComPtr<IXmlNode> actionNode; hr = actionElement.As(&actionNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> appendedChild; hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild); } } } } } return hr; } HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage) { ComPtr<IXmlNodeList> nodeList; HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); if (SUCCEEDED(hr)) { UINT32 length; hr = nodeList->get_Length(&length); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> bindingNode; if (length > 0) { hr = nodeList->Item(0, &bindingNode); } if (SUCCEEDED(hr)) { ComPtr<IXmlElement> imageElement; hr = xml->CreateElement(WinToastStringWrapper(L"image").Get(), &imageElement); if (SUCCEEDED(hr) && isInlineImage == false) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"hero").Get()); } if (SUCCEEDED(hr)) { hr = imageElement->SetAttribute(WinToastStringWrapper(L"src").Get(), WinToastStringWrapper(path).Get()); } if (SUCCEEDED(hr)) { ComPtr<IXmlNode> actionNode; hr = imageElement.As(&actionNode); if (SUCCEEDED(hr)) { ComPtr<IXmlNode> appendedChild; hr = bindingNode->AppendChild(actionNode.Get(), &appendedChild); } } } } } return hr; } void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value) { if (error) { *error = value; } } WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { constexpr static std::size_t TextFieldsCount[] = {1, 2, 2, 3, 1, 2, 2, 3}; _textFields = std::vector<std::wstring>(TextFieldsCount[type], L""); } WinToastTemplate::~WinToastTemplate() { _textFields.clear(); } void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ WinToastTemplate::TextField pos) { auto const position = static_cast<std::size_t>(pos); if (position >= _textFields.size()) { DEBUG_MSG("The selected template type supports only " << _textFields.size() << " text lines"); return; } _textFields[position] = txt; } void WinToastTemplate::setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint) { _imagePath = imgPath; _cropHint = cropHint; } void WinToastTemplate::setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage) { _heroImagePath = imgPath; _inlineHeroImage = inlineImage; } void WinToastTemplate::setAudioPath(_In_ std::wstring const& audioPath) { _audioPath = audioPath; } void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { static std::unordered_map<AudioSystemFile, std::wstring> const Files = { {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default" }, {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM" }, {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail" }, {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder" }, {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS" }, {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm" }, {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2" }, {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3" }, {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4" }, {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5" }, {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6" }, {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7" }, {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8" }, {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9" }, {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call" }, {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1" }, {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2" }, {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3" }, {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4" }, {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5" }, {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6" }, {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7" }, {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8" }, {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9" }, {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10" }, }; auto const iter = Files.find(file); assert(iter != Files.end()); _audioPath = iter->second; } void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { _audioOption = audioOption; } void WinToastTemplate::setFirstLine(_In_ std::wstring const& text) { setTextField(text, WinToastTemplate::FirstLine); } void WinToastTemplate::setSecondLine(_In_ std::wstring const& text) { setTextField(text, WinToastTemplate::SecondLine); } void WinToastTemplate::setThirdLine(_In_ std::wstring const& text) { setTextField(text, WinToastTemplate::ThirdLine); } void WinToastTemplate::setDuration(_In_ Duration duration) { _duration = duration; } void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { _expiration = millisecondsFromNow; } void WinToastLib::WinToastTemplate::setScenario(_In_ Scenario scenario) { switch (scenario) { case Scenario::Default: _scenario = L"Default"; break; case Scenario::Alarm: _scenario = L"Alarm"; break; case Scenario::IncomingCall: _scenario = L"IncomingCall"; break; case Scenario::Reminder: _scenario = L"Reminder"; break; } } void WinToastTemplate::setAttributionText(_In_ std::wstring const& attributionText) { _attributionText = attributionText; } void WinToastTemplate::addAction(_In_ std::wstring const& label) { _actions.push_back(label); } void WinToastTemplate::addInput() { _hasInput = true; } std::size_t WinToastTemplate::textFieldsCount() const { return _textFields.size(); } std::size_t WinToastTemplate::actionsCount() const { return _actions.size(); } bool WinToastTemplate::hasImage() const { return _type < WinToastTemplateType::Text01; } bool WinToastTemplate::hasHeroImage() const { return hasImage() && !_heroImagePath.empty(); } std::vector<std::wstring> const& WinToastTemplate::textFields() const { return _textFields; } std::wstring const& WinToastTemplate::textField(_In_ TextField pos) const { auto const position = static_cast<std::size_t>(pos); assert(position < _textFields.size()); return _textFields[position]; } std::wstring const& WinToastTemplate::actionLabel(_In_ std::size_t position) const { assert(position < _actions.size()); return _actions[position]; } std::wstring const& WinToastTemplate::imagePath() const { return _imagePath; } std::wstring const& WinToastTemplate::heroImagePath() const { return _heroImagePath; } std::wstring const& WinToastTemplate::audioPath() const { return _audioPath; } std::wstring const& WinToastTemplate::attributionText() const { return _attributionText; } std::wstring const& WinToastLib::WinToastTemplate::scenario() const { return _scenario; } INT64 WinToastTemplate::expiration() const { return _expiration; } WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { return _type; } WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { return _audioOption; } WinToastTemplate::Duration WinToastTemplate::duration() const { return _duration; } bool WinToastTemplate::isToastGeneric() const { return hasHeroImage() || _cropHint == WinToastTemplate::Circle; } bool WinToastTemplate::isInlineHeroImage() const { return _inlineHeroImage; } bool WinToastTemplate::isCropHintCircle() const { return _cropHint == CropHint::Circle; } bool WinToastTemplate::isInput() const { return _hasInput; } 基于上述源码,新增锁屏时系统弹窗不显示在锁屏之上逻辑
最新发布
10-18
<template> <div class="table-box"> <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :data-callback="dataCallback" > </ProTable> </div> </template> <script setup lang="tsx" name="operationLog"> import { ref, reactive, } from "vue"; import ProTable from "@/components/ProTable/index.vue"; import { setValueClildList } from &#39;@/api/modules/public&#39;; import { assetListOpList } from "@/api/modules/assetAllocation"; import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface"; import { Metering } from "@/api/interface"; import { OperationType } from "@/utils/dict"; const emit = defineEmits([&#39;previous-info&#39;]); // ProTable 实例 const proTable = ref<ProTableInstance>(); // 保存 id 的状态 const currentId = ref<string | undefined>(undefined); // ProTable 请求数据的方法 const getTableList = (params: any) => { // 添加 id 参数 if (currentId.value) { params.assetId = currentId.value; } if (params.createTime) { params.startTime = params.createTime[0] + &#39; 00:00:00&#39;; params.endTime = params.createTime[1] + &#39; 23:59:59&#39;; delete params.createTime; } return assetListOpList(params); } const dataCallback = (data: any) => { return { list: data.dataList, total: data.totalCount, pageNum: data.pageNum, pageSize: data.pageSize }; } interface AssetDetailData { id?: string; } interface DrawerProps { row?: Partial<AssetDetailData>; } const drawerProps = ref<DrawerProps>({ row: {}, }) const refreshTable = () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); }; // 父组件传过来的数据 const setDrawerProps = async (params: DrawerProps) => { drawerProps.value = { ...params } // 更新 currentId currentId.value = params.row?.id; // 手动触发 ProTable 的刷新 await refreshTable(); } // 表格配置项 const columns = reactive<ColumnProps<Metering.pageList>[]>([ { prop: "createUserName", label: "操作人" }, { prop: "userName", label: "操作人",isShow:false, search: { el: "input" }}, { prop: "optType", label: "操作类型", isShow:false, enum: async () => { // 获取资产来源 const data = await setValueClildList({ dictCode: &#39;LOG_OPERATION_TYPE&#39; }); if(data.code != 0) return false const list = data?.data.filter(item => item.enableStatus == 1); return { data: list } }, fieldNames: { label: "itemLabel", value: "itemValue" }, search: { el: "select", props: { filterable: true }}, }, { prop: "optTypeName", label: "操作类型" }, { prop: "content", label: "操作内容",search: { el: "input" } }, { prop: "orderNo", label: "关联单据", search: { el: "input" }, copy: false, render: (scope) => { return ( <span style="color: #49c625" onClick={() => handleAssetCodeClick(scope.row)}> {scope.row.orderNo} </span> ); } }, { prop: "createTime", label: "操作时间", search: { el: "date-picker", span: 2, props: { type: "daterange", valueFormat: "YYYY-MM-DD" }, }, }, ]) const handleAssetCodeClick = async (row: any) => { emit(&#39;previous-info&#39;,row); // 触发父组件事件 } defineExpose({ setDrawerProps }); </script> <template> <el-drawer v-model="drawerVisible" :append-to-body="true" :destroy-on-close="true" size="70%" :show-close="false" :with-header="false" class="no-header-drawer" > <div class="asset_details_header"> <!-- 头部内容 --> <div class="header_title"> <div class="header_left"> <div class="header_zcInfo"> <span class="header_zcInfo_name">{{ infos.baseInfo.assetName }}</span> <!-- 资产标记 --> <span class="header_zcInfo_status" v-if="infos.markTags?.length"> <span v-for="(item, index) in markStatusList" :key="index"> <span class="span-zc" :style="`color:${item.color}`">{{ item.name }}</span> </span> </span> </div> <div class="header_userInfo"> <span>新增人:{{ infos.baseInfo.createUserName }}</span> <span>新增时间:{{ infos.baseInfo.createTime }}</span> <span>修改人:{{ infos.baseInfo.updateUserName }}</span> <span>修改时间:{{ infos.baseInfo.updateTime }}</span> </div> </div> <div class="header_right"> <div class="trapezoid-isosceles" :style="statusStyle" > <span>{{ statusText }}</span> </div> </div> </div> <div class="header_btns"> <el-button type="primary" v-if="hasBtnPermission(&#39;asset:inventory:update&#39;)" @click="deleteData" > <el-icon class="el-icon--left"><Delete /></el-icon>删除 </el-button> <el-button type="primary" @click="openTagPrint" v-if="hasBtnPermission(&#39;asset:inventory:printMark&#39;)" > <el-icon class="el-icon--left"><Printer /></el-icon>打印资产标签 </el-button> </div> </div> <div class="asset_details_content"> <el-tabs v-model="activeName" class="demo-tabs"> <div class="content-height"> <el-tab-pane label="资产信息" name="first"> <basicDetails ref="basicDetailsRef" @previous-info="previousInfo"/> </el-tab-pane> <el-tab-pane label="操作日志" name="second"> <div class="sty-second" :style="{ height: windowHeight+&#39;px&#39; }"> <operationLog ref="operationLogRef" /> </div> </el-tab-pane> </div> </el-tabs> </div> </el-drawer> </template> <script setup lang="ts" name="assetInfo"> import { ref, computed,nextTick,inject } from "vue"; import { ElMessage } from &#39;element-plus&#39;; import operationLog from "./operationLog.vue"; import basicDetails from "./basicDetails.vue"; import { markStatusType } from "@/utils/dict"; import { printAssetMark, assetListInfo, deleteAssetList } from "@/api/modules/assetAllocation"; import { useHandleData } from "@/hooks/useHandleData"; import { AssetClassification } from "@/api/interface"; const windowHeight: number = window.innerHeight - 210; interface DrawerProps { title: string; row?: Partial<AssetClassification.pageList>; isView: boolean; api?: (params: any) => Promise<any>; refreshTable?: () => void; configuration?: any } const hasBtnPermission: any = inject(&#39;hasBtnPermission&#39;); const previousInfo = (row:any) => { console.log(&#39;rrr&#39;,row) } const activeName = ref(&#39;first&#39;); const drawerVisible = ref(false); const drawerProps = ref<DrawerProps>({ isView: false, title: "", row: {} }); const infos = ref({ id: "", baseInfo: {} as any, markTags: [] }); const markStatusList = ref<Array<{name: string; color: string}>>([]); const basicDetailsRef = ref(); const operationLogRef = ref(); // 计算属性 const statusStyle = computed(() => { const status = infos.value.baseInfo?.assetStatus; const colorMap = { &#39;1&#39;: &#39;#49c625&#39;, &#39;2&#39;: &#39;#ff7f00&#39;, &#39;3&#39;: &#39;#1890ff&#39; }; return { &#39;border-color&#39;: colorMap[status] || &#39;#1890ff&#39; }; }); const statusText = computed(() => { const status = infos.value.baseInfo?.assetStatus; const textMap = { &#39;1&#39;: &#39;空闲&#39;, &#39;2&#39;: &#39;在用&#39;, &#39;3&#39;: &#39;借用&#39; }; return textMap[status] || &#39;&#39;; }); const acceptParams = async (params: DrawerProps) => { infos.value = { baseInfo: {}, markTags: [], id: "" }; markStatusList.value = []; drawerProps.value = params; activeName.value = &#39;first&#39; try { type reqType = { code?: number, data?: any, msg?: string } const { code, data, msg } = await assetListInfo({ id: params.row!.id as string }) as reqType; if (code === 0) { infos.value = data as any; drawerVisible.value = true; // 处理标记状态 if (data.markTags?.length) { markStatusList.value = data.markTags .map(tag => { const status = markStatusType.find(item => item.label === tag); return status ? { name: tag, color: status.color } : null; }) .filter(Boolean); } nextTick(() => { basicDetailsRef.value?.setDrawerProps({ row: data, configuration: params.configuration }); operationLogRef.value?.setDrawerProps({ row:data }) }); } else { ElMessage.error(msg); } } catch (error) { ElMessage.error(&#39;获取资产信息失败&#39;); console.error(error); } }; // 删除资产 const deleteData = async () => { await useHandleData( deleteAssetList, { idList: [infos.value.id] }, `确认删除` ); drawerVisible.value = false; drawerProps.value.refreshTable?.(); }; // 打印标签 const openTagPrint = async () => { try { const { code, msg } = await printAssetMark({ idList: [drawerProps.value!.row!.id], type: &#39;baseInfo&#39; }); if (code === 0) { ElMessage.success(msg); drawerProps.value.refreshTable?.(); } else { ElMessage.error(msg); } } catch (error) { ElMessage.error(&#39;打印失败&#39;); console.error(error); } }; defineExpose({ acceptParams }); </script> <style lang="scss" scoped> .no-header-drawer { .el-drawer__body { padding: 0; .asset_details_header { .header_title { border-bottom: 1px solid #ebeef5; margin: 0 0 12px 0; display: flex; padding-bottom: 10px; .header_left { flex: 1; .header_zcInfo { margin-bottom: 10px; .header_zcInfo_name { color: #303133; font-size: 20px; font-weight: 600; padding-right: 15px; } .span-zc { border-radius: 10px; border: 1px solid; padding: 2px 5px; font-size: 12px; margin-right: 4px; } } .header_userInfo { color: #606266; font-size: 14px; span { margin-right: 25px; } } } .header_right { .trapezoid-isosceles { content: ""; display: block; height: 0; border-width: 0px 19px 35px; border-style: none solid solid; position: absolute; transform: rotate(44deg); right: -32px; top: 16px; width: 94px; span { line-height: 35px; text-align: center; display: block; color: #ffffff; font-size: 18px; } } } } } .asset_details_content { height: calc(100% - 115px); overflow-y: auto; } } } .no-header-drawer { .el-drawer__body { padding: 10px 20px; overflow: hidden; } header.el-drawer__header { border: none !important; padding: 0 !important; } } </style> <template> <div class="table-box"> <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :data-callback="dataCallback"> <!-- 表格 header 按钮 --> <template #tableHeader="scope"> <el-button type="primary" v-if="hasBtnPermission(&#39;asset:inventory:save&#39;)" @click="addNewData(&#39;新增资产&#39;, &#39;add&#39;, {})">新增资产</el-button> <el-button type="primary" v-if="hasBtnPermission(&#39;asset:inventory:update&#39;)" @click="batcEdit(scope.selectedListIds)">批量编辑</el-button> <el-button type="primary" v-if="hasBtnPermission(&#39;asset:inventory:delete&#39;)" @click="batchDelete(scope.selectedListIds)">批量删除</el-button> <el-dropdown style="margin-left: 10px" v-if="hasBtnPermission(&#39;asset:inventory:downloadData&#39;)" @command="batchExport"> <el-button type="primary"> 批量导出<i class="el-icon-arrow-down el-icon--right"></i> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="1">导出所选数据</el-dropdown-item> <el-dropdown-item command="2">导出全部</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> <el-button type="primary" @click="openTagPrint(scope.selectedListIds)" v-if="hasBtnPermission(&#39;asset:inventory:printMark&#39;)" style="margin-left: 10px">打印资产标签</el-button> </template> <!-- 图片 --> <template #imageUrl="{ row }"> <div class="more_imgs" v-if="row.imageUrlList && row.imageUrlList.length > 0"> <viewer :images="row.imageUrlList"> <span class="viewImage" v-for="(itemImg, index) in row.imageUrlList" :key="index"> <img v-if="itemImg" :src="itemImg" style="width: 100%; height: 100%" /> </span> </viewer> </div> </template> <template #operation="scope" v-if="hasBtnPermission(&#39;asset:inventory:update&#39;)"> <el-button type="primary" link @click="editData(&#39;编辑资产&#39;, &#39;edit&#39;, scope.row)">编辑</el-button> </template> </ProTable> <!-- 选择新增资产类型 --> <div class="new-Dialog-type" v-if="dialogFormVisible"> <el-dialog v-model="dialogFormVisible" title="选择资产类型" width="450" draggable> <el-form ref="ruleFormRef" :model="form" :rules="rules" label-width="93px"> <el-form-item label="类型" prop="type"> <el-select v-model="form.type" placeholder="请选择"> <el-option v-for="item in assetType" :key="item.value" :label="item.label" :value="item.value"></el-option> </el-select> </el-form-item> <el-form-item label="非标准资产" v-if="form.type === 2" prop="nonStandardAssetsId"> <el-select v-model="form.nonStandardAssetsId" placeholder="请选择"> <el-option v-for="item in nonstandardData" :key="item.id" :label="item.name" :value="item.id" :disabled="item.status == 0"></el-option> </el-select> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="closeDialog">取消</el-button> <el-button type="primary" @click="nextTips">下一步</el-button> </div> </template> </el-dialog> </div> <!-- 编辑,批量编辑,新增组件 --> <addAsset ref="addAssetRef" @previous-step="handlePreviousStep" /> <!-- 详情公共组件 --> <assetInfo ref="assetInfoRef"></assetInfo> </div> </template> <script setup lang="tsx" name="assetInventory"> import { ref, reactive, inject, onMounted, nextTick } from "vue"; import ProTable from "@/components/ProTable/index.vue"; import { assetListData, addAssetList, deleteAssetList, editBatchAssetList, assetListInfo, editAssetList } from "@/api/modules/assetAllocation"; import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface"; import { formatToTree, extractSelectedNodes } from "@/utils/tools"; import { assetClassificationList, getPositionList, setValueClildList, nonstandardList, getOrgSubjectList, getInstitution } from &#39;@/api/modules/public&#39;; import { getUserDepartment } from "@/api/modules/user"; import { assetListType, assetType, markStatusType } from "@/utils/dict"; import addAsset from "./mode/addAsset.vue"; import assetInfo from "./mode/assetInfo.vue"; import { useHandleData } from "@/hooks/useHandleData"; import { ElMessage, FormInstance } from "element-plus"; import { printAssetMark } from "@/api/modules/assetAllocation"; import moment from "moment"; import axios from &#39;axios&#39;; type nonstandardType = { id: string, name: string, status: number } const hasBtnPermission: any = inject(&#39;hasBtnPermission&#39;); // ProTable 实例 const proTable = ref<ProTableInstance>(); // 部门级联树数据 const cascaderTreeData = ref([]); // 子界面需要用到的设置值 const configuration = reactive({ assetCategory: [] as any[], // 资产分类 positionList: [], // 存放地点 departmentList: [] as any[], // 使用部门 sourceList: [] as any[], // 资产来源 nonstandardList: [] as nonstandardType[], // 非标准资产 unitList: [] as any[], // 计量单位 institutionalEntity: [] as any[], //机构主体 assentityList: [] as any[], // 主体 assinstitutional: [] as any[],//机构 }) const selectedIds = ref<string[]>([]); // 在组件顶部定义 // 非标准资产 const nonstandardData = ref<nonstandardType[]>([]); const assentityList = ref<any[]>([]) const assinstitutional = ref<any[]>([]) // 资产类型弹窗 const dialogFormVisible = ref(false) type formType = { type: string | number, nonStandardAssetsId: string } const form = reactive<formType>({ type: &#39;&#39;, nonStandardAssetsId: &#39;&#39; }) const rules = reactive({ type: [{ required: true, message: "请选择资产类型", trigger: ["blur", "change"] }], nonStandardAssetsId: [{ required: true, message: "请选择非标准资产", trigger: ["blur", "change"] }], }) const getTableList = (params: any) => { if (params.purchaseDate) { params.purchaseDateStart = params.purchaseDate[0] + &#39; 00:00:00&#39;; params.purchaseDateEnd = params.purchaseDate[1] + &#39; 23:59:59&#39;; delete params.purchaseDate; } if (params.maintenanceExpirationDate) { params.maintenanceExpirationDateStart = params.maintenanceExpirationDate[0] + &#39; 00:00:00&#39;; params.maintenanceExpirationDateEnd = params.maintenanceExpirationDate[1] + &#39; 23:59:59&#39;; delete params.maintenanceExpirationDate; } if (params.useUserNameData) { params.useUserNameList = params.useUserNameData.split(&#39;\n&#39;); delete params.useUserNameData } if(params.assetCodeData) { const filteredArray = params.assetCodeData.split(&#39;\n&#39;); params.assetCodeList = filteredArray.filter(item => item.trim() !== &#39;&#39;); delete params.assetCodeData } let deptNoList: any[] = []; // 部门级联数据处理 if (params.useDepartmentId && params.useDepartmentId.length > 0) { // 根据级联值获取所有选中的部门集合,如果子节点全部选中,父节点也要获取 deptNoList = extractSelectedNodes(params.useDepartmentId, cascaderTreeData.value); } if (deptNoList.length > 0) { params.useDepartmentIdList = deptNoList; } delete params.useDepartmentId; // 清除表格勾选项 if (proTable.value) { proTable.value.clearSelection(); } return assetListData(params) } const refreshTable = () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); // 清除表格勾选项 if (proTable.value) { proTable.value.clearSelection(); } }; const dataCallback = (data: any) => { const dataList = data?.dataList || []; const processedList = dataList.map(item => { try { return { ...item, imageUrlList: typeof item?.imageUrl === &#39;string&#39; ? item.imageUrl.split(&#39;,&#39;) : [], purchaseDate: item.purchaseDate ? item.purchaseDate.split(" ")[0] : &#39;&#39; }; } catch (error) { return { ...item, imageUrlList: [] }; } }); return { list: processedList, total: data?.totalCount, pageNum: data?.page, pageSize: data?.pageSize }; }; // 查询计量单位 const getUnitList = async () => { const response = await setValueClildList({ dictCode: &#39;UNIT_MEASUREMENT&#39; }); if (Array.isArray(response.data)) { configuration.unitList = response.data || []; } } // 查询机构主体 const getOrgSubjectListData = async () => { // 机构 const responseAss = await getInstitution({ id: &#39;ASS_INSTITUTIONAL&#39; }); const data = responseAss.data || []; if (Array.isArray(data) && data.length > 0) { configuration.assinstitutional = data assinstitutional.value = data } // 主体 const response = await getInstitution({ id: &#39;OFFICIAL_SEAL_ORG&#39; }); const data1 = response.data || []; if (Array.isArray(data1) && data1.length > 0) { configuration.assentityList = data1 assentityList.value = data1 } // 机构主体(二和一接口),用来把主体,机构以及部门三者关联起来,单独调用上面接口,主要是为了排序好看,无语子...... const res = await getOrgSubjectList({}, false); const data2 = res.data || []; if (Array.isArray(data) && data.length > 0) { configuration.institutionalEntity = data2 as any[]; } } const formatToDepTree = (arr, pid = 0) => { let result: any[] = []; for (let i = 0; i < arr.length; i++) { if (arr[i].pid === pid) { arr[i].label = arr[i].name let children = formatToDepTree(arr, arr[i].workOADepartmentId); if (children.length > 0) { arr[i].children = children; } result.push(arr[i]); } } return result; } // 表格配置项 const columns: ColumnProps[] = [ { type: "selection", fixed: "left", width: 50, }, { prop: "assetStatus", label: "资产状态", fixed: "left", minWidth: 100, enum: assetListType, search: { el: "select", props: { filterable: true } }, render: scope => { if (scope.row.assetStatus == &#39;1&#39;) { return ( <span style="color: #49c625">空闲</span> ); } else if (scope.row.assetStatus == &#39;2&#39;) { return ( <span style="color: #ff7f00">在用</span> ); } else if (scope.row.assetStatus == &#39;3&#39;) { return ( <span style="color: #1890ff">已处置</span> ); } } }, { prop: "markStatus", label: "资产标记", isShow: false, enum: markStatusType, search: { el: "select", props: { filterable: true } }, fieldNames: { label: "label", value: "value" } }, { prop: "markTagsName", label: "资产标记", fixed: "left", minWidth: 100, render: scope => { if (scope.row.markTags == &#39;0&#39;) { return ( <span style="color: #49c625">派发待领用</span> ); } else if (scope.row.markTags == &#39;1&#39;) { return ( <span style="color: #ff7f00">领用审批中</span> ); } else if (scope.row.markTags == &#39;2&#39;) { return ( <span style="color: #ff7f00">退还审批中</span> ); } else if (scope.row.markTags == &#39;3&#39;) { return ( <span style="color: #ff7f00">借用审批中</span> ); } else if (scope.row.markTags == &#39;4&#39;) { return ( <span style="color: #1890ff">借用</span> ); } else if (scope.row.markTags == &#39;5&#39;) { return ( <span style="color: #ff7f00">调拨审批中</span> ); } else if (scope.row.markTags == &#39;6&#39;) { return ( <span style="color: #ff7f00">维修审批中</span> ); } else if (scope.row.markTags == &#39;7&#39;) { return ( <span style="color: #ff7f00">处置审批中</span> ); } else if (scope.row.markTags == &#39;8&#39;) { return ( <span style="color: #ff0000">待处理</span> ); } else if (scope.row.markTags == &#39;9&#39;) { return ( <span style="color: #ff7f00">归还审批中</span> ); } } }, { prop: "assetCodeData", label: "资产编码", isShow: false, search: { el: "input", type: &#39;textarea&#39;, placeholder: &#39;多个编码请换行&#39; }, minWidth: 100, }, { prop: "assetCode", label: "资产编码", fixed: "left", copy: false, minWidth: 100, render: (scope) => { return ( <span style="color: #49c625" onClick={() => handleAssetCodeClick(scope.row)}> {scope.row.assetCode} </span> ); } }, { prop: "imageUrl", label: "图片", minWidth:100, copy: false }, { prop: "assetCategoryIdList", label: "资产分类", isShow:false, enum: async () => { // 获取资产分类数据,扁平数据 const { data } = await assetClassificationList({}); if (Array.isArray(data)) { const treeData = formatToTree(data); configuration.assetCategory = treeData; return { data: treeData } } return { data: [] } }, fieldNames: { label: "categoryName", value: "id" }, search: { el: "tree-select", props: { filterable: true, multiple: true, checkStrictly: true, // 允许选择父节点 nodeKey: "id", // 每个节点的唯一标识字段 props: { label: "label", value: "id", children: "children" } } }, }, { prop: "assetName", label: "资产名称",isShow:false, search: { el: "input" },minWidth:100 }, { prop: "assetName", label: "资产名称", fixed: "left",minWidth:100 }, { prop: "assetCategoryName", label: "资产分类", minWidth:100 , }, { prop: "nonStandardAssetsId", label: "资产类型", minWidth: 100, isShow: false, }, { prop: "type", label: "资产类型", minWidth: 100, enum: assetType, search: { el: "select", props: { filterable: true } }, }, { prop: "useUserName", label: "使用人" }, { prop: "useUserNameData", label: "使用人", isShow: false, search: { el: "input", type: &#39;textarea&#39;, placeholder: &#39;多个使用人请换行&#39; } }, { prop: "useOrgIdList", label: "使用机构", search: { el: "select", props: { filterable: true, multiple: true } }, minWidth: 100, enum: assinstitutional, isShow: false, fieldNames: { label: "name", value: "detailCode" }, }, { prop: "useOrgName", label: "使用机构", minWidth: 100, }, { prop: "useSubjectId", label: "使用主体", search: { el: "select" }, minWidth: 100, enum: assentityList, isShow: false, fieldNames: { label: "remarks", value: "detailCode" }, }, { prop: "useSubjectName", label: "使用主体", minWidth: 100, }, { prop: "useDepartmentId", label: "使用部门", isShow: false, enum: async () => { // 获取组织架构数据,扁平数据 const { data } = await getUserDepartment(); data.forEach(item => { item.pid = item.extMap.parentWorkOADepartmentId item.workOADepartmentId = item.value item.id = item.value item.name = item.label }) // 将组织架构数据处理成树结构 const treeData = formatToDepTree(data); configuration.departmentList = treeData; return { data: treeData } }, search: { el: "cascader", props: { props: { multiple: true } } } }, { prop: "useDepartmentName", label: "使用部门", minWidth:100 , }, { prop: "storageLocationIdList", label: "存放地点", minWidth: 100, isShow: false, enum: async () => { // 获取存放地点 const res = await getPositionList({ pageNum: 1, pageSize: 9999 }); const data = res.data as any; const deepCopy = JSON.parse(JSON.stringify(data[&#39;dataList&#39;])); configuration.positionList = deepCopy; return { data: data[&#39;dataList&#39;] }; }, fieldNames: { label: "position", value: "id" }, search: { el: "select", props: { filterable: true, multiple: true } }, }, { prop: "storageLocationName", label: "存放地点", minWidth: 100, }, { prop: "adminName", label: "管理员", search: { el: "input" } }, { prop: "affiliatedInstitutionName", label: "所属机构", minWidth: 100 }, { prop: "affiliatedInstitutionIdList", label: "所属机构", isShow: false, search: { el: "select", props: { filterable: true, multiple: true } }, minWidth: 100, enum: assinstitutional, fieldNames: { label: "name", value: "detailCode" }, }, { prop: "affiliatedSubjectName", label: "所属主体", minWidth: 100 }, { prop: "affiliatedSubjectId", label: "所属主体", isShow: false, search: { el: "select" }, minWidth: 100, enum: assentityList, fieldNames: { label: "remarks", value: "detailCode" } }, { prop: "assetSourceTypeList", label: "资产来源", isShow: false, enum: async () => { // 获取资产来源 const data = await setValueClildList({ dictCode: &#39;SOURCE_ASSETS&#39; }); configuration.sourceList = data[&#39;data&#39;] as any[]; return { data: data[&#39;data&#39;] } }, fieldNames: { label: "itemLabel", value: "itemValue" }, search: { el: "select", props: { filterable: true, multiple: true } }, }, { prop: "sourceCode", label: "资产来源", minWidth: 100, }, { prop: "brand", label: "品牌", search: { el: "input" }, }, { prop: "specificationModel", label: "规格型号", search: { el: "input" }, minWidth: 100 }, { prop: "serialNumber", label: "序列号", search: { el: "input" } }, { prop: "measurementUnit", label: "计量单位", minWidth: 100 }, { prop: "remarks", label: "备注", search: { el: "input" } }, { prop: "supplierName", label: "供应商", search: { el: "input" }, }, { prop: "inBoundNo", label: "入库单号", minWidth: 100 }, { prop: "nonStandardAssetsId", label: "非标准资产", enum: async () => { // 获取非标准资产 const data = await nonstandardList({}); if (Array.isArray(data)) { nonstandardData.value = data.filter(item => item.status == 1); configuration.nonstandardList = data } return { data: data } }, isShow: false, fieldNames: { label: "name", value: "id" }, search: { el: "select", props: { filterable: true } }, }, { prop: "purchaseDate", label: "购入日期", minWidth: 148, search: { el: "date-picker", props: { type: "daterange", valueFormat: "YYYY-MM-DD" }, }, }, { prop: "maintenanceExpirationDate", label: "维保到期日期", isShow: false, search: { el: "date-picker", props: { type: "daterange", valueFormat: "YYYY-MM-DD" }, }, }, { prop: "operation", label: "操作", fixed: "right", isShow: true, sortable: false } ] // 批量导出 const batchExport = async (command: any) => { try { const selectedIds = proTable.value?.selectedListIds || []; // 验证选择(如果command不是2,则需要选择数据) if (command != 2 && selectedIds.length === 0) { ElMessage.error({ message: `请选择要操作的数据` }); return; } const params = { idList: command === 2 ? [] : selectedIds // command=2表示导出全部 }; const response = await axios.post(&#39;/api/asset/inventory/downloadData&#39;, params, { responseType: &#39;blob&#39; } ); let filename = `资产清单_${moment().format(&#39;YYYYMMDDHHmmss&#39;)}.xlsx`;// 设置下载文件的名称 const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement(&#39;a&#39;); link.href = url; link.setAttribute(&#39;download&#39;, filename); document.body.appendChild(link); link.click(); link.remove(); // 清除表格勾选项 if (proTable.value) { proTable.value.clearSelection(); } } catch (error) { console.error("导出失败:", error); } }; // 批量删除 const batchDelete = async (ids: string[]) => { if (ids && ids.length === 0) { ElMessage.error({ message: `请选择要操作的数据` }); return } await useHandleData(deleteAssetList, { idList: ids }, `确认删除`); refreshTable() } // 批量编辑 const batcEdit = async (ids: string[]) => { if (ids && ids.length === 0) { ElMessage.error({ message: `请选择要操作的数据` }); return; } // 从表格中获取当前所有选中的行数据 const selectedRows = proTable.value?.selectedList || []; const types = selectedRows.map(row => row.type); const nonStandardAssets = selectedRows.map(row => row.nonStandardAssetsId); const uniqueTypes = [...new Set(types)]; const uniqueNoStandar = [...new Set(nonStandardAssets)]; if (uniqueTypes.length > 1) { ElMessage.warning("只能选择相同类型的资产进行批量编辑"); return; } if (uniqueNoStandar.length > 1) { ElMessage.warning("只能选择相同类型的非标准资产进行批量编辑"); return } selectedIds.value = ids; form.type = uniqueTypes.join(); form.nonStandardAssetsId = uniqueNoStandar.join(); editBatchData(&#39;批量编辑&#39;, &#39;batchEdit&#39;, {}); } // 打印标签 const openTagPrint = async (ids: string[]) => { if (ids && ids.length === 0) { ElMessage.error({ message: `请选择要操作的数据` }); return } const data = await printAssetMark({ idList: ids, type: &#39;baseInfo&#39; }); if (data.code == 0) { ElMessage.success({ message: data.msg }); refreshTable() } else { ElMessage.error({ message: data.msg }); } } const closeDialog = () => { dialogFormVisible.value = false // 清除表格勾选项 if (proTable.value) { proTable.value.clearSelection(); } } // 子组件的上一步操作 const handlePreviousStep = () => { dialogFormVisible.value = true; // 重新打开对话框 // if( Type.value == &#39;batchEdit&#39;) {} // 回显之前选择的数据(form 已在 openDrawer 时保存) nextTick(() => { ruleFormRef.value?.clearValidate(); // 清除校验状态 }); // proTable.value!.setCheckedRows(proTable.value?.selectedList); // 回显之前选择的数据 }; const Title = ref(""); const Type = ref(&#39;add&#39;) const Row = ref({}) // 新增 const addNewData = (title: string, type: string, row: any = {}) => { Title.value = title Type.value = type Row.value = row // 清空表单值 form.type = &#39;&#39;; form.nonStandardAssetsId = &#39;&#39;; // 重置表单校验状态 nextTick(() => { ruleFormRef.value?.resetFields(); }); dialogFormVisible.value = true } // 编辑 const editData = async (title: string, type: string, row: any = {}) => { const { code, data, msg } = await assetListInfo({ id: row.id }); if (code == 0) { form.type = row.type form.nonStandardAssetsId = &#39;&#39; let listData = [data] Title.value = title Type.value = type Row.value = listData openDrawer() } else { ElMessage.error(msg); } } // 批量编辑 const editBatchData = (title: string, type: string, row: any = {}) => { Title.value = title Type.value = type Row.value = row openDrawer() } // 查看详情 const assetInfoRef = ref<InstanceType<typeof addAsset> | null>(null); const handleAssetCodeClick = async (row: any) => { const params = { row: { ...row }, api: deleteAssetList, configuration: configuration, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } assetInfoRef.value?.acceptParams(params as any) } // 下一步 const nextTips = () => { ruleFormRef.value!.validate(async valid => { if (!valid) return; try { openDrawer() } catch (error) { console.log(error); } }) } // 新增/编辑 const ruleFormRef = ref<FormInstance>(); const addAssetRef = ref<InstanceType<typeof addAsset> | null>(null); const openDialog = () => { // 清空表单值 form.type = &#39;&#39;; form.nonStandardAssetsId = &#39;&#39;; // 重置表单校验状态 nextTick(() => { ruleFormRef.value?.resetFields(); }); dialogFormVisible.value = true } const openDrawer = () => { if (Type.value === &#39;add&#39;) { dialogFormVisible.value = false const params = { title: Title.value, type: Type.value, row: { ...Row.value }, form: { ...form }, configuration: configuration, isView: false, api: addAssetList, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } addAssetRef.value?.acceptParams(params) } else if (Type.value === &#39;edit&#39;) { const params = { title: Title.value, type: Type.value, // configuration: configuration, isView: false, row: {}, form: { ...form }, infoRow: { ...Row.value }, api: editAssetList, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } addAssetRef.value?.acceptParams(params) } else { dialogFormVisible.value = false const params = { title: Title.value, type: Type.value, configuration: configuration, isView: false, form: { ...form }, row: { selectedIds: selectedIds.value }, api: editBatchAssetList, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } addAssetRef.value?.acceptParams(params) } } onMounted(() => { getUnitList(); getOrgSubjectListData(); }) </script> <style lang="scss" scoped> .more_imgs { div { display: inline-flex; } .viewImage { width: 25px; height: 25px; cursor: pointer; } } /* 确保选择列可见 */ ::v-deep .el-table__fixed-left { .el-table__cell.is-hidden>* { visibility: visible !important; } .el-checkbox { display: inline-block; } } </style> operationLog孙子界面的点击handleAssetCodeClick,并且带参数,在assetInventory界面的assetInfo组件返回中能调用
09-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值