mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 14:56:06 +02:00
Upgrade to Vue3 and Element Plus (#1374)
* Update Vue devtools
* upgrade vue pinia element-plus vue-i18n, add vite
* fix: i18n
* global components
* change v-deep
* upgrade vue-lazyload
* data table
* update enlint and safe-dialog
* package.json and vite.config.js
* el-icon
* el-message
* vue 2 -> vue3 migration changes
* $pinia
* dialog
* el-popover slot
* lint
* chore
* slot
* scss
* remote state access
* misc
* jsconfig
* el-button size mini -> small
* :model-value
* ElMessageBox
* datatable
* remove v-lazyload
* template #dropdown
* mini -> small
* css
* byebye hideTooltips
* use sass-embedded
* Update SQLite, remove unneeded libraries
* Fix shift remove local avatar favorites
* Electron arm64
* arm64 support
* bye pug
* f-word vite hah
* misc
* remove safe dialog component
* Add self invite to launch dialog
* Fix errors
* Icons 1
* improve localfavorite loading performance
* improve favorites world item performance
* dialog visibility changes for Element Plus
* clear element plus error
* import performance
* revert App.vue hah
* hah
* Revert "Add self invite to launch dialog"
This reverts commit 4801cfad58.
* Toggle self invite/open in-game
* Self invite on launch dialog
* el-button icon
* el-icon
* fix user dialog tab switching logic
* fix PlayerList
* Formatting changes
* More icons
* Fix friend log table
* loading margin
* fix markdown
* fix world dialog tab switching issue
* Fixes and formatting
* fix: global i18n.t export
* fix favorites world tab not working
* Create instance, displayName
* Remove group members sort by userId
* Fix loading dialog tabs on swtich
* Star
* charts console.warn
* wip: fix charts
* wip: fix charts
* wip: charts composables
* fix favorite item tooltip warning
* Fixes and formatting
* Clean up image dialogs
* Remove unused method
* Fix platform/size border
* Fix platform/size border
* $vr
* fix friendExportDialogVisible binding
* ElMessageBox and Settings
* Login formatting
* Rename VR overlay query
* Fix image popover and userdialog badges
* Formatting
* Big buttons
* Fixes, update Cef
* Fix gameLog table nav buttons jumping around while using nav buttons
* Fix z-index
* vr overlay
* vite input add theme
* defineAsyncComponent
* ISO 639-1
* fix i18n
* clean t
* Formatting, fix calendar, rotate arrows
* Show user status when user is offline
* Fix VR overlay
* fix theme and clean up
* split InstanceActivity
* tweak
* Fix VR overlay formatting
* fix scss var
* AppDebug hahahaha
* Years
* remove reactive
* improve perf
* state hah…
* fix user rendering poblems when user object is not yet loaded
* improve perf
* Update avatar/world image uploader, licenses, remove previous images dialog (old images are now deleted)
* improve perf 1
* Suppress stray errors
* fix traveling location display issue
* Fix empty instance creator
* improve friend list refresh performance
* fix main charts
* fix chart
* Fix darkmode
* Fix avatar dialog tags
---------
Co-authored-by: pa <maplenagisa@gmail.com>
This commit is contained in:
@@ -8,7 +8,6 @@
|
|||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"arrowParens": "always",
|
"arrowParens": "always",
|
||||||
"endOfLine": "auto",
|
"endOfLine": "auto",
|
||||||
"plugins": ["@prettier/plugin-pug"],
|
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": "*.js",
|
"files": "*.js",
|
||||||
@@ -16,14 +15,6 @@
|
|||||||
"parser": "meriyah"
|
"parser": "meriyah"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"files": "*.pug",
|
|
||||||
"options": {
|
|
||||||
"pugPrintWidth": 120,
|
|
||||||
"pugAttributeSeparator": "none",
|
|
||||||
"pugBracketSameLine": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files": "*.vue",
|
"files": "*.vue",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using librsync.net;
|
|
||||||
using Microsoft.Toolkit.Uwp.Notifications;
|
using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -123,12 +122,6 @@ namespace VRCX
|
|||||||
return File.Exists(Path.Join(Program.AppDataDirectory, "update.exe"));
|
return File.Exists(Path.Join(Program.AppDataDirectory, "update.exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ExecuteAppFunction(string function, string json)
|
|
||||||
{
|
|
||||||
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
|
||||||
MainForm.Instance.Browser.ExecuteScriptAsync($"$app.{function}", json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ExecuteVrFeedFunction(string function, string json)
|
public override void ExecuteVrFeedFunction(string function, string json)
|
||||||
{
|
{
|
||||||
Program.VRCXVRInstance.ExecuteVrFeedFunction(function, json);
|
Program.VRCXVRInstance.ExecuteVrFeedFunction(function, json);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace VRCX
|
|||||||
public override void VrInit()
|
public override void VrInit()
|
||||||
{
|
{
|
||||||
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
||||||
MainForm.Instance.Browser.ExecuteScriptAsync("$app.store.vr.vrInit", "");
|
MainForm.Instance.Browser.ExecuteScriptAsync("$pinia.vr.vrInit", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ToggleSystemMonitor(bool enabled)
|
public override void ToggleSystemMonitor(bool enabled)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace VRCX
|
|||||||
|
|
||||||
// TODO: fix this throwing an exception for being called before the browser is ready. somehow it gets past the checks
|
// TODO: fix this throwing an exception for being called before the browser is ready. somehow it gets past the checks
|
||||||
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
||||||
MainForm.Instance.Browser.ExecuteScriptAsync("$app.store.game.updateIsGameRunning", isGameRunning, isSteamVRRunning, isHmdAfk);
|
MainForm.Instance.Browser.ExecuteScriptAsync("$pinia.game.updateIsGameRunning", isGameRunning, isSteamVRRunning, isHmdAfk);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsGameRunning()
|
public override bool IsGameRunning()
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System.IO;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using librsync.net;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ namespace VRCX
|
|||||||
|
|
||||||
public JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
|
public JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
Error = delegate(object _, Newtonsoft.Json.Serialization.ErrorEventArgs args)
|
Error = delegate (object _, Newtonsoft.Json.Serialization.ErrorEventArgs args)
|
||||||
{
|
{
|
||||||
args.ErrorContext.Handled = true;
|
args.ErrorContext.Handled = true;
|
||||||
}
|
}
|
||||||
@@ -43,22 +42,6 @@ namespace VRCX
|
|||||||
return (hash[3] << 8) | hash[4];
|
return (hash[3] << 8) | hash[4];
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SignFile(string blob)
|
|
||||||
{
|
|
||||||
var fileData = Convert.FromBase64String(blob);
|
|
||||||
using var sig = Librsync.ComputeSignature(new MemoryStream(fileData));
|
|
||||||
using var memoryStream = new MemoryStream();
|
|
||||||
sig.CopyTo(memoryStream);
|
|
||||||
var sigBytes = memoryStream.ToArray();
|
|
||||||
return Convert.ToBase64String(sigBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FileLength(string blob)
|
|
||||||
{
|
|
||||||
var fileData = Convert.FromBase64String(blob);
|
|
||||||
return fileData.Length.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OpenLink(string url)
|
public void OpenLink(string url)
|
||||||
{
|
{
|
||||||
if (url.StartsWith("http://") ||
|
if (url.StartsWith("http://") ||
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ namespace VRCX
|
|||||||
|
|
||||||
public abstract void RestartApplication(bool isUpgrade);
|
public abstract void RestartApplication(bool isUpgrade);
|
||||||
public abstract bool CheckForUpdateExe();
|
public abstract bool CheckForUpdateExe();
|
||||||
public abstract void ExecuteAppFunction(string function, string json);
|
|
||||||
public abstract void ExecuteVrFeedFunction(string function, string json);
|
public abstract void ExecuteVrFeedFunction(string function, string json);
|
||||||
public abstract void ExecuteVrOverlayFunction(string function, string json);
|
public abstract void ExecuteVrOverlayFunction(string function, string json);
|
||||||
public abstract void FocusWindow();
|
public abstract void FocusWindow();
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ namespace VRCX
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ExecuteAppFunction(string function, string json)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ExecuteVrFeedFunction(string function, string json)
|
public override void ExecuteVrFeedFunction(string function, string json)
|
||||||
{
|
{
|
||||||
Program.VRCXVRInstance.ExecuteVrFeedFunction(function, json);
|
Program.VRCXVRInstance.ExecuteVrFeedFunction(function, json);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
||||||
MainForm.Instance.Browser.ExecuteScriptAsync("$app.store.vrcx.dragEnterCef", file);
|
MainForm.Instance.Browser.ExecuteScriptAsync("$pinia.vrcx.dragEnterCef", file);
|
||||||
|
|
||||||
dragData.Dispose();
|
dragData.Dispose();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ namespace VRCX
|
|||||||
|
|
||||||
#if !LINUX
|
#if !LINUX
|
||||||
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame)
|
||||||
MainForm.Instance.Browser.ExecuteScriptAsync("$app.store.vrcx.ipcEvent", packet);
|
MainForm.Instance.Browser.ExecuteScriptAsync("$pinia.vrcx.ipcEvent", packet);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ namespace VRCX
|
|||||||
m_LogQueue.Enqueue(logLine);
|
m_LogQueue.Enqueue(logLine);
|
||||||
#else
|
#else
|
||||||
if (MainForm.Instance != null && MainForm.Instance.Browser != null)
|
if (MainForm.Instance != null && MainForm.Instance.Browser != null)
|
||||||
MainForm.Instance.Browser.ExecuteScriptAsync("$app.store.gameLog.addGameLogEvent", logLine);
|
MainForm.Instance.Browser.ExecuteScriptAsync("$pinia.gameLog.addGameLogEvent", logLine);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,13 +190,13 @@ namespace VRCX
|
|||||||
var dashboardHandle = 0UL;
|
var dashboardHandle = 0UL;
|
||||||
|
|
||||||
_wristOverlay = new OffScreenBrowser(
|
_wristOverlay = new OffScreenBrowser(
|
||||||
Program.LaunchDebug ? "http://localhost:9000/vr.html?1": "file://vrcx/vr.html?1",
|
Program.LaunchDebug ? "http://localhost:9000/vr.html?wrist" : "file://vrcx/vr.html?wrist",
|
||||||
512,
|
512,
|
||||||
512
|
512
|
||||||
);
|
);
|
||||||
|
|
||||||
_hmdOverlay = new OffScreenBrowser(
|
_hmdOverlay = new OffScreenBrowser(
|
||||||
Program.LaunchDebug ? "http://localhost:9000/vr.html?2": "file://vrcx/vr.html?2",
|
Program.LaunchDebug ? "http://localhost:9000/vr.html?hmd" : "file://vrcx/vr.html?hmd",
|
||||||
1024,
|
1024,
|
||||||
1024
|
1024
|
||||||
);
|
);
|
||||||
@@ -846,7 +846,7 @@ namespace VRCX
|
|||||||
if (_wristOverlay == null) return;
|
if (_wristOverlay == null) return;
|
||||||
// if (_wristOverlay.IsLoading)
|
// if (_wristOverlay.IsLoading)
|
||||||
// Restart();
|
// Restart();
|
||||||
_wristOverlay.ExecuteScriptAsync($"$app.{function}", json);
|
_wristOverlay.ExecuteScriptAsync($"$vr.{function}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ConcurrentQueue<KeyValuePair<string, string>> GetExecuteVrOverlayFunctionQueue()
|
public override ConcurrentQueue<KeyValuePair<string, string>> GetExecuteVrOverlayFunctionQueue()
|
||||||
@@ -859,7 +859,7 @@ namespace VRCX
|
|||||||
if (_hmdOverlay == null) return;
|
if (_hmdOverlay == null) return;
|
||||||
// if (_hmdOverlay.IsLoading)
|
// if (_hmdOverlay.IsLoading)
|
||||||
// Restart();
|
// Restart();
|
||||||
_hmdOverlay.ExecuteScriptAsync($"$app.{function}", json);
|
_hmdOverlay.ExecuteScriptAsync($"$vr.{function}", json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,13 +141,13 @@ namespace VRCX
|
|||||||
var overlayHandle2 = 0UL;
|
var overlayHandle2 = 0UL;
|
||||||
|
|
||||||
_wristOverlay = new OffScreenBrowserLegacy(
|
_wristOverlay = new OffScreenBrowserLegacy(
|
||||||
"file://vrcx/vr.html?1",
|
Program.LaunchDebug ? "http://localhost:9000/vr.html?wrist" : "file://vrcx/vr.html?wrist",
|
||||||
512,
|
512,
|
||||||
512
|
512
|
||||||
);
|
);
|
||||||
|
|
||||||
_hmdOverlay = new OffScreenBrowserLegacy(
|
_hmdOverlay = new OffScreenBrowserLegacy(
|
||||||
"file://vrcx/vr.html?2",
|
Program.LaunchDebug ? "http://localhost:9000/vr.html?hmd" : "file://vrcx/vr.html?hmd",
|
||||||
1024,
|
1024,
|
||||||
1024
|
1024
|
||||||
);
|
);
|
||||||
@@ -774,7 +774,7 @@ namespace VRCX
|
|||||||
if (_wristOverlay == null) return;
|
if (_wristOverlay == null) return;
|
||||||
if (_wristOverlay.IsLoading)
|
if (_wristOverlay.IsLoading)
|
||||||
Restart();
|
Restart();
|
||||||
_wristOverlay.ExecuteScriptAsync($"$app.{function}", json);
|
_wristOverlay.ExecuteScriptAsync($"$vr.{function}", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ConcurrentQueue<KeyValuePair<string, string>> GetExecuteVrOverlayFunctionQueue()
|
public override ConcurrentQueue<KeyValuePair<string, string>> GetExecuteVrOverlayFunctionQueue()
|
||||||
@@ -787,7 +787,7 @@ namespace VRCX
|
|||||||
if (_hmdOverlay == null) return;
|
if (_hmdOverlay == null) return;
|
||||||
if (_hmdOverlay.IsLoading)
|
if (_hmdOverlay.IsLoading)
|
||||||
Restart();
|
Restart();
|
||||||
_hmdOverlay.ExecuteScriptAsync($"$app.{function}", json);
|
_hmdOverlay.ExecuteScriptAsync($"$vr.{function}", json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ namespace VRCX
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_browser1 = new ChromiumWebBrowser(
|
_browser1 = new ChromiumWebBrowser(
|
||||||
Path.Join(Program.BaseDirectory, "html/vr.html?1")
|
Path.Join(Program.BaseDirectory, "html/vr.html?wrist")
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
DragHandler = new CefNoopDragHandler(),
|
DragHandler = new CefNoopDragHandler(),
|
||||||
@@ -35,7 +35,7 @@ namespace VRCX
|
|||||||
};
|
};
|
||||||
|
|
||||||
_browser2 = new ChromiumWebBrowser(
|
_browser2 = new ChromiumWebBrowser(
|
||||||
Path.Join(Program.BaseDirectory, "html/vr.html?2")
|
Path.Join(Program.BaseDirectory, "html/vr.html?hmd")
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
DragHandler = new CefNoopDragHandler(),
|
DragHandler = new CefNoopDragHandler(),
|
||||||
|
|||||||
+2
-10
@@ -61,14 +61,6 @@
|
|||||||
<EmbeddedResource Remove="DBMerger\**" />
|
<EmbeddedResource Remove="DBMerger\**" />
|
||||||
<None Remove="DBMerger\**" />
|
<None Remove="DBMerger\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Blake2Sharp">
|
|
||||||
<HintPath>libs\Blake2Sharp.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="librsync.net">
|
|
||||||
<HintPath>libs\librsync.net.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.manifest" />
|
<None Include="app.manifest" />
|
||||||
<None Include="..\Version">
|
<None Include="..\Version">
|
||||||
@@ -88,8 +80,8 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CefSharp.OffScreen.NETCore" Version="138.0.340" />
|
<PackageReference Include="CefSharp.OffScreen.NETCore" Version="139.0.280" />
|
||||||
<PackageReference Include="CefSharp.WinForms.NETCore" Version="138.0.340" />
|
<PackageReference Include="CefSharp.WinForms.NETCore" Version="139.0.280" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
|
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
|||||||
@@ -49,14 +49,6 @@
|
|||||||
<ApplicationIcon>..\VRCX.ico</ApplicationIcon>
|
<ApplicationIcon>..\VRCX.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Blake2Sharp">
|
|
||||||
<HintPath>libs\Blake2Sharp.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="librsync.net">
|
|
||||||
<HintPath>libs\librsync.net.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.manifest" />
|
<None Include="app.manifest" />
|
||||||
<None Include="..\Version">
|
<None Include="..\Version">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<BaseIntermediateOutputPath>obj1\</BaseIntermediateOutputPath>
|
<BaseIntermediateOutputPath>obj1\</BaseIntermediateOutputPath>
|
||||||
<OutputPath>..\build\Electron\</OutputPath>
|
<OutputPath>..\build\Electron\</OutputPath>
|
||||||
@@ -54,14 +54,6 @@
|
|||||||
<ApplicationIcon>..\VRCX.ico</ApplicationIcon>
|
<ApplicationIcon>..\VRCX.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Blake2Sharp">
|
|
||||||
<HintPath>libs\Blake2Sharp.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="librsync.net">
|
|
||||||
<HintPath>libs\librsync.net.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.manifest" />
|
<None Include="app.manifest" />
|
||||||
<None Include="..\Version">
|
<None Include="..\Version">
|
||||||
|
|||||||
Binary file not shown.
@@ -1,11 +1,3 @@
|
|||||||
### librsync.net.dll
|
|
||||||
- [https://github.com/braddodson/librsync.net](https://github.com/braddodson/librsync.net)
|
|
||||||
|
|
||||||
### Blake2Sharp.dll
|
|
||||||
- [https://github.com/BLAKE2/BLAKE2/tree/master/csharp](https://github.com/BLAKE2/BLAKE2/tree/master/csharp)
|
|
||||||
|
|
||||||
### openvr_api.dll
|
### openvr_api.dll
|
||||||
- [https://github.com/ValveSoftware/openvr](https://github.com/ValveSoftware/openvr)
|
|
||||||
|
|
||||||
### sqlite3.dll
|
- [https://github.com/ValveSoftware/openvr](https://github.com/ValveSoftware/openvr)
|
||||||
- [https://www.sqlite.org/index.html](https://www.sqlite.org/index.html)
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
+1
-1
@@ -57,7 +57,7 @@ export default defineConfig([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pluginVue.configs['flat/vue2-essential'],
|
pluginVue.configs['flat/essential'],
|
||||||
{
|
{
|
||||||
rules: {
|
rules: {
|
||||||
'no-unused-vars': 'warn',
|
'no-unused-vars': 'warn',
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
<meta
|
||||||
<meta name="referrer" content="no-referrer">
|
name="viewport"
|
||||||
<meta http-equiv="Cache-Control" content="no-cache">
|
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||||
|
/>
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache" />
|
||||||
|
|
||||||
<title>VRCX</title>
|
<title>VRCX</title>
|
||||||
|
|
||||||
@@ -15,5 +18,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+2
-1
@@ -10,7 +10,8 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"lib": ["esnext", "dom", "dom.iterable"]
|
"lib": ["esnext", "dom", "dom.iterable"],
|
||||||
|
"types": ["vite/client", "element-plus/global", "node"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "src-electron/**/*"],
|
"include": ["src/**/*", "src-electron/**/*"],
|
||||||
"exclude": ["node_modules", "build"]
|
"exclude": ["node_modules", "build"]
|
||||||
|
|||||||
Generated
+2032
-9693
File diff suppressed because it is too large
Load Diff
+23
-38
@@ -4,14 +4,14 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"main": "src-electron/main.js",
|
"main": "src-electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env PLATFORM=windows webpack serve --config webpack.config.js --mode development",
|
"dev": "cross-env PLATFORM=windows vite serve src",
|
||||||
"dev:test": "concurrently \"npm run dev\" \"jest --watchAll\"",
|
"dev:test": "concurrently \"npm run dev\" \"jest --watchAll\"",
|
||||||
"watch": "cross-env PLATFORM=windows webpack --config webpack.config.js --mode development --watch",
|
"watch": "cross-env PLATFORM=windows webpack --config webpack.config.js --mode development --watch",
|
||||||
"watch-linux": "cross-env PLATFORM=linux webpack --config webpack.config.js --mode development --watch",
|
"watch-linux": "cross-env PLATFORM=linux webpack --config webpack.config.js --mode development --watch",
|
||||||
"localization": "node ./src/localization/localizationHelperCLI.js",
|
"localization": "node ./src/localization/localizationHelperCLI.js",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"prod": "cross-env PLATFORM=windows webpack --config webpack.config.js --mode production",
|
"prod": "cross-env PLATFORM=windows vite build src",
|
||||||
"prod-linux": "cross-env PLATFORM=linux webpack --config webpack.config.js --mode production",
|
"prod-linux": "cross-env PLATFORM=linux webpack --config webpack.config.js --mode production",
|
||||||
"build-electron": "node ./src-electron/download-dotnet-runtime.js --arch=x64 && node ./src-electron/patch-package-version.js && electron-builder --x64 --publish never",
|
"build-electron": "node ./src-electron/download-dotnet-runtime.js --arch=x64 && node ./src-electron/patch-package-version.js && electron-builder --x64 --publish never",
|
||||||
"build-electron-arm64": "node ./src-electron/download-dotnet-runtime.js --arch=arm64 && node ./src-electron/patch-package-version.js && electron-builder --arm64 --publish never",
|
"build-electron-arm64": "node ./src-electron/download-dotnet-runtime.js --arch=arm64 && node ./src-electron/patch-package-version.js && electron-builder --arm64 --publish never",
|
||||||
@@ -34,54 +34,39 @@
|
|||||||
"homepage": "https://github.com/vrcx-team/VRCX#readme",
|
"homepage": "https://github.com/vrcx-team/VRCX#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron/rebuild": "^4.0.1",
|
"@electron/rebuild": "^4.0.1",
|
||||||
"@eslint/js": "^9.33.0",
|
"@eslint/js": "^9.35.0",
|
||||||
"@fontsource/noto-sans-jp": "^5.2.6",
|
"@fontsource/noto-sans-jp": "^5.2.7",
|
||||||
"@fontsource/noto-sans-kr": "^5.2.6",
|
"@fontsource/noto-sans-kr": "^5.2.7",
|
||||||
"@fontsource/noto-sans-sc": "^5.2.6",
|
"@fontsource/noto-sans-sc": "^5.2.6",
|
||||||
"@fontsource/noto-sans-tc": "^5.2.6",
|
"@fontsource/noto-sans-tc": "^5.2.7",
|
||||||
"@prettier/plugin-pug": "^3.4.2",
|
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.1",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"concurrently": "^9.2.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"copy-webpack-plugin": "^13.0.1",
|
"concurrently": "^9.2.1",
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.0.0",
|
||||||
"css-loader": "^7.1.2",
|
"dayjs": "^1.11.18",
|
||||||
"dayjs": "^1.11.13",
|
|
||||||
"echarts": "^6.0.0",
|
"echarts": "^6.0.0",
|
||||||
"electron": "^37.3.0",
|
"electron": "^37.4.0",
|
||||||
"electron-builder": "^26.0.12",
|
"electron-builder": "^26.0.12",
|
||||||
"element-ui": "^2.15.14",
|
"element-plus": "^2.11.2",
|
||||||
"esbuild-jest": "^0.5.0",
|
"esbuild-jest": "^0.4.0",
|
||||||
"esbuild-loader": "^4.3.0",
|
"eslint": "^9.35.0",
|
||||||
"eslint": "^9.33.0",
|
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-vue": "^9.33.0",
|
"eslint-plugin-vue": "^9.33.0",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
"html-webpack-plugin": "^5.6.4",
|
"jest": "^30.1.3",
|
||||||
"jest": "^30.0.5",
|
|
||||||
"mini-css-extract-plugin": "^2.9.4",
|
|
||||||
"noty": "^3.2.0-beta-deprecated",
|
"noty": "^3.2.0-beta-deprecated",
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^3.0.3",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"pug": "^3.0.3",
|
|
||||||
"pug-plain-loader": "^1.1.0",
|
|
||||||
"raw-loader": "^4.0.2",
|
|
||||||
"remixicon": "^4.6.0",
|
"remixicon": "^4.6.0",
|
||||||
"sass": "^1.90.0",
|
"sass-embedded": "^1.92.1",
|
||||||
"sass-loader": "^16.0.5",
|
"vite": "^7.1.4",
|
||||||
"vue": "^2.7.16",
|
"vue": "^3.5.21",
|
||||||
"vue-data-tables": "^3.4.5",
|
"vue-i18n": "^11.1.12",
|
||||||
"vue-demi": "^0.14.10",
|
"vue-marquee-text-component": "^2.0.1",
|
||||||
"vue-i18n": "^8.28.2",
|
"vue-showdown": "^4.2.0",
|
||||||
"vue-i18n-bridge": "^9.14.1",
|
|
||||||
"vue-lazyload": "^1.3.5",
|
|
||||||
"vue-loader": "^15.11.1",
|
|
||||||
"vue-markdown": "^2.2.4",
|
|
||||||
"vue-marquee-text-component": "^1.2.0",
|
|
||||||
"webpack": "^5.101.2",
|
|
||||||
"webpack-cli": "^6.0.1",
|
|
||||||
"webpack-dev-server": "^5.2.2",
|
|
||||||
"worker-timers": "^8.0.24",
|
"worker-timers": "^8.0.24",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ const gotTheLock = app.requestSingleInstanceLock();
|
|||||||
const strip_vrcx_prefix_regex = new RegExp('^' + VRCX_URI_PREFIX + '://');
|
const strip_vrcx_prefix_regex = new RegExp('^' + VRCX_URI_PREFIX + '://');
|
||||||
|
|
||||||
if (!gotTheLock) {
|
if (!gotTheLock) {
|
||||||
|
console.log('Another instance is already running. Exiting.');
|
||||||
app.quit();
|
app.quit();
|
||||||
} else {
|
} else {
|
||||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||||
@@ -427,7 +428,7 @@ function createWristOverlayWindowOffscreen() {
|
|||||||
wristOverlayWindow.webContents.setFrameRate(2);
|
wristOverlayWindow.webContents.setFrameRate(2);
|
||||||
|
|
||||||
const indexPath = path.join(rootDir, 'build/html/vr.html');
|
const indexPath = path.join(rootDir, 'build/html/vr.html');
|
||||||
const fileUrl = `file://${indexPath}?1`;
|
const fileUrl = `file://${indexPath}?wrist`;
|
||||||
wristOverlayWindow.loadURL(fileUrl, { userAgent: version });
|
wristOverlayWindow.loadURL(fileUrl, { userAgent: version });
|
||||||
|
|
||||||
// Use paint event for offscreen rendering
|
// Use paint event for offscreen rendering
|
||||||
@@ -490,7 +491,7 @@ function createHmdOverlayWindowOffscreen() {
|
|||||||
hmdOverlayWindow.webContents.setFrameRate(48);
|
hmdOverlayWindow.webContents.setFrameRate(48);
|
||||||
|
|
||||||
const indexPath = path.join(rootDir, 'build/html/vr.html');
|
const indexPath = path.join(rootDir, 'build/html/vr.html');
|
||||||
const fileUrl = `file://${indexPath}?2`;
|
const fileUrl = `file://${indexPath}?hmd`;
|
||||||
hmdOverlayWindow.loadURL(fileUrl, { userAgent: version });
|
hmdOverlayWindow.loadURL(fileUrl, { userAgent: version });
|
||||||
|
|
||||||
// Use paint event for offscreen rendering
|
// Use paint event for offscreen rendering
|
||||||
|
|||||||
+132
-54
@@ -1,11 +1,99 @@
|
|||||||
<script>
|
<template>
|
||||||
// @ts-ignore
|
<!DOCTYPE html>
|
||||||
import template from './app.pug';
|
<el-config-provider :locale="currentLocale">
|
||||||
import Vue, { onMounted } from 'vue';
|
<div
|
||||||
|
id="x-app"
|
||||||
|
class="x-app"
|
||||||
|
ondragenter="event.preventDefault()"
|
||||||
|
ondragover="event.preventDefault()"
|
||||||
|
ondrop="event.preventDefault()">
|
||||||
|
<!-- ### Login ### -->
|
||||||
|
<Login v-if="!watchState.isLoggedIn"></Login>
|
||||||
|
|
||||||
|
<VRCXUpdateDialog></VRCXUpdateDialog>
|
||||||
|
|
||||||
|
<template v-if="watchState.isLoggedIn">
|
||||||
|
<!-- ### Menu ### -->
|
||||||
|
<NavMenu></NavMenu>
|
||||||
|
|
||||||
|
<!-- ### Sidebar ### -->
|
||||||
|
<Sidebar></Sidebar>
|
||||||
|
|
||||||
|
<!-- ### Tabs ### -->
|
||||||
|
<Feed></Feed>
|
||||||
|
|
||||||
|
<GameLog></GameLog>
|
||||||
|
|
||||||
|
<PlayerList></PlayerList>
|
||||||
|
|
||||||
|
<Search></Search>
|
||||||
|
|
||||||
|
<Favorites></Favorites>
|
||||||
|
|
||||||
|
<FriendLog></FriendLog>
|
||||||
|
|
||||||
|
<Moderation></Moderation>
|
||||||
|
|
||||||
|
<Notification></Notification>
|
||||||
|
|
||||||
|
<FriendList></FriendList>
|
||||||
|
|
||||||
|
<Charts></Charts>
|
||||||
|
|
||||||
|
<Tools></Tools>
|
||||||
|
|
||||||
|
<Profile></Profile>
|
||||||
|
|
||||||
|
<Settings></Settings>
|
||||||
|
|
||||||
|
<!-- ## Dialogs ## -->
|
||||||
|
<UserDialog></UserDialog>
|
||||||
|
|
||||||
|
<WorldDialog></WorldDialog>
|
||||||
|
|
||||||
|
<AvatarDialog></AvatarDialog>
|
||||||
|
|
||||||
|
<GroupDialog></GroupDialog>
|
||||||
|
|
||||||
|
<GroupMemberModerationDialog></GroupMemberModerationDialog>
|
||||||
|
|
||||||
|
<FullscreenImageDialog></FullscreenImageDialog>
|
||||||
|
|
||||||
|
<PreviousInstancesInfoDialog></PreviousInstancesInfoDialog>
|
||||||
|
|
||||||
|
<LaunchDialog></LaunchDialog>
|
||||||
|
|
||||||
|
<LaunchOptionsDialog></LaunchOptionsDialog>
|
||||||
|
|
||||||
|
<FriendImportDialog></FriendImportDialog>
|
||||||
|
|
||||||
|
<WorldImportDialog></WorldImportDialog>
|
||||||
|
|
||||||
|
<AvatarImportDialog></AvatarImportDialog>
|
||||||
|
|
||||||
|
<ChooseFavoriteGroupDialog></ChooseFavoriteGroupDialog>
|
||||||
|
|
||||||
|
<EditInviteMessageDialog></EditInviteMessageDialog>
|
||||||
|
|
||||||
|
<VRChatConfigDialog></VRChatConfigDialog>
|
||||||
|
|
||||||
|
<PrimaryPasswordDialog></PrimaryPasswordDialog>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-config-provider>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { createGlobalStores } from './stores';
|
import { createGlobalStores } from './stores';
|
||||||
import { watchState } from './service/watchState';
|
import { watchState } from './service/watchState';
|
||||||
|
|
||||||
|
import '@fontsource/noto-sans-kr';
|
||||||
|
import '@fontsource/noto-sans-jp';
|
||||||
|
import '@fontsource/noto-sans-sc';
|
||||||
|
import '@fontsource/noto-sans-tc';
|
||||||
|
|
||||||
import Login from './views/Login/Login.vue';
|
import Login from './views/Login/Login.vue';
|
||||||
import NavMenu from './components/NavMenu.vue';
|
import NavMenu from './components/NavMenu.vue';
|
||||||
import Sidebar from './views/Sidebar/Sidebar.vue';
|
import Sidebar from './views/Sidebar/Sidebar.vue';
|
||||||
@@ -28,7 +116,6 @@
|
|||||||
import AvatarDialog from './components/dialogs/AvatarDialog/AvatarDialog.vue';
|
import AvatarDialog from './components/dialogs/AvatarDialog/AvatarDialog.vue';
|
||||||
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
|
import GroupDialog from './components/dialogs/GroupDialog/GroupDialog.vue';
|
||||||
import GroupMemberModerationDialog from './components/dialogs/GroupDialog/GroupMemberModerationDialog.vue';
|
import GroupMemberModerationDialog from './components/dialogs/GroupDialog/GroupMemberModerationDialog.vue';
|
||||||
import GalleryDialog from './components/dialogs/GalleryDialog.vue';
|
|
||||||
import FullscreenImageDialog from './components/dialogs/FullscreenImageDialog.vue';
|
import FullscreenImageDialog from './components/dialogs/FullscreenImageDialog.vue';
|
||||||
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
|
import PreviousInstancesInfoDialog from './components/dialogs/PreviousInstancesDialog/PreviousInstancesInfoDialog.vue';
|
||||||
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
|
import LaunchDialog from './components/dialogs/LaunchDialog.vue';
|
||||||
@@ -42,51 +129,49 @@
|
|||||||
import VRChatConfigDialog from './views/Settings/dialogs/VRChatConfigDialog.vue';
|
import VRChatConfigDialog from './views/Settings/dialogs/VRChatConfigDialog.vue';
|
||||||
import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue';
|
import PrimaryPasswordDialog from './views/Settings/dialogs/PrimaryPasswordDialog.vue';
|
||||||
|
|
||||||
import { utils } from './shared/utils/_utils';
|
import en from 'element-plus/es/locale/lang/en';
|
||||||
|
import es from 'element-plus/es/locale/lang/es';
|
||||||
|
import fr from 'element-plus/es/locale/lang/fr';
|
||||||
|
import hu from 'element-plus/es/locale/lang/hu';
|
||||||
|
import ja from 'element-plus/es/locale/lang/ja';
|
||||||
|
import ko from 'element-plus/es/locale/lang/ko';
|
||||||
|
import pl from 'element-plus/es/locale/lang/pl';
|
||||||
|
import pt from 'element-plus/es/locale/lang/pt';
|
||||||
|
import cs from 'element-plus/es/locale/lang/cs';
|
||||||
|
import ru from 'element-plus/es/locale/lang/ru';
|
||||||
|
import vi from 'element-plus/es/locale/lang/vi';
|
||||||
|
import zhCN from 'element-plus/es/locale/lang/zh-CN';
|
||||||
|
import zhTW from 'element-plus/es/locale/lang/zh-TW';
|
||||||
|
import th from 'element-plus/es/locale/lang/th';
|
||||||
|
|
||||||
export default {
|
const langMap = {
|
||||||
template,
|
en: en,
|
||||||
components: {
|
es: es,
|
||||||
Login,
|
fr: fr,
|
||||||
NavMenu,
|
hu: hu,
|
||||||
Sidebar,
|
ja: ja,
|
||||||
Feed,
|
ko: ko,
|
||||||
GameLog,
|
pl: pl,
|
||||||
PlayerList,
|
pt: pt,
|
||||||
Search,
|
cs: cs,
|
||||||
Favorites,
|
ru: ru,
|
||||||
FriendLog,
|
vi: vi,
|
||||||
Moderation,
|
'zh-CN': zhCN,
|
||||||
Notification,
|
'zh-TW': zhTW,
|
||||||
FriendList,
|
th: th
|
||||||
Charts,
|
};
|
||||||
Tools,
|
|
||||||
Profile,
|
const currentLocale = computed(() => {
|
||||||
Settings,
|
return langMap[locale.value] || en;
|
||||||
|
});
|
||||||
|
|
||||||
|
const { locale } = useI18n();
|
||||||
|
|
||||||
UserDialog,
|
|
||||||
WorldDialog,
|
|
||||||
AvatarDialog,
|
|
||||||
GroupDialog,
|
|
||||||
GroupMemberModerationDialog,
|
|
||||||
GalleryDialog,
|
|
||||||
FullscreenImageDialog,
|
|
||||||
PreviousInstancesInfoDialog,
|
|
||||||
LaunchDialog,
|
|
||||||
LaunchOptionsDialog,
|
|
||||||
FriendImportDialog,
|
|
||||||
WorldImportDialog,
|
|
||||||
AvatarImportDialog,
|
|
||||||
ChooseFavoriteGroupDialog,
|
|
||||||
EditInviteMessageDialog,
|
|
||||||
VRCXUpdateDialog,
|
|
||||||
VRChatConfigDialog,
|
|
||||||
PrimaryPasswordDialog
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const store = createGlobalStores();
|
const store = createGlobalStores();
|
||||||
Vue.prototype.store = store;
|
|
||||||
Vue.prototype.utils = utils;
|
if (typeof window !== 'undefined') {
|
||||||
|
window.$pinia = store;
|
||||||
|
}
|
||||||
|
|
||||||
store.updateLoop.updateLoop();
|
store.updateLoop.updateLoop();
|
||||||
|
|
||||||
@@ -97,11 +182,4 @@
|
|||||||
store.vrcx.checkAutoBackupRestoreVrcRegistry();
|
store.vrcx.checkAutoBackupRestoreVrcRegistry();
|
||||||
store.game.checkVRChatDebugLogging();
|
store.game.checkVRChatDebugLogging();
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
store,
|
|
||||||
watchState
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
-681
@@ -1,681 +0,0 @@
|
|||||||
<script>
|
|
||||||
import '@fontsource/noto-sans-kr';
|
|
||||||
import '@fontsource/noto-sans-jp';
|
|
||||||
import '@fontsource/noto-sans-sc';
|
|
||||||
import '@fontsource/noto-sans-tc';
|
|
||||||
// @ts-ignore
|
|
||||||
import pugTemplate from './vr.pug';
|
|
||||||
import Vue, { onMounted, reactive, toRefs, nextTick } from 'vue';
|
|
||||||
import Noty from 'noty';
|
|
||||||
import * as workerTimers from 'worker-timers';
|
|
||||||
import MarqueeText from 'vue-marquee-text-component';
|
|
||||||
import VrLocation from './components/VrLocation.vue';
|
|
||||||
import { displayLocation } from './shared/utils/location';
|
|
||||||
import { escapeTag, escapeTagRecursive } from './shared/utils/base/string';
|
|
||||||
import { removeFromArray } from './shared/utils/base/array';
|
|
||||||
import { timeToText } from './shared/utils/base/format';
|
|
||||||
|
|
||||||
import { i18n, t as $t } from './plugin/i18n.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'vr',
|
|
||||||
template: pugTemplate,
|
|
||||||
components: {
|
|
||||||
'marquee-text': MarqueeText,
|
|
||||||
location: VrLocation
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const vrState = reactive({
|
|
||||||
appType: location.href.substr(-1),
|
|
||||||
appLanguage: 'en',
|
|
||||||
currentCulture: 'en-nz',
|
|
||||||
isRunningUnderWine: false,
|
|
||||||
currentTime: new Date().toJSON(),
|
|
||||||
cpuUsageEnabled: false,
|
|
||||||
cpuUsage: '0',
|
|
||||||
pcUptimeEnabled: false,
|
|
||||||
pcUptime: '',
|
|
||||||
customInfo: '',
|
|
||||||
config: {},
|
|
||||||
onlineFriendCount: 0,
|
|
||||||
nowPlaying: {
|
|
||||||
url: '',
|
|
||||||
name: '',
|
|
||||||
length: 0,
|
|
||||||
startTime: 0,
|
|
||||||
elapsed: 0,
|
|
||||||
percentage: 0,
|
|
||||||
remainingText: '',
|
|
||||||
playing: false
|
|
||||||
},
|
|
||||||
lastLocation: {
|
|
||||||
date: 0,
|
|
||||||
location: '',
|
|
||||||
name: '',
|
|
||||||
playerList: [],
|
|
||||||
friendList: [],
|
|
||||||
progressPie: false,
|
|
||||||
onlineFor: 0
|
|
||||||
},
|
|
||||||
lastLocationTimer: '',
|
|
||||||
onlineForTimer: '',
|
|
||||||
wristFeed: [],
|
|
||||||
devices: [],
|
|
||||||
deviceCount: 0,
|
|
||||||
notificationOpacity: 100,
|
|
||||||
hudFeed: [],
|
|
||||||
hudTimeout: [],
|
|
||||||
cleanHudFeedLoopStatus: false
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
if (LINUX) {
|
|
||||||
updateVrElectronLoop();
|
|
||||||
}
|
|
||||||
if (vrState.appType === '1') {
|
|
||||||
refreshCustomScript();
|
|
||||||
updateStatsLoop();
|
|
||||||
}
|
|
||||||
setDatetimeFormat();
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
window.$app.configUpdate = configUpdate;
|
|
||||||
window.$app.updateOnlineFriendCount = updateOnlineFriendCount;
|
|
||||||
window.$app.nowPlayingUpdate = nowPlayingUpdate;
|
|
||||||
window.$app.lastLocationUpdate = lastLocationUpdate;
|
|
||||||
window.$app.wristFeedUpdate = wristFeedUpdate;
|
|
||||||
window.$app.refreshCustomScript = refreshCustomScript;
|
|
||||||
window.$app.playNoty = playNoty;
|
|
||||||
window.$app.statusClass = statusClass;
|
|
||||||
window.$app.notyClear = notyClear;
|
|
||||||
window.$app.addEntryHudFeed = addEntryHudFeed;
|
|
||||||
window.$app.updateHudFeedTag = updateHudFeedTag;
|
|
||||||
window.$app.updateHudTimeout = updateHudTimeout;
|
|
||||||
window.$app.setDatetimeFormat = setDatetimeFormat;
|
|
||||||
window.$app.setAppLanguage = setAppLanguage;
|
|
||||||
window.$app.trackingResultToClass = trackingResultToClass;
|
|
||||||
window.$app.updateFeedLength = updateFeedLength;
|
|
||||||
window.$app.updateStatsLoop = updateStatsLoop;
|
|
||||||
window.$app.updateVrElectronLoop = updateVrElectronLoop;
|
|
||||||
window.$app.cleanHudFeedLoop = cleanHudFeedLoop;
|
|
||||||
window.$app.cleanHudFeed = cleanHudFeed;
|
|
||||||
|
|
||||||
window.$app.vrState = vrState;
|
|
||||||
|
|
||||||
AppApiVr.VrInit();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function configUpdate(json) {
|
|
||||||
vrState.config = JSON.parse(json);
|
|
||||||
vrState.hudFeed = [];
|
|
||||||
vrState.hudTimeout = [];
|
|
||||||
setDatetimeFormat();
|
|
||||||
setAppLanguage(vrState.config.appLanguage);
|
|
||||||
updateFeedLength();
|
|
||||||
if (
|
|
||||||
vrState.config.vrOverlayCpuUsage !== vrState.cpuUsageEnabled ||
|
|
||||||
vrState.config.pcUptimeOnFeed !== vrState.pcUptimeEnabled
|
|
||||||
) {
|
|
||||||
vrState.cpuUsageEnabled = vrState.config.vrOverlayCpuUsage;
|
|
||||||
vrState.pcUptimeEnabled = vrState.config.pcUptimeOnFeed;
|
|
||||||
AppApiVr.ToggleSystemMonitor(vrState.cpuUsageEnabled || vrState.pcUptimeEnabled);
|
|
||||||
}
|
|
||||||
if (vrState.config.notificationOpacity !== vrState.notificationOpacity) {
|
|
||||||
vrState.notificationOpacity = vrState.config.notificationOpacity;
|
|
||||||
setNotyOpacity(vrState.notificationOpacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateOnlineFriendCount(count) {
|
|
||||||
vrState.onlineFriendCount = parseInt(count, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
function nowPlayingUpdate(json) {
|
|
||||||
vrState.nowPlaying = JSON.parse(json);
|
|
||||||
if (vrState.appType === '2') {
|
|
||||||
const circle = /** @type {SVGCircleElement} */ (
|
|
||||||
document.querySelector('.np-progress-circle-stroke')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (vrState.lastLocation.progressPie && vrState.nowPlaying.percentage !== 0) {
|
|
||||||
circle.style.opacity = (0.5).toString();
|
|
||||||
const circumference = circle.getTotalLength();
|
|
||||||
circle.style.strokeDashoffset = (
|
|
||||||
circumference -
|
|
||||||
(vrState.nowPlaying.percentage / 100) * circumference
|
|
||||||
).toString();
|
|
||||||
} else {
|
|
||||||
circle.style.opacity = '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateFeedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
function lastLocationUpdate(json) {
|
|
||||||
vrState.lastLocation = JSON.parse(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wristFeedUpdate(json) {
|
|
||||||
vrState.wristFeed = JSON.parse(json);
|
|
||||||
updateFeedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFeedLength() {
|
|
||||||
if (vrState.appType === '2' || vrState.wristFeed.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let length = 16;
|
|
||||||
if (!vrState.config.hideDevicesFromFeed) {
|
|
||||||
length -= 2;
|
|
||||||
if (vrState.deviceCount > 8) {
|
|
||||||
length -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (vrState.nowPlaying.playing) {
|
|
||||||
length -= 1;
|
|
||||||
}
|
|
||||||
if (length < vrState.wristFeed.length) {
|
|
||||||
vrState.wristFeed.length = length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshCustomScript() {
|
|
||||||
if (document.contains(document.getElementById('vr-custom-script'))) {
|
|
||||||
document.getElementById('vr-custom-script').remove();
|
|
||||||
}
|
|
||||||
const customScript = await AppApiVr.CustomVrScript();
|
|
||||||
if (customScript) {
|
|
||||||
const head = document.head;
|
|
||||||
const $vrCustomScript = document.createElement('script');
|
|
||||||
$vrCustomScript.setAttribute('id', 'vr-custom-script');
|
|
||||||
$vrCustomScript.type = 'text/javascript';
|
|
||||||
$vrCustomScript.textContent = customScript;
|
|
||||||
head.appendChild($vrCustomScript);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setNotyOpacity(value) {
|
|
||||||
const opacity = (value / 100).toFixed(2);
|
|
||||||
let element = document.getElementById('noty-opacity');
|
|
||||||
if (!element) {
|
|
||||||
document.body.insertAdjacentHTML(
|
|
||||||
'beforeend',
|
|
||||||
`<style id="noty-opacity">.noty_layout { opacity: ${opacity}; }</style>`
|
|
||||||
);
|
|
||||||
element = document.getElementById('noty-opacity');
|
|
||||||
}
|
|
||||||
element.innerHTML = `.noty_layout { opacity: ${opacity}; }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateStatsLoop() {
|
|
||||||
try {
|
|
||||||
vrState.currentTime = new Date()
|
|
||||||
.toLocaleDateString(vrState.currentCulture, {
|
|
||||||
month: '2-digit',
|
|
||||||
day: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: 'numeric',
|
|
||||||
minute: 'numeric',
|
|
||||||
second: 'numeric',
|
|
||||||
hourCycle: vrState.config.dtHour12 ? 'h12' : 'h23'
|
|
||||||
})
|
|
||||||
.replace(' AM', ' am')
|
|
||||||
.replace(' PM', ' pm')
|
|
||||||
.replace(',', '');
|
|
||||||
|
|
||||||
if (vrState.cpuUsageEnabled) {
|
|
||||||
const cpuUsage = await AppApiVr.CpuUsage();
|
|
||||||
vrState.cpuUsage = cpuUsage.toFixed(0);
|
|
||||||
}
|
|
||||||
if (vrState.lastLocation.date !== 0) {
|
|
||||||
vrState.lastLocationTimer = timeToText(Date.now() - vrState.lastLocation.date);
|
|
||||||
} else {
|
|
||||||
vrState.lastLocationTimer = '';
|
|
||||||
}
|
|
||||||
if (vrState.lastLocation.onlineFor) {
|
|
||||||
vrState.onlineForTimer = timeToText(Date.now() - vrState.lastLocation.onlineFor);
|
|
||||||
} else {
|
|
||||||
vrState.onlineForTimer = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!vrState.config.hideDevicesFromFeed) {
|
|
||||||
AppApiVr.GetVRDevices().then((devices) => {
|
|
||||||
let deviceList = [];
|
|
||||||
let baseStations = 0;
|
|
||||||
devices.forEach((device) => {
|
|
||||||
device[3] = parseInt(device[3], 10).toString();
|
|
||||||
if (device[0] === 'base' && device[1] === 'connected') {
|
|
||||||
baseStations++;
|
|
||||||
} else {
|
|
||||||
deviceList.push(device);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
vrState.deviceCount = deviceList.length;
|
|
||||||
const deviceValue = (dev) => {
|
|
||||||
if (dev[0] === 'headset') return 0;
|
|
||||||
if (dev[0] === 'leftController') return 1;
|
|
||||||
if (dev[0] === 'rightController') return 2;
|
|
||||||
if (dev[0].toLowerCase().includes('controller')) return 3;
|
|
||||||
if (dev[0] === 'tracker' || dev[0] === 'base') return 4;
|
|
||||||
return 5;
|
|
||||||
};
|
|
||||||
deviceList.sort((a, b) => deviceValue(a) - deviceValue(b));
|
|
||||||
deviceList.sort((a, b) => {
|
|
||||||
if (a[1] === b[1]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (a[1] === 'connected') {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (a[1] === 'disconnected') {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
if (baseStations > 0) {
|
|
||||||
deviceList.push(['base', 'connected', '', baseStations]);
|
|
||||||
vrState.deviceCount += 1;
|
|
||||||
}
|
|
||||||
vrState.devices = deviceList;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
vrState.devices = [];
|
|
||||||
}
|
|
||||||
if (vrState.config.pcUptimeOnFeed) {
|
|
||||||
AppApiVr.GetUptime().then((uptime) => {
|
|
||||||
if (uptime) {
|
|
||||||
vrState.pcUptime = timeToText(uptime);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
vrState.pcUptime = '';
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
workerTimers.setTimeout(() => updateStatsLoop(), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateVrElectronLoop() {
|
|
||||||
try {
|
|
||||||
if (vrState.appType === '1') {
|
|
||||||
const wristOverlayQueue = await AppApiVr.GetExecuteVrFeedFunctionQueue();
|
|
||||||
if (wristOverlayQueue) {
|
|
||||||
wristOverlayQueue.forEach((item) => {
|
|
||||||
// item[0] is the function name, item[1] is already an object
|
|
||||||
const fullFunctionName = item[0];
|
|
||||||
const jsonArg = item[1];
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof window.$app === 'object' &&
|
|
||||||
typeof window.$app[fullFunctionName] === 'function'
|
|
||||||
) {
|
|
||||||
window.$app[fullFunctionName](jsonArg);
|
|
||||||
} else {
|
|
||||||
console.error(`$app.${fullFunctionName} is not defined or is not a function`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const hmdOverlayQueue = await AppApiVr.GetExecuteVrOverlayFunctionQueue();
|
|
||||||
if (hmdOverlayQueue) {
|
|
||||||
hmdOverlayQueue.forEach((item) => {
|
|
||||||
// item[0] is the function name, item[1] is already an object
|
|
||||||
const fullFunctionName = item[0];
|
|
||||||
const jsonArg = item[1];
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof window.$app === 'object' &&
|
|
||||||
typeof window.$app[fullFunctionName] === 'function'
|
|
||||||
) {
|
|
||||||
window.$app[fullFunctionName](jsonArg);
|
|
||||||
} else {
|
|
||||||
console.error(`$app.${fullFunctionName} is not defined or is not a function`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
workerTimers.setTimeout(() => updateVrElectronLoop(), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function playNoty(json) {
|
|
||||||
let { noty, message, image } = JSON.parse(json);
|
|
||||||
if (typeof noty === 'undefined') {
|
|
||||||
console.error('noty is undefined');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
noty = escapeTagRecursive(noty);
|
|
||||||
message = escapeTag(message) || '';
|
|
||||||
let text = '';
|
|
||||||
let img = '';
|
|
||||||
if (image) {
|
|
||||||
img = `<img class="noty-img" src="${image}"></img>`;
|
|
||||||
}
|
|
||||||
switch (noty.type) {
|
|
||||||
case 'OnPlayerJoined':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has joined`;
|
|
||||||
break;
|
|
||||||
case 'OnPlayerLeft':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has left`;
|
|
||||||
break;
|
|
||||||
case 'OnPlayerJoining':
|
|
||||||
text = `<strong>${noty.displayName}</strong> is joining`;
|
|
||||||
break;
|
|
||||||
case 'GPS':
|
|
||||||
text = `<strong>${noty.displayName}</strong> is in ${displayLocation(
|
|
||||||
noty.location,
|
|
||||||
noty.worldName,
|
|
||||||
noty.groupName
|
|
||||||
)}`;
|
|
||||||
break;
|
|
||||||
case 'Online':
|
|
||||||
let locationName = '';
|
|
||||||
if (noty.worldName) {
|
|
||||||
locationName = ` to ${displayLocation(noty.location, noty.worldName, noty.groupName)}`;
|
|
||||||
}
|
|
||||||
text = `<strong>${noty.displayName}</strong> has logged in${locationName}`;
|
|
||||||
break;
|
|
||||||
case 'Offline':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has logged out`;
|
|
||||||
break;
|
|
||||||
case 'Status':
|
|
||||||
text = `<strong>${noty.displayName}</strong> status is now <i>${noty.status}</i> ${noty.statusDescription}`;
|
|
||||||
break;
|
|
||||||
case 'invite':
|
|
||||||
text = `<strong>${noty.senderUsername}</strong> has invited you to ${displayLocation(
|
|
||||||
noty.details.worldId,
|
|
||||||
noty.details.worldName,
|
|
||||||
''
|
|
||||||
)}${message}`;
|
|
||||||
break;
|
|
||||||
case 'requestInvite':
|
|
||||||
text = `<strong>${noty.senderUsername}</strong> has requested an invite ${message}`;
|
|
||||||
break;
|
|
||||||
case 'inviteResponse':
|
|
||||||
text = `<strong>${noty.senderUsername}</strong> has responded to your invite ${message}`;
|
|
||||||
break;
|
|
||||||
case 'requestInviteResponse':
|
|
||||||
text = `<strong>${noty.senderUsername}</strong> has responded to your invite request ${message}`;
|
|
||||||
break;
|
|
||||||
case 'friendRequest':
|
|
||||||
text = `<strong>${noty.senderUsername}</strong> has sent you a friend request`;
|
|
||||||
break;
|
|
||||||
case 'Friend':
|
|
||||||
text = `<strong>${noty.displayName}</strong> is now your friend`;
|
|
||||||
break;
|
|
||||||
case 'Unfriend':
|
|
||||||
text = `<strong>${noty.displayName}</strong> is no longer your friend`;
|
|
||||||
break;
|
|
||||||
case 'TrustLevel':
|
|
||||||
text = `<strong>${noty.displayName}</strong> trust level is now ${noty.trustLevel}`;
|
|
||||||
break;
|
|
||||||
case 'DisplayName':
|
|
||||||
text = `<strong>${noty.previousDisplayName}</strong> changed their name to ${noty.displayName}`;
|
|
||||||
break;
|
|
||||||
case 'boop':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'groupChange':
|
|
||||||
text = `<strong>${noty.senderUsername}</strong> ${noty.message}`;
|
|
||||||
break;
|
|
||||||
case 'group.announcement':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'group.informative':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'group.invite':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'group.joinRequest':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'group.transfer':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'group.queueReady':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'instance.closed':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'PortalSpawn':
|
|
||||||
if (noty.displayName) {
|
|
||||||
text = `<strong>${noty.displayName}</strong> has spawned a portal to ${displayLocation(
|
|
||||||
noty.instanceId,
|
|
||||||
noty.worldName,
|
|
||||||
noty.groupName
|
|
||||||
)}`;
|
|
||||||
} else {
|
|
||||||
text = 'User has spawned a portal';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'AvatarChange':
|
|
||||||
text = `<strong>${noty.displayName}</strong> changed into avatar ${noty.name}`;
|
|
||||||
break;
|
|
||||||
case 'ChatBoxMessage':
|
|
||||||
text = `<strong>${noty.displayName}</strong> said ${noty.text}`;
|
|
||||||
break;
|
|
||||||
case 'Event':
|
|
||||||
text = noty.data;
|
|
||||||
break;
|
|
||||||
case 'External':
|
|
||||||
text = noty.message;
|
|
||||||
break;
|
|
||||||
case 'VideoPlay':
|
|
||||||
text = `<strong>Now playing:</strong> ${noty.notyName}`;
|
|
||||||
break;
|
|
||||||
case 'BlockedOnPlayerJoined':
|
|
||||||
text = `Blocked user <strong>${noty.displayName}</strong> has joined`;
|
|
||||||
break;
|
|
||||||
case 'BlockedOnPlayerLeft':
|
|
||||||
text = `Blocked user <strong>${noty.displayName}</strong> has left`;
|
|
||||||
break;
|
|
||||||
case 'MutedOnPlayerJoined':
|
|
||||||
text = `Muted user <strong>${noty.displayName}</strong> has joined`;
|
|
||||||
break;
|
|
||||||
case 'MutedOnPlayerLeft':
|
|
||||||
text = `Muted user <strong>${noty.displayName}</strong> has left`;
|
|
||||||
break;
|
|
||||||
case 'Blocked':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has blocked you`;
|
|
||||||
break;
|
|
||||||
case 'Unblocked':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has unblocked you`;
|
|
||||||
break;
|
|
||||||
case 'Muted':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has muted you`;
|
|
||||||
break;
|
|
||||||
case 'Unmuted':
|
|
||||||
text = `<strong>${noty.displayName}</strong> has unmuted you`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (text) {
|
|
||||||
new Noty({
|
|
||||||
type: 'alert',
|
|
||||||
theme: vrState.config.notificationTheme,
|
|
||||||
timeout: vrState.config.notificationTimeout,
|
|
||||||
layout: vrState.config.notificationPosition,
|
|
||||||
text: `${img}<div class="noty-text">${text}</div>`
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function statusClass(status) {
|
|
||||||
let style = {};
|
|
||||||
if (typeof status === 'undefined') {
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
if (status === 'active') {
|
|
||||||
// Online
|
|
||||||
style.online = true;
|
|
||||||
} else if (status === 'join me') {
|
|
||||||
// Join Me
|
|
||||||
style.joinme = true;
|
|
||||||
} else if (status === 'ask me') {
|
|
||||||
// Ask Me
|
|
||||||
style.askme = true;
|
|
||||||
} else if (status === 'busy') {
|
|
||||||
// Do Not Disturb
|
|
||||||
style.busy = true;
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
function notyClear() {
|
|
||||||
Noty.closeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanHudFeedLoop() {
|
|
||||||
if (!vrState.cleanHudFeedLoopStatus) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cleanHudFeed();
|
|
||||||
if (vrState.hudFeed.length === 0) {
|
|
||||||
vrState.cleanHudFeedLoopStatus = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
workerTimers.setTimeout(() => cleanHudFeedLoop(), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanHudFeed() {
|
|
||||||
const dt = Date.now();
|
|
||||||
vrState.hudFeed.forEach((item) => {
|
|
||||||
if (item.time + vrState.config.photonOverlayMessageTimeout < dt) {
|
|
||||||
removeFromArray(vrState.hudFeed, item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (vrState.hudFeed.length > 10) {
|
|
||||||
vrState.hudFeed.length = 10;
|
|
||||||
}
|
|
||||||
if (!vrState.cleanHudFeedLoopStatus) {
|
|
||||||
vrState.cleanHudFeedLoopStatus = true;
|
|
||||||
cleanHudFeedLoop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addEntryHudFeed(json) {
|
|
||||||
const data = JSON.parse(json);
|
|
||||||
let combo = 1;
|
|
||||||
vrState.hudFeed.forEach((item) => {
|
|
||||||
if (item.displayName === data.displayName && item.text === data.text) {
|
|
||||||
combo = item.combo + 1;
|
|
||||||
removeFromArray(vrState.hudFeed, item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
vrState.hudFeed.unshift({
|
|
||||||
time: Date.now(),
|
|
||||||
combo,
|
|
||||||
...data
|
|
||||||
});
|
|
||||||
cleanHudFeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateHudFeedTag(json) {
|
|
||||||
const ref = JSON.parse(json);
|
|
||||||
vrState.hudFeed.forEach((item) => {
|
|
||||||
if (item.userId === ref.userId) {
|
|
||||||
item.colour = ref.colour;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateHudTimeout(json) {
|
|
||||||
vrState.hudTimeout = JSON.parse(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setDatetimeFormat() {
|
|
||||||
vrState.currentCulture = await AppApiVr.CurrentCulture();
|
|
||||||
const formatDate = (date) => {
|
|
||||||
if (!date) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const dt = new Date(date);
|
|
||||||
return dt
|
|
||||||
.toLocaleTimeString(vrState.currentCulture, {
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: 'numeric',
|
|
||||||
hourCycle: vrState.config.dtHour12 ? 'h12' : 'h23'
|
|
||||||
})
|
|
||||||
.replace(' am', '')
|
|
||||||
.replace(' pm', '');
|
|
||||||
};
|
|
||||||
Vue.filter('formatDate', formatDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAppLanguage(appLanguage) {
|
|
||||||
if (!appLanguage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (appLanguage !== vrState.appLanguage) {
|
|
||||||
vrState.appLanguage = appLanguage;
|
|
||||||
// @ts-ignore
|
|
||||||
i18n.locale = vrState.appLanguage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function trackingResultToClass(deviceStatus) {
|
|
||||||
switch (deviceStatus) {
|
|
||||||
case 'Uninitialized':
|
|
||||||
case 'Calibrating_OutOfRange':
|
|
||||||
case 'Fallback_RotationOnly':
|
|
||||||
return 'tracker-error';
|
|
||||||
|
|
||||||
case 'Calibrating_InProgress':
|
|
||||||
case 'Running_OutOfRange':
|
|
||||||
return 'tracker-warning';
|
|
||||||
|
|
||||||
case 'Running_OK':
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
appType,
|
|
||||||
config,
|
|
||||||
wristFeed,
|
|
||||||
devices,
|
|
||||||
nowPlaying,
|
|
||||||
lastLocation,
|
|
||||||
lastLocationTimer,
|
|
||||||
onlineForTimer,
|
|
||||||
pcUptime,
|
|
||||||
currentTime,
|
|
||||||
cpuUsageEnabled,
|
|
||||||
cpuUsage,
|
|
||||||
onlineFriendCount,
|
|
||||||
customInfo,
|
|
||||||
hudFeed,
|
|
||||||
hudTimeout
|
|
||||||
} = toRefs(vrState);
|
|
||||||
|
|
||||||
return {
|
|
||||||
appType,
|
|
||||||
config,
|
|
||||||
wristFeed,
|
|
||||||
devices,
|
|
||||||
nowPlaying,
|
|
||||||
lastLocation,
|
|
||||||
lastLocationTimer,
|
|
||||||
onlineForTimer,
|
|
||||||
pcUptime,
|
|
||||||
currentTime,
|
|
||||||
cpuUsageEnabled,
|
|
||||||
cpuUsage,
|
|
||||||
onlineFriendCount,
|
|
||||||
customInfo,
|
|
||||||
hudFeed,
|
|
||||||
hudTimeout,
|
|
||||||
statusClass,
|
|
||||||
trackingResultToClass,
|
|
||||||
$t
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
+20
-2
@@ -33,9 +33,9 @@ const avatarReq = {
|
|||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ id: string, releaseStatus?: 'public' | 'private', name?: string, description?: string,tags?: string[] }} params
|
* @type {import('../types/api/avatar').SaveAvatar}
|
||||||
* @returns {Promise<{json: any, params}>}
|
|
||||||
*/
|
*/
|
||||||
saveAvatar(params) {
|
saveAvatar(params) {
|
||||||
return request(`avatars/${params.id}`, {
|
return request(`avatars/${params.id}`, {
|
||||||
@@ -173,6 +173,24 @@ const avatarReq = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
uploadAvatarImage(imageData) {
|
||||||
|
const params = {
|
||||||
|
tag: 'avatarimage'
|
||||||
|
};
|
||||||
|
return request('file/image', {
|
||||||
|
uploadImage: true,
|
||||||
|
matchingDimensions: false,
|
||||||
|
postData: JSON.stringify(params),
|
||||||
|
imageData
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
return args;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ imageData: string, avatarId: string }}
|
* @param {{ imageData: string, avatarId: string }}
|
||||||
* @returns {Promise<{json: any, params}>}
|
* @returns {Promise<{json: any, params}>}
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
import { request } from '../service/request';
|
|
||||||
import { useAvatarStore, useWorldStore } from '../stores';
|
|
||||||
|
|
||||||
const imageReq = {
|
|
||||||
async uploadAvatarFailCleanup(id) {
|
|
||||||
const avatarStore = useAvatarStore();
|
|
||||||
try {
|
|
||||||
const json = await request(`file/${id}`, {
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
const fileId = json.id;
|
|
||||||
const fileVersion = json.versions[json.versions.length - 1].version;
|
|
||||||
request(`file/${fileId}/${fileVersion}/signature/finish`, {
|
|
||||||
method: 'PUT'
|
|
||||||
}).catch(err => console.error('Failed to finish signature:', err));
|
|
||||||
request(`file/${fileId}/${fileVersion}/file/finish`, {
|
|
||||||
method: 'PUT'
|
|
||||||
}).catch(err => console.error('Failed to finish file:', err));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to cleanup avatar upload:', error);
|
|
||||||
}
|
|
||||||
avatarStore.avatarDialog.loading = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadAvatarImage(params, fileId) {
|
|
||||||
try {
|
|
||||||
return await request(`file/${fileId}`, {
|
|
||||||
method: 'POST',
|
|
||||||
params
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params,
|
|
||||||
fileId
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
imageReq.uploadAvatarFailCleanup(fileId);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadAvatarImageFileStart(params) {
|
|
||||||
try {
|
|
||||||
return await request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/file/start`,
|
|
||||||
{
|
|
||||||
method: 'PUT'
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
imageReq.uploadAvatarFailCleanup(params.fileId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadAvatarImageFileFinish(params) {
|
|
||||||
return request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/file/finish`,
|
|
||||||
{
|
|
||||||
method: 'PUT',
|
|
||||||
params: {
|
|
||||||
maxParts: 0,
|
|
||||||
nextPartNumber: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadAvatarImageSigStart(params) {
|
|
||||||
try {
|
|
||||||
return await request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/signature/start`,
|
|
||||||
{
|
|
||||||
method: 'PUT'
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
imageReq.uploadAvatarFailCleanup(params.fileId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadAvatarImageSigFinish(params) {
|
|
||||||
return request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/signature/finish`,
|
|
||||||
{
|
|
||||||
method: 'PUT',
|
|
||||||
params: {
|
|
||||||
maxParts: 0,
|
|
||||||
nextPartNumber: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setAvatarImage(params) {
|
|
||||||
return request(`avatars/${params.id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
params
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadWorldFailCleanup(id) {
|
|
||||||
const worldStore = useWorldStore();
|
|
||||||
try {
|
|
||||||
const json = await request(`file/${id}`, {
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
const fileId = json.id;
|
|
||||||
const fileVersion = json.versions[json.versions.length - 1].version;
|
|
||||||
request(`file/${fileId}/${fileVersion}/signature/finish`, {
|
|
||||||
method: 'PUT'
|
|
||||||
}).catch(err => console.error('Failed to finish signature:', err));
|
|
||||||
request(`file/${fileId}/${fileVersion}/file/finish`, {
|
|
||||||
method: 'PUT'
|
|
||||||
}).catch(err => console.error('Failed to finish file:', err));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to cleanup world upload:', error);
|
|
||||||
}
|
|
||||||
worldStore.worldDialog.loading = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadWorldImage(params, fileId) {
|
|
||||||
try {
|
|
||||||
return await request(`file/${fileId}`, {
|
|
||||||
method: 'POST',
|
|
||||||
params
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params,
|
|
||||||
fileId
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
imageReq.uploadWorldFailCleanup(fileId);
|
|
||||||
}
|
|
||||||
return void 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadWorldImageFileStart(params) {
|
|
||||||
try {
|
|
||||||
return await request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/file/start`,
|
|
||||||
{
|
|
||||||
method: 'PUT'
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
imageReq.uploadWorldFailCleanup(params.fileId);
|
|
||||||
}
|
|
||||||
return void 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadWorldImageFileFinish(params) {
|
|
||||||
return request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/file/finish`,
|
|
||||||
{
|
|
||||||
method: 'PUT',
|
|
||||||
params: {
|
|
||||||
maxParts: 0,
|
|
||||||
nextPartNumber: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadWorldImageSigStart(params) {
|
|
||||||
try {
|
|
||||||
return await request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/signature/start`,
|
|
||||||
{
|
|
||||||
method: 'PUT'
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
imageReq.uploadWorldFailCleanup(params.fileId);
|
|
||||||
}
|
|
||||||
return void 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadWorldImageSigFinish(params) {
|
|
||||||
return request(
|
|
||||||
`file/${params.fileId}/${params.fileVersion}/signature/finish`,
|
|
||||||
{
|
|
||||||
method: 'PUT',
|
|
||||||
params: {
|
|
||||||
maxParts: 0,
|
|
||||||
nextPartNumber: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setWorldImage(params) {
|
|
||||||
const worldStore = useWorldStore();
|
|
||||||
return request(`worlds/${params.id}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
params
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
args.ref = worldStore.applyWorld(json);
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getAvatarImages(params) {
|
|
||||||
return request(`file/${params.fileId}`, {
|
|
||||||
method: 'GET'
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getWorldImages(params) {
|
|
||||||
return request(`file/${params.fileId}`, {
|
|
||||||
method: 'GET',
|
|
||||||
params
|
|
||||||
}).then((json) => {
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default imageReq;
|
|
||||||
@@ -16,7 +16,6 @@ import favoriteRequest from './favorite';
|
|||||||
import vrcPlusIconRequest from './vrcPlusIcon';
|
import vrcPlusIconRequest from './vrcPlusIcon';
|
||||||
import vrcPlusImageRequest from './vrcPlusImage';
|
import vrcPlusImageRequest from './vrcPlusImage';
|
||||||
import inviteMessagesRequest from './inviteMessages';
|
import inviteMessagesRequest from './inviteMessages';
|
||||||
import imageRequest from './image';
|
|
||||||
import miscRequest from './misc';
|
import miscRequest from './misc';
|
||||||
import groupRequest from './group';
|
import groupRequest from './group';
|
||||||
import authRequest from './auth';
|
import authRequest from './auth';
|
||||||
@@ -37,7 +36,6 @@ window.request = {
|
|||||||
vrcPlusIconRequest,
|
vrcPlusIconRequest,
|
||||||
vrcPlusImageRequest,
|
vrcPlusImageRequest,
|
||||||
inviteMessagesRequest,
|
inviteMessagesRequest,
|
||||||
imageRequest,
|
|
||||||
miscRequest,
|
miscRequest,
|
||||||
authRequest,
|
authRequest,
|
||||||
groupRequest,
|
groupRequest,
|
||||||
@@ -59,7 +57,6 @@ export {
|
|||||||
vrcPlusIconRequest,
|
vrcPlusIconRequest,
|
||||||
vrcPlusImageRequest,
|
vrcPlusImageRequest,
|
||||||
inviteMessagesRequest,
|
inviteMessagesRequest,
|
||||||
imageRequest,
|
|
||||||
miscRequest,
|
miscRequest,
|
||||||
authRequest,
|
authRequest,
|
||||||
groupRequest,
|
groupRequest,
|
||||||
|
|||||||
+5
-5
@@ -1,5 +1,5 @@
|
|||||||
import { $app } from '../app';
|
import { ElMessage } from 'element-plus';
|
||||||
import { t } from '../plugin';
|
import { i18n } from '../plugin/i18n';
|
||||||
import { request } from '../service/request';
|
import { request } from '../service/request';
|
||||||
import { useInstanceStore } from '../stores';
|
import { useInstanceStore } from '../stores';
|
||||||
|
|
||||||
@@ -110,14 +110,14 @@ const instanceReq = {
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err?.error?.message) {
|
if (err?.error?.message) {
|
||||||
$app.$message({
|
ElMessage({
|
||||||
message: err.error.message,
|
message: err.error.message,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
$app.$message({
|
ElMessage({
|
||||||
message: t('message.instance.not_allowed'),
|
message: i18n.global.t('message.instance.not_allowed'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
+4
-3
@@ -6,12 +6,13 @@ function getCurrentUserId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const miscReq = {
|
const miscReq = {
|
||||||
getBundles(fileId) {
|
getFile(params) {
|
||||||
return request(`file/${fileId}`, {
|
return request(`file/${params.fileId}`, {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
}).then((json) => {
|
}).then((json) => {
|
||||||
const args = {
|
const args = {
|
||||||
json
|
json,
|
||||||
|
params
|
||||||
};
|
};
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -140,6 +140,24 @@ const worldReq = {
|
|||||||
args.ref = worldStore.applyWorld(json);
|
args.ref = worldStore.applyWorld(json);
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadWorldImage(imageData) {
|
||||||
|
const params = {
|
||||||
|
tag: 'worldimage'
|
||||||
|
};
|
||||||
|
return request('file/image', {
|
||||||
|
uploadImage: true,
|
||||||
|
matchingDimensions: false,
|
||||||
|
postData: JSON.stringify(params),
|
||||||
|
imageData
|
||||||
|
}).then((json) => {
|
||||||
|
const args = {
|
||||||
|
json,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
return args;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+14
-5
@@ -4,20 +4,29 @@
|
|||||||
// This work is licensed under the terms of the MIT license.
|
// This work is licensed under the terms of the MIT license.
|
||||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
import Vue from 'vue';
|
import { createApp } from 'vue';
|
||||||
import './bootstrap';
|
import './bootstrap';
|
||||||
|
import { i18n } from './plugin/i18n';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import { pinia } from './stores';
|
import { pinia } from './stores';
|
||||||
|
import ElementPlus from 'element-plus';
|
||||||
|
|
||||||
|
import './app.scss';
|
||||||
|
import registerComponents from './plugin/components';
|
||||||
|
|
||||||
console.log(`isLinux: ${LINUX}`);
|
console.log(`isLinux: ${LINUX}`);
|
||||||
|
|
||||||
// #region | Hey look it's most of VRCX!
|
// #region | Hey look it's most of VRCX!
|
||||||
// prompt: 'Please clean up and refactor the VRCX codebase.'
|
// prompt: 'Please clean up and refactor the VRCX codebase.'
|
||||||
|
|
||||||
const $app = new Vue({
|
const $app = createApp(App);
|
||||||
pinia,
|
$app.use(pinia);
|
||||||
render: (h) => h(App)
|
$app.use(i18n);
|
||||||
}).$mount('#root');
|
$app.use(ElementPlus);
|
||||||
|
|
||||||
|
registerComponents($app);
|
||||||
|
|
||||||
|
$app.mount('#root');
|
||||||
|
|
||||||
window.$app = $app;
|
window.$app = $app;
|
||||||
export { $app };
|
export { $app };
|
||||||
|
|||||||
-75
@@ -1,75 +0,0 @@
|
|||||||
doctype html
|
|
||||||
#x-app.x-app(@dragenter.prevent @dragover.prevent @drop.prevent)
|
|
||||||
//- ### Login ###
|
|
||||||
Login(v-if='!watchState.isLoggedIn')
|
|
||||||
|
|
||||||
VRCXUpdateDialog
|
|
||||||
|
|
||||||
template(v-if='watchState.isLoggedIn')
|
|
||||||
//- ### Menu ###
|
|
||||||
NavMenu
|
|
||||||
|
|
||||||
//- ### Sidebar ###
|
|
||||||
Sidebar
|
|
||||||
|
|
||||||
//- ### Tabs ###
|
|
||||||
Feed
|
|
||||||
|
|
||||||
GameLog
|
|
||||||
|
|
||||||
PlayerList
|
|
||||||
|
|
||||||
Search
|
|
||||||
|
|
||||||
Favorites
|
|
||||||
|
|
||||||
FriendLog
|
|
||||||
|
|
||||||
Moderation
|
|
||||||
|
|
||||||
Notification
|
|
||||||
|
|
||||||
FriendList
|
|
||||||
|
|
||||||
Charts
|
|
||||||
|
|
||||||
Tools
|
|
||||||
|
|
||||||
Profile
|
|
||||||
|
|
||||||
Settings
|
|
||||||
|
|
||||||
//- ## Dialogs ## -\\
|
|
||||||
UserDialog
|
|
||||||
|
|
||||||
WorldDialog
|
|
||||||
|
|
||||||
AvatarDialog
|
|
||||||
|
|
||||||
GroupDialog
|
|
||||||
|
|
||||||
GroupMemberModerationDialog
|
|
||||||
|
|
||||||
GalleryDialog
|
|
||||||
|
|
||||||
FullscreenImageDialog
|
|
||||||
|
|
||||||
PreviousInstancesInfoDialog
|
|
||||||
|
|
||||||
LaunchDialog
|
|
||||||
|
|
||||||
LaunchOptionsDialog
|
|
||||||
|
|
||||||
FriendImportDialog
|
|
||||||
|
|
||||||
WorldImportDialog
|
|
||||||
|
|
||||||
AvatarImportDialog
|
|
||||||
|
|
||||||
ChooseFavoriteGroupDialog
|
|
||||||
|
|
||||||
EditInviteMessageDialog
|
|
||||||
|
|
||||||
VRChatConfigDialog
|
|
||||||
|
|
||||||
PrimaryPasswordDialog
|
|
||||||
+218
-188
@@ -10,10 +10,12 @@
|
|||||||
|
|
||||||
@use './assets/scss/flags.scss';
|
@use './assets/scss/flags.scss';
|
||||||
@use './assets/scss/animated-emoji.scss';
|
@use './assets/scss/animated-emoji.scss';
|
||||||
|
@use 'element-plus/theme-chalk/src/index.scss' as *;
|
||||||
|
|
||||||
@import '~animate.css/animate.min.css';
|
@import 'element-plus/theme-chalk/src/dark/css-vars.scss';
|
||||||
@import '~noty/lib/noty.css';
|
@import 'animate.css/animate.min.css';
|
||||||
@import '~element-ui/lib/theme-chalk/index.css';
|
@import 'noty/lib/noty.css';
|
||||||
|
@import 'remixicon/fonts/remixicon.css';
|
||||||
|
|
||||||
.color-palettes {
|
.color-palettes {
|
||||||
background: #409eff;
|
background: #409eff;
|
||||||
@@ -26,6 +28,10 @@
|
|||||||
background: #c0c4cc;
|
background: #c0c4cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-icon.is-loading {
|
||||||
|
animation: rotating 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.noty_layout {
|
.noty_layout {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
@@ -78,42 +84,46 @@
|
|||||||
border-bottom: 1px solid #a0b55c;
|
border-bottom: 1px solid #a0b55c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table + .pagination-bar {
|
// .el-table + .pagination-bar {
|
||||||
margin-top: 15px;
|
// margin-top: 15px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.el-table__expanded-cell[class*='cell'] {
|
||||||
|
padding: 20px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table--mini .el-table__expanded-cell[class*='cell'] {
|
.el-table--small .cell {
|
||||||
padding: 20px 50px;
|
// fix expand arrow position
|
||||||
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table--mini .el-table__cell {
|
// .el-table--mini .el-table__cell {
|
||||||
padding: 5px 0;
|
// padding: 5px 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// expand table cell on hover, TODO: replace with a better solution
|
// .el-table .cell {
|
||||||
.el-table .cell {
|
// display: -webkit-box;
|
||||||
display: -webkit-box;
|
// -webkit-box-orient: vertical;
|
||||||
-webkit-box-orient: vertical;
|
// -webkit-line-clamp: 1;
|
||||||
-webkit-line-clamp: 1;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
.el-table__row:hover .el-table__cell .cell {
|
// .el-table__row:hover .el-table__cell .cell {
|
||||||
display: revert;
|
// display: revert;
|
||||||
-webkit-line-clamp: unset;
|
// -webkit-line-clamp: unset;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-table th.is-sortable .cell {
|
// .el-table th.is-sortable .cell {
|
||||||
display: flex;
|
// display: flex;
|
||||||
align-items: center;
|
// align-items: center;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-table .caret-wrapper {
|
// .el-table .caret-wrapper {
|
||||||
margin-top: 4.5px;
|
// margin-top: 4.5px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.notification-table .el-table .cell {
|
// .notification-table .el-table .cell {
|
||||||
-webkit-line-clamp: 2;
|
// -webkit-line-clamp: 2;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-dialog__body {
|
.el-dialog__body {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -146,6 +156,10 @@
|
|||||||
unicode-range: U+2026;
|
unicode-range: U+2026;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family:
|
font-family:
|
||||||
'ellipsis-font', 'Noto Sans KR', 'Noto Sans JP', 'Noto Sans TC',
|
'ellipsis-font', 'Noto Sans KR', 'Noto Sans JP', 'Noto Sans TC',
|
||||||
@@ -313,11 +327,11 @@ hr.x-vertical-divider {
|
|||||||
order: 99;
|
order: 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper.x-quick-search {
|
// .el-popper.x-quick-search {
|
||||||
width: 225px;
|
// width: 225px;
|
||||||
min-width: 0 !important;
|
// min-width: 0 !important;
|
||||||
border: none;
|
// border: none;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-popper.x-quick-search .el-select-dropdown__item {
|
.el-popper.x-quick-search .el-select-dropdown__item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -383,13 +397,13 @@ hr.x-vertical-divider {
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-select-dropdown__item .x-friend-item:hover {
|
// .el-select-dropdown__item .x-friend-item:hover {
|
||||||
background: none;
|
// background: none;
|
||||||
border-radius: 0;
|
// border-radius: 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.x-dialog .x-friend-item {
|
.x-dialog .x-friend-item {
|
||||||
width: 175px;
|
width: 167px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar {
|
.x-friend-item > .avatar {
|
||||||
@@ -438,7 +452,7 @@ img.friends-list-avatar {
|
|||||||
.x-friend-item > .avatar.joinme.mobile > img,
|
.x-friend-item > .avatar.joinme.mobile > img,
|
||||||
.x-friend-item > .avatar.askme.mobile > img,
|
.x-friend-item > .avatar.askme.mobile > img,
|
||||||
.x-friend-item > .avatar.busy.mobile > img {
|
.x-friend-item > .avatar.busy.mobile > img {
|
||||||
mask-image: url(assets/images/masks/usercutoutmobile.svg);
|
mask-image: url(./assets/images/masks/usercutoutmobile.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar.online.mobile::after,
|
.x-friend-item > .avatar.online.mobile::after,
|
||||||
@@ -452,7 +466,7 @@ img.friends-list-avatar {
|
|||||||
height: 14px;
|
height: 14px;
|
||||||
content: '';
|
content: '';
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
mask-image: url(assets/images/masks/phone.svg);
|
mask-image: url(./assets/images/masks/phone.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar.active > img,
|
.x-friend-item > .avatar.active > img,
|
||||||
@@ -461,7 +475,7 @@ img.friends-list-avatar {
|
|||||||
.x-friend-item > .avatar.askme > img,
|
.x-friend-item > .avatar.askme > img,
|
||||||
.x-friend-item > .avatar.busy > img,
|
.x-friend-item > .avatar.busy > img,
|
||||||
.x-friend-item > .avatar.offline > img {
|
.x-friend-item > .avatar.offline > img {
|
||||||
mask-image: url(assets/images/masks/usercutout.svg);
|
mask-image: url(./assets/images/masks/usercutout.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar.active::after,
|
.x-friend-item > .avatar.active::after,
|
||||||
@@ -490,17 +504,17 @@ img.friends-list-avatar {
|
|||||||
|
|
||||||
.x-friend-item > .avatar.joinme::after {
|
.x-friend-item > .avatar.joinme::after {
|
||||||
background: #409eff;
|
background: #409eff;
|
||||||
mask-image: url(assets/images/masks/joinme.svg);
|
mask-image: url(./assets/images/masks/joinme.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar.askme::after {
|
.x-friend-item > .avatar.askme::after {
|
||||||
background: #ff9500;
|
background: #ff9500;
|
||||||
mask-image: url(assets/images/masks/askme.svg);
|
mask-image: url(./assets/images/masks/askme.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar.busy::after {
|
.x-friend-item > .avatar.busy::after {
|
||||||
background: #ff2c2c;
|
background: #ff2c2c;
|
||||||
mask-image: url(assets/images/masks/busy.svg);
|
mask-image: url(./assets/images/masks/busy.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-friend-item > .avatar.offline::after {
|
.x-friend-item > .avatar.offline::after {
|
||||||
@@ -565,16 +579,11 @@ img.friends-list-avatar {
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-change-image-item > img,
|
// .x-change-image-item > img,
|
||||||
.x-change-image-item > .el-popover__reference-wrapper > img {
|
// .x-change-image-item > .el-popover__reference-wrapper > img {
|
||||||
width: 240px;
|
// width: 240px;
|
||||||
height: 180px;
|
// height: 180px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.current-image {
|
|
||||||
border: 2px solid #67c23a;
|
|
||||||
padding: 2px 2px 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.x-dialog > .el-dialog {
|
.x-dialog > .el-dialog {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -595,12 +604,12 @@ img.friends-list-avatar {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper.hex {
|
// .el-popper.hex {
|
||||||
min-width: auto;
|
// min-width: auto;
|
||||||
padding: 10px;
|
// padding: 10px;
|
||||||
font-family: monospace;
|
// font-family: monospace;
|
||||||
text-align: center;
|
// text-align: center;
|
||||||
}
|
// }
|
||||||
|
|
||||||
i.x-user-status,
|
i.x-user-status,
|
||||||
i.x-status-icon {
|
i.x-status-icon {
|
||||||
@@ -621,17 +630,17 @@ i.x-user-status.online {
|
|||||||
|
|
||||||
i.x-user-status.joinme {
|
i.x-user-status.joinme {
|
||||||
background: #409eff;
|
background: #409eff;
|
||||||
mask-image: url(assets/images/masks/joinme.svg);
|
mask-image: url(./assets/images/masks/joinme.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
i.x-user-status.askme {
|
i.x-user-status.askme {
|
||||||
background: #ff9500;
|
background: #ff9500;
|
||||||
mask-image: url(assets/images/masks/askme.svg);
|
mask-image: url(./assets/images/masks/askme.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
i.x-user-status.busy {
|
i.x-user-status.busy {
|
||||||
background: #ff2c2c;
|
background: #ff2c2c;
|
||||||
mask-image: url(assets/images/masks/busy.svg);
|
mask-image: url(./assets/images/masks/busy.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
i.x-status-icon.green {
|
i.x-status-icon.green {
|
||||||
@@ -651,67 +660,79 @@ i.x-status-icon.red {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-friend {
|
.x-tag-friend {
|
||||||
color: rgb(255, 208, 0) !important;
|
color: rgb(255, 208, 0);
|
||||||
border-color: rgb(255, 208, 0) !important;
|
border-color: rgb(255, 208, 0) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-vrcplus {
|
.x-tag-vrcplus {
|
||||||
color: rgb(255, 208, 0) !important;
|
color: rgb(255, 208, 0);
|
||||||
border-color: rgb(255, 208, 0) !important;
|
border-color: rgb(255, 208, 0) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-platform-pc {
|
.x-tag-platform-pc {
|
||||||
color: #409eff !important;
|
color: #409eff;
|
||||||
border-color: #409eff !important;
|
border-color: #409eff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-platform-quest {
|
.x-tag-platform-quest {
|
||||||
color: #67c23a !important;
|
color: #67c23a;
|
||||||
border-color: #67c23a !important;
|
border-color: #67c23a !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-platform-ios {
|
.x-tag-platform-ios {
|
||||||
color: #c7c7ce !important;
|
color: #c7c7ce;
|
||||||
border-color: #c7c7ce !important;
|
border-color: #c7c7ce !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-platform-other {
|
.x-tag-platform-other {
|
||||||
color: #ff4177 !important;
|
color: #ff4177;
|
||||||
border-color: #ff4177 !important;
|
border-color: #ff4177 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.x-tag-age-verification {
|
.x-tag-age-verification {
|
||||||
color: #5c70ec !important;
|
color: #5c70ec;
|
||||||
border-color: #5c70ec !important;
|
border-color: #5c70ec !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.x-tag-border-left {
|
||||||
|
border-left: 0.8px solid;
|
||||||
|
margin-left: 5px;
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-bottom: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
.x-grey {
|
.x-grey {
|
||||||
color: #909399;
|
color: #909399;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tree-node {
|
.x-popover-image {
|
||||||
white-space: normal;
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tree-node__content {
|
// .el-tree-node {
|
||||||
height: auto;
|
// white-space: normal;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-progress-bar {
|
// .el-tree-node__content {
|
||||||
padding-right: 80px;
|
// height: auto;
|
||||||
margin-right: -85px;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
.el-progress__text {
|
// .el-progress-bar {
|
||||||
color: #c8c8c8;
|
// padding-right: 80px;
|
||||||
}
|
// margin-right: -85px;
|
||||||
|
// }
|
||||||
|
|
||||||
.x-user-dialog .el-textarea__inner {
|
// .el-progress__text {
|
||||||
padding: 0;
|
// color: #c8c8c8;
|
||||||
background: none;
|
// }
|
||||||
border: 0;
|
|
||||||
border-radius: 2px;
|
// .x-user-dialog .el-textarea__inner {
|
||||||
}
|
// padding: 0;
|
||||||
|
// background: none;
|
||||||
|
// border: 0;
|
||||||
|
// border-radius: 2px;
|
||||||
|
// }
|
||||||
|
|
||||||
.options-container {
|
.options-container {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
@@ -767,28 +788,28 @@ i.x-status-icon.red {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-color-picker__trigger {
|
// .el-color-picker__trigger {
|
||||||
border: unset;
|
// border: unset;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-color-picker__color {
|
// .el-color-picker__color {
|
||||||
border: 0.5px solid #999;
|
// border: 0.5px solid #999;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-button--success {
|
// .el-button--success {
|
||||||
background-color: #67c23a !important;
|
// background-color: #67c23a !important;
|
||||||
border-color: #67c23a !important;
|
// border-color: #67c23a !important;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.x-dialog .el-button--danger {
|
// .x-dialog .el-button--danger {
|
||||||
background-color: #f56c6c !important;
|
// background-color: #f56c6c !important;
|
||||||
border-color: #f56c6c !important;
|
// border-color: #f56c6c !important;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-button--warning {
|
// .el-button--warning {
|
||||||
background-color: #e6a23c !important;
|
// background-color: #e6a23c !important;
|
||||||
border-color: #e6a23c !important;
|
// border-color: #e6a23c !important;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.avatar-info {
|
.avatar-info {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
@@ -812,14 +833,14 @@ i.x-status-icon.red {
|
|||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item {
|
// .el-form-item {
|
||||||
margin-bottom: 4px;
|
// margin-bottom: 4px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.photon-event-table .el-table--mini .el-table__cell,
|
// .photon-event-table .el-table--mini .el-table__cell,
|
||||||
.current-instance-table .el-table--mini .el-table__cell {
|
// .current-instance-table .el-table--mini .el-table__cell {
|
||||||
padding: 0;
|
// padding: 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.photon-event-table {
|
.photon-event-table {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
@@ -835,14 +856,14 @@ i.x-status-icon.red {
|
|||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-pagination .el-select .el-input .el-input__inner,
|
// .el-pagination .el-select .el-input .el-input__inner,
|
||||||
.el-input--mini .el-input__icon {
|
// .el-input--mini .el-input__icon {
|
||||||
height: 22px;
|
// height: 22px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-pagination .btn-next {
|
// .el-pagination .btn-next {
|
||||||
margin-right: 10px;
|
// margin-right: 10px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-dialog,
|
.el-dialog,
|
||||||
.el-message-box {
|
.el-message-box {
|
||||||
@@ -850,35 +871,31 @@ i.x-status-icon.red {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tabs__nav-wrap::after {
|
// .el-tabs__nav-wrap::after {
|
||||||
background-color: #e4e7ed14;
|
// background-color: #e4e7ed14;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.dialog-title {
|
.dialog-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-banner-image {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vrc-instance-queue-message {
|
.vrc-instance-queue-message {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
top: 0 !important;
|
top: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vrc-instance-queue-message .el-message__content {
|
// .vrc-instance-queue-message .el-message__content {
|
||||||
margin-right: 20px;
|
// margin-right: 20px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-tabs {
|
// .el-tabs {
|
||||||
display: flex;
|
// display: flex;
|
||||||
flex-direction: column;
|
// flex-direction: column;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-tabs__content {
|
.el-tabs__content {
|
||||||
flex: 1;
|
// flex: 1;
|
||||||
max-height: 100%;
|
// max-height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,60 +906,56 @@ i.x-status-icon.red {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tabs__header {
|
// .el-tabs__header {
|
||||||
padding: 0 1px;
|
// padding: 0 1px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.zero-margin-tabs .el-tabs__header {
|
// .zero-margin-tabs .el-tabs__header {
|
||||||
margin-bottom: 0;
|
// margin-bottom: 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.x-friend-item .el-checkbox__inner,
|
// .x-friend-item .el-checkbox__inner,
|
||||||
.el-table__row .el-checkbox__inner {
|
// .el-table__row .el-checkbox__inner {
|
||||||
width: 28px;
|
// width: 28px;
|
||||||
height: 28px;
|
// height: 28px;
|
||||||
border-radius: 4px;
|
// border-radius: 4px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.x-friend-item .el-checkbox__inner::after,
|
// .x-friend-item .el-checkbox__inner::after,
|
||||||
.el-table__row .el-checkbox__inner::after {
|
// .el-table__row .el-checkbox__inner::after {
|
||||||
width: 8px;
|
// width: 8px;
|
||||||
height: 14px;
|
// height: 14px;
|
||||||
left: 8px;
|
// left: 8px;
|
||||||
top: 2px;
|
// top: 2px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.max-height-el-select .el-select-dropdown__wrap {
|
// .max-height-el-select .el-select-dropdown__wrap {
|
||||||
max-height: 83vh;
|
// max-height: 83vh;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-pagination .el-input .el-input__icon {
|
// .el-pagination .el-input .el-input__icon {
|
||||||
line-height: 22px;
|
// line-height: 22px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// User dialog memo: tag line-height
|
// .el-dialog__body .el-tag--mini {
|
||||||
.el-dialog__body .el-tag--mini {
|
// line-height: 17px;
|
||||||
line-height: 17px;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
// feed table detail time tag line-height
|
// .el-table__expanded-cell .el-tag--mini {
|
||||||
.el-table__expanded-cell .el-tag--mini {
|
// line-height: 18px;
|
||||||
line-height: 18px;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
// User dialog memo: input count background color
|
// .x-friend-item:hover .el-input__count {
|
||||||
.x-friend-item:hover .el-input__count {
|
// background: #f0f0f0;
|
||||||
background: #f0f0f0;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
// Align the left page with the right friend bar
|
|
||||||
.x-app > .x-container {
|
.x-app > .x-container {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item .el-tag--mini {
|
// .el-collapse-item .el-tag--mini {
|
||||||
line-height: 17px;
|
// line-height: 17px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.x-text-removed {
|
.x-text-removed {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
@@ -959,21 +972,38 @@ i.x-status-icon.red {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-carousel__mask {
|
// expand table cell on hover, TODO: replace with a better solution
|
||||||
// looks bad
|
.el-table .cell {
|
||||||
display: none;
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-table__row:hover .el-table__cell .cell {
|
||||||
|
display: revert;
|
||||||
|
-webkit-line-clamp: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .el-carousel__mask {
|
||||||
|
// // looks bad
|
||||||
|
// display: none;
|
||||||
|
// }
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
// Something changed the CSS of element-ui
|
// Something changed the CSS of element-ui
|
||||||
// The other parts are the same
|
// The other parts are the same
|
||||||
// It feels like can't fundamentally modify it
|
// It feels like can't fundamentally modify it
|
||||||
// And can only fix it bit by bit by overriding
|
// And can only fix it bit by bit by overriding
|
||||||
.el-switch__core:after {
|
// .el-switch__core:after {
|
||||||
top: 1.5px;
|
// top: 1.5px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.el-popover {
|
// .el-popover {
|
||||||
text-align: left;
|
// text-align: left;
|
||||||
word-break: break-word;
|
// word-break: break-word;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.el-dropdown-menu__item:not(.is-disabled):focus {
|
||||||
|
// dropdown item focus blue background looks bad
|
||||||
|
background-color: unset;
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
@@ -3,7 +3,7 @@
|
|||||||
--offy: calc(var(--offx) / 72 * 52);
|
--offy: calc(var(--offx) / 72 * 52);
|
||||||
}
|
}
|
||||||
.flags {
|
.flags {
|
||||||
background: url('/images/flags.png') no-repeat;
|
background: url('../images/flags.png') no-repeat;
|
||||||
background-size: calc(var(--offx) * 6);
|
background-size: calc(var(--offx) * 6);
|
||||||
width: var(--offx);
|
width: var(--offx);
|
||||||
height: var(--offy);
|
height: var(--offy);
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ button {
|
|||||||
div.x-friend-list
|
div.x-friend-list
|
||||||
> div:nth-child(1)
|
> div:nth-child(1)
|
||||||
> div
|
> div
|
||||||
> div.el-textarea.el-input--mini
|
> div.el-textarea.el-input--small
|
||||||
> span.el-input__count {
|
> span.el-input__count {
|
||||||
background-color: $--theme-bg-4;
|
background-color: $--theme-bg-4;
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ div.x-friend-list
|
|||||||
background-color: $--theme-bg-5;
|
background-color: $--theme-bg-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item .el-tag--mini {
|
.el-collapse-item .el-tag--small {
|
||||||
background-color: $--theme-bg-5;
|
background-color: $--theme-bg-5;
|
||||||
border: transparent;
|
border: transparent;
|
||||||
}
|
}
|
||||||
@@ -220,11 +220,11 @@ div.x-friend-list
|
|||||||
background-color: hsl($--theme-hue, $--theme-saturation, 27%);
|
background-color: hsl($--theme-hue, $--theme-saturation, 27%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::after {
|
||||||
border-bottom-color: $--theme-bg-5;
|
border-bottom-color: $--theme-bg-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
|
||||||
border-bottom-color: $--theme-border-2;
|
border-bottom-color: $--theme-border-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,11 +303,11 @@ div.x-friend-list
|
|||||||
border-color: hsl($--theme-hue, $--theme-saturation, 37%);
|
border-color: hsl($--theme-hue, $--theme-saturation, 37%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow::after {
|
||||||
border-right-color: hsl($--theme-hue, $--theme-saturation, 37%);
|
border-right-color: hsl($--theme-hue, $--theme-saturation, 37%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow {
|
||||||
border-right-color: hsl($--theme-hue, $--theme-saturation, 37%);
|
border-right-color: hsl($--theme-hue, $--theme-saturation, 37%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,104 @@ $--theme-warning: #e6a23c;
|
|||||||
$--theme-danger: #f56c6c;
|
$--theme-danger: #f56c6c;
|
||||||
$--theme-info: #909399;
|
$--theme-info: #909399;
|
||||||
|
|
||||||
// Mapping
|
$--border-color-lighter: hsl($--theme-hue, $--theme-saturation, 16%);
|
||||||
|
$--border-color-extra-light: hsl($--theme-hue, $--theme-saturation, 15%);
|
||||||
|
$--background-color-base: hsl($--theme-hue, $--theme-saturation, 16%);
|
||||||
|
$--input-focus-border: hsl($--theme-hue, $--theme-saturation, 33%);
|
||||||
|
$--table-header-background-color: hsl($--theme-hue, $--theme-saturation, 15%);
|
||||||
|
$--table-row-hover-background-color: hsl(
|
||||||
|
$--theme-hue,
|
||||||
|
$--theme-saturation,
|
||||||
|
18%
|
||||||
|
);
|
||||||
|
$--skeleton-to-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
||||||
|
$--tree-node-hover-background-color: hsl(
|
||||||
|
$--theme-hue,
|
||||||
|
$--theme-saturation,
|
||||||
|
16%
|
||||||
|
);
|
||||||
|
$--collapse-content-font-color: hsl($--theme-hue, $--theme-saturation, 66%);
|
||||||
|
$--message-close-icon-color: hsl($--theme-hue, $--theme-saturation, 60%);
|
||||||
|
$--dropdown-menu-box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
$--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
|
||||||
|
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
||||||
|
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
|
||||||
|
|
||||||
|
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||||
|
$colors: (
|
||||||
|
'white': $--theme-text-1,
|
||||||
|
'black': $--theme-bg-1,
|
||||||
|
'primary': (
|
||||||
|
'base': $--theme-primary
|
||||||
|
),
|
||||||
|
'success': (
|
||||||
|
'base': $--theme-success
|
||||||
|
),
|
||||||
|
'warning': (
|
||||||
|
'base': $--theme-warning
|
||||||
|
),
|
||||||
|
'danger': (
|
||||||
|
'base': $--theme-danger
|
||||||
|
),
|
||||||
|
'error': (
|
||||||
|
'base': $--theme-danger
|
||||||
|
),
|
||||||
|
'info': (
|
||||||
|
'base': $--theme-info
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$text-color: (
|
||||||
|
'primary': $--theme-text-1,
|
||||||
|
'regular': $--theme-text-2,
|
||||||
|
'secondary': $--theme-info,
|
||||||
|
'placeholder': $--theme-text-4,
|
||||||
|
'disabled': $--theme-text-4
|
||||||
|
),
|
||||||
|
$border-color: (
|
||||||
|
'': $--theme-border-1,
|
||||||
|
'light': $--theme-border-2,
|
||||||
|
'lighter': $--border-color-lighter,
|
||||||
|
'extra-light': $--border-color-extra-light,
|
||||||
|
'dark': $--theme-border-1,
|
||||||
|
'darker': $--theme-border-2
|
||||||
|
),
|
||||||
|
$fill-color: (
|
||||||
|
'': $--background-color-base,
|
||||||
|
'light': $--theme-bg-4,
|
||||||
|
'lighter': $--theme-bg-5,
|
||||||
|
'extra-light': $--border-color-extra-light,
|
||||||
|
'dark': $--theme-bg-2,
|
||||||
|
'darker': $--theme-bg-1,
|
||||||
|
'blank': $--theme-bg-4
|
||||||
|
),
|
||||||
|
$bg-color: (
|
||||||
|
'': $--theme-bg-2,
|
||||||
|
'page': $--theme-bg-1,
|
||||||
|
'overlay': $--theme-bg-4
|
||||||
|
),
|
||||||
|
$box-shadow: (
|
||||||
|
'': (
|
||||||
|
0px 12px 32px 4px rgba(0, 0, 0, 0.36),
|
||||||
|
0px 8px 20px rgba(0, 0, 0, 0.72)
|
||||||
|
),
|
||||||
|
'light': (
|
||||||
|
0px 0px 12px rgba(0, 0, 0, 0.72)
|
||||||
|
),
|
||||||
|
'lighter': (
|
||||||
|
0px 0px 6px rgba(0, 0, 0, 0.72)
|
||||||
|
),
|
||||||
|
'dark': (
|
||||||
|
0px 16px 48px 16px rgba(0, 0, 0, 0.72),
|
||||||
|
0px 12px 32px rgba(0, 0, 0, 0.72),
|
||||||
|
0px 8px 16px -8px rgba(0, 0, 0, 0.96)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$disabled: (
|
||||||
|
'bg-color': $--theme-bg-5,
|
||||||
|
'text-color': $--theme-text-4,
|
||||||
|
'border-color': $--theme-border-2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$--color-primary: $--theme-primary;
|
$--color-primary: $--theme-primary;
|
||||||
$--color-success: $--theme-success;
|
$--color-success: $--theme-success;
|
||||||
@@ -52,10 +149,6 @@ $--color-text-placeholder: $--theme-text-4;
|
|||||||
|
|
||||||
$--border-color-base: $--theme-border-1;
|
$--border-color-base: $--theme-border-1;
|
||||||
$--border-color-light: $--theme-bg-5;
|
$--border-color-light: $--theme-bg-5;
|
||||||
$--border-color-lighter: hsl($--theme-hue, $--theme-saturation, 16%);
|
|
||||||
$--border-color-extra-light: hsl($--theme-hue, $--theme-saturation, 15%);
|
|
||||||
|
|
||||||
$--background-color-base: hsl($--theme-hue, $--theme-saturation, 16%);
|
|
||||||
|
|
||||||
$--button-default-background-color: $--theme-bg-5;
|
$--button-default-background-color: $--theme-bg-5;
|
||||||
$--button-default-border-color: $--theme-border-2;
|
$--button-default-border-color: $--theme-border-2;
|
||||||
@@ -64,20 +157,13 @@ $--button-default-font-color: $--theme-text-2;
|
|||||||
$--input-background-color: $--theme-bg-5;
|
$--input-background-color: $--theme-bg-5;
|
||||||
$--input-border-color: $--theme-border-1;
|
$--input-border-color: $--theme-border-1;
|
||||||
$--input-font-color: $--theme-text-1;
|
$--input-font-color: $--theme-text-1;
|
||||||
$--input-focus-border: hsl($--theme-hue, $--theme-saturation, 33%);
|
|
||||||
|
|
||||||
$--select-input-focus-border-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
$--select-input-focus-border-color: $--input-focus-border;
|
||||||
|
|
||||||
$--dialog-background-color: $--theme-bg-4;
|
$--dialog-background-color: $--theme-bg-4;
|
||||||
$--popover-background-color: $--theme-bg-4;
|
$--popover-background-color: $--theme-bg-4;
|
||||||
|
|
||||||
$--table-border-color: $--theme-border-2;
|
$--table-border-color: $--theme-border-2;
|
||||||
$--table-header-background-color: hsl($--theme-hue, $--theme-saturation, 15%);
|
|
||||||
$--table-row-hover-background-color: hsl(
|
|
||||||
$--theme-hue,
|
|
||||||
$--theme-saturation,
|
|
||||||
18%
|
|
||||||
);
|
|
||||||
|
|
||||||
$--pagination-font-color: $--theme-text-2;
|
$--pagination-font-color: $--theme-text-2;
|
||||||
$--pagination-background-color: $--theme-bg-4;
|
$--pagination-background-color: $--theme-bg-4;
|
||||||
@@ -95,21 +181,11 @@ $--datepicker-header-font-color: $--theme-text-1;
|
|||||||
$--datepicker-icon-color: $--theme-text-1;
|
$--datepicker-icon-color: $--theme-text-1;
|
||||||
|
|
||||||
$--skeleton-color: $--theme-bg-5;
|
$--skeleton-color: $--theme-bg-5;
|
||||||
$--skeleton-to-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
|
||||||
|
|
||||||
$--select-dropdown-background: $--theme-bg-4;
|
$--select-dropdown-background: $--theme-bg-4;
|
||||||
$--select-dropdown-border: 1px solid $--theme-border-1;
|
$--select-dropdown-border: 1px solid $--theme-border-1;
|
||||||
|
|
||||||
$--dropdown-menu-box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
|
||||||
$--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
|
|
||||||
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
|
||||||
|
|
||||||
$--tree-font-color: $--theme-text-2;
|
$--tree-font-color: $--theme-text-2;
|
||||||
$--tree-node-hover-background-color: hsl(
|
|
||||||
$--theme-hue,
|
|
||||||
$--theme-saturation,
|
|
||||||
16%
|
|
||||||
);
|
|
||||||
|
|
||||||
$--menu-item-font-color: $--theme-text-2;
|
$--menu-item-font-color: $--theme-text-2;
|
||||||
$--menu-background-color: $--theme-bg-2;
|
$--menu-background-color: $--theme-bg-2;
|
||||||
@@ -118,10 +194,8 @@ $--collapse-header-background-color: $--theme-bg-5;
|
|||||||
$--collapse-content-background-color: $--theme-bg-4;
|
$--collapse-content-background-color: $--theme-bg-4;
|
||||||
$--collapse-border-color: $--theme-border-2;
|
$--collapse-border-color: $--theme-border-2;
|
||||||
$--collapse-header-font-color: $--theme-text-2;
|
$--collapse-header-font-color: $--theme-text-2;
|
||||||
$--collapse-content-font-color: hsl($--theme-hue, $--theme-saturation, 66%);
|
|
||||||
|
|
||||||
$--message-background-color: $--theme-bg-5;
|
$--message-background-color: $--theme-bg-5;
|
||||||
$--message-close-icon-color: hsl($--theme-hue, $--theme-saturation, 60%);
|
|
||||||
$--message-close-hover-color: $--theme-text-2;
|
$--message-close-hover-color: $--theme-text-2;
|
||||||
$--message-success-font-color: #52c41a;
|
$--message-success-font-color: #52c41a;
|
||||||
$--message-info-font-color: #1890ff;
|
$--message-info-font-color: #1890ff;
|
||||||
@@ -130,13 +204,8 @@ $--message-danger-font-color: #ff4d4f;
|
|||||||
|
|
||||||
$--pagination-hover-color: $--theme-text-4;
|
$--pagination-hover-color: $--theme-text-4;
|
||||||
|
|
||||||
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
|
|
||||||
$--card-background-color: $--theme-bg-4;
|
$--card-background-color: $--theme-bg-4;
|
||||||
|
|
||||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|
||||||
|
|
||||||
@import '~element-ui/packages/theme-chalk/src/index';
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--group-calendar-event-bg: #{$--calendar-selected-background-color};
|
--group-calendar-event-bg: #{$--calendar-selected-background-color};
|
||||||
--group-calendar-badge-following: #{darken($--theme-primary, 20%)};
|
--group-calendar-badge-following: #{darken($--theme-primary, 20%)};
|
||||||
@@ -218,35 +287,35 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|||||||
background-color: #{darken($--theme-primary, 20%)} !important;
|
background-color: #{darken($--theme-primary, 20%)} !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow {
|
||||||
border-top-color: $--theme-bg-4 !important;
|
border-top-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow::after {
|
||||||
border-top-color: $--theme-bg-4 !important;
|
border-top-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
|
||||||
border-bottom-color: $--theme-bg-4 !important;
|
border-bottom-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::after {
|
||||||
border-bottom-color: $--theme-bg-4 !important;
|
border-bottom-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow {
|
||||||
border-left-color: $--theme-bg-4 !important;
|
border-left-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow::after {
|
||||||
border-left-color: $--theme-bg-4 !important;
|
border-left-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow {
|
||||||
border-right-color: $--theme-bg-4 !important;
|
border-right-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow::after {
|
||||||
border-right-color: $--theme-bg-4 !important;
|
border-right-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,105 @@ $--theme-warning: #e6a23c;
|
|||||||
$--theme-danger: #f56c6c;
|
$--theme-danger: #f56c6c;
|
||||||
$--theme-info: #909399;
|
$--theme-info: #909399;
|
||||||
|
|
||||||
// Mapping
|
$--border-color-lighter: hsl($--theme-hue, $--theme-saturation, 16%);
|
||||||
|
$--border-color-extra-light: hsl($--theme-hue, $--theme-saturation, 15%);
|
||||||
|
$--background-color-base: hsl($--theme-hue, $--theme-saturation, 16%);
|
||||||
|
$--input-focus-border: hsl($--theme-hue, $--theme-saturation, 33%);
|
||||||
|
$--table-header-background-color: hsl($--theme-hue, $--theme-saturation, 15%);
|
||||||
|
$--table-row-hover-background-color: hsl(
|
||||||
|
$--theme-hue,
|
||||||
|
$--theme-saturation,
|
||||||
|
18%
|
||||||
|
);
|
||||||
|
$--skeleton-to-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
||||||
|
$--tree-node-hover-background-color: hsl(
|
||||||
|
$--theme-hue,
|
||||||
|
$--theme-saturation,
|
||||||
|
16%
|
||||||
|
);
|
||||||
|
$--collapse-content-font-color: hsl($--theme-hue, $--theme-saturation, 66%);
|
||||||
|
$--message-close-icon-color: hsl($--theme-hue, $--theme-saturation, 60%);
|
||||||
|
$--dropdown-menu-box-shadow: 0 2px 12px
|
||||||
|
hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
||||||
|
$--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
|
||||||
|
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
||||||
|
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
|
||||||
|
|
||||||
|
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||||
|
$colors: (
|
||||||
|
'white': $--theme-text-1,
|
||||||
|
'black': $--theme-bg-1,
|
||||||
|
'primary': (
|
||||||
|
'base': $--theme-primary
|
||||||
|
),
|
||||||
|
'success': (
|
||||||
|
'base': $--theme-success
|
||||||
|
),
|
||||||
|
'warning': (
|
||||||
|
'base': $--theme-warning
|
||||||
|
),
|
||||||
|
'danger': (
|
||||||
|
'base': $--theme-danger
|
||||||
|
),
|
||||||
|
'error': (
|
||||||
|
'base': $--theme-danger
|
||||||
|
),
|
||||||
|
'info': (
|
||||||
|
'base': $--theme-info
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$text-color: (
|
||||||
|
'primary': $--theme-text-1,
|
||||||
|
'regular': $--theme-text-2,
|
||||||
|
'secondary': $--theme-info,
|
||||||
|
'placeholder': $--theme-text-4,
|
||||||
|
'disabled': $--theme-text-4
|
||||||
|
),
|
||||||
|
$border-color: (
|
||||||
|
'': $--theme-border-1,
|
||||||
|
'light': $--theme-border-2,
|
||||||
|
'lighter': $--border-color-lighter,
|
||||||
|
'extra-light': $--border-color-extra-light,
|
||||||
|
'dark': $--theme-border-1,
|
||||||
|
'darker': $--theme-border-2
|
||||||
|
),
|
||||||
|
$fill-color: (
|
||||||
|
'': $--background-color-base,
|
||||||
|
'light': $--theme-bg-4,
|
||||||
|
'lighter': $--theme-bg-5,
|
||||||
|
'extra-light': $--border-color-extra-light,
|
||||||
|
'dark': $--theme-bg-2,
|
||||||
|
'darker': $--theme-bg-1,
|
||||||
|
'blank': $--theme-bg-4
|
||||||
|
),
|
||||||
|
$bg-color: (
|
||||||
|
'': $--theme-bg-2,
|
||||||
|
'page': $--theme-bg-1,
|
||||||
|
'overlay': $--theme-bg-4
|
||||||
|
),
|
||||||
|
$box-shadow: (
|
||||||
|
'': (
|
||||||
|
0px 12px 32px 4px rgba(0, 0, 0, 0.36),
|
||||||
|
0px 8px 20px rgba(0, 0, 0, 0.72)
|
||||||
|
),
|
||||||
|
'light': (
|
||||||
|
0px 0px 12px rgba(0, 0, 0, 0.72)
|
||||||
|
),
|
||||||
|
'lighter': (
|
||||||
|
0px 0px 6px rgba(0, 0, 0, 0.72)
|
||||||
|
),
|
||||||
|
'dark': (
|
||||||
|
0px 16px 48px 16px rgba(0, 0, 0, 0.72),
|
||||||
|
0px 12px 32px rgba(0, 0, 0, 0.72),
|
||||||
|
0px 8px 16px -8px rgba(0, 0, 0, 0.96)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$disabled: (
|
||||||
|
'bg-color': $--theme-bg-5,
|
||||||
|
'text-color': $--theme-text-4,
|
||||||
|
'border-color': $--theme-border-2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$--color-primary: $--theme-primary;
|
$--color-primary: $--theme-primary;
|
||||||
$--color-success: $--theme-success;
|
$--color-success: $--theme-success;
|
||||||
@@ -50,10 +148,6 @@ $--color-text-placeholder: $--theme-text-4;
|
|||||||
|
|
||||||
$--border-color-base: $--theme-border-1;
|
$--border-color-base: $--theme-border-1;
|
||||||
$--border-color-light: $--theme-bg-5;
|
$--border-color-light: $--theme-bg-5;
|
||||||
$--border-color-lighter: hsl($--theme-hue, $--theme-saturation, 16%);
|
|
||||||
$--border-color-extra-light: hsl($--theme-hue, $--theme-saturation, 15%);
|
|
||||||
|
|
||||||
$--background-color-base: hsl($--theme-hue, $--theme-saturation, 16%);
|
|
||||||
|
|
||||||
$--button-default-background-color: $--theme-bg-5;
|
$--button-default-background-color: $--theme-bg-5;
|
||||||
$--button-default-border-color: $--theme-border-2;
|
$--button-default-border-color: $--theme-border-2;
|
||||||
@@ -62,20 +156,13 @@ $--button-default-font-color: $--theme-text-2;
|
|||||||
$--input-background-color: $--theme-bg-5;
|
$--input-background-color: $--theme-bg-5;
|
||||||
$--input-border-color: $--theme-border-1;
|
$--input-border-color: $--theme-border-1;
|
||||||
$--input-font-color: $--theme-text-1;
|
$--input-font-color: $--theme-text-1;
|
||||||
$--input-focus-border: hsl($--theme-hue, $--theme-saturation, 33%);
|
|
||||||
|
|
||||||
$--select-input-focus-border-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
$--select-input-focus-border-color: $--input-focus-border;
|
||||||
|
|
||||||
$--dialog-background-color: $--theme-bg-4;
|
$--dialog-background-color: $--theme-bg-4;
|
||||||
$--popover-background-color: $--theme-bg-4;
|
$--popover-background-color: $--theme-bg-4;
|
||||||
|
|
||||||
$--table-border-color: $--theme-border-2;
|
$--table-border-color: $--theme-border-2;
|
||||||
$--table-header-background-color: hsl($--theme-hue, $--theme-saturation, 15%);
|
|
||||||
$--table-row-hover-background-color: hsl(
|
|
||||||
$--theme-hue,
|
|
||||||
$--theme-saturation,
|
|
||||||
18%
|
|
||||||
);
|
|
||||||
|
|
||||||
$--pagination-font-color: $--theme-text-2;
|
$--pagination-font-color: $--theme-text-2;
|
||||||
$--pagination-background-color: $--theme-bg-4;
|
$--pagination-background-color: $--theme-bg-4;
|
||||||
@@ -93,22 +180,11 @@ $--datepicker-header-font-color: $--theme-text-1;
|
|||||||
$--datepicker-icon-color: $--theme-text-1;
|
$--datepicker-icon-color: $--theme-text-1;
|
||||||
|
|
||||||
$--skeleton-color: $--theme-bg-5;
|
$--skeleton-color: $--theme-bg-5;
|
||||||
$--skeleton-to-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
|
||||||
|
|
||||||
$--select-dropdown-background: $--theme-bg-4;
|
$--select-dropdown-background: $--theme-bg-4;
|
||||||
$--select-dropdown-border: 1px solid $--theme-border-1;
|
$--select-dropdown-border: 1px solid $--theme-border-1;
|
||||||
|
|
||||||
$--dropdown-menu-box-shadow: 0 2px 12px
|
|
||||||
hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
|
||||||
$--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
|
|
||||||
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
|
||||||
|
|
||||||
$--tree-font-color: $--theme-text-2;
|
$--tree-font-color: $--theme-text-2;
|
||||||
$--tree-node-hover-background-color: hsl(
|
|
||||||
$--theme-hue,
|
|
||||||
$--theme-saturation,
|
|
||||||
16%
|
|
||||||
);
|
|
||||||
|
|
||||||
$--menu-item-font-color: $--theme-text-2;
|
$--menu-item-font-color: $--theme-text-2;
|
||||||
$--menu-background-color: $--theme-bg-2;
|
$--menu-background-color: $--theme-bg-2;
|
||||||
@@ -117,10 +193,8 @@ $--collapse-header-background-color: $--theme-bg-5;
|
|||||||
$--collapse-content-background-color: $--theme-bg-4;
|
$--collapse-content-background-color: $--theme-bg-4;
|
||||||
$--collapse-border-color: $--theme-border-2;
|
$--collapse-border-color: $--theme-border-2;
|
||||||
$--collapse-header-font-color: $--theme-text-2;
|
$--collapse-header-font-color: $--theme-text-2;
|
||||||
$--collapse-content-font-color: hsl($--theme-hue, $--theme-saturation, 66%);
|
|
||||||
|
|
||||||
$--message-background-color: $--theme-bg-5;
|
$--message-background-color: $--theme-bg-5;
|
||||||
$--message-close-icon-color: hsl($--theme-hue, $--theme-saturation, 60%);
|
|
||||||
$--message-close-hover-color: $--theme-text-2;
|
$--message-close-hover-color: $--theme-text-2;
|
||||||
$--message-success-font-color: #52c41a;
|
$--message-success-font-color: #52c41a;
|
||||||
$--message-info-font-color: #1890ff;
|
$--message-info-font-color: #1890ff;
|
||||||
@@ -129,13 +203,8 @@ $--message-danger-font-color: #ff4d4f;
|
|||||||
|
|
||||||
$--pagination-hover-color: $--theme-text-4;
|
$--pagination-hover-color: $--theme-text-4;
|
||||||
|
|
||||||
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
|
|
||||||
$--card-background-color: $--theme-bg-4;
|
$--card-background-color: $--theme-bg-4;
|
||||||
|
|
||||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|
||||||
|
|
||||||
@import '~element-ui/packages/theme-chalk/src/index';
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--group-calendar-event-bg: #{$--calendar-selected-background-color};
|
--group-calendar-event-bg: #{$--calendar-selected-background-color};
|
||||||
--group-calendar-badge-following: #{$--color-success};
|
--group-calendar-badge-following: #{$--color-success};
|
||||||
@@ -170,35 +239,35 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|||||||
background-color: $--theme-border-2 !important;
|
background-color: $--theme-border-2 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow {
|
||||||
border-top-color: $--theme-bg-4 !important;
|
border-top-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow::after {
|
||||||
border-top-color: $--theme-bg-4 !important;
|
border-top-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
|
||||||
border-bottom-color: $--theme-bg-4 !important;
|
border-bottom-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::after {
|
||||||
border-bottom-color: $--theme-bg-4 !important;
|
border-bottom-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow {
|
||||||
border-left-color: $--theme-bg-4 !important;
|
border-left-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow::after {
|
||||||
border-left-color: $--theme-bg-4 !important;
|
border-left-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow {
|
||||||
border-right-color: $--theme-bg-4 !important;
|
border-right-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow::after {
|
||||||
border-right-color: $--theme-bg-4 !important;
|
border-right-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,105 @@ $--theme-warning: #e6a23c;
|
|||||||
$--theme-danger: #f56c6c;
|
$--theme-danger: #f56c6c;
|
||||||
$--theme-info: #909399;
|
$--theme-info: #909399;
|
||||||
|
|
||||||
// Mapping
|
$--border-color-lighter: hsl($--theme-hue, $--theme-saturation, 16%);
|
||||||
|
$--border-color-extra-light: hsl($--theme-hue, $--theme-saturation, 15%);
|
||||||
|
$--background-color-base: hsl($--theme-hue, $--theme-saturation, 16%);
|
||||||
|
$--input-focus-border: hsl($--theme-hue, $--theme-saturation, 33%);
|
||||||
|
$--table-header-background-color: hsl($--theme-hue, $--theme-saturation, 15%);
|
||||||
|
$--table-row-hover-background-color: hsl(
|
||||||
|
$--theme-hue,
|
||||||
|
$--theme-saturation,
|
||||||
|
18%
|
||||||
|
);
|
||||||
|
$--skeleton-to-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
||||||
|
$--tree-node-hover-background-color: hsl(
|
||||||
|
$--theme-hue,
|
||||||
|
$--theme-saturation,
|
||||||
|
16%
|
||||||
|
);
|
||||||
|
$--collapse-content-font-color: hsl($--theme-hue, $--theme-saturation, 66%);
|
||||||
|
$--message-close-icon-color: hsl($--theme-hue, $--theme-saturation, 60%);
|
||||||
|
$--dropdown-menu-box-shadow: 0 2px 12px
|
||||||
|
hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
||||||
|
$--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
|
||||||
|
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
||||||
|
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
|
||||||
|
|
||||||
|
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
|
||||||
|
$colors: (
|
||||||
|
'white': $--theme-text-1,
|
||||||
|
'black': $--theme-bg-1,
|
||||||
|
'primary': (
|
||||||
|
'base': $--theme-primary
|
||||||
|
),
|
||||||
|
'success': (
|
||||||
|
'base': $--theme-success
|
||||||
|
),
|
||||||
|
'warning': (
|
||||||
|
'base': $--theme-warning
|
||||||
|
),
|
||||||
|
'danger': (
|
||||||
|
'base': $--theme-danger
|
||||||
|
),
|
||||||
|
'error': (
|
||||||
|
'base': $--theme-danger
|
||||||
|
),
|
||||||
|
'info': (
|
||||||
|
'base': $--theme-info
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$text-color: (
|
||||||
|
'primary': $--theme-text-1,
|
||||||
|
'regular': $--theme-text-2,
|
||||||
|
'secondary': $--theme-info,
|
||||||
|
'placeholder': $--theme-text-4,
|
||||||
|
'disabled': $--theme-text-4
|
||||||
|
),
|
||||||
|
$border-color: (
|
||||||
|
'': $--theme-border-1,
|
||||||
|
'light': $--theme-border-2,
|
||||||
|
'lighter': $--border-color-lighter,
|
||||||
|
'extra-light': $--border-color-extra-light,
|
||||||
|
'dark': $--theme-border-1,
|
||||||
|
'darker': $--theme-border-2
|
||||||
|
),
|
||||||
|
$fill-color: (
|
||||||
|
'': $--background-color-base,
|
||||||
|
'light': $--theme-bg-4,
|
||||||
|
'lighter': $--theme-bg-5,
|
||||||
|
'extra-light': $--border-color-extra-light,
|
||||||
|
'dark': $--theme-bg-2,
|
||||||
|
'darker': $--theme-bg-1,
|
||||||
|
'blank': $--theme-bg-4
|
||||||
|
),
|
||||||
|
$bg-color: (
|
||||||
|
'': $--theme-bg-2,
|
||||||
|
'page': $--theme-bg-1,
|
||||||
|
'overlay': $--theme-bg-4
|
||||||
|
),
|
||||||
|
$box-shadow: (
|
||||||
|
'': (
|
||||||
|
0px 12px 32px 4px hsla($--theme-hue, $--theme-saturation, 0%, 0.36),
|
||||||
|
0px 8px 20px hsla($--theme-hue, $--theme-saturation, 0%, 0.72)
|
||||||
|
),
|
||||||
|
'light': (
|
||||||
|
0px 0px 12px hsla($--theme-hue, $--theme-saturation, 0%, 0.72)
|
||||||
|
),
|
||||||
|
'lighter': (
|
||||||
|
0px 0px 6px hsla($--theme-hue, $--theme-saturation, 0%, 0.72)
|
||||||
|
),
|
||||||
|
'dark': (
|
||||||
|
0px 16px 48px 16px hsla($--theme-hue, $--theme-saturation, 0%, 0.72),
|
||||||
|
0px 12px 32px hsla($--theme-hue, $--theme-saturation, 0%, 0.72),
|
||||||
|
0px 8px 16px -8px hsla($--theme-hue, $--theme-saturation, 0%, 0.96)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$disabled: (
|
||||||
|
'bg-color': $--theme-bg-5,
|
||||||
|
'text-color': $--theme-text-4,
|
||||||
|
'border-color': $--theme-border-2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
$--color-primary: $--theme-primary;
|
$--color-primary: $--theme-primary;
|
||||||
$--color-success: $--theme-success;
|
$--color-success: $--theme-success;
|
||||||
@@ -50,10 +148,6 @@ $--color-text-placeholder: $--theme-text-4;
|
|||||||
|
|
||||||
$--border-color-base: $--theme-border-1;
|
$--border-color-base: $--theme-border-1;
|
||||||
$--border-color-light: $--theme-bg-5;
|
$--border-color-light: $--theme-bg-5;
|
||||||
$--border-color-lighter: hsl($--theme-hue, $--theme-saturation, 16%);
|
|
||||||
$--border-color-extra-light: hsl($--theme-hue, $--theme-saturation, 15%);
|
|
||||||
|
|
||||||
$--background-color-base: hsl($--theme-hue, $--theme-saturation, 16%);
|
|
||||||
|
|
||||||
$--button-default-background-color: $--theme-bg-5;
|
$--button-default-background-color: $--theme-bg-5;
|
||||||
$--button-default-border-color: $--theme-border-2;
|
$--button-default-border-color: $--theme-border-2;
|
||||||
@@ -62,20 +156,13 @@ $--button-default-font-color: $--theme-text-2;
|
|||||||
$--input-background-color: $--theme-bg-5;
|
$--input-background-color: $--theme-bg-5;
|
||||||
$--input-border-color: $--theme-border-1;
|
$--input-border-color: $--theme-border-1;
|
||||||
$--input-font-color: $--theme-text-1;
|
$--input-font-color: $--theme-text-1;
|
||||||
$--input-focus-border: hsl($--theme-hue, $--theme-saturation, 33%);
|
|
||||||
|
|
||||||
$--select-input-focus-border-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
$--select-input-focus-border-color: $--input-focus-border;
|
||||||
|
|
||||||
$--dialog-background-color: $--theme-bg-4;
|
$--dialog-background-color: $--theme-bg-4;
|
||||||
$--popover-background-color: $--theme-bg-4;
|
$--popover-background-color: $--theme-bg-4;
|
||||||
|
|
||||||
$--table-border-color: $--theme-border-2;
|
$--table-border-color: $--theme-border-2;
|
||||||
$--table-header-background-color: hsl($--theme-hue, $--theme-saturation, 15%);
|
|
||||||
$--table-row-hover-background-color: hsl(
|
|
||||||
$--theme-hue,
|
|
||||||
$--theme-saturation,
|
|
||||||
18%
|
|
||||||
);
|
|
||||||
|
|
||||||
$--pagination-font-color: $--theme-text-2;
|
$--pagination-font-color: $--theme-text-2;
|
||||||
$--pagination-background-color: $--theme-bg-4;
|
$--pagination-background-color: $--theme-bg-4;
|
||||||
@@ -93,22 +180,11 @@ $--datepicker-header-font-color: $--theme-text-1;
|
|||||||
$--datepicker-icon-color: $--theme-text-1;
|
$--datepicker-icon-color: $--theme-text-1;
|
||||||
|
|
||||||
$--skeleton-color: $--theme-bg-5;
|
$--skeleton-color: $--theme-bg-5;
|
||||||
$--skeleton-to-color: hsl($--theme-hue, $--theme-saturation, 33%);
|
|
||||||
|
|
||||||
$--select-dropdown-background: $--theme-bg-4;
|
$--select-dropdown-background: $--theme-bg-4;
|
||||||
$--select-dropdown-border: 1px solid $--theme-border-1;
|
$--select-dropdown-border: 1px solid $--theme-border-1;
|
||||||
|
|
||||||
$--dropdown-menu-box-shadow: 0 2px 12px
|
|
||||||
hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
|
||||||
$--box-shadow-base: 0 1px 2px hsla($--theme-hue, $--theme-saturation, 0%, 0.1);
|
|
||||||
$--box-shadow-dark: 0 1px 3px hsla($--theme-hue, $--theme-saturation, 0%, 0.15);
|
|
||||||
|
|
||||||
$--tree-font-color: $--theme-text-2;
|
$--tree-font-color: $--theme-text-2;
|
||||||
$--tree-node-hover-background-color: hsl(
|
|
||||||
$--theme-hue,
|
|
||||||
$--theme-saturation,
|
|
||||||
16%
|
|
||||||
);
|
|
||||||
|
|
||||||
$--menu-item-font-color: $--theme-text-2;
|
$--menu-item-font-color: $--theme-text-2;
|
||||||
$--menu-background-color: $--theme-bg-2;
|
$--menu-background-color: $--theme-bg-2;
|
||||||
@@ -117,10 +193,8 @@ $--collapse-header-background-color: $--theme-bg-5;
|
|||||||
$--collapse-content-background-color: $--theme-bg-4;
|
$--collapse-content-background-color: $--theme-bg-4;
|
||||||
$--collapse-border-color: $--theme-border-2;
|
$--collapse-border-color: $--theme-border-2;
|
||||||
$--collapse-header-font-color: $--theme-text-2;
|
$--collapse-header-font-color: $--theme-text-2;
|
||||||
$--collapse-content-font-color: hsl($--theme-hue, $--theme-saturation, 66%);
|
|
||||||
|
|
||||||
$--message-background-color: $--theme-bg-5;
|
$--message-background-color: $--theme-bg-5;
|
||||||
$--message-close-icon-color: hsl($--theme-hue, $--theme-saturation, 60%);
|
|
||||||
$--message-close-hover-color: $--theme-text-2;
|
$--message-close-hover-color: $--theme-text-2;
|
||||||
$--message-success-font-color: #52c41a;
|
$--message-success-font-color: #52c41a;
|
||||||
$--message-info-font-color: #1890ff;
|
$--message-info-font-color: #1890ff;
|
||||||
@@ -129,13 +203,8 @@ $--message-danger-font-color: #ff4d4f;
|
|||||||
|
|
||||||
$--pagination-hover-color: $--theme-text-4;
|
$--pagination-hover-color: $--theme-text-4;
|
||||||
|
|
||||||
$--calendar-selected-background-color: rgba($--theme-primary, 0.1);
|
|
||||||
$--card-background-color: $--theme-bg-4;
|
$--card-background-color: $--theme-bg-4;
|
||||||
|
|
||||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|
||||||
|
|
||||||
@import '~element-ui/packages/theme-chalk/src/index';
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--group-calendar-event-bg: #{$--calendar-selected-background-color};
|
--group-calendar-event-bg: #{$--calendar-selected-background-color};
|
||||||
--group-calendar-badge-following: #{$--color-success};
|
--group-calendar-badge-following: #{$--color-success};
|
||||||
@@ -170,35 +239,35 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
|
|||||||
background-color: $--theme-border-2 !important;
|
background-color: $--theme-border-2 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow {
|
||||||
border-top-color: $--theme-bg-4 !important;
|
border-top-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow::after {
|
||||||
border-top-color: $--theme-bg-4 !important;
|
border-top-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
|
||||||
border-bottom-color: $--theme-bg-4 !important;
|
border-bottom-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::after {
|
||||||
border-bottom-color: $--theme-bg-4 !important;
|
border-bottom-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow {
|
||||||
border-left-color: $--theme-bg-4 !important;
|
border-left-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow::after {
|
||||||
border-left-color: $--theme-bg-4 !important;
|
border-left-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow {
|
||||||
border-right-color: $--theme-bg-4 !important;
|
border-right-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow::after {
|
||||||
border-right-color: $--theme-bg-4 !important;
|
border-right-color: $--theme-bg-4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -598,7 +598,7 @@ img.friends-list-avatar {
|
|||||||
border-radius: var(--dv_lg-rounded) !important;
|
border-radius: var(--dv_lg-rounded) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tag--mini {
|
.el-tag--small {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
@@ -627,7 +627,7 @@ img.friends-list-avatar {
|
|||||||
background-color: var(--dv_muted);
|
background-color: var(--dv_muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-input--mini .el-textarea__inner:hover {
|
.el-input--small .el-textarea__inner:hover {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -730,7 +730,7 @@ i[class='el-icon-star-off']:not(.el-menu-item div.el-tooltip i) {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item .el-tag--mini {
|
.el-collapse-item .el-tag--small {
|
||||||
border: transparent;
|
border: transparent;
|
||||||
background-color: var(--dv_bg-bot);
|
background-color: var(--dv_bg-bot);
|
||||||
}
|
}
|
||||||
@@ -786,7 +786,7 @@ i[class='el-icon-star-off']:not(.el-menu-item div.el-tooltip i) {
|
|||||||
div.x-friend-list
|
div.x-friend-list
|
||||||
> div:nth-child(1)
|
> div:nth-child(1)
|
||||||
> div
|
> div
|
||||||
> div.el-textarea.el-input--mini
|
> div.el-textarea.el-input--small
|
||||||
> span.el-input__count {
|
> span.el-input__count {
|
||||||
background-color: var(--mid) !important;
|
background-color: var(--mid) !important;
|
||||||
}
|
}
|
||||||
@@ -831,3 +831,27 @@ div.x-friend-list
|
|||||||
.group-header {
|
.group-header {
|
||||||
border-bottom: 2px solid var(--dv_muted) !important;
|
border-bottom: 2px solid var(--dv_muted) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper {
|
||||||
|
background-color: var(--dv_bg-top) !important;
|
||||||
|
border: 1px solid var(--dv_muted) !important;
|
||||||
|
color: var(--dv_bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-focused {
|
||||||
|
border-color: var(--dv_bright) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-disabled {
|
||||||
|
background-color: var(--dv_bg-bot) !important;
|
||||||
|
color: var(--dv_muted) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper {
|
||||||
|
background-color: var(--dv_bg-top) !important;
|
||||||
|
border: 1px solid var(--dv_muted) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper.is-focus {
|
||||||
|
border-color: var(--dv_bright) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ path[stroke='#e5e9f2'] {
|
|||||||
stroke: var(--farback) !important;
|
stroke: var(--farback) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item .el-tag--mini {
|
.el-collapse-item .el-tag--small {
|
||||||
border: transparent;
|
border: transparent;
|
||||||
background-color: #333 !important;
|
background-color: #333 !important;
|
||||||
}
|
}
|
||||||
@@ -372,7 +372,7 @@ path[stroke='#e5e9f2'] {
|
|||||||
div.x-friend-list
|
div.x-friend-list
|
||||||
> div:nth-child(1)
|
> div:nth-child(1)
|
||||||
> div
|
> div
|
||||||
> div.el-textarea.el-input--mini
|
> div.el-textarea.el-input--small
|
||||||
> span.el-input__count {
|
> span.el-input__count {
|
||||||
background-color: var(--mid) !important;
|
background-color: var(--mid) !important;
|
||||||
}
|
}
|
||||||
@@ -402,3 +402,27 @@ div.x-friend-list
|
|||||||
.el-timeline-item__node {
|
.el-timeline-item__node {
|
||||||
background-color: var(--theme-text-muted) !important;
|
background-color: var(--theme-text-muted) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper {
|
||||||
|
background-color: var(--top) !important;
|
||||||
|
border: 1px solid var(--top-border) !important;
|
||||||
|
color: var(--theme-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-focused {
|
||||||
|
border-color: var(--theme-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-disabled {
|
||||||
|
background-color: var(--farback) !important;
|
||||||
|
color: var(--theme-text-muted) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper {
|
||||||
|
background-color: var(--top) !important;
|
||||||
|
border: 1px solid var(--top-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper.is-focus {
|
||||||
|
border-color: var(--theme-text) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1651,35 +1651,35 @@ img.x-link.el-popover__reference {
|
|||||||
border-color: rgb(var(--md-sys-color-outline-variant)) !important;
|
border-color: rgb(var(--md-sys-color-outline-variant)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow {
|
||||||
border-top-color: var(--md-sys-color-surface-3) !important;
|
border-top-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='top'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='top'] .el-popper__arrow::after {
|
||||||
border-top-color: var(--md-sys-color-surface-3) !important;
|
border-top-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow {
|
||||||
border-bottom-color: var(--md-sys-color-surface-3) !important;
|
border-bottom-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='bottom'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='bottom'] .el-popper__arrow::after {
|
||||||
border-bottom-color: var(--md-sys-color-surface-3) !important;
|
border-bottom-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow {
|
||||||
border-left-color: var(--md-sys-color-surface-3) !important;
|
border-left-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='left'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='left'] .el-popper__arrow::after {
|
||||||
border-left-color: var(--md-sys-color-surface-3) !important;
|
border-left-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow {
|
||||||
border-right-color: var(--md-sys-color-surface-3) !important;
|
border-right-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-popper[x-placement^='right'] .popper__arrow::after {
|
.el-popper[data-popper-placement^='right'] .el-popper__arrow::after {
|
||||||
border-right-color: var(--md-sys-color-surface-3) !important;
|
border-right-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2065,7 +2065,7 @@ i.x-user-status {
|
|||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item .el-tag--mini {
|
.el-collapse-item .el-tag--small {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: transparent;
|
border: transparent;
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
@@ -2074,7 +2074,7 @@ i.x-user-status {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dialog__body .el-tag--mini {
|
.el-dialog__body .el-tag--small {
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
.el-divider {
|
.el-divider {
|
||||||
@@ -2149,7 +2149,7 @@ i.x-user-status {
|
|||||||
div.x-friend-list
|
div.x-friend-list
|
||||||
> div:nth-child(1)
|
> div:nth-child(1)
|
||||||
> div
|
> div
|
||||||
> div.el-textarea.el-input--mini
|
> div.el-textarea.el-input--small
|
||||||
> span.el-input__count {
|
> span.el-input__count {
|
||||||
background-color: var(--md-sys-color-surface-3) !important;
|
background-color: var(--md-sys-color-surface-3) !important;
|
||||||
}
|
}
|
||||||
@@ -2192,3 +2192,44 @@ div.x-friend-list
|
|||||||
.group-header {
|
.group-header {
|
||||||
border-bottom: 2px solid rgba(var(--md-sys-color-outline), 0.5) !important;
|
border-bottom: 2px solid rgba(var(--md-sys-color-outline), 0.5) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper {
|
||||||
|
background-color: rgb(var(--md-sys-color-surface-variant)) !important;
|
||||||
|
border: 1px solid rgb(var(--md-sys-color-outline-variant)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-focused {
|
||||||
|
border-color: rgb(var(--md-sys-color-primary)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-disabled {
|
||||||
|
background-color: rgba(var(--md-sys-color-on-surface), 0.12) !important;
|
||||||
|
color: rgba(var(--md-sys-color-on-surface), 0.38) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper {
|
||||||
|
background-color: rgb(var(--md-sys-color-surface-variant)) !important;
|
||||||
|
border: 1px solid rgb(var(--md-sys-color-outline-variant)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper.is-focus {
|
||||||
|
border-color: rgb(var(--md-sys-color-primary)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
background-color: rgb(var(--md-sys-color-surface)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table tr,
|
||||||
|
.el-table td.el-table__cell,
|
||||||
|
.el-table th.el-table__cell {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||||||
|
background-color: rgba(var(--md-sys-color-primary), 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__body-wrapper .el-table__row:hover > .el-table__cell {
|
||||||
|
background-color: rgba(var(--md-sys-color-primary), 0.08) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ input[type='checkbox']:checked + .el-switch__core {
|
|||||||
background-color: var(--lighter-lighter-bg);
|
background-color: var(--lighter-lighter-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-collapse-item .el-tag--mini {
|
.el-collapse-item .el-tag--small {
|
||||||
border: transparent;
|
border: transparent;
|
||||||
background-color: var(--lighter-lighter-bg);
|
background-color: var(--lighter-lighter-bg);
|
||||||
}
|
}
|
||||||
@@ -506,3 +506,45 @@ input[type='checkbox']:checked + .el-switch__core {
|
|||||||
.group-header {
|
.group-header {
|
||||||
border-bottom: 2px solid var(--lighter-bg) !important;
|
border-bottom: 2px solid var(--lighter-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper {
|
||||||
|
background-color: var(--light-bg) !important;
|
||||||
|
border: 1px solid var(--lighter-bg) !important;
|
||||||
|
color: var(--theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-focused {
|
||||||
|
border-color: var(--theme) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select__wrapper.is-disabled {
|
||||||
|
background-color: var(--bg) !important;
|
||||||
|
color: var(--lighter-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper {
|
||||||
|
background-color: var(--light-bg) !important;
|
||||||
|
border: 1px solid var(--lighter-bg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__wrapper.is-focus {
|
||||||
|
border-color: var(--theme) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
background-color: var(--bg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table tr,
|
||||||
|
.el-table td.el-table__cell,
|
||||||
|
.el-table th.el-table__cell {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell {
|
||||||
|
background-color: var(--light-bg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__body-wrapper .el-table__row:hover > .el-table__cell {
|
||||||
|
background-color: var(--lighter-bg) !important;
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+4
-38
@@ -1,16 +1,6 @@
|
|||||||
import '@fontsource/noto-sans-kr';
|
|
||||||
import '@fontsource/noto-sans-jp';
|
|
||||||
import '@fontsource/noto-sans-sc';
|
|
||||||
import '@fontsource/noto-sans-tc';
|
|
||||||
|
|
||||||
import 'remixicon/fonts/remixicon.css';
|
|
||||||
|
|
||||||
import Vue from 'vue';
|
|
||||||
import { PiniaVuePlugin } from 'pinia';
|
|
||||||
import { DataTables } from 'vue-data-tables';
|
|
||||||
import VueLazyload from 'vue-lazyload';
|
|
||||||
import configRepository from './service/config';
|
import configRepository from './service/config';
|
||||||
import vrcxJsonStorage from './service/jsonStorage';
|
import vrcxJsonStorage from './service/jsonStorage';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
changeAppDarkStyle,
|
changeAppDarkStyle,
|
||||||
changeAppThemeStyle,
|
changeAppThemeStyle,
|
||||||
@@ -18,8 +8,10 @@ import {
|
|||||||
refreshCustomScript,
|
refreshCustomScript,
|
||||||
systemIsDarkMode
|
systemIsDarkMode
|
||||||
} from './shared/utils';
|
} from './shared/utils';
|
||||||
import { i18n } from './plugin';
|
import { i18n } from './plugin/i18n';
|
||||||
import { initNoty } from './plugin/noty';
|
import { initNoty } from './plugin/noty';
|
||||||
|
import './plugin/ipc';
|
||||||
|
import './plugin/dayjs';
|
||||||
|
|
||||||
initNoty(false);
|
initNoty(false);
|
||||||
|
|
||||||
@@ -88,33 +80,7 @@ try {
|
|||||||
refreshCustomCss();
|
refreshCustomCss();
|
||||||
refreshCustomScript();
|
refreshCustomScript();
|
||||||
|
|
||||||
Vue.use(PiniaVuePlugin);
|
|
||||||
Vue.use(DataTables);
|
|
||||||
|
|
||||||
Vue.use(VueLazyload, {
|
|
||||||
preLoad: 1,
|
|
||||||
observer: true,
|
|
||||||
observerOptions: {
|
|
||||||
rootMargin: '0px',
|
|
||||||
threshold: 0
|
|
||||||
},
|
|
||||||
attempt: 3
|
|
||||||
});
|
|
||||||
|
|
||||||
new vrcxJsonStorage(VRCXStorage);
|
new vrcxJsonStorage(VRCXStorage);
|
||||||
|
|
||||||
// some workaround for failing to get voice list first run
|
// some workaround for failing to get voice list first run
|
||||||
speechSynthesis.getVoices();
|
speechSynthesis.getVoices();
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
Vue.config.errorHandler = function (err, vm, info) {
|
|
||||||
console.error('Vue Error:', err);
|
|
||||||
console.error('Component:', vm);
|
|
||||||
console.error('Error Info:', info);
|
|
||||||
};
|
|
||||||
Vue.config.warnHandler = function (msg, vm, trace) {
|
|
||||||
console.warn('Vue Warning:', msg);
|
|
||||||
console.warn('Component:', vm);
|
|
||||||
console.warn('Trace:', trace);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
<template>
|
||||||
|
<div class="data-table-wrapper">
|
||||||
|
<el-table
|
||||||
|
ref="tableRef"
|
||||||
|
v-loading="loading"
|
||||||
|
:data="paginatedData"
|
||||||
|
v-bind="mergedTableProps"
|
||||||
|
default-sort-prop="created_at"
|
||||||
|
default-sort-order="descending"
|
||||||
|
lazy
|
||||||
|
@sort-change="handleSortChange"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-click="handleRowClick">
|
||||||
|
<slot></slot>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div v-if="showPagination" class="pagination-wrapper">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="internalCurrentPage"
|
||||||
|
:page-size="internalPageSize"
|
||||||
|
:total="filteredData.length"
|
||||||
|
v-bind="mergedPaginationProps"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { computed, ref, watch, toRefs } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DataTable',
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
tableProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
paginationProps: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
currentPage: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 20
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
type: [Array, Object],
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: String,
|
||||||
|
default: 'table, pagination'
|
||||||
|
},
|
||||||
|
defaultSort: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({ prop: 'created_at', order: 'descending' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: [
|
||||||
|
'update:currentPage',
|
||||||
|
'update:pageSize',
|
||||||
|
'size-change',
|
||||||
|
'current-change',
|
||||||
|
'selection-change',
|
||||||
|
'row-click',
|
||||||
|
'filtered-data'
|
||||||
|
],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { data, currentPage, pageSize, tableProps, paginationProps, filters } = toRefs(props);
|
||||||
|
|
||||||
|
const internalCurrentPage = ref(currentPage.value);
|
||||||
|
const internalPageSize = ref(pageSize.value);
|
||||||
|
const sortData = ref({
|
||||||
|
prop: props.defaultSort?.prop || null,
|
||||||
|
order: props.defaultSort?.order || null
|
||||||
|
});
|
||||||
|
|
||||||
|
const showPagination = computed(() => {
|
||||||
|
return props.layout.includes('pagination');
|
||||||
|
});
|
||||||
|
|
||||||
|
const mergedTableProps = computed(() => ({
|
||||||
|
stripe: true,
|
||||||
|
...tableProps.value
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mergedPaginationProps = computed(() => ({
|
||||||
|
layout: 'sizes, prev, pager, next, total',
|
||||||
|
pageSizes: [20, 50, 100, 200],
|
||||||
|
small: true,
|
||||||
|
...paginationProps.value
|
||||||
|
}));
|
||||||
|
|
||||||
|
const applyFilter = function (row, filter) {
|
||||||
|
if (Array.isArray(filter.prop)) {
|
||||||
|
return filter.prop.some((propItem) => applyFilter(row, { prop: propItem, value: filter.value }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const cellValue = row[filter.prop];
|
||||||
|
if (cellValue === undefined || cellValue === null) return false;
|
||||||
|
|
||||||
|
if (Array.isArray(filter.value)) {
|
||||||
|
return filter.value.some((val) =>
|
||||||
|
String(cellValue).toLowerCase().includes(String(val).toLowerCase())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return String(cellValue).toLowerCase().includes(String(filter.value).toLowerCase());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredData = computed(() => {
|
||||||
|
let result = [...data.value];
|
||||||
|
|
||||||
|
if (filters.value && Array.isArray(filters.value) && filters.value.length > 0) {
|
||||||
|
filters.value.forEach((filter) => {
|
||||||
|
if (filter.value && (!Array.isArray(filter.value) || filter.value.length > 0)) {
|
||||||
|
result = result.filter((row) => applyFilter(row, filter));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortData.value.prop && sortData.value.order) {
|
||||||
|
const { prop, order } = sortData.value;
|
||||||
|
result.sort((a, b) => {
|
||||||
|
const aVal = a[prop];
|
||||||
|
const bVal = b[prop];
|
||||||
|
let comparison = 0;
|
||||||
|
|
||||||
|
if (aVal == null && bVal == null) return 0;
|
||||||
|
if (aVal == null) return 1;
|
||||||
|
if (bVal == null) return -1;
|
||||||
|
|
||||||
|
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
||||||
|
comparison = aVal - bVal;
|
||||||
|
} else if (aVal instanceof Date && bVal instanceof Date) {
|
||||||
|
comparison = aVal.getTime() - bVal.getTime();
|
||||||
|
} else {
|
||||||
|
const aStr = String(aVal).toLowerCase();
|
||||||
|
const bStr = String(bVal).toLowerCase();
|
||||||
|
if (aStr > bStr) comparison = 1;
|
||||||
|
else if (aStr < bStr) comparison = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return order === 'descending' ? -comparison : comparison;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('filtered-data', result);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
const paginatedData = computed(() => {
|
||||||
|
if (!showPagination.value) {
|
||||||
|
return filteredData.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = (internalCurrentPage.value - 1) * internalPageSize.value;
|
||||||
|
const end = start + internalPageSize.value;
|
||||||
|
return filteredData.value.slice(start, end);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSortChange = ({ prop, order }) => {
|
||||||
|
sortData.value = { prop, order };
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectionChange = (selection) => {
|
||||||
|
emit('selection-change', selection);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = (row, column, event) => {
|
||||||
|
emit('row-click', row, column, event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSizeChange = (size) => {
|
||||||
|
internalPageSize.value = size;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCurrentChange = (page) => {
|
||||||
|
internalCurrentPage.value = page;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(currentPage, (newVal) => {
|
||||||
|
internalCurrentPage.value = newVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(pageSize, (newVal) => {
|
||||||
|
internalPageSize.value = newVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.defaultSort,
|
||||||
|
(newSort) => {
|
||||||
|
if (newSort) {
|
||||||
|
sortData.value = {
|
||||||
|
prop: newSort.prop,
|
||||||
|
order: newSort.order
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
internalCurrentPage,
|
||||||
|
internalPageSize,
|
||||||
|
showPagination,
|
||||||
|
mergedTableProps,
|
||||||
|
mergedPaginationProps,
|
||||||
|
filteredData,
|
||||||
|
paginatedData,
|
||||||
|
handleSortChange,
|
||||||
|
handleSelectionChange,
|
||||||
|
handleRowClick,
|
||||||
|
handleSizeChange,
|
||||||
|
handleCurrentChange
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.data-table-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<div
|
<div
|
||||||
class="avatar"
|
class="avatar"
|
||||||
:class="isFriendActiveOrOffline ? undefined : userStatusClass(friend.ref, friend.pendingOffline)">
|
:class="isFriendActiveOrOffline ? undefined : userStatusClass(friend.ref, friend.pendingOffline)">
|
||||||
<img v-lazy="userImage(friend.ref, true)" />
|
<img :src="userImage(friend.ref, true)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span v-if="!hideNicknames && friend.$nickName" class="name" :style="{ color: friend.ref.$userColour }">
|
<span v-if="!hideNicknames && friend.$nickName" class="name" :style="{ color: friend.ref.$userColour }">
|
||||||
@@ -17,21 +17,18 @@
|
|||||||
<span v-if="isFriendActiveOrOffline" class="extra">{{ friend.ref.statusDescription }}</span>
|
<span v-if="isFriendActiveOrOffline" class="extra">{{ friend.ref.statusDescription }}</span>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<span v-if="friend.pendingOffline" class="extra">
|
<span v-if="friend.pendingOffline" class="extra">
|
||||||
<i class="el-icon-warning-outline" /> {{ $t('side_panel.pending_offline') }}
|
<el-icon><WarningFilled /></el-icon> {{ t('side_panel.pending_offline') }}
|
||||||
</span>
|
</span>
|
||||||
<template v-else-if="isGroupByInstance">
|
<template v-else-if="isGroupByInstance">
|
||||||
<i v-if="isFriendTraveling" class="el-icon el-icon-loading"></i>
|
<el-icon v-if="isFriendTraveling" class="is-loading" style="margin-right: 3px"
|
||||||
|
><Loading
|
||||||
|
/></el-icon>
|
||||||
<Timer
|
<Timer
|
||||||
class="extra"
|
class="extra"
|
||||||
:epoch="epoch"
|
:epoch="epoch"
|
||||||
:style="isFriendTraveling ? { display: 'inline-block', overflow: 'unset' } : undefined" />
|
:style="isFriendTraveling ? { display: 'inline-block', overflow: 'unset' } : undefined" />
|
||||||
</template>
|
</template>
|
||||||
<Location
|
<Location v-else class="extra" :location="locationProp" :traveling="travelingProp" :link="false" />
|
||||||
v-else
|
|
||||||
class="extra"
|
|
||||||
:location="friend.ref.location"
|
|
||||||
:traveling="friend.ref.travelingToLocation"
|
|
||||||
:link="false" />
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -39,15 +36,15 @@
|
|||||||
<span>{{ friend.name || friend.id }}</span>
|
<span>{{ friend.name || friend.id }}</span>
|
||||||
<el-button
|
<el-button
|
||||||
ttype="text"
|
ttype="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
style="margin-left: 5px"
|
style="margin-left: 5px"
|
||||||
@click.stop="$emit('confirm-delete-friend', friend.id)">
|
@click.stop="$emit('confirm-delete-friend', friend.id)">
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-skeleton v-else animated class="skeleton" :throttle="100">
|
<el-skeleton v-else animated class="skeleton" :throttle="100">
|
||||||
<template slot="template">
|
<template #template>
|
||||||
<div>
|
<div>
|
||||||
<el-skeleton-item variant="circle" />
|
<el-skeleton-item variant="circle" />
|
||||||
<div>
|
<div>
|
||||||
@@ -61,8 +58,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { WarningFilled, Close, Loading } from '@element-plus/icons-vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { userImage, userStatusClass } from '../shared/utils';
|
import { userImage, userStatusClass } from '../shared/utils';
|
||||||
import { useAppearanceSettingsStore, useFriendStore } from '../stores';
|
import { useAppearanceSettingsStore, useFriendStore } from '../stores';
|
||||||
|
|
||||||
@@ -71,21 +70,27 @@
|
|||||||
isGroupByInstance: Boolean
|
isGroupByInstance: Boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineEmits(['click', 'confirm-delete-friend']);
|
||||||
|
|
||||||
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
const { hideNicknames } = storeToRefs(useAppearanceSettingsStore());
|
||||||
const { isRefreshFriendsLoading } = storeToRefs(useFriendStore());
|
const { isRefreshFriendsLoading } = storeToRefs(useFriendStore());
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const isFriendTraveling = computed(() => props.friend.ref?.location === 'traveling');
|
const isFriendTraveling = computed(() => props.friend.ref?.location === 'traveling');
|
||||||
const isFriendActiveOrOffline = computed(() => props.friend.state === 'active' || props.friend.state === 'offline');
|
const isFriendActiveOrOffline = computed(() => props.friend.state === 'active' || props.friend.state === 'offline');
|
||||||
const epoch = computed(() =>
|
const epoch = computed(() =>
|
||||||
isFriendTraveling.value ? props.friend.ref?.$travelingToTime : props.friend.ref?.$location_at
|
isFriendTraveling.value ? props.friend.ref?.$travelingToTime : props.friend.ref?.$location_at
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const locationProp = computed(() => props.friend.ref?.location || '');
|
||||||
|
const travelingProp = computed(() => props.friend.ref?.travelingToLocation || '');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.skeleton {
|
.skeleton {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
::v-deep .el-skeleton {
|
:deep(.el-skeleton) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
> div {
|
> div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<template v-if="state.canCloseInstance">
|
<template v-if="state.canCloseInstance">
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="state.isClosed"
|
:disabled="state.isClosed"
|
||||||
size="mini"
|
size="small"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="closeInstance(props.location)">
|
@click="closeInstance(props.location)">
|
||||||
{{ t('dialog.user.info.close_instance') }} </el-button
|
{{ t('dialog.user.info.close_instance') }} </el-button
|
||||||
@@ -24,17 +24,14 @@
|
|||||||
>{{ t('dialog.user.info.instance_disabled_content') }} {{ state.disabledContentSettings }}<br
|
>{{ t('dialog.user.info.instance_disabled_content') }} {{ state.disabledContentSettings }}<br
|
||||||
/></span>
|
/></span>
|
||||||
<span v-if="state.userList.length">{{ t('dialog.user.info.instance_users') }}<br /></span>
|
<span v-if="state.userList.length">{{ t('dialog.user.info.instance_users') }}<br /></span>
|
||||||
<template v-for="user in state.userList">
|
<template v-for="user in state.userList" :key="user.id">
|
||||||
<span
|
<span style="cursor: pointer; margin-right: 5px" @click="showUserDialog(user.id)">{{
|
||||||
style="cursor: pointer; margin-right: 5px"
|
user.displayName
|
||||||
@click="showUserDialog(user.id)"
|
}}</span>
|
||||||
:key="user.id"
|
|
||||||
>{{ user.displayName }}</span
|
|
||||||
>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<i class="el-icon-caret-bottom"></i>
|
<el-icon><CaretBottom /></el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span v-if="state.occupants" style="margin-left: 5px">{{ state.occupants }}/{{ state.capacity }}</span>
|
<span v-if="state.occupants" style="margin-left: 5px">{{ state.occupants }}/{{ state.capacity }}</span>
|
||||||
<span v-if="props.friendcount" style="margin-left: 5px">({{ props.friendcount }})</span>
|
<span v-if="props.friendcount" style="margin-left: 5px">({{ props.friendcount }})</span>
|
||||||
@@ -57,8 +54,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getCurrentInstance, reactive, watch } from 'vue';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { CaretBottom } from '@element-plus/icons-vue';
|
||||||
|
import { reactive, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { miscRequest } from '../api';
|
import { miscRequest } from '../api';
|
||||||
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
|
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
|
||||||
import { useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../stores';
|
import { useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../stores';
|
||||||
@@ -94,8 +93,6 @@
|
|||||||
disabledContentSettings: ''
|
disabledContentSettings: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
function parse() {
|
function parse() {
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
isValidInstance: false,
|
isValidInstance: false,
|
||||||
@@ -153,18 +150,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeInstance(location) {
|
function closeInstance(location) {
|
||||||
proxy.$confirm('Continue? Close Instance, nobody will be able to join', 'Confirm', {
|
ElMessageBox.confirm('Continue? Close Instance, nobody will be able to join', 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
cancelButtonText: 'Cancel',
|
cancelButtonText: 'Cancel',
|
||||||
type: 'warning',
|
type: 'warning'
|
||||||
callback: async (action) => {
|
})
|
||||||
|
.then(async (action) => {
|
||||||
if (action !== 'confirm') return;
|
if (action !== 'confirm') return;
|
||||||
const args = await miscRequest.closeInstance({ location, hardClose: false });
|
const args = await miscRequest.closeInstance({ location, hardClose: false });
|
||||||
if (args.json) {
|
if (args.json) {
|
||||||
proxy.$message({ message: t('message.instance.closed'), type: 'success' });
|
ElMessage({ message: t('message.instance.closed'), type: 'success' });
|
||||||
instanceStore.applyInstance(args.json);
|
instanceStore.applyInstance(args.json);
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div v-if="isVisible" :class="['inline-block']">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="!canOpenInstanceInGame()"
|
v-if="!canOpenInstanceInGame()"
|
||||||
placement="top"
|
placement="top"
|
||||||
:content="t('dialog.user.info.self_invite_tooltip')"
|
:content="t('dialog.user.info.self_invite_tooltip')">
|
||||||
:disabled="hideTooltips">
|
<el-button v-show="isVisible" @click="confirmInvite" size="small" :icon="Message" circle />
|
||||||
<el-button v-show="isVisible" @click="confirmInvite" size="mini" icon="el-icon-message" circle />
|
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip v-else placement="top" :content="t('dialog.user.info.open_in_vrchat_tooltip')" :disabled="hideTooltips">
|
<el-tooltip v-else placement="top" :content="t('dialog.user.info.open_in_vrchat_tooltip')">
|
||||||
<el-button @click="openInstance" size="mini" icon="el-icon-message" circle />
|
<el-button @click="openInstance" size="small" :icon="Message" circle />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from 'pinia';
|
import { ElMessage } from 'element-plus';
|
||||||
import { computed, getCurrentInstance } from 'vue';
|
import { Message } from '@element-plus/icons-vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { instanceRequest } from '../api';
|
import { instanceRequest } from '../api';
|
||||||
import { checkCanInviteSelf, parseLocation } from '../shared/utils';
|
import { checkCanInviteSelf, parseLocation } from '../shared/utils';
|
||||||
import { useAppearanceSettingsStore } from '../stores/settings/appearance';
|
import { useInviteStore, useLaunchStore } from '../stores';
|
||||||
import { useLaunchStore } from '../stores/launch';
|
|
||||||
import { useInviteStore } from '../stores/invite';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
location: String,
|
location: String,
|
||||||
@@ -28,13 +28,9 @@
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
|
||||||
|
|
||||||
const { canOpenInstanceInGame } = useInviteStore();
|
const { canOpenInstanceInGame } = useInviteStore();
|
||||||
const { tryOpenInstanceInVrc } = useLaunchStore();
|
const { tryOpenInstanceInVrc } = useLaunchStore();
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const isVisible = computed(() => checkCanInviteSelf(props.location));
|
const isVisible = computed(() => checkCanInviteSelf(props.location));
|
||||||
|
|
||||||
function confirmInvite() {
|
function confirmInvite() {
|
||||||
@@ -50,7 +46,7 @@
|
|||||||
shortName: props.shortname
|
shortName: props.shortname
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
proxy.$message({ message: 'Self invite sent', type: 'success' });
|
ElMessage({ message: 'Self invite sent', type: 'success' });
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -64,3 +60,9 @@
|
|||||||
tryOpenInstanceInVrc(L.tag, props.shortname);
|
tryOpenInstanceInVrc(L.tag, props.shortname);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<span v-if="lastJoin">
|
<span v-if="lastJoin" :class="['inline-block', 'ml-5']">
|
||||||
<el-tooltip placement="top" style="margin-left: 5px">
|
<el-tooltip placement="top" class="ml-5">
|
||||||
<template #content>
|
<template #content>
|
||||||
<span>{{ $t('dialog.user.info.last_join') }} <Timer :epoch="lastJoin" /></span>
|
<span>{{ t('dialog.user.info.last_join') }} <Timer :epoch="lastJoin" /></span>
|
||||||
</template>
|
</template>
|
||||||
<i class="el-icon el-icon-location-outline" style="display: inline-block" />
|
<el-icon style="display: inline-block"><Location /></el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Location } from '@element-plus/icons-vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useInstanceStore } from '../stores';
|
import { useInstanceStore } from '../stores';
|
||||||
|
|
||||||
const { instanceJoinHistory } = storeToRefs(useInstanceStore());
|
const { instanceJoinHistory } = storeToRefs(useInstanceStore());
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
location: String,
|
location: String,
|
||||||
@@ -30,3 +33,12 @@
|
|||||||
watch(() => props.location, parse, { immediate: true });
|
watch(() => props.location, parse, { immediate: true });
|
||||||
watch(() => props.currentlocation, parse);
|
watch(() => props.currentlocation, parse);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ml-5 {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-tooltip
|
<div v-if="isVisible" class="inline-block">
|
||||||
v-show="isVisible"
|
<el-tooltip placement="top" :content="t('dialog.user.info.launch_invite_tooltip')"
|
||||||
placement="top"
|
><el-button @click="confirm" size="small" :icon="SwitchButton" circle />
|
||||||
:content="t('dialog.user.info.launch_invite_tooltip')"
|
|
||||||
:disabled="hideTooltips">
|
|
||||||
<el-button @click="confirm" size="mini" icon="el-icon-switch-button" circle />
|
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { SwitchButton } from '@element-plus/icons-vue';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { checkCanInviteSelf } from '../shared/utils';
|
import { checkCanInviteSelf } from '../shared/utils';
|
||||||
import { useLaunchStore } from '../stores';
|
import { useLaunchStore } from '../stores';
|
||||||
|
|
||||||
@@ -18,8 +17,7 @@
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
location: String,
|
location: String
|
||||||
hideTooltips: Boolean
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const isVisible = computed(() => {
|
const isVisible = computed(() => {
|
||||||
@@ -30,3 +28,9 @@
|
|||||||
launchStore.showLaunchDialog(props.location);
|
launchStore.showLaunchDialog(props.location);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
+26
-16
@@ -1,28 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<span v-if="!text" style="color: transparent">-</span>
|
<span v-if="!text" class="transparent">-</span>
|
||||||
<span v-show="text">
|
<span v-show="text">
|
||||||
<span
|
<span
|
||||||
:class="{ 'x-link': link && location !== 'private' && location !== 'offline' }"
|
:class="{ 'x-link': link && location !== 'private' && location !== 'offline' }"
|
||||||
@click="handleShowWorldDialog">
|
@click="handleShowWorldDialog">
|
||||||
<i v-if="isTraveling" class="el-icon el-icon-loading" style="display: inline-block"></i>
|
<el-icon :class="['is-loading', 'inline-block']" style="margin-right: 3px" v-if="isTraveling"
|
||||||
|
><Loading
|
||||||
|
/></el-icon>
|
||||||
<span>{{ text }}</span>
|
<span>{{ text }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="groupName" :class="{ 'x-link': link }" @click="handleShowGroupDialog">({{ groupName }})</span>
|
<span v-if="groupName" :class="{ 'x-link': link }" @click="handleShowGroupDialog">({{ groupName }})</span>
|
||||||
<span v-if="region" class="flags" :class="region" style="display: inline-block; margin-left: 5px"></span>
|
<span v-if="region" :class="['flags', 'inline-block', 'ml-5', region]"></span>
|
||||||
<i v-if="strict" class="el-icon el-icon-lock" style="display: inline-block; margin-left: 5px"></i>
|
<el-icon v-if="strict" :class="['inline-block', 'ml-5']"><Lock /></el-icon>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from 'pinia';
|
import { Loading, Lock } from '@element-plus/icons-vue';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watchEffect } from 'vue';
|
||||||
import { getGroupName, getWorldName, parseLocation } from '../shared/utils';
|
import { getGroupName, getWorldName, parseLocation } from '../shared/utils';
|
||||||
import { useGroupStore, useInstanceStore, useSearchStore, useWorldStore } from '../stores';
|
import { useGroupStore, useInstanceStore, useSearchStore, useWorldStore } from '../stores';
|
||||||
|
|
||||||
const { cachedWorlds } = storeToRefs(useWorldStore());
|
const { cachedWorlds, showWorldDialog } = useWorldStore();
|
||||||
const { showWorldDialog } = useWorldStore();
|
|
||||||
const { showGroupDialog } = useGroupStore();
|
const { showGroupDialog } = useGroupStore();
|
||||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||||
const { verifyShortName } = useSearchStore();
|
const { verifyShortName } = useSearchStore();
|
||||||
@@ -51,14 +52,9 @@
|
|||||||
const isTraveling = ref(false);
|
const isTraveling = ref(false);
|
||||||
const groupName = ref('');
|
const groupName = ref('');
|
||||||
|
|
||||||
watch(
|
watchEffect(() => {
|
||||||
() => props.location,
|
|
||||||
() => {
|
|
||||||
parse();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
parse();
|
parse();
|
||||||
|
});
|
||||||
|
|
||||||
function parse() {
|
function parse() {
|
||||||
isTraveling.value = false;
|
isTraveling.value = false;
|
||||||
@@ -82,7 +78,7 @@
|
|||||||
text.value = props.hint;
|
text.value = props.hint;
|
||||||
}
|
}
|
||||||
} else if (L.worldId) {
|
} else if (L.worldId) {
|
||||||
const ref = cachedWorlds.value.get(L.worldId);
|
const ref = cachedWorlds.get(L.worldId);
|
||||||
if (typeof ref === 'undefined') {
|
if (typeof ref === 'undefined') {
|
||||||
getWorldName(L.worldId).then((worldName) => {
|
getWorldName(L.worldId).then((worldName) => {
|
||||||
if (L.tag === instanceId) {
|
if (L.tag === instanceId) {
|
||||||
@@ -152,3 +148,17 @@
|
|||||||
showGroupDialog(L.groupId);
|
showGroupDialog(L.groupId);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-5 {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transparent {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<span>
|
<span>
|
||||||
<span @click="showLaunchDialog" class="x-link">
|
<span @click="showLaunchDialog" class="x-link">
|
||||||
<i v-if="isUnlocked" class="el-icon el-icon-unlock" style="display: inline-block; margin-right: 5px"></i>
|
<el-icon v-if="isUnlocked" style="display: inline-block; margin-right: 5px"><Unlock /></el-icon>
|
||||||
<span>#{{ instanceName }} {{ accessTypeName }}</span>
|
<span>#{{ instanceName }} {{ accessTypeName }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="groupName" @click="showGroupDialog" class="x-link">({{ groupName }})</span>
|
<span v-if="groupName" @click="showGroupDialog" class="x-link">({{ groupName }})</span>
|
||||||
<span class="flags" :class="region" style="display: inline-block; margin-left: 5px"></span>
|
<span class="flags" :class="region" style="display: inline-block; margin-left: 5px"></span>
|
||||||
<i v-if="strict" class="el-icon el-icon-lock" style="display: inline-block; margin-left: 5px"></i>
|
<el-icon v-if="strict" style="display: inline-block; margin-left: 5px"><Lock /></el-icon>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Lock, Unlock } from '@element-plus/icons-vue';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { getGroupName, parseLocation } from '../shared/utils';
|
import { getGroupName, parseLocation } from '../shared/utils';
|
||||||
import { useGroupStore, useLaunchStore } from '../stores';
|
import { useGroupStore, useLaunchStore } from '../stores';
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
<div v-else-if="pendingVRCXUpdate || pendingVRCXInstall" class="pending-update">
|
<div v-else-if="pendingVRCXUpdate || pendingVRCXInstall" class="pending-update">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-download"
|
:icon="Download"
|
||||||
circle
|
circle
|
||||||
style="font-size: 14px; height: 50px; width: 50px"
|
style="font-size: 14px; height: 50px; width: 50px"
|
||||||
@click="showVRCXUpdateDialog" />
|
@click="showVRCXUpdateDialog" />
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
:class="{ notify: notifiedMenus.includes(item.index) }">
|
:class="{ notify: notifiedMenus.includes(item.index) }">
|
||||||
<i :class="item.icon"></i>
|
<i :class="item.icon"></i>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span>{{ $t(item.tooltip) }}</span>
|
<span>{{ t(item.tooltip) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
@@ -38,9 +38,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Download } from '@element-plus/icons-vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useUiStore, useVRCXUpdaterStore } from '../stores';
|
import { useUiStore, useVRCXUpdaterStore } from '../stores';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ index: 'feed', icon: 'ri-rss-line', tooltip: 'nav_tooltip.feed' },
|
{ index: 'feed', icon: 'ri-rss-line', tooltip: 'nav_tooltip.feed' },
|
||||||
{ index: 'gameLog', icon: 'ri-history-line', tooltip: 'nav_tooltip.game_log' },
|
{ index: 'gameLog', icon: 'ri-history-line', tooltip: 'nav_tooltip.game_log' },
|
||||||
|
|||||||
@@ -3,15 +3,16 @@
|
|||||||
<div class="name" :style="{ width: longLabel ? '300px' : undefined }">
|
<div class="name" :style="{ width: longLabel ? '300px' : undefined }">
|
||||||
{{ label }}
|
{{ label }}
|
||||||
<el-tooltip v-if="tooltip" placement="top" class="tooltip" :content="tooltip"
|
<el-tooltip v-if="tooltip" placement="top" class="tooltip" :content="tooltip"
|
||||||
><i class="el-icon-info"
|
><el-icon><InfoFilled /></el-icon
|
||||||
/></el-tooltip>
|
></el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-switch class="switch" :value="value" @change="change" :disabled="disabled"></el-switch>
|
<el-switch class="switch" :model-value="value" @change="change" :disabled="disabled"></el-switch>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
defineProps({
|
defineProps({
|
||||||
label: String,
|
label: String,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
@@ -30,13 +31,13 @@
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.simple-switch {
|
.simple-switch {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 5px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.simple-switch > .name {
|
.simple-switch > .name {
|
||||||
width: 225px;
|
width: 225px;
|
||||||
min-width: 225px;
|
min-width: 225px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
padding-top: 7px;
|
||||||
}
|
}
|
||||||
.simple-switch > .switch {
|
.simple-switch > .switch {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
<template>
|
|
||||||
<span>
|
|
||||||
<span>{{ text }}</span>
|
|
||||||
<span v-if="groupName">({{ groupName }})</span>
|
|
||||||
<span
|
|
||||||
v-if="region"
|
|
||||||
class="flags"
|
|
||||||
:class="region"
|
|
||||||
style="display:inline-block;margin-bottom:2px;margin-left:5px">
|
|
||||||
</span>
|
|
||||||
<i
|
|
||||||
v-if="strict"
|
|
||||||
class="el-icon el-icon-lock"
|
|
||||||
style="display:inline-block;margin-left:5px">
|
|
||||||
</i>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, watch, onMounted } from 'vue';
|
|
||||||
import { parseLocation } from '../shared/utils/location';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
location: String,
|
|
||||||
hint: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
grouphint: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const text = ref('');
|
|
||||||
const region = ref('');
|
|
||||||
const strict = ref(false);
|
|
||||||
const groupName = ref('');
|
|
||||||
|
|
||||||
function parse() {
|
|
||||||
text.value = props.location;
|
|
||||||
const L = parseLocation(props.location);
|
|
||||||
|
|
||||||
if (L.isOffline) {
|
|
||||||
text.value = 'Offline';
|
|
||||||
} else if (L.isPrivate) {
|
|
||||||
text.value = 'Private';
|
|
||||||
} else if (L.isTraveling) {
|
|
||||||
text.value = 'Traveling';
|
|
||||||
} else if (typeof props.hint === 'string' && props.hint !== '') {
|
|
||||||
if (L.instanceId) {
|
|
||||||
text.value = `${props.hint} #${L.instanceName} ${L.accessTypeName}`;
|
|
||||||
} else {
|
|
||||||
text.value = props.hint;
|
|
||||||
}
|
|
||||||
} else if (L.worldId) {
|
|
||||||
if (L.instanceId) {
|
|
||||||
text.value = ` #${L.instanceName} ${L.accessTypeName}`;
|
|
||||||
} else {
|
|
||||||
text.value = props.location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
region.value = '';
|
|
||||||
if (
|
|
||||||
props.location !== '' &&
|
|
||||||
L.instanceId &&
|
|
||||||
!L.isOffline &&
|
|
||||||
!L.isPrivate
|
|
||||||
) {
|
|
||||||
region.value = L.region;
|
|
||||||
if (!L.region) {
|
|
||||||
region.value = 'us';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strict.value = L.strict;
|
|
||||||
groupName.value = props.grouphint;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => props.location, parse);
|
|
||||||
|
|
||||||
onMounted(parse);
|
|
||||||
</script>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible="changeAvatarImageDialogVisible"
|
:model-value="changeAvatarImageDialogVisible"
|
||||||
:title="t('dialog.change_content_image.avatar')"
|
:title="t('dialog.change_content_image.avatar')"
|
||||||
width="850px"
|
width="850px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -16,73 +16,47 @@
|
|||||||
<span>{{ t('dialog.change_content_image.description') }}</span>
|
<span>{{ t('dialog.change_content_image.description') }}</span>
|
||||||
<br />
|
<br />
|
||||||
<el-button-group style="padding-bottom: 10px; padding-top: 10px">
|
<el-button-group style="padding-bottom: 10px; padding-top: 10px">
|
||||||
<el-button type="default" size="small" icon="el-icon-refresh" @click="refresh">
|
<el-button type="default" size="small" :icon="Upload" @click="uploadAvatarImage">
|
||||||
{{ t('dialog.change_content_image.refresh') }}
|
|
||||||
</el-button>
|
|
||||||
<el-button type="default" size="small" icon="el-icon-upload2" @click="uploadAvatarImage">
|
|
||||||
{{ t('dialog.change_content_image.upload') }}
|
{{ t('dialog.change_content_image.upload') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
<br />
|
<br />
|
||||||
<div v-for="image in previousImagesTable" :key="image.version" style="display: inline-block">
|
<div class="x-change-image-item">
|
||||||
<div
|
<img :src="currentImageUrl" class="img-size" loading="lazy" />
|
||||||
v-if="image.file"
|
|
||||||
class="x-change-image-item"
|
|
||||||
style="cursor: pointer"
|
|
||||||
:class="{ 'current-image': compareCurrentImage(image) }"
|
|
||||||
@click="setAvatarImage(image)">
|
|
||||||
<img v-lazy="image.file.url" class="image" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-dialog>
|
||||||
</safe-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { Upload } from '@element-plus/icons-vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { imageRequest } from '../../../api';
|
import { avatarRequest } from '../../../api';
|
||||||
import { AppGlobal } from '../../../service/appConfig';
|
import { useAvatarStore } from '../../../stores';
|
||||||
import { $throw } from '../../../service/request';
|
|
||||||
import { extractFileId } from '../../../shared/utils';
|
|
||||||
import { useAvatarStore, useGalleryStore } from '../../../stores';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const { avatarDialog } = storeToRefs(useAvatarStore());
|
const { avatarDialog } = storeToRefs(useAvatarStore());
|
||||||
const { previousImagesTable } = storeToRefs(useGalleryStore());
|
|
||||||
const { applyAvatar } = useAvatarStore();
|
const { applyAvatar } = useAvatarStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
changeAvatarImageDialogVisible: {
|
changeAvatarImageDialogVisible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
required: true
|
||||||
},
|
},
|
||||||
previousImagesFileId: {
|
previousImageUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeAvatarImageDialogLoading = ref(false);
|
const changeAvatarImageDialogLoading = ref(false);
|
||||||
const avatarImage = ref({
|
const currentImageUrl = computed(() => props.previousImageUrl);
|
||||||
base64File: '',
|
|
||||||
fileMd5: '',
|
|
||||||
base64SignatureFile: '',
|
|
||||||
signatureMd5: '',
|
|
||||||
fileId: '',
|
|
||||||
avatarId: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:changeAvatarImageDialogVisible', 'refresh']);
|
const emit = defineEmits(['update:changeAvatarImageDialogVisible', 'update:previousImageUrl']);
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
emit('refresh', 'Change');
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
emit('update:changeAvatarImageDialogVisible', false);
|
emit('update:changeAvatarImageDialogVisible', false);
|
||||||
@@ -93,23 +67,9 @@
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function genMd5(file) {
|
|
||||||
const response = await AppApi.MD5File(file);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function genSig(file) {
|
|
||||||
const response = await AppApi.SignFile(file);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function genLength(file) {
|
|
||||||
const response = await AppApi.FileLength(file);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFileChangeAvatarImage(e) {
|
function onFileChangeAvatarImage(e) {
|
||||||
const clearFile = function () {
|
const clearFile = function () {
|
||||||
|
changeAvatarImageDialogLoading.value = false;
|
||||||
const fileInput = /** @type{HTMLInputElement} */ (document.querySelector('#AvatarImageUploadButton'));
|
const fileInput = /** @type{HTMLInputElement} */ (document.querySelector('#AvatarImageUploadButton'));
|
||||||
if (fileInput) {
|
if (fileInput) {
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
@@ -124,7 +84,7 @@
|
|||||||
// validate file
|
// validate file
|
||||||
if (files[0].size >= 100000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 100MB
|
// 100MB
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.file.too_large'),
|
message: t('message.file.too_large'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -132,7 +92,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!files[0].type.match(/image.*/)) {
|
if (!files[0].type.match(/image.*/)) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.file.not_image'),
|
message: t('message.file.not_image'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -141,50 +101,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const r = new FileReader();
|
const r = new FileReader();
|
||||||
r.onload = async function (file) {
|
r.onload = async function () {
|
||||||
try {
|
try {
|
||||||
const base64File = await resizeImageToFitLimits(btoa(r.result.toString()));
|
const base64File = await resizeImageToFitLimits(btoa(r.result.toString()));
|
||||||
// 10MB
|
// 10MB
|
||||||
const fileMd5 = await genMd5(base64File);
|
await initiateUpload(base64File);
|
||||||
const fileSizeInBytes = parseInt(file.total.toString(), 10);
|
|
||||||
const base64SignatureFile = await genSig(base64File);
|
|
||||||
const signatureMd5 = await genMd5(base64SignatureFile);
|
|
||||||
const signatureSizeInBytes = parseInt(await genLength(base64SignatureFile), 10);
|
|
||||||
|
|
||||||
const avatarId = avatarDialog.value.id;
|
|
||||||
const { imageUrl } = avatarDialog.value.ref;
|
|
||||||
|
|
||||||
const fileId = extractFileId(imageUrl);
|
|
||||||
if (!fileId) {
|
|
||||||
$message({
|
|
||||||
message: t('message.avatar.image_invalid'),
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
clearFile();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
avatarImage.value = {
|
|
||||||
base64File,
|
|
||||||
fileMd5,
|
|
||||||
base64SignatureFile,
|
|
||||||
signatureMd5,
|
|
||||||
fileId,
|
|
||||||
avatarId
|
|
||||||
};
|
|
||||||
const params = {
|
|
||||||
fileMd5,
|
|
||||||
fileSizeInBytes,
|
|
||||||
signatureMd5,
|
|
||||||
signatureSizeInBytes
|
|
||||||
};
|
|
||||||
|
|
||||||
// Upload chaining
|
|
||||||
await initiateUpload(params, fileId);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Avatar image upload process failed:', error);
|
console.error('Avatar image upload process failed:', error);
|
||||||
} finally {
|
} finally {
|
||||||
changeAvatarImageDialogLoading.value = false;
|
|
||||||
clearFile();
|
clearFile();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -193,169 +117,32 @@
|
|||||||
r.readAsBinaryString(files[0]);
|
r.readAsBinaryString(files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------ Upload Process Start ------------
|
async function initiateUpload(base64File) {
|
||||||
|
const args = await avatarRequest.uploadAvatarImage(base64File);
|
||||||
async function initiateUpload(params, fileId) {
|
const fileUrl = args.json.versions[args.json.versions.length - 1].file.url;
|
||||||
const res = await imageRequest.uploadAvatarImage(params, fileId);
|
const avatarArgs = await avatarRequest.saveAvatar({
|
||||||
return avatarImageInit(res);
|
id: avatarDialog.value.id,
|
||||||
}
|
imageUrl: fileUrl
|
||||||
|
|
||||||
async function avatarImageInit(args) {
|
|
||||||
const fileId = args.json.id;
|
|
||||||
const fileVersion = args.json.versions[args.json.versions.length - 1].version;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadAvatarImageFileStart(params);
|
|
||||||
return avatarImageFileStart(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageFileStart(args) {
|
|
||||||
const { url } = args.json;
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
url,
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
return uploadAvatarImageFileAWS(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadAvatarImageFileAWS(params) {
|
|
||||||
const json = await webApiService.execute({
|
|
||||||
url: params.url,
|
|
||||||
uploadFilePUT: true,
|
|
||||||
fileData: avatarImage.value.base64File,
|
|
||||||
fileMIME: 'image/png',
|
|
||||||
headers: {
|
|
||||||
'Content-MD5': avatarImage.value.fileMd5
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
const ref = applyAvatar(avatarArgs.json);
|
||||||
if (json.status !== 200) {
|
emit('update:previousImageUrl', ref.imageUrl);
|
||||||
changeAvatarImageDialogLoading.value = false;
|
changeAvatarImageDialogLoading.value = false;
|
||||||
$throw(json.status, 'Avatar image upload failed', params.url);
|
ElMessage({
|
||||||
}
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return avatarImageFileAWS(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageFileAWS(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadAvatarImageFileFinish(params);
|
|
||||||
return avatarImageFileFinish(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageFileFinish(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadAvatarImageSigStart(params);
|
|
||||||
return avatarImageSigStart(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageSigStart(args) {
|
|
||||||
const { url } = args.json;
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
url,
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
return uploadAvatarImageSigAWS(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadAvatarImageSigAWS(params) {
|
|
||||||
const json = await webApiService.execute({
|
|
||||||
url: params.url,
|
|
||||||
uploadFilePUT: true,
|
|
||||||
fileData: avatarImage.value.base64SignatureFile,
|
|
||||||
fileMIME: 'application/x-rsync-signature',
|
|
||||||
headers: {
|
|
||||||
'Content-MD5': avatarImage.value.signatureMd5
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (json.status !== 200) {
|
|
||||||
changeAvatarImageDialogLoading.value = false;
|
|
||||||
$throw(json.status, 'Avatar image upload failed', params.url);
|
|
||||||
}
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return avatarImageSigAWS(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageSigAWS(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadAvatarImageSigFinish(params);
|
|
||||||
return avatarImageSigFinish(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageSigFinish(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const parmas = {
|
|
||||||
id: avatarImage.value.avatarId,
|
|
||||||
imageUrl: `${AppGlobal.endpointDomain}/file/${fileId}/${fileVersion}/file`
|
|
||||||
};
|
|
||||||
const res = await imageRequest.setAvatarImage(parmas);
|
|
||||||
return avatarImageSet(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function avatarImageSet(args) {
|
|
||||||
applyAvatar(args.json);
|
|
||||||
changeAvatarImageDialogLoading.value = false;
|
|
||||||
if (args.json.imageUrl === args.params.imageUrl) {
|
|
||||||
$message({
|
|
||||||
message: t('message.avatar.image_changed'),
|
message: t('message.avatar.image_changed'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
refresh();
|
|
||||||
} else {
|
|
||||||
$throw(0, 'Avatar image change failed', args.params.imageUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------ Upload Process End ------------
|
// closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
function uploadAvatarImage() {
|
function uploadAvatarImage() {
|
||||||
document.getElementById('AvatarImageUploadButton').click();
|
document.getElementById('AvatarImageUploadButton').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAvatarImage(image) {
|
|
||||||
changeAvatarImageDialogLoading.value = true;
|
|
||||||
const parmas = {
|
|
||||||
id: avatarDialog.value.id,
|
|
||||||
imageUrl: `${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file`
|
|
||||||
};
|
|
||||||
imageRequest
|
|
||||||
.setAvatarImage(parmas)
|
|
||||||
.then((args) => applyAvatar(args.json))
|
|
||||||
.finally(() => {
|
|
||||||
changeAvatarImageDialogLoading.value = false;
|
|
||||||
closeDialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareCurrentImage(image) {
|
|
||||||
return (
|
|
||||||
`${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file` ===
|
|
||||||
avatarDialog.value.ref.imageUrl
|
|
||||||
);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.img-size {
|
||||||
|
width: 500px;
|
||||||
|
height: 375px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="setAvatarStylesDialog"
|
ref="setAvatarStylesDialog"
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="setAvatarStylesDialog.visible"
|
v-model="setAvatarStylesDialog.visible"
|
||||||
:title="t('dialog.set_avatar_styles.header')"
|
:title="t('dialog.set_avatar_styles.header')"
|
||||||
width="400px"
|
width="400px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div style="font-size: 12px; margin-top: 10px">{{ $t('dialog.set_world_tags.author_tags') }}<br /></div>
|
<div style="font-size: 12px; margin-top: 10px">{{ t('dialog.set_world_tags.author_tags') }}<br /></div>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="setAvatarStylesDialog.authorTags"
|
v-model="setAvatarStylesDialog.authorTags"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
size="mini"
|
size="small"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
@@ -57,21 +57,18 @@
|
|||||||
t('dialog.set_avatar_styles.save')
|
t('dialog.set_avatar_styles.save')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { watch, getCurrentInstance } from 'vue';
|
import { watch } from 'vue';
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { arraysMatch } from '../../../shared/utils';
|
import { arraysMatch } from '../../../shared/utils';
|
||||||
import { avatarRequest } from '../../../api';
|
import { avatarRequest } from '../../../api';
|
||||||
import { useAvatarStore } from '../../../stores';
|
import { useAvatarStore } from '../../../stores';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const { applyAvatar } = useAvatarStore();
|
const { applyAvatar } = useAvatarStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="setAvatarTagsDialog"
|
ref="setAvatarTagsDialog"
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="setAvatarTagsDialog.visible"
|
v-model="setAvatarTagsDialog.visible"
|
||||||
:title="t('dialog.set_avatar_tags.header')"
|
:title="t('dialog.set_avatar_tags.header')"
|
||||||
width="770px"
|
width="770px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<el-input
|
<el-input
|
||||||
v-model="setAvatarTagsDialog.selectedTagsCsv"
|
v-model="setAvatarTagsDialog.selectedTagsCsv"
|
||||||
size="mini"
|
size="small"
|
||||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||||
:placeholder="t('dialog.set_avatar_tags.custom_tags_placeholder')"
|
:placeholder="t('dialog.set_avatar_tags.custom_tags_placeholder')"
|
||||||
style="margin-top: 10px"
|
style="margin-top: 10px"
|
||||||
@@ -47,19 +47,18 @@
|
|||||||
<span style="margin-left: 5px"
|
<span style="margin-left: 5px"
|
||||||
>{{ setAvatarTagsDialog.selectedCount }} / {{ setAvatarTagsDialog.ownAvatars.length }}</span
|
>{{ setAvatarTagsDialog.selectedCount }} / {{ setAvatarTagsDialog.ownAvatars.length }}</span
|
||||||
>
|
>
|
||||||
<span v-if="setAvatarTagsDialog.loading" style="margin-left: 5px">
|
<el-icon v-if="setAvatarTagsDialog.loading" class="is-loading" style="margin-left: 5px"
|
||||||
<i class="el-icon-loading"></i>
|
><Loading
|
||||||
</span>
|
/></el-icon>
|
||||||
<br />
|
<br />
|
||||||
<div class="x-friend-list" style="margin-top: 10px; min-height: 60px; max-height: 280px">
|
<div class="x-friend-list" style="margin-top: 10px; min-height: 60px; max-height: 280px">
|
||||||
<div
|
<div
|
||||||
v-for="avatar in setAvatarTagsDialog.ownAvatars"
|
v-for="avatar in setAvatarTagsDialog.ownAvatars"
|
||||||
:key="avatar.id"
|
:key="avatar.id"
|
||||||
class="x-friend-item x-friend-item-border"
|
:class="['item-width', 'x-friend-item', 'x-friend-item-border']"
|
||||||
style="width: 350px"
|
|
||||||
@click="showAvatarDialog(avatar.id)">
|
@click="showAvatarDialog(avatar.id)">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-if="avatar.thumbnailImageUrl" v-lazy="avatar.thumbnailImageUrl" />
|
<img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="avatar.name"></span>
|
<span class="name" v-text="avatar.name"></span>
|
||||||
@@ -76,7 +75,7 @@
|
|||||||
<span v-else class="extra" v-text="avatar.releaseStatus"></span>
|
<span v-else class="extra" v-text="avatar.releaseStatus"></span>
|
||||||
<span class="extra" v-text="avatar.$tagString"></span>
|
<span class="extra" v-text="avatar.$tagString"></span>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="text" size="mini" style="margin-left: 5px" @click.stop>
|
<el-button type="text" size="small" style="margin-left: 5px" @click.stop>
|
||||||
<el-checkbox v-model="avatar.$selected" @change="updateAvatarTagsSelection"></el-checkbox>
|
<el-checkbox v-model="avatar.$selected" @change="updateAvatarTagsSelection"></el-checkbox>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,21 +89,21 @@
|
|||||||
t('dialog.set_avatar_tags.save')
|
t('dialog.set_avatar_tags.save')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getCurrentInstance, watch } from 'vue';
|
import { Loading } from '@element-plus/icons-vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { avatarRequest } from '../../../api';
|
import { avatarRequest } from '../../../api';
|
||||||
import { useAvatarStore } from '../../../stores';
|
import { useAvatarStore } from '../../../stores';
|
||||||
|
|
||||||
const { showAvatarDialog, applyAvatar } = useAvatarStore();
|
const { showAvatarDialog, applyAvatar } = useAvatarStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
setAvatarTagsDialog: {
|
setAvatarTagsDialog: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -229,7 +228,7 @@
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Error saving avatar tags',
|
message: 'Error saving avatar tags',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -273,4 +272,8 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.item-width {
|
||||||
|
width: 335px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog ref="favoriteDialogRef" :visible.sync="isVisible" :title="t('dialog.favorite.header')" width="300px">
|
<el-dialog :z-index="favoriteDialogIndex" v-model="isVisible" :title="t('dialog.favorite.header')" width="300px">
|
||||||
<div v-loading="loading">
|
<div v-loading="loading">
|
||||||
<span style="display: block; text-align: center">{{ t('dialog.favorite.vrchat_favorites') }}</span>
|
<span style="display: block; text-align: center">{{ t('dialog.favorite.vrchat_favorites') }}</span>
|
||||||
<template v-if="favoriteDialog.currentGroup && favoriteDialog.currentGroup.key">
|
<template v-if="favoriteDialog.currentGroup && favoriteDialog.currentGroup.key">
|
||||||
<el-button
|
<el-button
|
||||||
style="display: block; width: 100%; margin: 10px 0"
|
style="display: block; width: 100%; margin: 10px 0"
|
||||||
@click="deleteFavoriteNoConfirm(favoriteDialog.objectId)">
|
@click="deleteFavoriteNoConfirm(favoriteDialog.objectId)">
|
||||||
<i class="el-icon-check"></i>
|
<el-icon><Check /></el-icon>
|
||||||
{{ favoriteDialog.currentGroup.displayName }} ({{ favoriteDialog.currentGroup.count }} /
|
{{ favoriteDialog.currentGroup.displayName }} ({{ favoriteDialog.currentGroup.count }} /
|
||||||
{{ favoriteDialog.currentGroup.capacity }})
|
{{ favoriteDialog.currentGroup.capacity }})
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -23,18 +23,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="favoriteDialog.type === 'world'" style="margin-top: 20px">
|
<div v-if="favoriteDialog.type === 'world'" style="margin-top: 20px">
|
||||||
<span style="display: block; text-align: center">{{ t('dialog.favorite.local_favorites') }}</span>
|
<span style="display: block; text-align: center">{{ t('dialog.favorite.local_favorites') }}</span>
|
||||||
<template v-for="group in localWorldFavoriteGroups">
|
<template v-for="group in localWorldFavoriteGroups" :key="group">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="hasLocalWorldFavorite(favoriteDialog.objectId, group)"
|
v-if="hasLocalWorldFavorite(favoriteDialog.objectId, group)"
|
||||||
:key="group"
|
|
||||||
style="display: block; width: 100%; margin: 10px 0"
|
style="display: block; width: 100%; margin: 10px 0"
|
||||||
@click="removeLocalWorldFavorite(favoriteDialog.objectId, group)">
|
@click="removeLocalWorldFavorite(favoriteDialog.objectId, group)">
|
||||||
<i class="el-icon-check"></i>
|
<el-icon><Check /></el-icon>
|
||||||
{{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
{{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
:key="group"
|
|
||||||
style="display: block; width: 100%; margin: 10px 0"
|
style="display: block; width: 100%; margin: 10px 0"
|
||||||
@click="addLocalWorldFavorite(favoriteDialog.objectId, group)">
|
@click="addLocalWorldFavorite(favoriteDialog.objectId, group)">
|
||||||
{{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
{{ group }} ({{ getLocalWorldFavoriteGroupLength(group) }})
|
||||||
@@ -43,18 +41,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="favoriteDialog.type === 'avatar'" style="margin-top: 20px">
|
<div v-if="favoriteDialog.type === 'avatar'" style="margin-top: 20px">
|
||||||
<span style="display: block; text-align: center">{{ t('dialog.favorite.local_avatar_favorites') }}</span>
|
<span style="display: block; text-align: center">{{ t('dialog.favorite.local_avatar_favorites') }}</span>
|
||||||
<template v-for="group in localAvatarFavoriteGroups">
|
<template v-for="group in localAvatarFavoriteGroups" :key="group">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="hasLocalAvatarFavorite(favoriteDialog.objectId, group)"
|
v-if="hasLocalAvatarFavorite(favoriteDialog.objectId, group)"
|
||||||
:key="group"
|
|
||||||
style="display: block; width: 100%; margin: 10px 0"
|
style="display: block; width: 100%; margin: 10px 0"
|
||||||
@click="removeLocalAvatarFavorite(favoriteDialog.objectId, group)">
|
@click="removeLocalAvatarFavorite(favoriteDialog.objectId, group)">
|
||||||
<i class="el-icon-check"></i>
|
<el-icon><Check /></el-icon>
|
||||||
{{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
{{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
:key="group"
|
|
||||||
style="display: block; width: 100%; margin: 10px 0"
|
style="display: block; width: 100%; margin: 10px 0"
|
||||||
:disabled="!isLocalUserVrcplusSupporter"
|
:disabled="!isLocalUserVrcplusSupporter"
|
||||||
@click="addLocalAvatarFavorite(favoriteDialog.objectId, group)">
|
@click="addLocalAvatarFavorite(favoriteDialog.objectId, group)">
|
||||||
@@ -62,16 +58,18 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Check } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import Noty from 'noty';
|
import Noty from 'noty';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, nextTick, ref, watch } from 'vue';
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { favoriteRequest } from '../../api';
|
import { favoriteRequest } from '../../api';
|
||||||
import { adjustDialogZ } from '../../shared/utils';
|
import { getNextDialogIndex } from '../../shared/utils';
|
||||||
import { useFavoriteStore, useUserStore } from '../../stores';
|
import { useFavoriteStore, useUserStore } from '../../stores';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -98,7 +96,7 @@
|
|||||||
} = favoriteStore;
|
} = favoriteStore;
|
||||||
const { currentUser } = storeToRefs(useUserStore());
|
const { currentUser } = storeToRefs(useUserStore());
|
||||||
|
|
||||||
const favoriteDialogRef = ref(null);
|
const favoriteDialogIndex = ref(2000);
|
||||||
const groups = ref([]);
|
const groups = ref([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
@@ -113,11 +111,12 @@
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => favoriteDialog.value.visible,
|
() => favoriteDialog.value.visible,
|
||||||
async (value) => {
|
(value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
initFavoriteDialog();
|
initFavoriteDialog();
|
||||||
await nextTick();
|
nextTick(() => {
|
||||||
adjustDialogZ(favoriteDialogRef.value.$el);
|
favoriteDialogIndex.value = getNextDialogIndex();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,49 +1,90 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog class="x-dialog" :visible.sync="fullscreenImageDialog.visible" append-to-body width="97vw">
|
<el-dialog class="x-dialog" v-model="fullscreenImageDialog.visible" append-to-body width="97vw">
|
||||||
<div>
|
<div>
|
||||||
<div style="margin: 0 0 5px 5px">
|
<div style="margin: 0 0 5px 5px">
|
||||||
|
<el-tooltip :content="t('dialog.fullscreen_image.copy_image_to_clipboard')" placement="top">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-s-order"
|
:icon="CopyDocument"
|
||||||
circle
|
circle
|
||||||
@click="copyImageUrl(fullscreenImageDialog.imageUrl)"></el-button>
|
@click="copyImageToClipboard(fullscreenImageDialog.imageUrl)"></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip :content="t('dialog.fullscreen_image.download_and_save_image')" placement="top">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-download"
|
:icon="Download"
|
||||||
circle
|
circle
|
||||||
style="margin-left: 5px"
|
style="margin-left: 5px"
|
||||||
:disabled="!fullscreenImageDialog.imageUrl.startsWith('http')"
|
:disabled="!fullscreenImageDialog.imageUrl.startsWith('http')"
|
||||||
@click="
|
@click="
|
||||||
downloadAndSaveImage(fullscreenImageDialog.imageUrl, fullscreenImageDialog.fileName)
|
downloadAndSaveImage(fullscreenImageDialog.imageUrl, fullscreenImageDialog.fileName)
|
||||||
"></el-button>
|
"></el-button>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<img v-lazy="fullscreenImageDialog.imageUrl" style="width: 100%; height: 85vh; object-fit: contain" />
|
<img
|
||||||
|
:src="fullscreenImageDialog.imageUrl"
|
||||||
|
style="width: 100%; height: 85vh; object-fit: contain"
|
||||||
|
loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { Download, CopyDocument } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import Noty from 'noty';
|
import Noty from 'noty';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance } from 'vue';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { copyToClipboard, escapeTag, extractFileId } from '../../shared/utils';
|
import { escapeTag, extractFileId } from '../../shared/utils';
|
||||||
import { useGalleryStore } from '../../stores';
|
import { useGalleryStore } from '../../stores';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
const { t } = useI18n();
|
||||||
const { $message } = proxy;
|
|
||||||
|
|
||||||
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
|
const { fullscreenImageDialog } = storeToRefs(useGalleryStore());
|
||||||
|
|
||||||
function copyImageUrl(imageUrl) {
|
async function copyImageToClipboard(url) {
|
||||||
copyToClipboard(imageUrl, 'ImageUrl copied to clipboard');
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg = ElMessage({
|
||||||
|
message: 'Downloading image...',
|
||||||
|
type: 'info'
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const response = await webApiService.execute({
|
||||||
|
url,
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
if (response.status !== 200 || !response.data.startsWith('data:image/png')) {
|
||||||
|
throw new Error(`Error: ${response.data}`);
|
||||||
|
}
|
||||||
|
await navigator.clipboard.write([
|
||||||
|
new ClipboardItem({
|
||||||
|
'image/png': await (await fetch(response.data)).blob()
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
ElMessage({
|
||||||
|
message: 'Image copied to clipboard',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading image:', error);
|
||||||
|
new Noty({
|
||||||
|
type: 'error',
|
||||||
|
text: escapeTag(`Failed to download image. ${url}`)
|
||||||
|
}).show();
|
||||||
|
} finally {
|
||||||
|
msg.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadAndSaveImage(url, fileName) {
|
async function downloadAndSaveImage(url, fileName) {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$message({
|
const msg = ElMessage({
|
||||||
message: 'Downloading image...',
|
message: 'Downloading image...',
|
||||||
type: 'info'
|
type: 'info'
|
||||||
});
|
});
|
||||||
@@ -77,6 +118,8 @@
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
text: escapeTag(`Failed to download image. ${url}`)
|
text: escapeTag(`Failed to download image. ${url}`)
|
||||||
}).show();
|
}).show();
|
||||||
|
} finally {
|
||||||
|
msg.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="gallerySelectDialog.visible"
|
v-model="gallerySelectDialog.visible"
|
||||||
:title="t('dialog.gallery_select.header')"
|
:title="t('dialog.gallery_select.header')"
|
||||||
width="100%"
|
width="100%"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -16,16 +16,16 @@
|
|||||||
style="display: none"
|
style="display: none"
|
||||||
@change="onFileChangeGallery" />
|
@change="onFileChangeGallery" />
|
||||||
<el-button-group>
|
<el-button-group>
|
||||||
<el-button type="default" size="small" icon="el-icon-close" @click="selectImageGallerySelect('', '')">{{
|
<el-button type="default" size="small" :icon="Close" @click="selectImageGallerySelect('', '')">{{
|
||||||
t('dialog.gallery_select.none')
|
t('dialog.gallery_select.none')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<el-button type="default" size="small" icon="el-icon-refresh" @click="refreshGalleryTable">{{
|
<el-button type="default" size="small" :icon="Refresh" @click="refreshGalleryTable">{{
|
||||||
t('dialog.gallery_select.refresh')
|
t('dialog.gallery_select.refresh')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="small"
|
size="small"
|
||||||
icon="el-icon-upload2"
|
:icon="Upload"
|
||||||
:disabled="!currentUser.$isVRCPlus"
|
:disabled="!currentUser.$isVRCPlus"
|
||||||
@click="displayGalleryUpload"
|
@click="displayGalleryUpload"
|
||||||
>{{ t('dialog.gallery_select.upload') }}</el-button
|
>{{ t('dialog.gallery_select.upload') }}</el-button
|
||||||
@@ -42,24 +42,28 @@
|
|||||||
v-if="image.versions[image.versions.length - 1].file.url"
|
v-if="image.versions[image.versions.length - 1].file.url"
|
||||||
class="vrcplus-icon"
|
class="vrcplus-icon"
|
||||||
@click="selectImageGallerySelect(image.versions[image.versions.length - 1].file.url, image.id)">
|
@click="selectImageGallerySelect(image.versions[image.versions.length - 1].file.url, image.id)">
|
||||||
<img v-lazy="image.versions[image.versions.length - 1].file.url" class="avatar" /></div
|
<img
|
||||||
|
:src="image.versions[image.versions.length - 1].file.url"
|
||||||
|
class="avatar"
|
||||||
|
loading="lazy" /></div
|
||||||
></template>
|
></template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { Close, Refresh, Upload } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance } from 'vue';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
import { vrcPlusImageRequest } from '../../../api';
|
import { vrcPlusImageRequest } from '../../../api';
|
||||||
import { useGalleryStore, useUserStore } from '../../../stores';
|
import { useGalleryStore, useUserStore } from '../../../stores';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const { $message } = proxy;
|
|
||||||
const { galleryTable } = storeToRefs(useGalleryStore());
|
const { galleryTable } = storeToRefs(useGalleryStore());
|
||||||
const { refreshGalleryTable, handleGalleryImageAdd } = useGalleryStore();
|
const { refreshGalleryTable, handleGalleryImageAdd } = useGalleryStore();
|
||||||
const { currentUser } = storeToRefs(useUserStore());
|
const { currentUser } = storeToRefs(useUserStore());
|
||||||
@@ -95,7 +99,7 @@
|
|||||||
}
|
}
|
||||||
if (files[0].size >= 100000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 100MB
|
// 100MB
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.file.too_large'),
|
message: t('message.file.too_large'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -103,7 +107,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!files[0].type.match(/image.*/)) {
|
if (!files[0].type.match(/image.*/)) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.file.not_image'),
|
message: t('message.file.not_image'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -115,7 +119,7 @@
|
|||||||
const base64Body = btoa(r.result.toString());
|
const base64Body = btoa(r.result.toString());
|
||||||
vrcPlusImageRequest.uploadGalleryImage(base64Body).then((args) => {
|
vrcPlusImageRequest.uploadGalleryImage(base64Body).then((args) => {
|
||||||
handleGalleryImageAdd(args);
|
handleGalleryImageAdd(args);
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.gallery.uploaded'),
|
message: t('message.gallery.uploaded'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="groupMemberModeration.visible"
|
v-model="groupMemberModeration.visible"
|
||||||
:title="t('dialog.group_member_moderation.header')"
|
:title="t('dialog.group_member_moderation.header')"
|
||||||
append-to-body
|
append-to-body
|
||||||
width="90vw">
|
width="90vw">
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-refresh"
|
:icon="Refresh"
|
||||||
:loading="isGroupMembersLoading"
|
:loading="isGroupMembersLoading"
|
||||||
circle
|
circle
|
||||||
@click="loadAllGroupMembers" />
|
@click="loadAllGroupMembers" />
|
||||||
@@ -34,21 +34,23 @@
|
|||||||
memberSearch.length ||
|
memberSearch.length ||
|
||||||
!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')
|
!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')
|
||||||
)
|
)
|
||||||
"
|
">
|
||||||
@click.native.stop>
|
<el-button size="small" @click.stop>
|
||||||
<el-button size="mini">
|
|
||||||
<span
|
<span
|
||||||
>{{ t(memberSortOrder.name) }} <i class="el-icon-arrow-down el-icon--right"></i
|
>{{ t(memberSortOrder.name) }}
|
||||||
></span>
|
<el-icon style="margin-left: 5px"><ArrowDown /></el-icon>
|
||||||
|
</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
v-for="item in groupDialogSortingOptions"
|
v-for="item in groupDialogSortingOptions"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
@click.native="setGroupMemberSortOrder(item)">
|
@click="setGroupMemberSortOrder(item)">
|
||||||
{{ t(item.name) }}
|
{{ t(item.name) }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<span style="margin-right: 5px">{{ t('dialog.group.members.filter') }}</span>
|
<span style="margin-right: 5px">{{ t('dialog.group.members.filter') }}</span>
|
||||||
<el-dropdown
|
<el-dropdown
|
||||||
@@ -61,33 +63,36 @@
|
|||||||
memberSearch.length ||
|
memberSearch.length ||
|
||||||
!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')
|
!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')
|
||||||
)
|
)
|
||||||
"
|
">
|
||||||
@click.native.stop>
|
<el-button size="small" @click.stop>
|
||||||
<el-button size="mini">
|
|
||||||
<span
|
<span
|
||||||
>{{ t(memberFilter.name) }} <i class="el-icon-arrow-down el-icon--right"></i
|
>{{ t(memberFilter.name) }}
|
||||||
|
<el-icon style="margin-left: 5px"><ArrowDown /></el-icon
|
||||||
></span>
|
></span>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
v-for="item in groupDialogFilterOptions"
|
v-for="item in groupDialogFilterOptions"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
@click.native="setGroupMemberFilter(item)"
|
@click="setGroupMemberFilter(item)"
|
||||||
v-text="t(item.name)"></el-dropdown-item>
|
v-text="t(item.name)"></el-dropdown-item>
|
||||||
|
<template v-for="role in groupMemberModeration.groupRef.roles" :key="role.name">
|
||||||
<el-dropdown-item
|
<el-dropdown-item
|
||||||
v-for="item in groupMemberModeration.groupRef.roles"
|
v-if="!role.defaultRole"
|
||||||
:key="item.name"
|
@click="setGroupMemberFilter(role)">
|
||||||
@click.native="setGroupMemberFilter(item)"
|
{{ t(role.name) }}
|
||||||
><span v-if="!item.defaultRole">{{ t(item.name) }}</span></el-dropdown-item
|
</el-dropdown-item>
|
||||||
>
|
</template>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="memberSearch"
|
v-model="memberSearch"
|
||||||
:disabled="!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')"
|
:disabled="!hasGroupPermission(groupMemberModeration.groupRef, 'group-bans-manage')"
|
||||||
clearable
|
clearable
|
||||||
size="mini"
|
size="small"
|
||||||
:placeholder="t('dialog.group.members.search')"
|
:placeholder="t('dialog.group.members.search')"
|
||||||
style="margin-top: 10px; margin-bottom: 10px"
|
style="margin-top: 10px; margin-bottom: 10px"
|
||||||
@input="groupMembersSearch"></el-input>
|
@input="groupMembersSearch"></el-input>
|
||||||
@@ -95,13 +100,13 @@
|
|||||||
<el-button size="small" @click="selectAllGroupMembers">{{
|
<el-button size="small" @click="selectAllGroupMembers">{{
|
||||||
t('dialog.group_member_moderation.select_all')
|
t('dialog.group_member_moderation.select_all')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<data-tables
|
<DataTable
|
||||||
v-if="groupMemberModerationTable.data.length"
|
v-if="groupMemberModerationTable.data.length"
|
||||||
v-bind="groupMemberModerationTable"
|
v-bind="groupMemberModerationTable"
|
||||||
style="margin-top: 10px">
|
style="margin-top: 10px">
|
||||||
<el-table-column width="55" prop="$selected">
|
<el-table-column width="55" prop="$selected">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="text" size="mini" @click.stop>
|
<el-button type="text" size="small" @click.stop>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="scope.row.$selected"
|
v-model="scope.row.$selected"
|
||||||
@change="
|
@change="
|
||||||
@@ -115,16 +120,19 @@
|
|||||||
width="70"
|
width="70"
|
||||||
prop="photo">
|
prop="photo">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-popover placement="right" height="500px" trigger="hover">
|
<el-popover placement="right" :width="500" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
<img
|
<img
|
||||||
slot="reference"
|
:src="userImage(scope.row.user)"
|
||||||
v-lazy="userImage(scope.row.user)"
|
|
||||||
class="friends-list-avatar" />
|
|
||||||
<img
|
|
||||||
v-lazy="userImageFull(scope.row.user)"
|
|
||||||
class="friends-list-avatar"
|
class="friends-list-avatar"
|
||||||
style="height: 500px; cursor: pointer"
|
loading="lazy" />
|
||||||
@click="showFullscreenImageDialog(userImageFull(scope.row.user))" />
|
</template>
|
||||||
|
<img
|
||||||
|
:src="userImageFull(scope.row.user)"
|
||||||
|
:class="['friends-list-avatar', 'x-popover-image']"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="showFullscreenImageDialog(userImageFull(scope.row.user))"
|
||||||
|
loading="lazy" />
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -145,9 +153,11 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="t('dialog.group_member_moderation.roles')" prop="roleIds" sortable>
|
<el-table-column :label="t('dialog.group_member_moderation.roles')" prop="roleIds" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<template v-for="(roleId, index) in scope.row.roleIds">
|
<template v-for="(roleId, index) in scope.row.roleIds" :key="roleId">
|
||||||
<template v-for="(role, rIndex) in groupMemberModeration.groupRef.roles">
|
<template
|
||||||
<span v-if="role?.id === roleId" :key="roleId + rIndex"
|
v-for="(role, rIndex) in groupMemberModeration.groupRef.roles"
|
||||||
|
:key="roleId + rIndex">
|
||||||
|
<span v-if="role?.id === roleId"
|
||||||
>{{ role.name
|
>{{ role.name
|
||||||
}}<span v-if="index < scope.row.roleIds.length - 1">, </span></span
|
}}<span v-if="index < scope.row.roleIds.length - 1">, </span></span
|
||||||
></template
|
></template
|
||||||
@@ -181,7 +191,7 @@
|
|||||||
<span v-text="scope.row.visibility"></span>
|
<span v-text="scope.row.visibility"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
@@ -191,8 +201,8 @@
|
|||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-refresh"
|
:icon="Refresh"
|
||||||
:loading="isGroupMembersLoading"
|
:loading="isGroupMembersLoading"
|
||||||
circle
|
circle
|
||||||
@click="getAllGroupBans(groupMemberModeration.id)"></el-button>
|
@click="getAllGroupBans(groupMemberModeration.id)"></el-button>
|
||||||
@@ -203,17 +213,17 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="groupBansModerationTable.filters[0].value"
|
v-model="groupBansModerationTable.filters[0].value"
|
||||||
clearable
|
clearable
|
||||||
size="mini"
|
size="small"
|
||||||
:placeholder="t('dialog.group.members.search')"
|
:placeholder="t('dialog.group.members.search')"
|
||||||
style="margin-top: 10px; margin-bottom: 10px"></el-input>
|
style="margin-top: 10px; margin-bottom: 10px"></el-input>
|
||||||
<br />
|
<br />
|
||||||
<el-button size="small" @click="selectAllGroupBans">{{
|
<el-button size="small" @click="selectAllGroupBans">{{
|
||||||
t('dialog.group_member_moderation.select_all')
|
t('dialog.group_member_moderation.select_all')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<data-tables v-bind="groupBansModerationTable" style="margin-top: 10px">
|
<DataTable v-bind="groupBansModerationTable" style="margin-top: 10px">
|
||||||
<el-table-column width="55" prop="$selected">
|
<el-table-column width="55" prop="$selected">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="text" size="mini" @click.stop>
|
<el-button type="text" size="small" @click.stop>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="scope.row.$selected"
|
v-model="scope.row.$selected"
|
||||||
@change="
|
@change="
|
||||||
@@ -227,16 +237,19 @@
|
|||||||
width="70"
|
width="70"
|
||||||
prop="photo">
|
prop="photo">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-popover placement="right" height="500px" trigger="hover">
|
<el-popover placement="right" :width="500" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
<img
|
<img
|
||||||
slot="reference"
|
:src="userImage(scope.row.user)"
|
||||||
v-lazy="userImage(scope.row.user)"
|
|
||||||
class="friends-list-avatar" />
|
|
||||||
<img
|
|
||||||
v-lazy="userImageFull(scope.row.user)"
|
|
||||||
class="friends-list-avatar"
|
class="friends-list-avatar"
|
||||||
style="height: 500px; cursor: pointer"
|
loading="lazy" />
|
||||||
@click="showFullscreenImageDialog(userImageFull(scope.row.user))" />
|
</template>
|
||||||
|
<img
|
||||||
|
:src="userImageFull(scope.row.user)"
|
||||||
|
:class="['friends-list-avatar', 'x-popover-image']"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="showFullscreenImageDialog(userImageFull(scope.row.user))"
|
||||||
|
loading="lazy" />
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -257,16 +270,14 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="t('dialog.group_member_moderation.roles')" prop="roleIds" sortable>
|
<el-table-column :label="t('dialog.group_member_moderation.roles')" prop="roleIds" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<template v-for="(roleId, index) in scope.row.roleIds">
|
<template v-for="(roleId, index) in scope.row.roleIds" :key="roleId">
|
||||||
<span
|
<span
|
||||||
v-for="(role, rIndex) in groupMemberModeration.groupRef.roles"
|
v-for="(role, rIndex) in groupMemberModeration.groupRef.roles"
|
||||||
v-if="role.id === roleId"
|
v-if="role.id === roleId"
|
||||||
:key="rIndex + roleId"
|
:key="rIndex + roleId"
|
||||||
>{{ role.name }}</span
|
>{{ role.name }}</span
|
||||||
>
|
>
|
||||||
<span v-if="index < scope.row.roleIds.length - 1" :key="index + roleId"
|
<span v-if="index < scope.row.roleIds.length - 1">, </span>
|
||||||
>,
|
|
||||||
</span>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -296,7 +307,7 @@
|
|||||||
<span>{{ formatDateFilter(scope.row.bannedAt, 'long') }}</span>
|
<span>{{ formatDateFilter(scope.row.bannedAt, 'long') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
@@ -306,29 +317,29 @@
|
|||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-refresh"
|
:icon="Refresh"
|
||||||
:loading="isGroupMembersLoading"
|
:loading="isGroupMembersLoading"
|
||||||
circle
|
circle
|
||||||
@click="getAllGroupInvitesAndJoinRequests(groupMemberModeration.id)"></el-button>
|
@click="getAllGroupInvitesAndJoinRequests(groupMemberModeration.id)"></el-button>
|
||||||
<br />
|
<br />
|
||||||
<el-tabs>
|
<el-tabs>
|
||||||
<el-tab-pane>
|
<el-tab-pane>
|
||||||
<span slot="label">
|
<template #label>
|
||||||
<span style="font-weight: bold; font-size: 16px">{{
|
<span style="font-weight: bold; font-size: 16px">{{
|
||||||
t('dialog.group_member_moderation.sent_invites')
|
t('dialog.group_member_moderation.sent_invites')
|
||||||
}}</span>
|
}}</span>
|
||||||
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
||||||
groupInvitesModerationTable.data.length
|
groupInvitesModerationTable.data.length
|
||||||
}}</span>
|
}}</span>
|
||||||
</span>
|
</template>
|
||||||
<el-button size="small" @click="selectAllGroupInvites">{{
|
<el-button size="small" @click="selectAllGroupInvites">{{
|
||||||
t('dialog.group_member_moderation.select_all')
|
t('dialog.group_member_moderation.select_all')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<data-tables v-bind="groupInvitesModerationTable" style="margin-top: 10px">
|
<DataTable v-bind="groupInvitesModerationTable" style="margin-top: 10px">
|
||||||
<el-table-column width="55" prop="$selected">
|
<el-table-column width="55" prop="$selected">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="text" size="mini" @click.stop>
|
<el-button type="text" size="small" @click.stop>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="scope.row.$selected"
|
v-model="scope.row.$selected"
|
||||||
@change="
|
@change="
|
||||||
@@ -342,16 +353,19 @@
|
|||||||
width="70"
|
width="70"
|
||||||
prop="photo">
|
prop="photo">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-popover placement="right" height="500px" trigger="hover">
|
<el-popover placement="right" :width="500" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
<img
|
<img
|
||||||
slot="reference"
|
:src="userImage(scope.row.user)"
|
||||||
v-lazy="userImage(scope.row.user)"
|
|
||||||
class="friends-list-avatar" />
|
|
||||||
<img
|
|
||||||
v-lazy="userImageFull(scope.row.user)"
|
|
||||||
class="friends-list-avatar"
|
class="friends-list-avatar"
|
||||||
style="height: 500px; cursor: pointer"
|
loading="lazy" />
|
||||||
@click="showFullscreenImageDialog(userImageFull(scope.row.user))" />
|
</template>
|
||||||
|
<img
|
||||||
|
:src="userImageFull(scope.row.user)"
|
||||||
|
:class="['friends-list-avatar', 'x-popover-image']"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="showFullscreenImageDialog(userImageFull(scope.row.user))"
|
||||||
|
loading="lazy" />
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -378,7 +392,7 @@
|
|||||||
<span @click.stop v-text="scope.row.managerNotes"></span>
|
<span @click.stop v-text="scope.row.managerNotes"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
<br />
|
<br />
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="
|
:disabled="
|
||||||
@@ -396,21 +410,21 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane>
|
<el-tab-pane>
|
||||||
<span slot="label">
|
<template #label>
|
||||||
<span style="font-weight: bold; font-size: 16px">{{
|
<span style="font-weight: bold; font-size: 16px">{{
|
||||||
t('dialog.group_member_moderation.join_requests')
|
t('dialog.group_member_moderation.join_requests')
|
||||||
}}</span>
|
}}</span>
|
||||||
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
||||||
groupJoinRequestsModerationTable.data.length
|
groupJoinRequestsModerationTable.data.length
|
||||||
}}</span>
|
}}</span>
|
||||||
</span>
|
</template>
|
||||||
<el-button size="small" @click="selectAllGroupJoinRequests">{{
|
<el-button size="small" @click="selectAllGroupJoinRequests">{{
|
||||||
t('dialog.group_member_moderation.select_all')
|
t('dialog.group_member_moderation.select_all')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<data-tables v-bind="groupJoinRequestsModerationTable" style="margin-top: 10px">
|
<DataTable v-bind="groupJoinRequestsModerationTable" style="margin-top: 10px">
|
||||||
<el-table-column width="55" prop="$selected">
|
<el-table-column width="55" prop="$selected">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="text" size="mini" @click.stop>
|
<el-button type="text" size="small" @click.stop>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="scope.row.$selected"
|
v-model="scope.row.$selected"
|
||||||
@change="
|
@change="
|
||||||
@@ -424,16 +438,19 @@
|
|||||||
width="70"
|
width="70"
|
||||||
prop="photo">
|
prop="photo">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-popover placement="right" height="500px" trigger="hover">
|
<el-popover placement="right" :width="500" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
<img
|
<img
|
||||||
slot="reference"
|
:src="userImage(scope.row.user)"
|
||||||
v-lazy="userImage(scope.row.user)"
|
|
||||||
class="friends-list-avatar" />
|
|
||||||
<img
|
|
||||||
v-lazy="userImageFull(scope.row.user)"
|
|
||||||
class="friends-list-avatar"
|
class="friends-list-avatar"
|
||||||
style="height: 500px; cursor: pointer"
|
loading="lazy" />
|
||||||
@click="showFullscreenImageDialog(userImageFull(scope.row.user))" />
|
</template>
|
||||||
|
<img
|
||||||
|
:src="userImageFull(scope.row.user)"
|
||||||
|
:class="['friends-list-avatar', 'x-popover-image']"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="showFullscreenImageDialog(userImageFull(scope.row.user))"
|
||||||
|
loading="lazy" />
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -460,7 +477,7 @@
|
|||||||
<span @click.stop v-text="scope.row.managerNotes"></span>
|
<span @click.stop v-text="scope.row.managerNotes"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
<br />
|
<br />
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="
|
:disabled="
|
||||||
@@ -504,21 +521,21 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane>
|
<el-tab-pane>
|
||||||
<span slot="label">
|
<template #label>
|
||||||
<span style="font-weight: bold; font-size: 16px">{{
|
<span style="font-weight: bold; font-size: 16px">{{
|
||||||
t('dialog.group_member_moderation.blocked_requests')
|
t('dialog.group_member_moderation.blocked_requests')
|
||||||
}}</span>
|
}}</span>
|
||||||
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
||||||
groupBlockedModerationTable.data.length
|
groupBlockedModerationTable.data.length
|
||||||
}}</span>
|
}}</span>
|
||||||
</span>
|
</template>
|
||||||
<el-button size="small" @click="selectAllGroupBlocked">{{
|
<el-button size="small" @click="selectAllGroupBlocked">{{
|
||||||
t('dialog.group_member_moderation.select_all')
|
t('dialog.group_member_moderation.select_all')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<data-tables v-bind="groupBlockedModerationTable" style="margin-top: 10px">
|
<DataTable v-bind="groupBlockedModerationTable" style="margin-top: 10px">
|
||||||
<el-table-column width="55" prop="$selected">
|
<el-table-column width="55" prop="$selected">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="text" size="mini" @click.stop>
|
<el-button type="text" size="small" @click.stop>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="scope.row.$selected"
|
v-model="scope.row.$selected"
|
||||||
@change="
|
@change="
|
||||||
@@ -532,16 +549,19 @@
|
|||||||
width="70"
|
width="70"
|
||||||
prop="photo">
|
prop="photo">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-popover placement="right" height="500px" trigger="hover">
|
<el-popover placement="right" :width="500" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
<img
|
<img
|
||||||
slot="reference"
|
:src="userImage(scope.row.user)"
|
||||||
v-lazy="userImage(scope.row.user)"
|
|
||||||
class="friends-list-avatar" />
|
|
||||||
<img
|
|
||||||
v-lazy="userImageFull(scope.row.user)"
|
|
||||||
class="friends-list-avatar"
|
class="friends-list-avatar"
|
||||||
style="height: 500px; cursor: pointer"
|
loading="lazy" />
|
||||||
@click="showFullscreenImageDialog(userImageFull(scope.row.user))" />
|
</template>
|
||||||
|
<img
|
||||||
|
:src="userImageFull(scope.row.user)"
|
||||||
|
:class="['friends-list-avatar', 'x-popover-image']"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="showFullscreenImageDialog(userImageFull(scope.row.user))"
|
||||||
|
loading="lazy" />
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -568,7 +588,7 @@
|
|||||||
<span @click.stop v-text="scope.row.managerNotes"></span>
|
<span @click.stop v-text="scope.row.managerNotes"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
<br />
|
<br />
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="
|
:disabled="
|
||||||
@@ -594,8 +614,8 @@
|
|||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-refresh"
|
:icon="Refresh"
|
||||||
:loading="isGroupMembersLoading"
|
:loading="isGroupMembersLoading"
|
||||||
circle
|
circle
|
||||||
@click="getAllGroupLogs(groupMemberModeration.id)"></el-button>
|
@click="getAllGroupLogs(groupMemberModeration.id)"></el-button>
|
||||||
@@ -637,7 +657,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<data-tables v-bind="groupLogsModerationTable" style="margin-top: 10px">
|
<DataTable v-bind="groupLogsModerationTable" style="margin-top: 10px">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="t('dialog.group_member_moderation.created_at')"
|
:label="t('dialog.group_member_moderation.created_at')"
|
||||||
width="170"
|
width="170"
|
||||||
@@ -684,7 +704,7 @@
|
|||||||
v-text="JSON.stringify(scope.row.data)"></span>
|
v-text="JSON.stringify(scope.row.data)"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
@@ -695,7 +715,7 @@
|
|||||||
<br />
|
<br />
|
||||||
<el-input
|
<el-input
|
||||||
v-model="selectUserId"
|
v-model="selectUserId"
|
||||||
size="mini"
|
size="small"
|
||||||
style="margin-top: 5px; width: 340px"
|
style="margin-top: 5px; width: 340px"
|
||||||
:placeholder="t('dialog.group_member_moderation.user_id_placeholder')"
|
:placeholder="t('dialog.group_member_moderation.user_id_placeholder')"
|
||||||
clearable></el-input>
|
clearable></el-input>
|
||||||
@@ -707,8 +727,8 @@
|
|||||||
<span class="name">{{ t('dialog.group_member_moderation.selected_users') }}</span>
|
<span class="name">{{ t('dialog.group_member_moderation.selected_users') }}</span>
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
type="default"
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-delete"
|
:icon="Delete"
|
||||||
circle
|
circle
|
||||||
style="margin-left: 5px"
|
style="margin-left: 5px"
|
||||||
@click="clearSelectedGroupMembers"></el-button>
|
@click="clearSelectedGroupMembers"></el-button>
|
||||||
@@ -725,7 +745,7 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<span>{{ t('dialog.group_member_moderation.user_isnt_in_group') }}</span>
|
<span>{{ t('dialog.group_member_moderation.user_isnt_in_group') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<i class="el-icon el-icon-warning" style="display: inline-block" />
|
<el-icon style="margin-left: 3px; display: inline-block"><Warning /></el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span v-text="user.user?.displayName || user.userId" style="font-weight: bold; margin-left: 5px"></span>
|
<span v-text="user.user?.displayName || user.userId" style="font-weight: bold; margin-left: 5px"></span>
|
||||||
</el-tag>
|
</el-tag>
|
||||||
@@ -739,7 +759,7 @@
|
|||||||
:rows="2"
|
:rows="2"
|
||||||
:autosize="{ minRows: 1, maxRows: 20 }"
|
:autosize="{ minRows: 1, maxRows: 20 }"
|
||||||
:placeholder="t('dialog.group_member_moderation.note_placeholder')"
|
:placeholder="t('dialog.group_member_moderation.note_placeholder')"
|
||||||
size="mini"
|
size="small"
|
||||||
resize="none"
|
resize="none"
|
||||||
style="margin-top: 5px"></el-input>
|
style="margin-top: 5px"></el-input>
|
||||||
<br />
|
<br />
|
||||||
@@ -826,7 +846,7 @@
|
|||||||
>{{ t('dialog.group_member_moderation.unban') }}</el-button
|
>{{ t('dialog.group_member_moderation.unban') }}</el-button
|
||||||
>
|
>
|
||||||
<span v-if="progressCurrent" style="margin-top: 10px">
|
<span v-if="progressCurrent" style="margin-top: 10px">
|
||||||
<i class="el-icon-loading" style="margin-left: 5px; margin-right: 5px"></i>
|
<el-icon class="is-loading" style="margin-left: 5px; margin-right: 5px"><Loading /></el-icon>
|
||||||
{{ t('dialog.group_member_moderation.progress') }} {{ progressCurrent }}/{{ progressTotal }}
|
{{ t('dialog.group_member_moderation.progress') }} {{ progressCurrent }}/{{ progressTotal }}
|
||||||
</span>
|
</span>
|
||||||
<el-button v-if="progressCurrent" style="margin-left: 5px" @click="progressTotal = 0">{{
|
<el-button v-if="progressCurrent" style="margin-left: 5px" @click="progressTotal = 0">{{
|
||||||
@@ -834,15 +854,18 @@
|
|||||||
}}</el-button>
|
}}</el-button>
|
||||||
</div>
|
</div>
|
||||||
<group-member-moderation-export-dialog
|
<group-member-moderation-export-dialog
|
||||||
:is-group-logs-export-dialog-visible.sync="isGroupLogsExportDialogVisible"
|
:is-group-logs-export-dialog-visible="isGroupLogsExportDialogVisible"
|
||||||
:group-logs-moderation-table="groupLogsModerationTable" />
|
:group-logs-moderation-table="groupLogsModerationTable" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Refresh, Delete, ArrowDown, Warning, Loading } from '@element-plus/icons-vue';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance, reactive, ref, watch } from 'vue';
|
import { reactive, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import * as workerTimers from 'worker-timers';
|
import * as workerTimers from 'worker-timers';
|
||||||
import { groupRequest, userRequest } from '../../../api';
|
import { groupRequest, userRequest } from '../../../api';
|
||||||
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
|
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
|
||||||
@@ -857,9 +880,6 @@
|
|||||||
const { applyGroupMember, handleGroupMember, handleGroupMemberProps } = useGroupStore();
|
const { applyGroupMember, handleGroupMember, handleGroupMemberProps } = useGroupStore();
|
||||||
const { showFullscreenImageDialog } = useGalleryStore();
|
const { showFullscreenImageDialog } = useGalleryStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const selectedUsers = reactive({});
|
const selectedUsers = reactive({});
|
||||||
const selectedUsersArray = ref([]);
|
const selectedUsersArray = ref([]);
|
||||||
const isGroupMembersLoading = ref(false);
|
const isGroupMembersLoading = ref(false);
|
||||||
@@ -880,7 +900,7 @@
|
|||||||
n: 100,
|
n: 100,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
groupId: '',
|
groupId: '',
|
||||||
sort: '',
|
sort: 'joinedAt:desc',
|
||||||
roleId: ''
|
roleId: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -917,7 +937,7 @@
|
|||||||
|
|
||||||
const groupInvitesModerationTable = reactive({
|
const groupInvitesModerationTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
tableProps: { stripe: true, size: 'mini' },
|
tableProps: { stripe: true, size: 'small' },
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
small: true,
|
small: true,
|
||||||
@@ -927,7 +947,7 @@
|
|||||||
});
|
});
|
||||||
const groupJoinRequestsModerationTable = reactive({
|
const groupJoinRequestsModerationTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
tableProps: { stripe: true, size: 'mini' },
|
tableProps: { stripe: true, size: 'small' },
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
small: true,
|
small: true,
|
||||||
@@ -937,7 +957,7 @@
|
|||||||
});
|
});
|
||||||
const groupBlockedModerationTable = reactive({
|
const groupBlockedModerationTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
tableProps: { stripe: true, size: 'mini' },
|
tableProps: { stripe: true, size: 'small' },
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
small: true,
|
small: true,
|
||||||
@@ -948,7 +968,7 @@
|
|||||||
const groupLogsModerationTable = reactive({
|
const groupLogsModerationTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
filters: [{ prop: ['description'], value: '' }],
|
filters: [{ prop: ['description'], value: '' }],
|
||||||
tableProps: { stripe: true, size: 'mini' },
|
tableProps: { stripe: true, size: 'small' },
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
small: true,
|
small: true,
|
||||||
@@ -959,7 +979,7 @@
|
|||||||
const groupBansModerationTable = reactive({
|
const groupBansModerationTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
filters: [{ prop: ['$displayName'], value: '' }],
|
filters: [{ prop: ['$displayName'], value: '' }],
|
||||||
tableProps: { stripe: true, size: 'mini' },
|
tableProps: { stripe: true, size: 'small' },
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
small: true,
|
small: true,
|
||||||
@@ -969,7 +989,7 @@
|
|||||||
});
|
});
|
||||||
const groupMemberModerationTable = reactive({
|
const groupMemberModerationTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
tableProps: { stripe: true, size: 'mini' },
|
tableProps: { stripe: true, size: 'small' },
|
||||||
pageSize: 15,
|
pageSize: 15,
|
||||||
paginationProps: {
|
paginationProps: {
|
||||||
small: true,
|
small: true,
|
||||||
@@ -1084,7 +1104,7 @@
|
|||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to delete group invites: ${err}`,
|
message: `Failed to delete group invites: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1092,7 +1112,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Deleted ${memberCount} group invites`,
|
message: `Deleted ${memberCount} group invites`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1136,7 +1156,7 @@
|
|||||||
}
|
}
|
||||||
groupBansModerationTable.data = fetchedBans;
|
groupBansModerationTable.data = fetchedBans;
|
||||||
} catch {
|
} catch {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Failed to get group bans',
|
message: 'Failed to get group bans',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1167,13 +1187,13 @@
|
|||||||
await groupRequest.banGroupMember({ groupId: D.id, userId: user.userId });
|
await groupRequest.banGroupMember({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to ban group member: ${err}`,
|
message: `Failed to ban group member: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$message({ message: `Banned ${memberCount} group members`, type: 'success' });
|
ElMessage({ message: `Banned ${memberCount} group members`, type: 'success' });
|
||||||
progressCurrent.value = 0;
|
progressCurrent.value = 0;
|
||||||
progressTotal.value = 0;
|
progressTotal.value = 0;
|
||||||
getAllGroupBans(D.id);
|
getAllGroupBans(D.id);
|
||||||
@@ -1196,7 +1216,7 @@
|
|||||||
await groupRequest.unbanGroupMember({ groupId: D.id, userId: user.userId });
|
await groupRequest.unbanGroupMember({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to unban group member: ${err}`,
|
message: `Failed to unban group member: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1205,7 +1225,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({ message: `Unbanned ${memberCount} group members`, type: 'success' });
|
ElMessage({ message: `Unbanned ${memberCount} group members`, type: 'success' });
|
||||||
}
|
}
|
||||||
progressCurrent.value = 0;
|
progressCurrent.value = 0;
|
||||||
progressTotal.value = 0;
|
progressTotal.value = 0;
|
||||||
@@ -1230,7 +1250,7 @@
|
|||||||
await groupRequest.kickGroupMember({ groupId: D.id, userId: user.userId });
|
await groupRequest.kickGroupMember({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to kick group member: ${err}`,
|
message: `Failed to kick group member: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1238,7 +1258,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({ message: `Kicked ${memberCount} group members`, type: 'success' });
|
ElMessage({ message: `Kicked ${memberCount} group members`, type: 'success' });
|
||||||
}
|
}
|
||||||
progressCurrent.value = 0;
|
progressCurrent.value = 0;
|
||||||
progressTotal.value = 0;
|
progressTotal.value = 0;
|
||||||
@@ -1266,7 +1286,7 @@
|
|||||||
handleGroupMemberProps(args);
|
handleGroupMemberProps(args);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to set group member note for ${err}`,
|
message: `Failed to set group member note for ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1274,7 +1294,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({ message: `Saved notes for ${memberCount} group members`, type: 'success' });
|
ElMessage({ message: `Saved notes for ${memberCount} group members`, type: 'success' });
|
||||||
}
|
}
|
||||||
progressCurrent.value = 0;
|
progressCurrent.value = 0;
|
||||||
progressTotal.value = 0;
|
progressTotal.value = 0;
|
||||||
@@ -1314,7 +1334,7 @@
|
|||||||
handleGroupMemberRoleChange(args);
|
handleGroupMemberRoleChange(args);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to remove group member roles: ${err}`,
|
message: `Failed to remove group member roles: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1323,7 +1343,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Roles removed`,
|
message: `Roles removed`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1362,7 +1382,7 @@
|
|||||||
handleGroupMemberRoleChange(args);
|
handleGroupMemberRoleChange(args);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to add group member roles: ${err}`,
|
message: `Failed to add group member roles: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1371,7 +1391,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Added group member roles`,
|
message: `Added group member roles`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1466,7 +1486,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Failed to get group logs',
|
message: 'Failed to get group logs',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1496,7 +1516,7 @@
|
|||||||
await groupRequest.deleteBlockedGroupRequest({ groupId: D.id, userId: user.userId });
|
await groupRequest.deleteBlockedGroupRequest({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to delete blocked group requests: ${err}`,
|
message: `Failed to delete blocked group requests: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1504,7 +1524,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Deleted ${memberCount} blocked group requests`,
|
message: `Deleted ${memberCount} blocked group requests`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1532,7 +1552,7 @@
|
|||||||
await groupRequest.blockGroupInviteRequest({ groupId: D.id, userId: user.userId });
|
await groupRequest.blockGroupInviteRequest({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to block group join requests: ${err}`,
|
message: `Failed to block group join requests: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1540,7 +1560,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Blocked ${memberCount} group join requests`,
|
message: `Blocked ${memberCount} group join requests`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1569,7 +1589,7 @@
|
|||||||
await groupRequest.rejectGroupInviteRequest({ groupId: D.id, userId: user.userId });
|
await groupRequest.rejectGroupInviteRequest({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to reject group join requests: ${err}`,
|
message: `Failed to reject group join requests: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1577,7 +1597,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Rejected ${memberCount} group join requests`,
|
message: `Rejected ${memberCount} group join requests`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1605,7 +1625,7 @@
|
|||||||
await groupRequest.acceptGroupInviteRequest({ groupId: D.id, userId: user.userId });
|
await groupRequest.acceptGroupInviteRequest({ groupId: D.id, userId: user.userId });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Failed to accept group join requests: ${err}`,
|
message: `Failed to accept group join requests: ${err}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1613,7 +1633,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: `Accepted ${memberCount} group join requests`,
|
message: `Accepted ${memberCount} group join requests`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -1661,7 +1681,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Failed to get group join requests',
|
message: 'Failed to get group join requests',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1694,7 +1714,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Failed to get group join requests',
|
message: 'Failed to get group join requests',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1731,7 +1751,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Failed to get group invites',
|
message: 'Failed to get group invites',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -1823,7 +1843,7 @@
|
|||||||
members.value = [];
|
members.value = [];
|
||||||
isGroupMembersDone.value = false;
|
isGroupMembersDone.value = false;
|
||||||
loadMoreGroupMembersParams.value = {
|
loadMoreGroupMembersParams.value = {
|
||||||
sort: '',
|
sort: 'joinedAt:desc',
|
||||||
roleId: '',
|
roleId: '',
|
||||||
n: 100,
|
n: 100,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible="isGroupLogsExportDialogVisible"
|
:model-value="isGroupLogsExportDialogVisible"
|
||||||
:title="t('dialog.group_member_moderation.export_logs')"
|
:title="t('dialog.group_member_moderation.export_logs')"
|
||||||
width="650px"
|
width="650px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
v-model="checkedGroupLogsExportLogsOptions"
|
v-model="checkedGroupLogsExportLogsOptions"
|
||||||
style="margin-bottom: 10px"
|
style="margin-bottom: 10px"
|
||||||
@change="updateGroupLogsExportContent">
|
@change="updateGroupLogsExportContent">
|
||||||
<template v-for="option in checkGroupsLogsExportLogsOptions">
|
<template v-for="option in checkGroupsLogsExportLogsOptions" :key="option.label">
|
||||||
<el-checkbox :key="option.label" :label="option.label">
|
<el-checkbox :label="option.label">
|
||||||
{{ t(option.text) }}
|
{{ t(option.text) }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</template>
|
</template>
|
||||||
@@ -20,18 +20,18 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="groupLogsExportContent"
|
v-model="groupLogsExportContent"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
size="mini"
|
size="small"
|
||||||
rows="15"
|
:rows="15"
|
||||||
resize="none"
|
resize="none"
|
||||||
readonly
|
readonly
|
||||||
style="margin-top: 15px"
|
style="margin-top: 15px"
|
||||||
@click.native="handleCopyGroupLogsExportContent" />
|
@click="handleCopyGroupLogsExportContent" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { copyToClipboard } from '../../../shared/utils';
|
import { copyToClipboard } from '../../../shared/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
:visible.sync="groupPostEditDialog.visible"
|
v-model="groupPostEditDialog.visible"
|
||||||
:title="t('dialog.group_post_edit.header')"
|
:title="t('dialog.group_post_edit.header')"
|
||||||
width="650px"
|
width="650px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<h3 v-text="groupPostEditDialog.groupRef.name"></h3>
|
<h3 v-text="groupPostEditDialog.groupRef.name"></h3>
|
||||||
<el-form :model="groupPostEditDialog" label-width="150px">
|
<el-form :model="groupPostEditDialog" label-width="150px">
|
||||||
<el-form-item :label="t('dialog.group_post_edit.title')">
|
<el-form-item :label="t('dialog.group_post_edit.title')">
|
||||||
<el-input v-model="groupPostEditDialog.title" size="mini"></el-input>
|
<el-input v-model="groupPostEditDialog.title" size="small"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('dialog.group_post_edit.message')">
|
<el-form-item :label="t('dialog.group_post_edit.message')">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -61,29 +61,32 @@
|
|||||||
<el-form-item :label="t('dialog.group_post_edit.image')">
|
<el-form-item :label="t('dialog.group_post_edit.image')">
|
||||||
<template v-if="gallerySelectDialog.selectedFileId">
|
<template v-if="gallerySelectDialog.selectedFileId">
|
||||||
<div style="display: inline-block; flex: none; margin-right: 5px">
|
<div style="display: inline-block; flex: none; margin-right: 5px">
|
||||||
<el-popover placement="right" width="500px" trigger="click">
|
<el-popover placement="right" :width="500" trigger="click">
|
||||||
|
<template #reference>
|
||||||
<img
|
<img
|
||||||
slot="reference"
|
:src="gallerySelectDialog.selectedImageUrl"
|
||||||
v-lazy="gallerySelectDialog.selectedImageUrl"
|
|
||||||
style="
|
style="
|
||||||
flex: none;
|
flex: none;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
" />
|
"
|
||||||
|
loading="lazy" />
|
||||||
|
</template>
|
||||||
<img
|
<img
|
||||||
v-lazy="gallerySelectDialog.selectedImageUrl"
|
:src="gallerySelectDialog.selectedImageUrl"
|
||||||
style="height: 500px"
|
:class="['x-link', 'x-popover-image']"
|
||||||
@click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)" />
|
@click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)"
|
||||||
|
loading="lazy" />
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<el-button size="mini" style="vertical-align: top" @click="clearImageGallerySelect">
|
<el-button size="small" style="vertical-align: top" @click="clearImageGallerySelect">
|
||||||
{{ t('dialog.invite_message.clear_selected_image') }}
|
{{ t('dialog.invite_message.clear_selected_image') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-button size="mini" style="margin-right: 5px" @click="showGallerySelectDialog">
|
<el-button size="small" style="margin-right: 5px" @click="showGallerySelectDialog">
|
||||||
{{ t('dialog.invite_message.select_image') }}
|
{{ t('dialog.invite_message.select_image') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -105,12 +108,13 @@
|
|||||||
:gallery-select-dialog="gallerySelectDialog"
|
:gallery-select-dialog="gallerySelectDialog"
|
||||||
:gallery-table="galleryTable"
|
:gallery-table="galleryTable"
|
||||||
@refresh-gallery-table="refreshGalleryTable" />
|
@refresh-gallery-table="refreshGalleryTable" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, getCurrentInstance } from 'vue';
|
import { ElMessage } from 'element-plus';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { groupRequest, vrcPlusIconRequest } from '../../../api';
|
import { groupRequest, vrcPlusIconRequest } from '../../../api';
|
||||||
import { useGalleryStore, useGroupStore } from '../../../stores';
|
import { useGalleryStore, useGroupStore } from '../../../stores';
|
||||||
import GallerySelectDialog from './GallerySelectDialog.vue';
|
import GallerySelectDialog from './GallerySelectDialog.vue';
|
||||||
@@ -125,7 +129,6 @@
|
|||||||
|
|
||||||
const emit = defineEmits(['update:dialogData']);
|
const emit = defineEmits(['update:dialogData']);
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { showFullscreenImageDialog, handleFilesList } = useGalleryStore();
|
const { showFullscreenImageDialog, handleFilesList } = useGalleryStore();
|
||||||
@@ -169,7 +172,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!D.title || !D.text) {
|
if (!D.title || !D.text) {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Title and text are required',
|
message: 'Title and text are required',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
});
|
});
|
||||||
@@ -188,8 +191,8 @@
|
|||||||
params.imageId = gallerySelectDialog.value.selectedFileId;
|
params.imageId = gallerySelectDialog.value.selectedFileId;
|
||||||
}
|
}
|
||||||
groupRequest.editGroupPost(params).then((args) => {
|
groupRequest.editGroupPost(params).then((args) => {
|
||||||
handleGroupPost();
|
handleGroupPost(args);
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Group post edited',
|
message: 'Group post edited',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -200,7 +203,7 @@
|
|||||||
function createGroupPost() {
|
function createGroupPost() {
|
||||||
const D = groupPostEditDialog.value;
|
const D = groupPostEditDialog.value;
|
||||||
if (!D.title || !D.text) {
|
if (!D.title || !D.text) {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Title and text are required',
|
message: 'Title and text are required',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
});
|
});
|
||||||
@@ -220,7 +223,7 @@
|
|||||||
}
|
}
|
||||||
groupRequest.createGroupPost(params).then((args) => {
|
groupRequest.createGroupPost(params).then((args) => {
|
||||||
handleGroupPost();
|
handleGroupPost();
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Group post created',
|
message: 'Group post created',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible="editAndSendInviteDialog.visible"
|
:model-value="editAndSendInviteDialog.visible"
|
||||||
:title="t('dialog.edit_send_invite_message.header')"
|
:title="t('dialog.edit_send_invite_message.header')"
|
||||||
width="400px"
|
width="400px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="editAndSendInviteDialog.newMessage"
|
v-model="editAndSendInviteDialog.newMessage"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
size="mini"
|
size="small"
|
||||||
maxlength="64"
|
maxlength="64"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||||
@@ -21,28 +21,25 @@
|
|||||||
style="margin-top: 10px"></el-input>
|
style="margin-top: 10px"></el-input>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button type="small" @click="cancelEditAndSendInvite">
|
<el-button @click="cancelEditAndSendInvite">
|
||||||
{{ t('dialog.edit_send_invite_message.cancel') }}
|
{{ t('dialog.edit_send_invite_message.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" size="small" @click="saveEditAndSendInvite">
|
<el-button type="primary" size="small" @click="saveEditAndSendInvite">
|
||||||
{{ t('dialog.edit_send_invite_message.send') }}
|
{{ t('dialog.edit_send_invite_message.send') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance } from 'vue';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
import { instanceRequest, inviteMessagesRequest, notificationRequest } from '../../../api';
|
import { instanceRequest, inviteMessagesRequest, notificationRequest } from '../../../api';
|
||||||
import { parseLocation } from '../../../shared/utils';
|
import { parseLocation } from '../../../shared/utils';
|
||||||
import { useGalleryStore, useUserStore } from '../../../stores';
|
import { useGalleryStore, useUserStore } from '../../../stores';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const { uploadImage } = storeToRefs(useGalleryStore());
|
const { uploadImage } = storeToRefs(useGalleryStore());
|
||||||
const { clearInviteImageUpload } = useGalleryStore();
|
const { clearInviteImageUpload } = useGalleryStore();
|
||||||
const { currentUser } = storeToRefs(useUserStore());
|
const { currentUser } = storeToRefs(useUserStore());
|
||||||
@@ -86,13 +83,13 @@
|
|||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
if (args.json[slot].message === I.messageSlot.message) {
|
if (args.json[slot].message === I.messageSlot.message) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: "VRChat API didn't update message, try again",
|
message: "VRChat API didn't update message, try again",
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
throw new Error("VRChat API didn't update message, try again");
|
throw new Error("VRChat API didn't update message, try again");
|
||||||
} else {
|
} else {
|
||||||
$message('Invite message updated');
|
ElMessage('Invite message updated');
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
});
|
});
|
||||||
@@ -139,7 +136,7 @@
|
|||||||
} else {
|
} else {
|
||||||
J.loading = false;
|
J.loading = false;
|
||||||
J.visible = false;
|
J.visible = false;
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite sent',
|
message: 'Invite sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -155,7 +152,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite photo message sent',
|
message: 'Invite photo message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -168,7 +165,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite message sent',
|
message: 'Invite message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -185,7 +182,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Request invite photo message sent',
|
message: 'Request invite photo message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -198,7 +195,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Request invite message sent',
|
message: 'Request invite message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="inviteDialog.visible"
|
v-model="inviteDialog.visible"
|
||||||
:title="t('dialog.invite.header')"
|
:title="t('dialog.invite.header')"
|
||||||
width="500px"
|
width="500px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<div v-if="inviteDialog.visible" v-loading="inviteDialog.loading">
|
<div v-if="inviteDialog.visible" v-loading="inviteDialog.loading">
|
||||||
<Location :location="inviteDialog.worldId" :link="false" />
|
<Location :location="inviteDialog.worldId" :link="false" />
|
||||||
<br />
|
<br />
|
||||||
<el-button size="mini" style="margin-top: 10px" @click="addSelfToInvite">{{
|
<el-button size="small" style="margin-top: 10px" @click="addSelfToInvite">{{
|
||||||
t('dialog.invite.add_self')
|
t('dialog.invite.add_self')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="small"
|
||||||
:disabled="inviteDialog.friendsInInstance.length === 0"
|
:disabled="inviteDialog.friendsInInstance.length === 0"
|
||||||
style="margin-top: 10px"
|
style="margin-top: 10px"
|
||||||
@click="addFriendsInInstanceToInvite"
|
@click="addFriendsInInstanceToInvite"
|
||||||
>{{ t('dialog.invite.add_friends_in_instance') }}</el-button
|
>{{ t('dialog.invite.add_friends_in_instance') }}</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="small"
|
||||||
:disabled="vipFriends.length === 0"
|
:disabled="vipFriends.length === 0"
|
||||||
style="margin-top: 10px"
|
style="margin-top: 10px"
|
||||||
@click="addFavoriteFriendsToInvite"
|
@click="addFavoriteFriendsToInvite"
|
||||||
@@ -34,24 +34,25 @@
|
|||||||
filterable
|
filterable
|
||||||
:disabled="inviteDialog.loading"
|
:disabled="inviteDialog.loading"
|
||||||
style="width: 100%; margin-top: 15px">
|
style="width: 100%; margin-top: 15px">
|
||||||
<el-option-group v-if="currentUser" :label="t('side_panel.me')">
|
<template v-if="currentUser">
|
||||||
|
<el-option-group :label="t('side_panel.me')">
|
||||||
<el-option
|
<el-option
|
||||||
class="x-friend-item"
|
class="x-friend-item"
|
||||||
:label="currentUser.displayName"
|
:label="currentUser.displayName"
|
||||||
:value="currentUser.id"
|
:value="currentUser.id"
|
||||||
style="height: auto">
|
style="height: auto">
|
||||||
<div :class="['avatar', userStatusClass(currentUser)]">
|
<div :class="['avatar', userStatusClass(currentUser)]">
|
||||||
<img v-lazy="userImage(currentUser)" />
|
<img :src="userImage(currentUser)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name">{{ currentUser.displayName }}</span>
|
<span class="name">{{ currentUser.displayName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-option-group
|
<template v-if="inviteDialog.friendsInInstance.length">
|
||||||
v-if="inviteDialog.friendsInInstance.length"
|
<el-option-group :label="t('dialog.invite.friends_in_instance')">
|
||||||
:label="t('dialog.invite.friends_in_instance')">
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in inviteDialog.friendsInInstance"
|
v-for="friend in inviteDialog.friendsInInstance"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div :class="['avatar', userStatusClass(friend.ref)]">
|
<div :class="['avatar', userStatusClass(friend.ref)]">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
||||||
@@ -72,8 +73,10 @@
|
|||||||
<span v-else>{{ friend.id }}</span>
|
<span v-else>{{ friend.id }}</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-option-group v-if="vipFriends.length" :label="t('side_panel.favorite')">
|
<template v-if="vipFriends.length">
|
||||||
|
<el-option-group :label="t('side_panel.favorite')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in vipFriends"
|
v-for="friend in vipFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -83,7 +86,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div :class="['avatar', userStatusClass(friend.ref)]">
|
<div :class="['avatar', userStatusClass(friend.ref)]">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
||||||
@@ -94,8 +97,10 @@
|
|||||||
<span v-else>{{ friend.id }}</span>
|
<span v-else>{{ friend.id }}</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-option-group v-if="onlineFriends.length" :label="t('side_panel.online')">
|
<template v-if="onlineFriends.length">
|
||||||
|
<el-option-group :label="t('side_panel.online')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in onlineFriends"
|
v-for="friend in onlineFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -105,7 +110,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div :class="['avatar', userStatusClass(friend.ref)]">
|
<div :class="['avatar', userStatusClass(friend.ref)]">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
||||||
@@ -116,8 +121,10 @@
|
|||||||
<span v-else>{{ friend.id }}</span>
|
<span v-else>{{ friend.id }}</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
<el-option-group v-if="activeFriends.length" :label="t('side_panel.active')">
|
<template v-if="activeFriends.length">
|
||||||
|
<el-option-group :label="t('side_panel.active')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in activeFriends"
|
v-for="friend in activeFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -126,7 +133,7 @@
|
|||||||
:value="friend.id"
|
:value="friend.id"
|
||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar"><img v-lazy="userImage(friend.ref)" /></div>
|
<div class="avatar"><img :src="userImage(friend.ref)" loading="lazy" /></div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
<span class="name" :style="{ color: friend.ref.$userColour }">{{
|
||||||
friend.ref.displayName
|
friend.ref.displayName
|
||||||
@@ -136,6 +143,7 @@
|
|||||||
<span v-else>{{ friend.id }}</span>
|
<span v-else>{{ friend.id }}</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
|
</template>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -155,17 +163,19 @@
|
|||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<SendInviteDialog
|
<SendInviteDialog
|
||||||
:send-invite-dialog-visible.sync="sendInviteDialogVisible"
|
:send-invite-dialog-visible="sendInviteDialogVisible"
|
||||||
:send-invite-dialog="sendInviteDialog"
|
:send-invite-dialog="sendInviteDialog"
|
||||||
:invite-dialog="inviteDialog"
|
:invite-dialog="inviteDialog"
|
||||||
@closeInviteDialog="closeInviteDialog" />
|
@closeInviteDialog="closeInviteDialog" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { instanceRequest, notificationRequest } from '../../../api';
|
import { instanceRequest, notificationRequest } from '../../../api';
|
||||||
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
|
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
|
||||||
import { useFriendStore, useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
import { useFriendStore, useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||||
@@ -177,10 +187,6 @@
|
|||||||
const { clearInviteImageUpload } = useGalleryStore();
|
const { clearInviteImageUpload } = useGalleryStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
const $confirm = instance.proxy.$confirm;
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
inviteDialog: {
|
inviteDialog: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -238,11 +244,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sendInvite() {
|
function sendInvite() {
|
||||||
$confirm('Continue? Invite', 'Confirm', {
|
ElMessageBox.confirm('Continue? Invite', 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
cancelButtonText: 'Cancel',
|
cancelButtonText: 'Cancel',
|
||||||
type: 'info',
|
type: 'info'
|
||||||
callback: (action) => {
|
})
|
||||||
|
.then((action) => {
|
||||||
const D = props.inviteDialog;
|
const D = props.inviteDialog;
|
||||||
if (action !== 'confirm' || D.loading === true) {
|
if (action !== 'confirm' || D.loading === true) {
|
||||||
return;
|
return;
|
||||||
@@ -275,14 +282,14 @@
|
|||||||
} else {
|
} else {
|
||||||
D.loading = false;
|
D.loading = false;
|
||||||
D.visible = false;
|
D.visible = false;
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite sent',
|
message: 'Invite sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
inviteLoop();
|
inviteLoop();
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible="visible"
|
:model-value="visible"
|
||||||
:title="t('dialog.invite_message.header')"
|
:title="t('dialog.invite_message.header')"
|
||||||
width="400px"
|
width="400px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -18,22 +18,19 @@
|
|||||||
{{ t('dialog.invite_message.confirm') }}
|
{{ t('dialog.invite_message.confirm') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance } from 'vue';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
import { instanceRequest, notificationRequest } from '../../../api';
|
import { instanceRequest, notificationRequest } from '../../../api';
|
||||||
import { parseLocation } from '../../../shared/utils';
|
import { parseLocation } from '../../../shared/utils';
|
||||||
import { useGalleryStore, useUserStore } from '../../../stores';
|
import { useGalleryStore, useUserStore } from '../../../stores';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const { uploadImage } = storeToRefs(useGalleryStore());
|
const { uploadImage } = storeToRefs(useGalleryStore());
|
||||||
const { clearInviteImageUpload } = useGalleryStore();
|
const { clearInviteImageUpload } = useGalleryStore();
|
||||||
const { currentUser } = storeToRefs(useUserStore());
|
const { currentUser } = storeToRefs(useUserStore());
|
||||||
@@ -54,10 +51,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'closeInviteDialog']);
|
const emit = defineEmits(['update:model-value', 'closeInviteDialog']);
|
||||||
|
|
||||||
function cancelInviteConfirm() {
|
function cancelInviteConfirm() {
|
||||||
emit('update:visible', false);
|
emit('update:model-value', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendInviteConfirm() {
|
function sendInviteConfirm() {
|
||||||
@@ -106,7 +103,7 @@
|
|||||||
} else {
|
} else {
|
||||||
J.loading = false;
|
J.loading = false;
|
||||||
J.visible = false;
|
J.visible = false;
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite message sent',
|
message: 'Invite message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -122,7 +119,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite photo message sent',
|
message: 'Invite photo message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -135,7 +132,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Invite message sent',
|
message: 'Invite message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -152,7 +149,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Request invite photo message sent',
|
message: 'Request invite photo message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -165,7 +162,7 @@
|
|||||||
throw err;
|
throw err;
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Request invite message sent',
|
message: 'Request invite message sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible="sendInviteDialogVisible"
|
:model-value="sendInviteDialogVisible"
|
||||||
:title="t('dialog.invite_message.header')"
|
:title="t('dialog.invite_message.header')"
|
||||||
width="800px"
|
width="800px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -9,46 +9,46 @@
|
|||||||
<template v-if="currentUser.$isVRCPlus">
|
<template v-if="currentUser.$isVRCPlus">
|
||||||
<!-- <template v-if="gallerySelectDialog.selectedFileId">-->
|
<!-- <template v-if="gallerySelectDialog.selectedFileId">-->
|
||||||
<!-- <div style="display: inline-block; flex: none; margin-right: 5px">-->
|
<!-- <div style="display: inline-block; flex: none; margin-right: 5px">-->
|
||||||
<!-- <el-popover placement="right" width="500px" trigger="click">-->
|
<!-- <el-popover placement="right" :width="500px" trigger="click">-->
|
||||||
<!-- <template #reference>-->
|
<!-- <template #reference>-->
|
||||||
<!-- <img-->
|
<!-- <img-->
|
||||||
<!-- class="x-link"-->
|
<!-- class="x-link"-->
|
||||||
<!-- v-lazy="gallerySelectDialog.selectedImageUrl"-->
|
<!-- :src="gallerySelectDialog.selectedImageUrl"-->
|
||||||
<!-- style="flex: none; width: 60px; height: 60px; border-radius: 4px; object-fit: cover" />-->
|
<!-- style="flex: none; width: 60px; height: 60px; border-radius: 4px; object-fit: cover" />-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- <img-->
|
<!-- <img-->
|
||||||
<!-- class="x-link"-->
|
<!-- class="x-link"-->
|
||||||
<!-- v-lazy="gallerySelectDialog.selectedImageUrl"-->
|
<!-- :src="gallerySelectDialog.selectedImageUrl"-->
|
||||||
<!-- style="height: 500px"-->
|
<!-- style="height: 500px"-->
|
||||||
<!-- @click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)" />-->
|
<!-- @click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)" />-->
|
||||||
<!-- </el-popover>-->
|
<!-- </el-popover>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <el-button size="mini" @click="clearImageGallerySelect" style="vertical-align: top">-->
|
<!-- <el-button size="small" @click="clearImageGallerySelect" style="vertical-align: top">-->
|
||||||
<!-- {{ t('dialog.invite_message.clear_selected_image') }}-->
|
<!-- {{ t('dialog.invite_message.clear_selected_image') }}-->
|
||||||
<!-- </el-button>-->
|
<!-- </el-button>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- <template v-else>-->
|
<!-- <template v-else>-->
|
||||||
<!-- <el-button size="mini" @click="showGallerySelectDialog" style="margin-right: 5px">-->
|
<!-- <el-button size="small" @click="showGallerySelectDialog" style="margin-right: 5px">-->
|
||||||
<!-- {{ t('dialog.invite_message.select_image') }}-->
|
<!-- {{ t('dialog.invite_message.select_image') }}-->
|
||||||
<!-- </el-button>-->
|
<!-- </el-button>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<input class="inviteImageUploadButton" type="file" accept="image/*" @change="inviteImageUpload" />
|
<input class="inviteImageUploadButton" type="file" accept="image/*" @change="inviteImageUpload" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<data-tables
|
<DataTable
|
||||||
v-bind="inviteMessageTable"
|
v-bind="inviteMessageTable"
|
||||||
style="margin-top: 10px; cursor: pointer"
|
style="margin-top: 10px; cursor: pointer"
|
||||||
@row-click="showSendInviteConfirmDialog">
|
@row-click="showSendInviteConfirmDialog">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="t('table.profile.invite_messages.slot')"
|
:label="t('table.profile.invite_messages.slot')"
|
||||||
prop="slot"
|
prop="slot"
|
||||||
sortable="custom"
|
:sortable="true"
|
||||||
width="70"></el-table-column>
|
width="70"></el-table-column>
|
||||||
<el-table-column :label="t('table.profile.invite_messages.message')" prop="message"></el-table-column>
|
<el-table-column :label="t('table.profile.invite_messages.message')" prop="message"></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="t('table.profile.invite_messages.cool_down')"
|
:label="t('table.profile.invite_messages.cool_down')"
|
||||||
prop="updatedAt"
|
prop="updatedAt"
|
||||||
sortable="custom"
|
:sortable="true"
|
||||||
width="110"
|
width="110"
|
||||||
align="right">
|
align="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -59,38 +59,40 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-edit"
|
:icon="Edit"
|
||||||
size="mini"
|
size="small"
|
||||||
@click.stop="showEditAndSendInviteDialog(scope.row)"></el-button>
|
@click.stop="showEditAndSendInviteDialog(scope.row)"></el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button type="small" @click="cancelSendInvite">
|
<el-button @click="cancelSendInvite">
|
||||||
{{ t('dialog.invite_message.cancel') }}
|
{{ t('dialog.invite_message.cancel') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="small" @click="refreshInviteMessageTableData('message')">
|
<el-button @click="refreshInviteMessageTableData('message')">
|
||||||
{{ t('dialog.invite_message.refresh') }}
|
{{ t('dialog.invite_message.refresh') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<SendInviteConfirmDialog
|
<SendInviteConfirmDialog
|
||||||
:visible.sync="isSendInviteConfirmDialogVisible"
|
v-model="isSendInviteConfirmDialogVisible"
|
||||||
:send-invite-dialog="sendInviteDialog"
|
:send-invite-dialog="sendInviteDialog"
|
||||||
:invite-dialog="inviteDialog"
|
:invite-dialog="inviteDialog"
|
||||||
@closeInviteDialog="closeInviteDialog" />
|
@closeInviteDialog="closeInviteDialog" />
|
||||||
<EditAndSendInviteDialog
|
<EditAndSendInviteDialog
|
||||||
:edit-and-send-invite-dialog.sync="editAndSendInviteDialog"
|
:edit-and-send-invite-dialog="editAndSendInviteDialog"
|
||||||
:send-invite-dialog="sendInviteDialog"
|
:send-invite-dialog="sendInviteDialog"
|
||||||
:invite-dialog="inviteDialog"
|
:invite-dialog="inviteDialog"
|
||||||
@closeInviteDialog="closeInviteDialog" />
|
@closeInviteDialog="closeInviteDialog" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Edit } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||||
import EditAndSendInviteDialog from './EditAndSendInviteDialog.vue';
|
import EditAndSendInviteDialog from './EditAndSendInviteDialog.vue';
|
||||||
import SendInviteConfirmDialog from './SendInviteConfirmDialog.vue';
|
import SendInviteConfirmDialog from './SendInviteConfirmDialog.vue';
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="inviteGroupDialogRef"
|
:z-index="inviteGroupDialogIndex"
|
||||||
:visible.sync="inviteGroupDialog.visible"
|
v-model="inviteGroupDialog.visible"
|
||||||
:title="$t('dialog.invite_to_group.header')"
|
:title="t('dialog.invite_to_group.header')"
|
||||||
width="450px"
|
width="450px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<div v-if="inviteGroupDialog.visible" v-loading="inviteGroupDialog.loading">
|
<div v-if="inviteGroupDialog.visible" v-loading="inviteGroupDialog.loading">
|
||||||
<span>{{ $t('dialog.invite_to_group.description') }}</span>
|
<span>{{ t('dialog.invite_to_group.description') }}</span>
|
||||||
<br />
|
<br />
|
||||||
<el-select
|
<el-select
|
||||||
v-model="inviteGroupDialog.groupId"
|
v-model="inviteGroupDialog.groupId"
|
||||||
clearable
|
clearable
|
||||||
:placeholder="$t('dialog.invite_to_group.choose_group_placeholder')"
|
:placeholder="t('dialog.invite_to_group.choose_group_placeholder')"
|
||||||
filterable
|
filterable
|
||||||
:disabled="inviteGroupDialog.loading"
|
:disabled="inviteGroupDialog.loading"
|
||||||
style="margin-top: 15px; width: 100%">
|
style="margin-top: 15px; width: 100%">
|
||||||
<el-option-group
|
<el-option-group
|
||||||
:label="$t('dialog.invite_to_group.groups_with_invite_permission')"
|
:label="t('dialog.invite_to_group.groups_with_invite_permission')"
|
||||||
style="width: 410px">
|
style="width: 410px">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="group in groupsWithInvitePermission"
|
v-for="group in groupsWithInvitePermission"
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
style="height: auto"
|
style="height: auto"
|
||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="group.iconUrl" />
|
<img :src="group.iconUrl" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="group.name"></span>
|
<span class="name" v-text="group.name"></span>
|
||||||
@@ -38,11 +38,11 @@
|
|||||||
v-model="inviteGroupDialog.userIds"
|
v-model="inviteGroupDialog.userIds"
|
||||||
multiple
|
multiple
|
||||||
clearable
|
clearable
|
||||||
:placeholder="$t('dialog.invite_to_group.choose_friends_placeholder')"
|
:placeholder="t('dialog.invite_to_group.choose_friends_placeholder')"
|
||||||
filterable
|
filterable
|
||||||
:disabled="inviteGroupDialog.loading"
|
:disabled="inviteGroupDialog.loading"
|
||||||
style="width: 100%; margin-top: 15px">
|
style="width: 100%; margin-top: 15px">
|
||||||
<el-option-group v-if="inviteGroupDialog.userId" :label="$t('dialog.invite_to_group.selected_users')">
|
<el-option-group v-if="inviteGroupDialog.userId" :label="t('dialog.invite_to_group.selected_users')">
|
||||||
<el-option
|
<el-option
|
||||||
:key="inviteGroupDialog.userObject.id"
|
:key="inviteGroupDialog.userObject.id"
|
||||||
:label="inviteGroupDialog.userObject.displayName"
|
:label="inviteGroupDialog.userObject.displayName"
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<template v-if="inviteGroupDialog.userObject.id">
|
<template v-if="inviteGroupDialog.userObject.id">
|
||||||
<div class="avatar" :class="userStatusClass(inviteGroupDialog.userObject)">
|
<div class="avatar" :class="userStatusClass(inviteGroupDialog.userObject)">
|
||||||
<img v-lazy="userImage(inviteGroupDialog.userObject)" />
|
<img :src="userImage(inviteGroupDialog.userObject)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
<span v-else v-text="inviteGroupDialog.userId"></span>
|
<span v-else v-text="inviteGroupDialog.userId"></span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
<el-option-group v-if="vipFriends.length" :label="$t('side_panel.favorite')">
|
<el-option-group v-if="vipFriends.length" :label="t('side_panel.favorite')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in vipFriends"
|
v-for="friend in vipFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar" :class="userStatusClass(friend.ref)">
|
<div class="avatar" :class="userStatusClass(friend.ref)">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
<span v-else v-text="friend.id"></span>
|
<span v-else v-text="friend.id"></span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
<el-option-group v-if="onlineFriends.length" :label="$t('side_panel.online')">
|
<el-option-group v-if="onlineFriends.length" :label="t('side_panel.online')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in onlineFriends"
|
v-for="friend in onlineFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar" :class="userStatusClass(friend.ref)">
|
<div class="avatar" :class="userStatusClass(friend.ref)">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
<span v-else v-text="friend.id"></span>
|
<span v-else v-text="friend.id"></span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
<el-option-group v-if="activeFriends.length" :label="$t('side_panel.active')">
|
<el-option-group v-if="activeFriends.length" :label="t('side_panel.active')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in activeFriends"
|
v-for="friend in activeFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
<span v-else v-text="friend.id"></span>
|
<span v-else v-text="friend.id"></span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-option-group>
|
</el-option-group>
|
||||||
<el-option-group v-if="offlineFriends.length" :label="$t('side_panel.offline')">
|
<el-option-group v-if="offlineFriends.length" :label="t('side_panel.offline')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="friend in offlineFriends"
|
v-for="friend in offlineFriends"
|
||||||
:key="friend.id"
|
:key="friend.id"
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -158,24 +158,25 @@
|
|||||||
size="small"
|
size="small"
|
||||||
:disabled="inviteGroupDialog.loading || !inviteGroupDialog.userIds.length || !inviteGroupDialog.groupId"
|
:disabled="inviteGroupDialog.loading || !inviteGroupDialog.userIds.length || !inviteGroupDialog.groupId"
|
||||||
@click="sendGroupInvite">
|
@click="sendGroupInvite">
|
||||||
{{ $t('dialog.invite_to_group.invite') }}
|
{{ t('dialog.invite_to_group.invite') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, getCurrentInstance, nextTick, computed } from 'vue';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import { ref, watch, nextTick, computed } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { groupRequest, userRequest } from '../../api';
|
import { groupRequest, userRequest } from '../../api';
|
||||||
import { adjustDialogZ, hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
|
import { getNextDialogIndex, hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
|
||||||
import { useFriendStore, useGroupStore } from '../../stores';
|
import { useFriendStore, useGroupStore } from '../../stores';
|
||||||
|
|
||||||
const { vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
|
const { vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
|
||||||
const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());
|
const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());
|
||||||
const { applyGroup } = useGroupStore();
|
const { applyGroup } = useGroupStore();
|
||||||
|
const { t } = useI18n();
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => {
|
() => {
|
||||||
@@ -188,7 +189,7 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const inviteGroupDialogRef = ref(null);
|
const inviteGroupDialogIndex = ref(2000);
|
||||||
|
|
||||||
const groupsWithInvitePermission = computed(() => {
|
const groupsWithInvitePermission = computed(() => {
|
||||||
return Array.from(currentUserGroups.value.values()).filter((group) =>
|
return Array.from(currentUserGroups.value.values()).filter((group) =>
|
||||||
@@ -197,7 +198,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function initDialog() {
|
function initDialog() {
|
||||||
nextTick(() => adjustDialogZ(inviteGroupDialogRef.value.$el));
|
nextTick(() => {
|
||||||
|
inviteGroupDialogIndex.value = getNextDialogIndex();
|
||||||
|
});
|
||||||
const D = inviteGroupDialog.value;
|
const D = inviteGroupDialog.value;
|
||||||
if (D.groupId) {
|
if (D.groupId) {
|
||||||
groupRequest
|
groupRequest
|
||||||
@@ -236,7 +239,7 @@
|
|||||||
}
|
}
|
||||||
// not allowed to invite
|
// not allowed to invite
|
||||||
inviteGroupDialog.value.groupId = '';
|
inviteGroupDialog.value.groupId = '';
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'You are not allowed to invite to this group'
|
message: 'You are not allowed to invite to this group'
|
||||||
});
|
});
|
||||||
@@ -247,11 +250,12 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
function sendGroupInvite() {
|
function sendGroupInvite() {
|
||||||
proxy.$confirm('Continue? Invite User(s) To Group', 'Confirm', {
|
ElMessageBox.confirm('Continue? Invite User(s) To Group', 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
cancelButtonText: 'Cancel',
|
cancelButtonText: 'Cancel',
|
||||||
type: 'info',
|
type: 'info'
|
||||||
callback: (action) => {
|
})
|
||||||
|
.then((action) => {
|
||||||
const D = inviteGroupDialog.value;
|
const D = inviteGroupDialog.value;
|
||||||
if (action !== 'confirm' || D.loading === true) {
|
if (action !== 'confirm' || D.loading === true) {
|
||||||
return;
|
return;
|
||||||
@@ -274,7 +278,7 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
inviteLoop();
|
inviteLoop();
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog ref="launchDialogRef" :visible.sync="isVisible" :title="t('dialog.launch.header')" width="450px">
|
<el-dialog :z-index="launchDialogIndex" v-model="isVisible" :title="t('dialog.launch.header')" width="450px">
|
||||||
<el-form :model="launchDialog" label-width="100px">
|
<el-form :model="launchDialog" label-width="100px">
|
||||||
<el-form-item :label="t('dialog.launch.url')">
|
<el-form-item :label="t('dialog.launch.url')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="launchDialog.url"
|
v-model="launchDialog.url"
|
||||||
size="mini"
|
size="small"
|
||||||
style="width: 260px"
|
style="width: 230px"
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-s-order"
|
:icon="CopyDocument"
|
||||||
style="margin-left: 5px"
|
style="margin-left: 5px"
|
||||||
circle
|
circle
|
||||||
@click="copyInstanceMessage(launchDialog.url)" />
|
@click="copyInstanceMessage(launchDialog.url)" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="launchDialog.shortUrl">
|
<el-form-item v-if="launchDialog.shortUrl">
|
||||||
<template slot="label">
|
<template #label>
|
||||||
<span>{{ t('dialog.launch.short_url') }}</span>
|
<span>{{ t('dialog.launch.short_url') }}</span>
|
||||||
<el-tooltip placement="top" style="margin-left: 5px" :content="t('dialog.launch.short_url_notice')">
|
<el-tooltip placement="top" :content="t('dialog.launch.short_url_notice')">
|
||||||
<i class="el-icon-warning" />
|
<el-icon style="display: inline-block; margin-left: 5px"><Warning /></el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="launchDialog.shortUrl"
|
v-model="launchDialog.shortUrl"
|
||||||
size="mini"
|
size="small"
|
||||||
style="width: 260px"
|
style="width: 230px"
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-s-order"
|
:icon="CopyDocument"
|
||||||
style="margin-left: 5px"
|
style="display: inline-block; margin-left: 5px"
|
||||||
circle
|
circle
|
||||||
@click="copyInstanceMessage(launchDialog.shortUrl)" />
|
@click="copyInstanceMessage(launchDialog.shortUrl)" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@@ -40,23 +40,26 @@
|
|||||||
<el-form-item :label="t('dialog.launch.location')">
|
<el-form-item :label="t('dialog.launch.location')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="launchDialog.location"
|
v-model="launchDialog.location"
|
||||||
size="mini"
|
size="small"
|
||||||
style="width: 260px"
|
style="width: 230px"
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()" />
|
||||||
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')" :disabled="hideTooltips">
|
<el-tooltip placement="right" :content="t('dialog.launch.copy_tooltip')">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="small"
|
||||||
icon="el-icon-s-order"
|
:icon="CopyDocument"
|
||||||
style="margin-left: 5px"
|
style="display: inline-block; margin-left: 5px"
|
||||||
circle
|
circle
|
||||||
@click="copyInstanceMessage(launchDialog.location)" />
|
@click="copyInstanceMessage(launchDialog.location)" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-checkbox v-model="launchDialog.desktop" style="float: left; margin-top: 5px" @change="saveLaunchDialog">
|
<el-checkbox
|
||||||
|
v-model="launchDialog.desktop"
|
||||||
|
style="display: inline-flex; align-items: center; margin-top: 5px"
|
||||||
|
@change="saveLaunchDialog">
|
||||||
{{ t('dialog.launch.start_as_desktop') }}
|
{{ t('dialog.launch.start_as_desktop') }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<template slot="footer">
|
<template #footer>
|
||||||
<el-button size="small" @click="showPreviousInstancesInfoDialog(launchDialog.location)">
|
<el-button size="small" @click="showPreviousInstancesInfoDialog(launchDialog.location)">
|
||||||
{{ t('dialog.launch.info') }}
|
{{ t('dialog.launch.info') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -68,7 +71,6 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<template v-if="canOpenInstanceInGame()">
|
<template v-if="canOpenInstanceInGame()">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
|
||||||
size="small"
|
size="small"
|
||||||
:disabled="!launchDialog.secureOrShortName"
|
:disabled="!launchDialog.secureOrShortName"
|
||||||
@click="handleLaunchGame(launchDialog.location, launchDialog.shortName, launchDialog.desktop)">
|
@click="handleLaunchGame(launchDialog.location, launchDialog.shortName, launchDialog.desktop)">
|
||||||
@@ -84,7 +86,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
|
||||||
size="small"
|
size="small"
|
||||||
:disabled="!launchDialog.secureOrShortName"
|
:disabled="!launchDialog.secureOrShortName"
|
||||||
@click="selfInvite(launchDialog.location, launchDialog.shortName)">
|
@click="selfInvite(launchDialog.location, launchDialog.shortName)">
|
||||||
@@ -100,18 +101,26 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<InviteDialog :invite-dialog="inviteDialog" @closeInviteDialog="closeInviteDialog" />
|
<InviteDialog :invite-dialog="inviteDialog" @closeInviteDialog="closeInviteDialog" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, nextTick, watch, getCurrentInstance } from 'vue';
|
import { CopyDocument, Warning } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { ref, computed, nextTick, watch } from 'vue';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { instanceRequest, worldRequest } from '../../api';
|
import { instanceRequest, worldRequest } from '../../api';
|
||||||
import configRepository from '../../service/config';
|
import configRepository from '../../service/config';
|
||||||
import { adjustDialogZ, checkCanInvite, getLaunchURL, isRealInstance, parseLocation } from '../../shared/utils';
|
|
||||||
import {
|
import {
|
||||||
useAppearanceSettingsStore,
|
getNextDialogIndex,
|
||||||
|
checkCanInvite,
|
||||||
|
getLaunchURL,
|
||||||
|
isRealInstance,
|
||||||
|
parseLocation
|
||||||
|
} from '../../shared/utils';
|
||||||
|
import {
|
||||||
useFriendStore,
|
useFriendStore,
|
||||||
useInviteStore,
|
useInviteStore,
|
||||||
useInstanceStore,
|
useInstanceStore,
|
||||||
@@ -121,19 +130,18 @@
|
|||||||
} from '../../stores';
|
} from '../../stores';
|
||||||
import InviteDialog from './InviteDialog/InviteDialog.vue';
|
import InviteDialog from './InviteDialog/InviteDialog.vue';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { friends } = storeToRefs(useFriendStore());
|
const { friends } = storeToRefs(useFriendStore());
|
||||||
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
|
||||||
const { lastLocation } = storeToRefs(useLocationStore());
|
const { lastLocation } = storeToRefs(useLocationStore());
|
||||||
const { launchGame, tryOpenInstanceInVrc } = useLaunchStore();
|
const { launchGame, tryOpenInstanceInVrc } = useLaunchStore();
|
||||||
const { launchDialogData } = storeToRefs(useLaunchStore());
|
const { launchDialogData } = storeToRefs(useLaunchStore());
|
||||||
|
|
||||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||||
const { canOpenInstanceInGame } = useInviteStore();
|
const { canOpenInstanceInGame } = useInviteStore();
|
||||||
const { isGameRunning } = storeToRefs(useGameStore());
|
const { isGameRunning } = storeToRefs(useGameStore());
|
||||||
|
|
||||||
const launchDialogRef = ref(null);
|
const launchDialogIndex = ref(2000);
|
||||||
|
|
||||||
const launchDialog = ref({
|
const launchDialog = ref({
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -207,17 +215,18 @@
|
|||||||
}
|
}
|
||||||
function handleLaunchGame(location, shortName, desktop) {
|
function handleLaunchGame(location, shortName, desktop) {
|
||||||
if (isGameRunning.value) {
|
if (isGameRunning.value) {
|
||||||
proxy.$confirm(t('dialog.launch.game_running_warning'), t('dialog.launch.header'), {
|
ElMessageBox.confirm(t('dialog.launch.game_running_warning'), t('dialog.launch.header'), {
|
||||||
confirmButtonText: t('dialog.launch.confirm_yes'),
|
confirmButtonText: t('dialog.launch.confirm_yes'),
|
||||||
cancelButtonText: t('dialog.launch.confirm_no'),
|
cancelButtonText: t('dialog.launch.confirm_no'),
|
||||||
type: 'warning',
|
type: 'warning'
|
||||||
callback: (action) => {
|
})
|
||||||
|
.then((action) => {
|
||||||
if (action === 'confirm') {
|
if (action === 'confirm') {
|
||||||
launchGame(location, shortName, desktop);
|
launchGame(location, shortName, desktop);
|
||||||
isVisible.value = false;
|
isVisible.value = false;
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
launchGame(location, shortName, desktop);
|
launchGame(location, shortName, desktop);
|
||||||
@@ -239,7 +248,7 @@
|
|||||||
shortName
|
shortName
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Self invite sent',
|
message: 'Self invite sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -257,7 +266,9 @@
|
|||||||
if (!isRealInstance(tag)) {
|
if (!isRealInstance(tag)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nextTick(() => adjustDialogZ(launchDialogRef.value.$el));
|
nextTick(() => {
|
||||||
|
launchDialogIndex.value = getNextDialogIndex();
|
||||||
|
});
|
||||||
const D = launchDialog.value;
|
const D = launchDialog.value;
|
||||||
D.tag = tag;
|
D.tag = tag;
|
||||||
D.secureOrShortName = shortName;
|
D.secureOrShortName = shortName;
|
||||||
@@ -300,12 +311,12 @@
|
|||||||
async function copyInstanceMessage(input) {
|
async function copyInstanceMessage(input) {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(input);
|
await navigator.clipboard.writeText(input);
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Instance copied to clipboard',
|
message: 'Instance copied to clipboard',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Instance copied failed',
|
message: 'Instance copied failed',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="moderateGroupDialogRef"
|
:z-index="moderateGroupDialogIndex"
|
||||||
:visible.sync="moderateGroupDialog.visible"
|
v-model="moderateGroupDialog.visible"
|
||||||
:title="$t('dialog.moderate_group.header')"
|
:title="t('dialog.moderate_group.header')"
|
||||||
width="450px"
|
width="450px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<div v-if="moderateGroupDialog.visible">
|
<div v-if="moderateGroupDialog.visible">
|
||||||
<div class="x-friend-item" style="cursor: default">
|
<div class="x-friend-item" style="cursor: default">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="userImage(moderateGroupDialog.userObject)" />
|
<img :src="userImage(moderateGroupDialog.userObject)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -22,12 +22,12 @@
|
|||||||
<el-select
|
<el-select
|
||||||
v-model="moderateGroupDialog.groupId"
|
v-model="moderateGroupDialog.groupId"
|
||||||
clearable
|
clearable
|
||||||
:placeholder="$t('dialog.moderate_group.choose_group_placeholder')"
|
:placeholder="t('dialog.moderate_group.choose_group_placeholder')"
|
||||||
filterable
|
filterable
|
||||||
style="margin-top: 15px; width: 100%">
|
style="margin-top: 15px; width: 100%">
|
||||||
<el-option-group
|
<el-option-group
|
||||||
v-if="currentUserGroups.size"
|
v-if="currentUserGroups.size"
|
||||||
:label="$t('dialog.moderate_group.groups_with_moderation_permission')">
|
:label="t('dialog.moderate_group.groups_with_moderation_permission')">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="group in groupsWithModerationPermission"
|
v-for="group in groupsWithModerationPermission"
|
||||||
:key="group.id"
|
:key="group.id"
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
style="height: auto"
|
style="height: auto"
|
||||||
class="x-friend-item">
|
class="x-friend-item">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="group.iconUrl" />
|
<img :src="group.iconUrl" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="group.name"></span>
|
<span class="name" v-text="group.name"></span>
|
||||||
@@ -54,29 +54,23 @@
|
|||||||
showGroupMemberModerationDialog(moderateGroupDialog.groupId, moderateGroupDialog.userId);
|
showGroupMemberModerationDialog(moderateGroupDialog.groupId, moderateGroupDialog.userId);
|
||||||
moderateGroupDialog.visible = false;
|
moderateGroupDialog.visible = false;
|
||||||
">
|
">
|
||||||
{{ $t('dialog.moderate_group.moderation_tools') }}
|
{{ t('dialog.moderate_group.moderation_tools') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, getCurrentInstance, nextTick, computed } from 'vue';
|
import { ref, watch, nextTick, computed } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { groupRequest, userRequest } from '../../api';
|
import { groupRequest, userRequest } from '../../api';
|
||||||
import {
|
import { getNextDialogIndex, hasGroupModerationPermission, userImage } from '../../shared/utils';
|
||||||
adjustDialogZ,
|
|
||||||
hasGroupPermission,
|
|
||||||
hasGroupModerationPermission,
|
|
||||||
userImage,
|
|
||||||
userStatusClass
|
|
||||||
} from '../../shared/utils';
|
|
||||||
import { useGroupStore } from '../../stores';
|
import { useGroupStore } from '../../stores';
|
||||||
|
|
||||||
const { currentUserGroups, moderateGroupDialog } = storeToRefs(useGroupStore());
|
const { currentUserGroups, moderateGroupDialog } = storeToRefs(useGroupStore());
|
||||||
const { applyGroup, showGroupMemberModerationDialog } = useGroupStore();
|
const { showGroupMemberModerationDialog } = useGroupStore();
|
||||||
|
const { t } = useI18n();
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const groupsWithModerationPermission = computed(() => {
|
const groupsWithModerationPermission = computed(() => {
|
||||||
return Array.from(currentUserGroups.value.values()).filter((group) => hasGroupModerationPermission(group));
|
return Array.from(currentUserGroups.value.values()).filter((group) => hasGroupModerationPermission(group));
|
||||||
@@ -93,10 +87,12 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const moderateGroupDialogRef = ref(null);
|
const moderateGroupDialogIndex = ref(2000);
|
||||||
|
|
||||||
function initDialog() {
|
function initDialog() {
|
||||||
nextTick(() => adjustDialogZ(moderateGroupDialogRef.value.$el));
|
nextTick(() => {
|
||||||
|
moderateGroupDialogIndex.value = getNextDialogIndex();
|
||||||
|
});
|
||||||
const D = moderateGroupDialog.value;
|
const D = moderateGroupDialog.value;
|
||||||
if (D.groupId) {
|
if (D.groupId) {
|
||||||
groupRequest
|
groupRequest
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="newInstanceDialogRef"
|
:z-index="newInstanceDialogIndex"
|
||||||
:visible.sync="newInstanceDialog.visible"
|
v-model="newInstanceDialog.visible"
|
||||||
:title="t('dialog.new_instance.header')"
|
:title="t('dialog.new_instance.header')"
|
||||||
width="650px"
|
width="650px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<el-tabs v-model="newInstanceDialog.selectedTab" type="card" @tab-click="newInstanceTabClick">
|
<el-tabs v-model="newInstanceDialog.selectedTab" type="card" @tab-click="newInstanceTabClick">
|
||||||
<el-tab-pane :label="t('dialog.new_instance.normal')">
|
<el-tab-pane name="Normal" :label="t('dialog.new_instance.normal')">
|
||||||
<el-form :model="newInstanceDialog" label-width="150px">
|
<el-form :model="newInstanceDialog" label-width="150px">
|
||||||
<el-form-item :label="t('dialog.new_instance.access_type')">
|
<el-form-item :label="t('dialog.new_instance.access_type')">
|
||||||
<el-radio-group v-model="newInstanceDialog.accessType" size="mini" @change="buildInstance">
|
<el-radio-group v-model="newInstanceDialog.accessType" size="small" @change="buildInstance">
|
||||||
<el-radio-button label="public">{{
|
<el-radio-button label="public">{{
|
||||||
t('dialog.new_instance.access_type_public')
|
t('dialog.new_instance.access_type_public')
|
||||||
}}</el-radio-button>
|
}}</el-radio-button>
|
||||||
@@ -33,7 +33,10 @@
|
|||||||
<el-form-item
|
<el-form-item
|
||||||
v-if="newInstanceDialog.accessType === 'group'"
|
v-if="newInstanceDialog.accessType === 'group'"
|
||||||
:label="t('dialog.new_instance.group_access_type')">
|
:label="t('dialog.new_instance.group_access_type')">
|
||||||
<el-radio-group v-model="newInstanceDialog.groupAccessType" size="mini" @change="buildInstance">
|
<el-radio-group
|
||||||
|
v-model="newInstanceDialog.groupAccessType"
|
||||||
|
size="small"
|
||||||
|
@change="buildInstance">
|
||||||
<el-radio-button
|
<el-radio-button
|
||||||
label="members"
|
label="members"
|
||||||
:disabled="
|
:disabled="
|
||||||
@@ -59,7 +62,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('dialog.new_instance.region')">
|
<el-form-item :label="t('dialog.new_instance.region')">
|
||||||
<el-radio-group v-model="newInstanceDialog.region" size="mini" @change="buildInstance">
|
<el-radio-group v-model="newInstanceDialog.region" size="small" @change="buildInstance">
|
||||||
<el-radio-button label="US West">{{ t('dialog.new_instance.region_usw') }}</el-radio-button>
|
<el-radio-button label="US West">{{ t('dialog.new_instance.region_usw') }}</el-radio-button>
|
||||||
<el-radio-button label="US East">{{ t('dialog.new_instance.region_use') }}</el-radio-button>
|
<el-radio-button label="US East">{{ t('dialog.new_instance.region_use') }}</el-radio-button>
|
||||||
<el-radio-button label="Europe">{{ t('dialog.new_instance.region_eu') }}</el-radio-button>
|
<el-radio-button label="Europe">{{ t('dialog.new_instance.region_eu') }}</el-radio-button>
|
||||||
@@ -84,8 +87,16 @@
|
|||||||
<el-form-item :label="t('dialog.new_instance.world_id')">
|
<el-form-item :label="t('dialog.new_instance.world_id')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newInstanceDialog.worldId"
|
v-model="newInstanceDialog.worldId"
|
||||||
size="mini"
|
size="small"
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()"
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||||
|
@change="buildInstance"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="t('dialog.new_instance.display_name')">
|
||||||
|
<el-input
|
||||||
|
:disabled="!isLocalUserVrcplusSupporter"
|
||||||
|
v-model="newInstanceDialog.displayName"
|
||||||
|
size="small"
|
||||||
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||||
@change="buildInstance"></el-input>
|
@change="buildInstance"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@@ -114,7 +125,7 @@
|
|||||||
hasGroupPermission(group, 'group-instance-open-create'))
|
hasGroupPermission(group, 'group-instance-open-create'))
|
||||||
">
|
">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="group.iconUrl" />
|
<img :src="group.iconUrl" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="group.name"></span>
|
<span class="name" v-text="group.name"></span>
|
||||||
@@ -155,22 +166,22 @@
|
|||||||
<el-form-item :label="t('dialog.new_instance.location')">
|
<el-form-item :label="t('dialog.new_instance.location')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newInstanceDialog.location"
|
v-model="newInstanceDialog.location"
|
||||||
size="mini"
|
size="small"
|
||||||
readonly
|
readonly
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()"></el-input>
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('dialog.new_instance.url')">
|
<el-form-item :label="t('dialog.new_instance.url')">
|
||||||
<el-input v-model="newInstanceDialog.url" size="mini" readonly></el-input>
|
<el-input v-model="newInstanceDialog.url" size="small" readonly></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="t('dialog.new_instance.legacy')">
|
<el-tab-pane name="Legacy" :label="t('dialog.new_instance.legacy')">
|
||||||
<el-form :model="newInstanceDialog" label-width="150px">
|
<el-form :model="newInstanceDialog" label-width="150px">
|
||||||
<el-form-item :label="t('dialog.new_instance.access_type')">
|
<el-form-item :label="t('dialog.new_instance.access_type')">
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="newInstanceDialog.accessType"
|
v-model="newInstanceDialog.accessType"
|
||||||
size="mini"
|
size="small"
|
||||||
@change="buildLegacyInstance">
|
@change="buildLegacyInstance">
|
||||||
<el-radio-button label="public">{{
|
<el-radio-button label="public">{{
|
||||||
t('dialog.new_instance.access_type_public')
|
t('dialog.new_instance.access_type_public')
|
||||||
@@ -197,7 +208,7 @@
|
|||||||
:label="t('dialog.new_instance.group_access_type')">
|
:label="t('dialog.new_instance.group_access_type')">
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="newInstanceDialog.groupAccessType"
|
v-model="newInstanceDialog.groupAccessType"
|
||||||
size="mini"
|
size="small"
|
||||||
@change="buildLegacyInstance">
|
@change="buildLegacyInstance">
|
||||||
<el-radio-button label="members">{{
|
<el-radio-button label="members">{{
|
||||||
t('dialog.new_instance.group_access_type_members')
|
t('dialog.new_instance.group_access_type_members')
|
||||||
@@ -211,7 +222,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('dialog.new_instance.region')">
|
<el-form-item :label="t('dialog.new_instance.region')">
|
||||||
<el-radio-group v-model="newInstanceDialog.region" size="mini" @change="buildLegacyInstance">
|
<el-radio-group v-model="newInstanceDialog.region" size="small" @change="buildLegacyInstance">
|
||||||
<el-radio-button label="US West">{{ t('dialog.new_instance.region_usw') }}</el-radio-button>
|
<el-radio-button label="US West">{{ t('dialog.new_instance.region_usw') }}</el-radio-button>
|
||||||
<el-radio-button label="US East">{{ t('dialog.new_instance.region_use') }}</el-radio-button>
|
<el-radio-button label="US East">{{ t('dialog.new_instance.region_use') }}</el-radio-button>
|
||||||
<el-radio-button label="Europe">{{ t('dialog.new_instance.region_eu') }}</el-radio-button>
|
<el-radio-button label="Europe">{{ t('dialog.new_instance.region_eu') }}</el-radio-button>
|
||||||
@@ -226,15 +237,15 @@
|
|||||||
<el-form-item :label="t('dialog.new_instance.world_id')">
|
<el-form-item :label="t('dialog.new_instance.world_id')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newInstanceDialog.worldId"
|
v-model="newInstanceDialog.worldId"
|
||||||
size="mini"
|
size="small"
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()"
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"
|
||||||
@change="buildLegacyInstance"></el-input>
|
@change="buildLegacyInstance"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('dialog.new_instance.instance_id')">
|
<el-form-item :label="t('dialog.new_instance.instance_id')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newInstanceDialog.instanceName"
|
v-model="newInstanceDialog.instanceName"
|
||||||
:placeholder="t('dialog.new_instance.instance_id_placeholder')"
|
:placeholder="t('dialog.new_instance.instance_id_placeholder')"
|
||||||
size="mini"
|
size="small"
|
||||||
@change="buildLegacyInstance"></el-input>
|
@change="buildLegacyInstance"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
@@ -254,7 +265,7 @@
|
|||||||
:value="currentUser.id"
|
:value="currentUser.id"
|
||||||
style="height: auto">
|
style="height: auto">
|
||||||
<div class="avatar" :class="userStatusClass(currentUser)">
|
<div class="avatar" :class="userStatusClass(currentUser)">
|
||||||
<img v-lazy="userImage(currentUser)" />
|
<img :src="userImage(currentUser)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="currentUser.displayName"></span>
|
<span class="name" v-text="currentUser.displayName"></span>
|
||||||
@@ -271,7 +282,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar" :class="userStatusClass(friend.ref)">
|
<div class="avatar" :class="userStatusClass(friend.ref)">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -293,7 +304,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar" :class="userStatusClass(friend.ref)">
|
<div class="avatar" :class="userStatusClass(friend.ref)">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -315,7 +326,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -337,7 +348,7 @@
|
|||||||
style="height: auto">
|
style="height: auto">
|
||||||
<template v-if="friend.ref">
|
<template v-if="friend.ref">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="userImage(friend.ref)" />
|
<img :src="userImage(friend.ref)" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span
|
<span
|
||||||
@@ -371,7 +382,7 @@
|
|||||||
style="height: auto; width: 478px">
|
style="height: auto; width: 478px">
|
||||||
<template v-if="group">
|
<template v-if="group">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-lazy="group.iconUrl" />
|
<img :src="group.iconUrl" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="name" v-text="group.name"></span></div
|
<span class="name" v-text="group.name"></span></div
|
||||||
@@ -383,17 +394,17 @@
|
|||||||
<el-form-item :label="t('dialog.new_instance.location')">
|
<el-form-item :label="t('dialog.new_instance.location')">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newInstanceDialog.location"
|
v-model="newInstanceDialog.location"
|
||||||
size="mini"
|
size="small"
|
||||||
readonly
|
readonly
|
||||||
@click.native="$event.target.tagName === 'INPUT' && $event.target.select()"></el-input>
|
@click="$event.target.tagName === 'INPUT' && $event.target.select()"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('dialog.new_instance.url')">
|
<el-form-item :label="t('dialog.new_instance.url')">
|
||||||
<el-input v-model="newInstanceDialog.url" size="mini" readonly></el-input>
|
<el-input v-model="newInstanceDialog.url" size="small" readonly></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<template v-if="newInstanceDialog.selectedTab === '0'" #footer>
|
<template v-if="newInstanceDialog.selectedTab === 'Normal'" #footer>
|
||||||
<template v-if="newInstanceDialog.instanceCreated">
|
<template v-if="newInstanceDialog.instanceCreated">
|
||||||
<el-button size="small" @click="copyInstanceUrl(newInstanceDialog.location)">{{
|
<el-button size="small" @click="copyInstanceUrl(newInstanceDialog.location)">{{
|
||||||
t('dialog.new_instance.copy_url')
|
t('dialog.new_instance.copy_url')
|
||||||
@@ -412,13 +423,11 @@
|
|||||||
>
|
>
|
||||||
<template v-if="canOpenInstanceInGame()">
|
<template v-if="canOpenInstanceInGame()">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
|
||||||
size="small"
|
size="small"
|
||||||
@click="showLaunchDialog(newInstanceDialog.location, newInstanceDialog.shortName)"
|
@click="showLaunchDialog(newInstanceDialog.location, newInstanceDialog.shortName)"
|
||||||
>{{ t('dialog.new_instance.launch') }}</el-button
|
>{{ t('dialog.new_instance.launch') }}</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
|
||||||
size="small"
|
size="small"
|
||||||
@click="handleAttachGame(newInstanceDialog.location, newInstanceDialog.shortName)">
|
@click="handleAttachGame(newInstanceDialog.location, newInstanceDialog.shortName)">
|
||||||
{{ t('dialog.new_instance.open_ingame') }}
|
{{ t('dialog.new_instance.open_ingame') }}
|
||||||
@@ -439,7 +448,7 @@
|
|||||||
}}</el-button>
|
}}</el-button>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="newInstanceDialog.selectedTab === '1'" #footer>
|
<template v-else-if="newInstanceDialog.selectedTab === 'Legacy'" #footer>
|
||||||
<el-button size="small" @click="copyInstanceUrl(newInstanceDialog.location)">{{
|
<el-button size="small" @click="copyInstanceUrl(newInstanceDialog.location)">{{
|
||||||
t('dialog.new_instance.copy_url')
|
t('dialog.new_instance.copy_url')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
@@ -457,7 +466,6 @@
|
|||||||
>
|
>
|
||||||
<template v-if="canOpenInstanceInGame()">
|
<template v-if="canOpenInstanceInGame()">
|
||||||
<el-button
|
<el-button
|
||||||
type="default"
|
|
||||||
size="small"
|
size="small"
|
||||||
@click="showLaunchDialog(newInstanceDialog.location, newInstanceDialog.shortName)"
|
@click="showLaunchDialog(newInstanceDialog.location, newInstanceDialog.shortName)"
|
||||||
>{{ t('dialog.new_instance.launch') }}</el-button
|
>{{ t('dialog.new_instance.launch') }}</el-button
|
||||||
@@ -479,17 +487,18 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<InviteDialog :invite-dialog="inviteDialog" @closeInviteDialog="closeInviteDialog" />
|
<InviteDialog :invite-dialog="inviteDialog" @closeInviteDialog="closeInviteDialog" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, nextTick, getCurrentInstance } from 'vue';
|
import { ref, watch, nextTick, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { groupRequest, instanceRequest, worldRequest } from '../../api';
|
import { groupRequest, instanceRequest, worldRequest } from '../../api';
|
||||||
import configRepository from '../../service/config';
|
import configRepository from '../../service/config';
|
||||||
import {
|
import {
|
||||||
adjustDialogZ,
|
getNextDialogIndex,
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
getLaunchURL,
|
getLaunchURL,
|
||||||
hasGroupPermission,
|
hasGroupPermission,
|
||||||
@@ -518,11 +527,9 @@
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const { friends, vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
|
const { friends, vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
|
||||||
const { currentUserGroups, cachedGroups } = storeToRefs(useGroupStore());
|
const { currentUserGroups } = storeToRefs(useGroupStore());
|
||||||
const { handleGroupPermissions } = useGroupStore();
|
const { cachedGroups, handleGroupPermissions } = useGroupStore();
|
||||||
const { lastLocation } = storeToRefs(useLocationStore());
|
const { lastLocation } = storeToRefs(useLocationStore());
|
||||||
const { showLaunchDialog } = useLaunchStore();
|
const { showLaunchDialog } = useLaunchStore();
|
||||||
const { createNewInstance } = useInstanceStore();
|
const { createNewInstance } = useInstanceStore();
|
||||||
@@ -530,12 +537,12 @@
|
|||||||
const { tryOpenInstanceInVrc } = useLaunchStore();
|
const { tryOpenInstanceInVrc } = useLaunchStore();
|
||||||
const { canOpenInstanceInGame } = useInviteStore();
|
const { canOpenInstanceInGame } = useInviteStore();
|
||||||
|
|
||||||
const newInstanceDialogRef = ref(null);
|
const newInstanceDialogIndex = ref(2000);
|
||||||
|
|
||||||
const newInstanceDialog = ref({
|
const newInstanceDialog = ref({
|
||||||
visible: false,
|
visible: false,
|
||||||
// loading: false,
|
// loading: false,
|
||||||
selectedTab: '0',
|
selectedTab: 'Normal',
|
||||||
instanceCreated: false,
|
instanceCreated: false,
|
||||||
queueEnabled: false,
|
queueEnabled: false,
|
||||||
worldId: '',
|
worldId: '',
|
||||||
@@ -551,6 +558,7 @@
|
|||||||
strict: false,
|
strict: false,
|
||||||
location: '',
|
location: '',
|
||||||
shortName: '',
|
shortName: '',
|
||||||
|
displayName: '',
|
||||||
url: '',
|
url: '',
|
||||||
secureOrShortName: '',
|
secureOrShortName: '',
|
||||||
lastSelectedGroupId: '',
|
lastSelectedGroupId: '',
|
||||||
@@ -575,6 +583,8 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isLocalUserVrcplusSupporter = computed(() => currentUser.value.$isVRCPlus);
|
||||||
|
|
||||||
initializeNewInstanceDialog();
|
initializeNewInstanceDialog();
|
||||||
|
|
||||||
function closeInviteDialog() {
|
function closeInviteDialog() {
|
||||||
@@ -617,7 +627,9 @@
|
|||||||
if (!isRealInstance(tag)) {
|
if (!isRealInstance(tag)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nextTick(() => adjustDialogZ(newInstanceDialogRef.value.$el));
|
nextTick(() => {
|
||||||
|
newInstanceDialogIndex.value = getNextDialogIndex();
|
||||||
|
});
|
||||||
const D = newInstanceDialog.value;
|
const D = newInstanceDialog.value;
|
||||||
const L = parseLocation(tag);
|
const L = parseLocation(tag);
|
||||||
if (D.worldId === L.worldId) {
|
if (D.worldId === L.worldId) {
|
||||||
@@ -634,6 +646,9 @@
|
|||||||
D.strict = false;
|
D.strict = false;
|
||||||
D.shortName = '';
|
D.shortName = '';
|
||||||
D.secureOrShortName = '';
|
D.secureOrShortName = '';
|
||||||
|
if (!isLocalUserVrcplusSupporter.value) {
|
||||||
|
D.displayName = '';
|
||||||
|
}
|
||||||
const args = await groupRequest.getGroupPermissions({ userId: currentUser.value.id });
|
const args = await groupRequest.getGroupPermissions({ userId: currentUser.value.id });
|
||||||
handleGroupPermissions(args);
|
handleGroupPermissions(args);
|
||||||
buildInstance();
|
buildInstance();
|
||||||
@@ -673,10 +688,23 @@
|
|||||||
configRepository
|
configRepository
|
||||||
.getBool('instanceDialogAgeGate', false)
|
.getBool('instanceDialogAgeGate', false)
|
||||||
.then((value) => (newInstanceDialog.value.ageGate = value));
|
.then((value) => (newInstanceDialog.value.ageGate = value));
|
||||||
|
|
||||||
|
configRepository
|
||||||
|
.getString('instanceDialogDisplayName', '')
|
||||||
|
.then((value) => (newInstanceDialog.value.displayName = value));
|
||||||
}
|
}
|
||||||
function saveNewInstanceDialog() {
|
function saveNewInstanceDialog() {
|
||||||
const { accessType, region, instanceName, userId, groupId, groupAccessType, queueEnabled, ageGate } =
|
const {
|
||||||
newInstanceDialog.value;
|
accessType,
|
||||||
|
region,
|
||||||
|
instanceName,
|
||||||
|
userId,
|
||||||
|
groupId,
|
||||||
|
groupAccessType,
|
||||||
|
queueEnabled,
|
||||||
|
ageGate,
|
||||||
|
displayName
|
||||||
|
} = newInstanceDialog.value;
|
||||||
|
|
||||||
configRepository.setString('instanceDialogAccessType', accessType);
|
configRepository.setString('instanceDialogAccessType', accessType);
|
||||||
configRepository.setString('instanceRegion', region);
|
configRepository.setString('instanceRegion', region);
|
||||||
@@ -686,9 +714,10 @@
|
|||||||
configRepository.setString('instanceDialogGroupAccessType', groupAccessType);
|
configRepository.setString('instanceDialogGroupAccessType', groupAccessType);
|
||||||
configRepository.setBool('instanceDialogQueueEnabled', queueEnabled);
|
configRepository.setBool('instanceDialogQueueEnabled', queueEnabled);
|
||||||
configRepository.setBool('instanceDialogAgeGate', ageGate);
|
configRepository.setBool('instanceDialogAgeGate', ageGate);
|
||||||
|
configRepository.setString('instanceDialogDisplayName', displayName);
|
||||||
}
|
}
|
||||||
function newInstanceTabClick(tab) {
|
function newInstanceTabClick(obj) {
|
||||||
if (tab === '1') {
|
if (obj.props.name === 'Normal') {
|
||||||
buildInstance();
|
buildInstance();
|
||||||
} else {
|
} else {
|
||||||
buildLegacyInstance();
|
buildLegacyInstance();
|
||||||
@@ -720,7 +749,7 @@
|
|||||||
worldId: L.worldId
|
worldId: L.worldId
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Self invite sent',
|
message: 'Self invite sent',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
@@ -749,7 +778,7 @@
|
|||||||
}
|
}
|
||||||
if (D.groupId && D.groupId !== D.lastSelectedGroupId) {
|
if (D.groupId && D.groupId !== D.lastSelectedGroupId) {
|
||||||
D.roleIds = [];
|
D.roleIds = [];
|
||||||
const ref = cachedGroups.value.get(D.groupId);
|
const ref = cachedGroups.get(D.groupId);
|
||||||
if (typeof ref !== 'undefined') {
|
if (typeof ref !== 'undefined') {
|
||||||
D.groupRef = ref;
|
D.groupRef = ref;
|
||||||
D.selectedGroupRoles = ref.roles;
|
D.selectedGroupRoles = ref.roles;
|
||||||
@@ -824,7 +853,7 @@
|
|||||||
}
|
}
|
||||||
if (D.groupId && D.groupId !== D.lastSelectedGroupId) {
|
if (D.groupId && D.groupId !== D.lastSelectedGroupId) {
|
||||||
D.roleIds = [];
|
D.roleIds = [];
|
||||||
const ref = cachedGroups.value.get(D.groupId);
|
const ref = cachedGroups.get(D.groupId);
|
||||||
if (typeof ref !== 'undefined') {
|
if (typeof ref !== 'undefined') {
|
||||||
D.groupRef = ref;
|
D.groupRef = ref;
|
||||||
D.selectedGroupRoles = ref.roles;
|
D.selectedGroupRoles = ref.roles;
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
<template>
|
|
||||||
<safe-dialog
|
|
||||||
class="x-dialog"
|
|
||||||
:visible="previousImagesDialogVisible"
|
|
||||||
:title="t('dialog.previous_images.header')"
|
|
||||||
width="800px"
|
|
||||||
append-to-body
|
|
||||||
@close="closeDialog">
|
|
||||||
<div>
|
|
||||||
<div v-for="image in previousImagesTable" :key="image.version" style="display: inline-block">
|
|
||||||
<el-popover
|
|
||||||
class="x-change-image-item"
|
|
||||||
placement="right"
|
|
||||||
width="500px"
|
|
||||||
trigger="click"
|
|
||||||
v-if="image.file">
|
|
||||||
<img slot="reference" v-lazy="image.file.url" class="x-link" />
|
|
||||||
<img
|
|
||||||
v-lazy="image.file.url"
|
|
||||||
class="x-link"
|
|
||||||
style="width: 500px; height: 375px"
|
|
||||||
@click="showFullscreenImageDialog(image.file.url)" />
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</safe-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
import { useGalleryStore } from '../../stores';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const { previousImagesDialogVisible, previousImagesTable } = storeToRefs(useGalleryStore());
|
|
||||||
const { showFullscreenImageDialog } = useGalleryStore();
|
|
||||||
|
|
||||||
function closeDialog() {
|
|
||||||
previousImagesDialogVisible.value = false;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="previousInstancesGroupDialogRef"
|
:z-index="previousInstancesGroupDialogIndex"
|
||||||
:visible.sync="isVisible"
|
v-model="isVisible"
|
||||||
:title="t('dialog.previous_instances.header')"
|
:title="t('dialog.previous_instances.header')"
|
||||||
width="1000px"
|
width="1000px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
style="width: 150px" />
|
style="width: 150px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<data-tables v-loading="loading" v-bind="previousInstancesGroupDialogTable" style="margin-top: 10px">
|
<DataTable v-loading="loading" v-bind="previousInstancesGroupDialogTable" style="margin-top: 10px">
|
||||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||||
@@ -39,48 +39,50 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-s-data"
|
:icon="DataLine"
|
||||||
size="mini"
|
size="small"
|
||||||
@click="showPreviousInstancesInfoDialog(scope.row.location)" />
|
@click="showPreviousInstancesInfoDialog(scope.row.location)" />
|
||||||
<el-button
|
<el-button
|
||||||
v-if="shiftHeld"
|
v-if="shiftHeld"
|
||||||
style="color: #f56c6c"
|
style="color: #f56c6c"
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
@click="deleteGameLogGroupInstance(scope.row)" />
|
@click="deleteGameLogGroupInstance(scope.row)" />
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
@click="deleteGameLogGroupInstancePrompt(scope.row)" />
|
@click="deleteGameLogGroupInstancePrompt(scope.row)" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { DataLine, Close } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { ref, reactive, computed, watch, nextTick, getCurrentInstance } from 'vue';
|
import { ref, reactive, computed, watch, nextTick, getCurrentInstance } from 'vue';
|
||||||
import {
|
import {
|
||||||
parseLocation,
|
parseLocation,
|
||||||
compareByCreatedAt,
|
compareByCreatedAt,
|
||||||
timeToText,
|
timeToText,
|
||||||
removeFromArray,
|
removeFromArray,
|
||||||
adjustDialogZ,
|
getNextDialogIndex,
|
||||||
formatDateFilter
|
formatDateFilter
|
||||||
} from '../../../shared/utils';
|
} from '../../../shared/utils';
|
||||||
import { database } from '../../../service/database';
|
import { database } from '../../../service/database';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useInstanceStore, useUiStore } from '../../../stores';
|
import { useInstanceStore, useUiStore } from '../../../stores';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||||
const { shiftHeld } = useUiStore();
|
const { shiftHeld } = useUiStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const previousInstancesGroupDialogRef = ref(null);
|
const previousInstancesGroupDialogIndex = ref(2000);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const previousInstancesGroupDialogTable = reactive({
|
const previousInstancesGroupDialogTable = reactive({
|
||||||
@@ -88,7 +90,7 @@
|
|||||||
filters: [{ prop: 'groupName', value: '' }],
|
filters: [{ prop: 'groupName', value: '' }],
|
||||||
tableProps: {
|
tableProps: {
|
||||||
stripe: true,
|
stripe: true,
|
||||||
size: 'mini',
|
size: 'small',
|
||||||
defaultSort: { prop: 'created_at', order: 'descending' }
|
defaultSort: { prop: 'created_at', order: 'descending' }
|
||||||
},
|
},
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@@ -115,7 +117,7 @@
|
|||||||
() => {
|
() => {
|
||||||
if (props.previousInstancesGroupDialog.visible) {
|
if (props.previousInstancesGroupDialog.visible) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
adjustDialogZ(previousInstancesGroupDialogRef.value.$el);
|
previousInstancesGroupDialogIndex.value = getNextDialogIndex();
|
||||||
});
|
});
|
||||||
refreshPreviousInstancesGroupTable();
|
refreshPreviousInstancesGroupTable();
|
||||||
}
|
}
|
||||||
@@ -144,15 +146,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteGameLogGroupInstancePrompt(row) {
|
function deleteGameLogGroupInstancePrompt(row) {
|
||||||
proxy.$confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
ElMessageBox.confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
cancelButtonText: 'Cancel',
|
cancelButtonText: 'Cancel',
|
||||||
type: 'info',
|
type: 'info'
|
||||||
callback: (action) => {
|
})
|
||||||
|
.then((action) => {
|
||||||
if (action === 'confirm') {
|
if (action === 'confirm') {
|
||||||
deleteGameLogGroupInstance(row);
|
deleteGameLogGroupInstance(row);
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="dialogRef"
|
:z-index="previousInstancesInfoDialogIndex"
|
||||||
:visible="previousInstancesInfoDialogVisible"
|
:model-value="previousInstancesInfoDialogVisible"
|
||||||
:title="$t('dialog.previous_instances.info')"
|
:title="t('dialog.previous_instances.info')"
|
||||||
width="800px"
|
width="800px"
|
||||||
:fullscreen="fullscreen"
|
:fullscreen="fullscreen"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
@@ -11,12 +11,12 @@
|
|||||||
<Location :location="location.tag" style="font-size: 14px" />
|
<Location :location="location.tag" style="font-size: 14px" />
|
||||||
<el-input
|
<el-input
|
||||||
v-model="dataTable.filters[0].value"
|
v-model="dataTable.filters[0].value"
|
||||||
:placeholder="$t('dialog.previous_instances.search_placeholder')"
|
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||||
style="width: 150px"
|
style="width: 150px"
|
||||||
clearable></el-input>
|
clearable></el-input>
|
||||||
</div>
|
</div>
|
||||||
<data-tables v-loading="loading" v-bind="dataTable" style="margin-top: 10px">
|
<DataTable v-loading="loading" v-bind="dataTable" style="margin-top: 10px">
|
||||||
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="110">
|
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="110">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip placement="left">
|
<el-tooltip placement="left">
|
||||||
<template #content>
|
<template #content>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.gameLog.icon')" prop="isFriend" width="70" align="center">
|
<el-table-column :label="t('table.gameLog.icon')" prop="isFriend" width="70" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<template v-if="gameLogIsFriend(scope.row)">
|
<template v-if="gameLogIsFriend(scope.row)">
|
||||||
<el-tooltip v-if="gameLogIsFavorite(scope.row)" placement="top" content="Favorite">
|
<el-tooltip v-if="gameLogIsFavorite(scope.row)" placement="top" content="Favorite">
|
||||||
@@ -36,33 +36,35 @@
|
|||||||
<span>💚</span>
|
<span>💚</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
<span v-else></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.display_name')" prop="displayName" sortable>
|
<el-table-column :label="t('table.previous_instances.display_name')" prop="displayName" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span class="x-link" @click="lookupUser(scope.row)">{{ scope.row.displayName }}</span>
|
<span class="x-link" @click="lookupUser(scope.row)">{{ scope.row.displayName }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.time')" prop="time" width="100" sortable>
|
<el-table-column :label="t('table.previous_instances.time')" prop="time" width="100" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.timer }}</span>
|
<span>{{ scope.row.timer }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.count')" prop="count" width="100" sortable>
|
<el-table-column :label="t('table.previous_instances.count')" prop="count" width="100" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ scope.row.count }}</span>
|
<span>{{ scope.row.count }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, nextTick } from 'vue';
|
import { ref, watch, nextTick } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { database } from '../../../service/database';
|
import { database } from '../../../service/database';
|
||||||
import {
|
import {
|
||||||
adjustDialogZ,
|
getNextDialogIndex,
|
||||||
compareByCreatedAt,
|
compareByCreatedAt,
|
||||||
parseLocation,
|
parseLocation,
|
||||||
timeToText,
|
timeToText,
|
||||||
@@ -74,8 +76,9 @@
|
|||||||
const { previousInstancesInfoDialogVisible, previousInstancesInfoDialogInstanceId } =
|
const { previousInstancesInfoDialogVisible, previousInstancesInfoDialogInstanceId } =
|
||||||
storeToRefs(useInstanceStore());
|
storeToRefs(useInstanceStore());
|
||||||
const { gameLogIsFriend, gameLogIsFavorite } = useGameLogStore();
|
const { gameLogIsFriend, gameLogIsFavorite } = useGameLogStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const dialogRef = ref(null);
|
const previousInstancesInfoDialogIndex = ref(2000);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const location = ref({
|
const location = ref({
|
||||||
@@ -111,7 +114,7 @@
|
|||||||
],
|
],
|
||||||
tableProps: {
|
tableProps: {
|
||||||
stripe: true,
|
stripe: true,
|
||||||
size: 'mini',
|
size: 'small',
|
||||||
defaultSort: {
|
defaultSort: {
|
||||||
prop: 'created_at',
|
prop: 'created_at',
|
||||||
order: 'descending'
|
order: 'descending'
|
||||||
@@ -139,7 +142,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
adjustDialogZ(dialogRef.value.$el);
|
previousInstancesInfoDialogIndex.value = getNextDialogIndex();
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
location.value = parseLocation(previousInstancesInfoDialogInstanceId.value);
|
location.value = parseLocation(previousInstancesInfoDialogInstanceId.value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="previousInstancesWorldDialogRef"
|
:z-index="previousInstancesWorldDialogIndex"
|
||||||
:visible.sync="isVisible"
|
v-model="isVisible"
|
||||||
:title="t('dialog.previous_instances.header')"
|
:title="t('dialog.previous_instances.header')"
|
||||||
width="1000px"
|
width="1000px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||||
style="display: block; width: 150px"></el-input>
|
style="display: block; width: 150px"></el-input>
|
||||||
</div>
|
</div>
|
||||||
<data-tables v-loading="loading" v-bind="previousInstancesWorldDialogTable" style="margin-top: 10px">
|
<DataTable v-loading="loading" v-bind="previousInstancesWorldDialogTable" style="margin-top: 10px">
|
||||||
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||||
@@ -43,35 +43,41 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-s-data"
|
:icon="DataLine"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="showPreviousInstancesInfoDialog(scope.row.location)"></el-button>
|
@click="showPreviousInstancesInfoDialog(scope.row.location)"></el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="shiftHeld"
|
v-if="shiftHeld"
|
||||||
style="color: #f56c6c"
|
style="color: #f56c6c"
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="deleteGameLogWorldInstance(scope.row)"></el-button>
|
@click="deleteGameLogWorldInstance(scope.row)"></el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="deleteGameLogWorldInstancePrompt(scope.row)"></el-button>
|
@click="deleteGameLogWorldInstancePrompt(scope.row)"></el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { DataLine, Close } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { database } from '../../../service/database';
|
import { database } from '../../../service/database';
|
||||||
import {
|
import {
|
||||||
adjustDialogZ,
|
getNextDialogIndex,
|
||||||
compareByCreatedAt,
|
compareByCreatedAt,
|
||||||
parseLocation,
|
parseLocation,
|
||||||
removeFromArray,
|
removeFromArray,
|
||||||
@@ -80,7 +86,6 @@
|
|||||||
} from '../../../shared/utils';
|
} from '../../../shared/utils';
|
||||||
import { useInstanceStore, useUiStore, useUserStore } from '../../../stores';
|
import { useInstanceStore, useUiStore, useUserStore } from '../../../stores';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -100,7 +105,7 @@
|
|||||||
filters: [{ prop: 'groupName', value: '' }],
|
filters: [{ prop: 'groupName', value: '' }],
|
||||||
tableProps: {
|
tableProps: {
|
||||||
stripe: true,
|
stripe: true,
|
||||||
size: 'mini',
|
size: 'small',
|
||||||
defaultSort: { prop: 'created_at', order: 'descending' }
|
defaultSort: { prop: 'created_at', order: 'descending' }
|
||||||
},
|
},
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@@ -111,7 +116,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const previousInstancesWorldDialogRef = ref(null);
|
const previousInstancesWorldDialogIndex = ref(2000);
|
||||||
|
|
||||||
const isVisible = computed({
|
const isVisible = computed({
|
||||||
get: () => props.previousInstancesWorldDialog.visible,
|
get: () => props.previousInstancesWorldDialog.visible,
|
||||||
@@ -145,16 +150,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteGameLogWorldInstancePrompt(row) {
|
function deleteGameLogWorldInstancePrompt(row) {
|
||||||
proxy.$confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
ElMessageBox.confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
cancelButtonText: 'Cancel',
|
cancelButtonText: 'Cancel',
|
||||||
type: 'info',
|
type: 'info'
|
||||||
callback: (action) => {
|
})
|
||||||
|
.then((action) => {
|
||||||
if (action === 'confirm') {
|
if (action === 'confirm') {
|
||||||
deleteGameLogWorldInstance(row);
|
deleteGameLogWorldInstance(row);
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -162,10 +168,16 @@
|
|||||||
() => {
|
() => {
|
||||||
if (props.previousInstancesWorldDialog.visible) {
|
if (props.previousInstancesWorldDialog.visible) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
adjustDialogZ(previousInstancesWorldDialogRef.value.$el);
|
previousInstancesWorldDialogIndex.value = getNextDialogIndex();
|
||||||
});
|
});
|
||||||
refreshPreviousInstancesWorldTable();
|
refreshPreviousInstancesWorldTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.button-pd-0 {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-dialog
|
|
||||||
ref="elDialogRef"
|
|
||||||
:visible="props.visible"
|
|
||||||
v-bind="attrs"
|
|
||||||
:close-on-click-modal="false"
|
|
||||||
@open="handleOpen"
|
|
||||||
@close="handleClose"
|
|
||||||
:top="marginTop">
|
|
||||||
<slot></slot>
|
|
||||||
|
|
||||||
<template v-if="slots.title" #title>
|
|
||||||
<slot name="title"></slot>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="slots.footer" #footer>
|
|
||||||
<slot name="footer"></slot>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onBeforeUnmount, nextTick, useAttrs, useSlots } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'open', 'close']);
|
|
||||||
|
|
||||||
const attrs = useAttrs();
|
|
||||||
const slots = useSlots();
|
|
||||||
|
|
||||||
const elDialogRef = ref(null);
|
|
||||||
const wrapperElement = ref(null);
|
|
||||||
const mouseDownOnWrapper = ref(false);
|
|
||||||
const styleObserver = ref(null);
|
|
||||||
const resizeObserver = ref(null);
|
|
||||||
const marginTop = ref('5px');
|
|
||||||
let handleResize = null;
|
|
||||||
|
|
||||||
const handleOpen = () => {
|
|
||||||
emit('open');
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
addWrapperListeners();
|
|
||||||
removeTitleAttribute();
|
|
||||||
adjustDialogMarginTop();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTitleAttribute = () => {
|
|
||||||
const wrapper = elDialogRef.value?.$el;
|
|
||||||
if (wrapper && wrapper.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
wrapper.removeAttribute('title');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
emit('close');
|
|
||||||
removeWrapperListeners();
|
|
||||||
|
|
||||||
if (styleObserver.value) {
|
|
||||||
styleObserver.value.disconnect();
|
|
||||||
styleObserver.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resizeObserver.value) {
|
|
||||||
resizeObserver.value.disconnect();
|
|
||||||
resizeObserver.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handleResize) {
|
|
||||||
window.removeEventListener('resize', handleResize);
|
|
||||||
handleResize = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('update:visible', false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleWrapperMouseDown = (event) => {
|
|
||||||
if (event.target === wrapperElement.value) {
|
|
||||||
mouseDownOnWrapper.value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleWrapperMouseUp = (event) => {
|
|
||||||
if (event.target === wrapperElement.value && mouseDownOnWrapper.value) {
|
|
||||||
handleClose();
|
|
||||||
}
|
|
||||||
mouseDownOnWrapper.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addWrapperListeners = () => {
|
|
||||||
const wrapper = elDialogRef.value?.$el;
|
|
||||||
|
|
||||||
if (
|
|
||||||
wrapper &&
|
|
||||||
wrapper.nodeType === Node.ELEMENT_NODE &&
|
|
||||||
wrapper.classList &&
|
|
||||||
wrapper.classList.contains('el-dialog__wrapper')
|
|
||||||
) {
|
|
||||||
wrapperElement.value = wrapper;
|
|
||||||
wrapperElement.value.addEventListener('mousedown', handleWrapperMouseDown);
|
|
||||||
wrapperElement.value.addEventListener('mouseup', handleWrapperMouseUp);
|
|
||||||
} else {
|
|
||||||
wrapperElement.value = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeWrapperListeners = () => {
|
|
||||||
if (wrapperElement.value) {
|
|
||||||
wrapperElement.value.removeEventListener('mousedown', handleWrapperMouseDown);
|
|
||||||
wrapperElement.value.removeEventListener('mouseup', handleWrapperMouseUp);
|
|
||||||
wrapperElement.value = null;
|
|
||||||
}
|
|
||||||
mouseDownOnWrapper.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const adjustDialogMarginTop = () => {
|
|
||||||
const wrapper = elDialogRef.value?.$el;
|
|
||||||
if (!wrapper) return;
|
|
||||||
|
|
||||||
const dialog = wrapper.querySelector('.el-dialog');
|
|
||||||
if (!dialog) return;
|
|
||||||
|
|
||||||
const applyStyle = () => {
|
|
||||||
const dialogHeight = dialog.offsetHeight;
|
|
||||||
const viewportHeight = window.innerHeight;
|
|
||||||
|
|
||||||
const topOffset = Math.max(0, (viewportHeight - dialogHeight) * 0.2);
|
|
||||||
marginTop.value = `${topOffset}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
applyStyle();
|
|
||||||
};
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
removeWrapperListeners();
|
|
||||||
|
|
||||||
if (styleObserver.value) {
|
|
||||||
styleObserver.value.disconnect();
|
|
||||||
styleObserver.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resizeObserver.value) {
|
|
||||||
resizeObserver.value.disconnect();
|
|
||||||
resizeObserver.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handleResize) {
|
|
||||||
window.removeEventListener('resize', handleResize);
|
|
||||||
handleResize = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!--<template>-->
|
<!--<template>-->
|
||||||
<!-- <safe-dialog-->
|
<!-- <el-dialog-->
|
||||||
<!-- class="x-dialog"-->
|
<!-- class="x-dialog"-->
|
||||||
<!-- :visible="sendBoopDialog.visible"-->
|
<!-- :model-value="sendBoopDialog.visible"-->
|
||||||
<!-- :title="t('dialog.boop_dialog.header')"-->
|
<!-- :title="t('dialog.boop_dialog.header')"-->
|
||||||
<!-- width="450px"-->
|
<!-- width="450px"-->
|
||||||
<!-- @close="closeDialog">-->
|
<!-- @close="closeDialog">-->
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<!-- style="height: auto">-->
|
<!-- style="height: auto">-->
|
||||||
<!-- <template v-if="friend.ref">-->
|
<!-- <template v-if="friend.ref">-->
|
||||||
<!-- <div class="avatar" :class="userStatusClass(friend.ref)">-->
|
<!-- <div class="avatar" :class="userStatusClass(friend.ref)">-->
|
||||||
<!-- <img v-lazy="userImage(friend.ref)" />-->
|
<!-- <img :src="userImage(friend.ref)" loading="lazy">-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="detail">-->
|
<!-- <div class="detail">-->
|
||||||
<!-- <span-->
|
<!-- <span-->
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<!-- style="height: auto">-->
|
<!-- style="height: auto">-->
|
||||||
<!-- <template v-if="friend.ref">-->
|
<!-- <template v-if="friend.ref">-->
|
||||||
<!-- <div class="avatar" :class="userStatusClass(friend.ref)">-->
|
<!-- <div class="avatar" :class="userStatusClass(friend.ref)">-->
|
||||||
<!-- <img v-lazy="userImage(friend.ref)" />-->
|
<!-- <img :src="userImage(friend.ref)" loading="lazy">-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="detail">-->
|
<!-- <div class="detail">-->
|
||||||
<!-- <span-->
|
<!-- <span-->
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
<!-- style="height: auto">-->
|
<!-- style="height: auto">-->
|
||||||
<!-- <template v-if="friend.ref">-->
|
<!-- <template v-if="friend.ref">-->
|
||||||
<!-- <div class="avatar">-->
|
<!-- <div class="avatar">-->
|
||||||
<!-- <img v-lazy="userImage(friend.ref)" />-->
|
<!-- <img :src="userImage(friend.ref)" loading="lazy">-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="detail">-->
|
<!-- <div class="detail">-->
|
||||||
<!-- <span-->
|
<!-- <span-->
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
<!-- style="height: auto">-->
|
<!-- style="height: auto">-->
|
||||||
<!-- <template v-if="friend.ref">-->
|
<!-- <template v-if="friend.ref">-->
|
||||||
<!-- <div class="avatar">-->
|
<!-- <div class="avatar">-->
|
||||||
<!-- <img v-lazy="userImage(friend.ref)" />-->
|
<!-- <img :src="userImage(friend.ref)" loading="lazy">-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- <div class="detail">-->
|
<!-- <div class="detail">-->
|
||||||
<!-- <span-->
|
<!-- <span-->
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- <template v-else>-->
|
<!-- <template v-else>-->
|
||||||
<!-- <img-->
|
<!-- <img-->
|
||||||
<!-- v-lazy="image.versions[image.versions.length - 1].file.url"-->
|
<!-- :src="image.versions[image.versions.length - 1].file.url"-->
|
||||||
<!-- class="avatar"-->
|
<!-- class="avatar"-->
|
||||||
<!-- style="width: 200px; height: 200px" />-->
|
<!-- style="width: 200px; height: 200px" />-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
<!-- t('dialog.boop_dialog.send')-->
|
<!-- t('dialog.boop_dialog.send')-->
|
||||||
<!-- }}</el-button>-->
|
<!-- }}</el-button>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </safe-dialog>-->
|
<!-- </el-dialog>-->
|
||||||
<!--</template>-->
|
<!--</template>-->
|
||||||
|
|
||||||
<!--<script setup>-->
|
<!--<script setup>-->
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="bioDialog.visible"
|
v-model="bioDialog.visible"
|
||||||
:title="t('dialog.bio.header')"
|
:title="t('dialog.bio.header')"
|
||||||
width="600px"
|
width="600px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="bioDialog.bio"
|
v-model="bioDialog.bio"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
size="mini"
|
size="small"
|
||||||
maxlength="512"
|
maxlength="512"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:autosize="{ minRows: 5, maxRows: 20 }"
|
:autosize="{ minRows: 5, maxRows: 20 }"
|
||||||
@@ -23,16 +23,13 @@
|
|||||||
v-model="bioDialog.bioLinks[index]"
|
v-model="bioDialog.bioLinks[index]"
|
||||||
size="small"
|
size="small"
|
||||||
style="margin-top: 5px">
|
style="margin-top: 5px">
|
||||||
<img
|
<img :src="getFaviconUrl(link)" style="width: 16px; height: 16px; vertical-align: middle" />
|
||||||
slot="prepend"
|
<el-button :icon="Delete" @click="bioDialog.bioLinks.splice(index, 1)" />
|
||||||
:src="getFaviconUrl(link)"
|
|
||||||
style="width: 16px; height: 16px; vertical-align: middle" />
|
|
||||||
<el-button slot="append" icon="el-icon-delete" @click="bioDialog.bioLinks.splice(index, 1)" />
|
|
||||||
</el-input>
|
</el-input>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
:disabled="bioDialog.bioLinks.length >= 3"
|
:disabled="bioDialog.bioLinks.length >= 3"
|
||||||
size="mini"
|
size="small"
|
||||||
style="margin-top: 5px"
|
style="margin-top: 5px"
|
||||||
@click="bioDialog.bioLinks.push('')">
|
@click="bioDialog.bioLinks.push('')">
|
||||||
{{ t('dialog.bio.add_link') }}
|
{{ t('dialog.bio.add_link') }}
|
||||||
@@ -44,18 +41,19 @@
|
|||||||
{{ t('dialog.bio.update') }}
|
{{ t('dialog.bio.update') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getCurrentInstance } from 'vue';
|
import { ElMessage } from 'element-plus';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
|
import { Delete } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { userRequest } from '../../../api';
|
import { userRequest } from '../../../api';
|
||||||
import { getFaviconUrl } from '../../../shared/utils';
|
import { getFaviconUrl } from '../../../shared/utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { $message } = getCurrentInstance().proxy;
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bioDialog: {
|
bioDialog: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -79,7 +77,7 @@
|
|||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
D.visible = false;
|
D.visible = false;
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Bio updated',
|
message: 'Bio updated',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="languageDialog.visible"
|
v-model="languageDialog.visible"
|
||||||
:title="t('dialog.language.header')"
|
:title="t('dialog.language.header')"
|
||||||
width="400px"
|
width="400px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<el-select
|
<el-select
|
||||||
value=""
|
|
||||||
:disabled="languageDialog.loading || (currentUser.$languages && currentUser.$languages.length === 3)"
|
:disabled="languageDialog.loading || (currentUser.$languages && currentUser.$languages.length === 3)"
|
||||||
:placeholder="t('dialog.language.select_language')"
|
:placeholder="t('dialog.language.select_language')"
|
||||||
style="margin-top: 14px"
|
style="margin-top: 14px"
|
||||||
@@ -40,12 +39,12 @@
|
|||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { userRequest } from '../../../api';
|
import { userRequest } from '../../../api';
|
||||||
import { languageClass } from '../../../shared/utils';
|
import { languageClass } from '../../../shared/utils';
|
||||||
import { useUserStore } from '../../../stores';
|
import { useUserStore } from '../../../stores';
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="previousInstancesUserDialogRef"
|
:z-index="previousInstancesUserDialogIndex"
|
||||||
:visible.sync="isVisible"
|
v-model="isVisible"
|
||||||
:title="$t('dialog.previous_instances.header')"
|
:title="t('dialog.previous_instances.header')"
|
||||||
width="1000px"
|
width="1000px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||||
<span style="font-size: 14px" v-text="previousInstancesUserDialog.userRef.displayName"></span>
|
<span style="font-size: 14px" v-text="previousInstancesUserDialog.userRef.displayName"></span>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="previousInstancesUserDialogTable.filters[0].value"
|
v-model="previousInstancesUserDialogTable.filters[0].value"
|
||||||
:placeholder="$t('dialog.previous_instances.search_placeholder')"
|
:placeholder="t('dialog.previous_instances.search_placeholder')"
|
||||||
style="display: block; width: 150px"></el-input>
|
style="display: block; width: 150px"></el-input>
|
||||||
</div>
|
</div>
|
||||||
<data-tables v-loading="loading" v-bind="previousInstancesUserDialogTable" style="margin-top: 10px">
|
<DataTable v-loading="loading" v-bind="previousInstancesUserDialogTable" style="margin-top: 10px">
|
||||||
<el-table-column :label="$t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
<el-table-column :label="t('table.previous_instances.date')" prop="created_at" sortable width="170">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
<span>{{ formatDateFilter(scope.row.created_at, 'long') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.world')" prop="name" sortable>
|
<el-table-column :label="t('table.previous_instances.world')" prop="name" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<Location
|
<Location
|
||||||
:location="scope.row.location"
|
:location="scope.row.location"
|
||||||
@@ -26,53 +26,61 @@
|
|||||||
:grouphint="scope.row.groupName" />
|
:grouphint="scope.row.groupName" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.instance_creator')" prop="location" width="170">
|
<el-table-column :label="t('table.previous_instances.instance_creator')" prop="location" width="170">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<DisplayName :userid="scope.row.$location.userId" :location="scope.row.$location.tag" />
|
<DisplayName :userid="scope.row.$location.userId" :location="scope.row.$location.tag" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.time')" prop="time" width="100" sortable>
|
<el-table-column :label="t('table.previous_instances.time')" prop="time" width="100" sortable>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span v-text="scope.row.timer"></span>
|
<span v-text="scope.row.timer"></span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('table.previous_instances.action')" width="90" align="right">
|
<el-table-column :label="t('table.previous_instances.action')" width="90" align="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-switch-button"
|
:icon="SwitchButton"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="showLaunchDialog(scope.row.location)"></el-button>
|
@click="showLaunchDialog(scope.row.location)"></el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-s-data"
|
:icon="DataLine"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="showPreviousInstancesInfoDialog(scope.row.location)"></el-button>
|
@click="showPreviousInstancesInfoDialog(scope.row.location)"></el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="shiftHeld"
|
v-if="shiftHeld"
|
||||||
style="color: #f56c6c"
|
style="color: #f56c6c"
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="deleteGameLogUserInstance(scope.row)"></el-button>
|
@click="deleteGameLogUserInstance(scope.row)"></el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-close"
|
:icon="Close"
|
||||||
size="mini"
|
size="small"
|
||||||
|
class="button-pd-0"
|
||||||
@click="deleteGameLogUserInstancePrompt(scope.row)"></el-button>
|
@click="deleteGameLogUserInstancePrompt(scope.row)"></el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { SwitchButton, DataLine, Close } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { database } from '../../../service/database';
|
import { database } from '../../../service/database';
|
||||||
import {
|
import {
|
||||||
adjustDialogZ,
|
getNextDialogIndex,
|
||||||
compareByCreatedAt,
|
compareByCreatedAt,
|
||||||
parseLocation,
|
parseLocation,
|
||||||
removeFromArray,
|
removeFromArray,
|
||||||
@@ -93,22 +101,20 @@
|
|||||||
previousInstancesTable: {
|
previousInstancesTable: {
|
||||||
data: [],
|
data: [],
|
||||||
filters: [{ prop: 'displayName', value: '' }],
|
filters: [{ prop: 'displayName', value: '' }],
|
||||||
tableProps: { stripe: true, size: 'mini', height: '400px' }
|
tableProps: { stripe: true, size: 'small', height: '400px' }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:previous-instances-user-dialog']);
|
const emit = defineEmits(['update:previous-instances-user-dialog']);
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const previousInstancesUserDialogTable = reactive({
|
const previousInstancesUserDialogTable = reactive({
|
||||||
data: [],
|
data: [],
|
||||||
filters: [{ prop: 'worldName', value: '' }],
|
filters: [{ prop: 'worldName', value: '' }],
|
||||||
tableProps: {
|
tableProps: {
|
||||||
stripe: true,
|
stripe: true,
|
||||||
size: 'mini',
|
size: 'small',
|
||||||
defaultSort: { prop: 'created_at', order: 'descending' }
|
defaultSort: { prop: 'created_at', order: 'descending' }
|
||||||
},
|
},
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@@ -122,8 +128,9 @@
|
|||||||
const { showLaunchDialog } = useLaunchStore();
|
const { showLaunchDialog } = useLaunchStore();
|
||||||
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
const { showPreviousInstancesInfoDialog } = useInstanceStore();
|
||||||
const { shiftHeld } = storeToRefs(useUiStore());
|
const { shiftHeld } = storeToRefs(useUiStore());
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const previousInstancesUserDialogRef = ref(null);
|
const previousInstancesUserDialogIndex = ref(2000);
|
||||||
|
|
||||||
const isVisible = computed({
|
const isVisible = computed({
|
||||||
get: () => props.previousInstancesUserDialog.visible,
|
get: () => props.previousInstancesUserDialog.visible,
|
||||||
@@ -154,7 +161,7 @@
|
|||||||
() => {
|
() => {
|
||||||
if (props.previousInstancesUserDialog.visible) {
|
if (props.previousInstancesUserDialog.visible) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
adjustDialogZ(previousInstancesUserDialogRef.value.$el);
|
previousInstancesUserDialogIndex.value = getNextDialogIndex();
|
||||||
});
|
});
|
||||||
refreshPreviousInstancesUserTable();
|
refreshPreviousInstancesUserTable();
|
||||||
}
|
}
|
||||||
@@ -172,13 +179,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteGameLogUserInstancePrompt(row) {
|
function deleteGameLogUserInstancePrompt(row) {
|
||||||
proxy.$confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
|
ElMessageBox.confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
|
||||||
confirmButtonText: 'Confirm',
|
confirmButtonText: 'Confirm',
|
||||||
cancelButtonText: 'Cancel',
|
cancelButtonText: 'Cancel',
|
||||||
type: 'info',
|
type: 'info'
|
||||||
callback: (action) => {
|
})
|
||||||
|
.then((action) => {
|
||||||
if (action === 'confirm') deleteGameLogUserInstance(row);
|
if (action === 'confirm') deleteGameLogUserInstance(row);
|
||||||
}
|
})
|
||||||
});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.button-pd-0 {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="pronounsDialog.visible"
|
v-model="pronounsDialog.visible"
|
||||||
:title="t('dialog.pronouns.header')"
|
:title="t('dialog.pronouns.header')"
|
||||||
width="600px"
|
width="600px"
|
||||||
append-to-body>
|
append-to-body>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
type="textarea"
|
||||||
v-model="pronounsDialog.pronouns"
|
v-model="pronounsDialog.pronouns"
|
||||||
size="mini"
|
size="small"
|
||||||
maxlength="32"
|
maxlength="32"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||||
@@ -21,18 +21,15 @@
|
|||||||
{{ t('dialog.pronouns.update') }}
|
{{ t('dialog.pronouns.update') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getCurrentInstance } from 'vue';
|
import { ElMessage } from 'element-plus';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { userRequest } from '../../../api';
|
import { userRequest } from '../../../api';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const { $message } = proxy;
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
pronounsDialog: {
|
pronounsDialog: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -55,7 +52,7 @@
|
|||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
D.visible = false;
|
D.visible = false;
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Pronouns updated',
|
message: 'Pronouns updated',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="sendInviteRequestDialogVisible"
|
:model-value="sendInviteRequestDialogVisible"
|
||||||
|
@update:model-value="$emit('update:sendInviteRequestDialogVisible', $event)"
|
||||||
:title="t('dialog.invite_request_message.header')"
|
:title="t('dialog.invite_request_message.header')"
|
||||||
width="800px"
|
width="800px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -10,20 +11,20 @@
|
|||||||
<input class="inviteImageUploadButton" type="file" accept="image/*" @change="inviteImageUpload" />
|
<input class="inviteImageUploadButton" type="file" accept="image/*" @change="inviteImageUpload" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<data-tables
|
<DataTable
|
||||||
v-bind="inviteRequestMessageTable"
|
v-bind="inviteRequestMessageTable"
|
||||||
style="margin-top: 10px; cursor: pointer"
|
style="margin-top: 10px; cursor: pointer"
|
||||||
@row-click="showSendInviteConfirmDialog">
|
@row-click="showSendInviteConfirmDialog">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="t('table.profile.invite_messages.slot')"
|
:label="t('table.profile.invite_messages.slot')"
|
||||||
prop="slot"
|
prop="slot"
|
||||||
sortable="custom"
|
:sortable="true"
|
||||||
width="70"></el-table-column>
|
width="70"></el-table-column>
|
||||||
<el-table-column :label="t('table.profile.invite_messages.message')" prop="message"></el-table-column>
|
<el-table-column :label="t('table.profile.invite_messages.message')" prop="message"></el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="t('table.profile.invite_messages.cool_down')"
|
:label="t('table.profile.invite_messages.cool_down')"
|
||||||
prop="updatedAt"
|
prop="updatedAt"
|
||||||
sortable="custom"
|
:sortable="true"
|
||||||
width="110"
|
width="110"
|
||||||
align="right">
|
align="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -34,38 +35,38 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-edit"
|
:icon="Edit"
|
||||||
size="mini"
|
size="small"
|
||||||
@click.stop="showEditAndSendInviteDialog(scope.row)"></el-button>
|
@click.stop="showEditAndSendInviteDialog(scope.row)"></el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button type="small" @click="cancelSendInviteRequest">{{
|
<el-button @click="cancelSendInviteRequest">{{ t('dialog.invite_request_message.cancel') }}</el-button>
|
||||||
t('dialog.invite_request_message.cancel')
|
<el-button @click="refreshInviteMessageTableData('request')">{{
|
||||||
}}</el-button>
|
|
||||||
<el-button type="small" @click="refreshInviteMessageTableData('request')">{{
|
|
||||||
t('dialog.invite_request_message.refresh')
|
t('dialog.invite_request_message.refresh')
|
||||||
}}</el-button>
|
}}</el-button>
|
||||||
</template>
|
</template>
|
||||||
<SendInviteConfirmDialog
|
<SendInviteConfirmDialog
|
||||||
:visible.sync="isSendInviteConfirmDialogVisible"
|
v-model="isSendInviteConfirmDialogVisible"
|
||||||
:send-invite-dialog="sendInviteDialog"
|
:send-invite-dialog="sendInviteDialog"
|
||||||
:invite-dialog="inviteDialog"
|
:invite-dialog="inviteDialog"
|
||||||
@closeInviteDialog="closeInviteDialog" />
|
@closeInviteDialog="closeInviteDialog" />
|
||||||
<EditAndSendInviteDialog
|
<EditAndSendInviteDialog
|
||||||
:edit-and-send-invite-dialog.sync="editAndSendInviteDialog"
|
:edit-and-send-invite-dialog="editAndSendInviteDialog"
|
||||||
:send-invite-dialog="sendInviteDialog"
|
:send-invite-dialog="sendInviteDialog"
|
||||||
:invite-dialog="inviteDialog"
|
:invite-dialog="inviteDialog"
|
||||||
@closeInviteDialog="closeInviteDialog" />
|
@closeInviteDialog="closeInviteDialog" />
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { Edit } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
import { useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||||
import EditAndSendInviteDialog from '../InviteDialog/EditAndSendInviteDialog.vue';
|
import EditAndSendInviteDialog from '../InviteDialog/EditAndSendInviteDialog.vue';
|
||||||
import SendInviteConfirmDialog from '../InviteDialog/SendInviteConfirmDialog.vue';
|
import SendInviteConfirmDialog from '../InviteDialog/SendInviteConfirmDialog.vue';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="socialStatusDialog.visible"
|
v-model="socialStatusDialog.visible"
|
||||||
:title="t('dialog.social_status.header')"
|
:title="t('dialog.social_status.header')"
|
||||||
append-to-body
|
append-to-body
|
||||||
width="400px">
|
width="400px">
|
||||||
@@ -11,13 +11,13 @@
|
|||||||
<template #title>
|
<template #title>
|
||||||
<span style="font-size: 16px">{{ t('dialog.social_status.history') }}</span>
|
<span style="font-size: 16px">{{ t('dialog.social_status.history') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<data-tables
|
<DataTable
|
||||||
v-bind="socialStatusHistoryTable"
|
v-bind="socialStatusHistoryTable"
|
||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
@row-click="setSocialStatusFromHistory">
|
@row-click="setSocialStatusFromHistory">
|
||||||
<el-table-column :label="t('table.social_status.no')" prop="no" width="50"></el-table-column>
|
<el-table-column :label="t('table.social_status.no')" prop="no" width="50"></el-table-column>
|
||||||
<el-table-column :label="t('table.social_status.status')" prop="status"></el-table-column>
|
<el-table-column :label="t('table.social_status.status')" prop="status"></el-table-column>
|
||||||
</data-tables>
|
</DataTable>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
|
|
||||||
@@ -53,19 +53,18 @@
|
|||||||
{{ t('dialog.social_status.update') }}
|
{{ t('dialog.social_status.update') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance } from 'vue';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
import { userRequest } from '../../../api';
|
import { userRequest } from '../../../api';
|
||||||
import { useUserStore } from '../../../stores';
|
import { useUserStore } from '../../../stores';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { $message } = getCurrentInstance().proxy;
|
|
||||||
|
|
||||||
const { currentUser } = storeToRefs(useUserStore());
|
const { currentUser } = storeToRefs(useUserStore());
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -103,7 +102,7 @@
|
|||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
D.visible = false;
|
D.visible = false;
|
||||||
$message({
|
ElMessage({
|
||||||
message: 'Status updated',
|
message: 'Status updated',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
ref="VRCXUpdateDialogRef"
|
:z-index="VRCXUpdateDialogIndex"
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible.sync="VRCXUpdateDialog.visible"
|
v-model="VRCXUpdateDialog.visible"
|
||||||
:title="t('dialog.vrcx_updater.header')"
|
:title="t('dialog.vrcx_updater.header')"
|
||||||
append-to-body
|
append-to-body
|
||||||
width="400px">
|
width="400px">
|
||||||
@@ -59,15 +59,15 @@
|
|||||||
{{ t('dialog.vrcx_updater.install') }}
|
{{ t('dialog.vrcx_updater.install') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { nextTick, ref, watch } from 'vue';
|
import { nextTick, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { branches } from '../../shared/constants';
|
import { branches } from '../../shared/constants';
|
||||||
import { adjustDialogZ } from '../../shared/utils';
|
import { getNextDialogIndex } from '../../shared/utils';
|
||||||
import { useVRCXUpdaterStore } from '../../stores';
|
import { useVRCXUpdaterStore } from '../../stores';
|
||||||
|
|
||||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||||
@@ -85,14 +85,14 @@
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const VRCXUpdateDialogRef = ref(null);
|
const VRCXUpdateDialogIndex = ref(2000);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => VRCXUpdateDialog,
|
() => VRCXUpdateDialog,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal.value.visible) {
|
if (newVal.value.visible) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
adjustDialogZ(VRCXUpdateDialogRef.value.$el);
|
VRCXUpdateDialogIndex.value = getNextDialogIndex();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
class="x-dialog"
|
class="x-dialog"
|
||||||
:visible="changeWorldImageDialogVisible"
|
:model-value="changeWorldImageDialogVisible"
|
||||||
:title="t('dialog.change_content_image.world')"
|
:title="t('dialog.change_content_image.world')"
|
||||||
width="850px"
|
width="850px"
|
||||||
append-to-body
|
append-to-body
|
||||||
@@ -16,106 +16,60 @@
|
|||||||
<span>{{ t('dialog.change_content_image.description') }}</span>
|
<span>{{ t('dialog.change_content_image.description') }}</span>
|
||||||
<br />
|
<br />
|
||||||
<el-button-group style="padding-bottom: 10px; padding-top: 10px">
|
<el-button-group style="padding-bottom: 10px; padding-top: 10px">
|
||||||
<el-button type="default" size="small" icon="el-icon-refresh" @click="refresh">{{
|
<el-button type="default" size="small" :icon="Upload" @click="uploadWorldImage">
|
||||||
t('dialog.change_content_image.refresh')
|
{{ t('dialog.change_content_image.upload') }}
|
||||||
}}</el-button>
|
</el-button>
|
||||||
<el-button type="default" size="small" icon="el-icon-upload2" @click="uploadWorldImage">{{
|
|
||||||
t('dialog.change_content_image.upload')
|
|
||||||
}}</el-button>
|
|
||||||
<!-- el-button(type="default" size="small" @click="deleteWorldImage" icon="el-icon-delete") Delete Latest Image-->
|
|
||||||
</el-button-group>
|
</el-button-group>
|
||||||
<br />
|
<br />
|
||||||
<div v-for="image in previousImagesTable" :key="image.version" style="display: inline-block">
|
<div class="x-change-image-item">
|
||||||
<div
|
<img :src="previousImageUrl" class="img-size" loading="lazy" />
|
||||||
v-if="image.file"
|
|
||||||
class="x-change-image-item"
|
|
||||||
style="cursor: pointer"
|
|
||||||
:class="{ 'current-image': compareCurrentImage(image) }"
|
|
||||||
@click="setWorldImage(image)">
|
|
||||||
<img v-lazy="image.file.url" class="image" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-dialog>
|
||||||
</safe-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { Upload } from '@element-plus/icons-vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { getCurrentInstance, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { imageRequest } from '../../../api';
|
import { worldRequest } from '../../../api';
|
||||||
import { AppGlobal } from '../../../service/appConfig';
|
import { useWorldStore } from '../../../stores';
|
||||||
import { $throw } from '../../../service/request';
|
|
||||||
import { extractFileId } from '../../../shared/utils';
|
|
||||||
import { useGalleryStore, useWorldStore } from '../../../stores';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const $message = instance.proxy.$message;
|
|
||||||
|
|
||||||
const { worldDialog } = storeToRefs(useWorldStore());
|
const { worldDialog } = storeToRefs(useWorldStore());
|
||||||
const { previousImagesTable } = storeToRefs(useGalleryStore());
|
const { applyWorld } = useWorldStore();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
changeWorldImageDialogVisible: {
|
changeWorldImageDialogVisible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
required: true
|
||||||
},
|
},
|
||||||
previousImagesFileId: {
|
previousImageUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:changeWorldImageDialogVisible', 'refresh']);
|
|
||||||
|
|
||||||
const changeWorldImageDialogLoading = ref(false);
|
const changeWorldImageDialogLoading = ref(false);
|
||||||
const worldImage = ref({
|
|
||||||
base64File: '',
|
|
||||||
fileMd5: '',
|
|
||||||
base64SignatureFile: '',
|
|
||||||
signatureMd5: '',
|
|
||||||
fileId: '',
|
|
||||||
avatarId: '',
|
|
||||||
worldId: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
function uploadWorldImage() {
|
const emit = defineEmits(['update:changeWorldImageDialogVisible', 'update:previousImageUrl']);
|
||||||
document.getElementById('WorldImageUploadButton').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
emit('update:changeWorldImageDialogVisible', false);
|
emit('update:changeWorldImageDialogVisible', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
|
||||||
emit('refresh', 'Change');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resizeImageToFitLimits(file) {
|
async function resizeImageToFitLimits(file) {
|
||||||
const response = await AppApi.ResizeImageToFitLimits(file);
|
const response = await AppApi.ResizeImageToFitLimits(file);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function genMd5(file) {
|
|
||||||
const response = await AppApi.MD5File(file);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function genSig(file) {
|
|
||||||
const response = await AppApi.SignFile(file);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function genLength(file) {
|
|
||||||
const response = await AppApi.FileLength(file);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFileChangeWorldImage(e) {
|
function onFileChangeWorldImage(e) {
|
||||||
const clearFile = function () {
|
const clearFile = function () {
|
||||||
const fileInput = /** @type {HTMLInputElement} */ (document.querySelector('#WorldImageUploadButton'));
|
changeWorldImageDialogLoading.value = false;
|
||||||
|
const fileInput = /** @type{HTMLInputElement} */ (document.querySelector('#WorldImageUploadButton'));
|
||||||
if (fileInput) {
|
if (fileInput) {
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
}
|
}
|
||||||
@@ -125,9 +79,11 @@
|
|||||||
clearFile();
|
clearFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate file
|
||||||
if (files[0].size >= 100000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 100MB
|
// 100MB
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.file.too_large'),
|
message: t('message.file.too_large'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
@@ -135,223 +91,57 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!files[0].type.match(/image.*/)) {
|
if (!files[0].type.match(/image.*/)) {
|
||||||
$message({
|
ElMessage({
|
||||||
message: t('message.file.not_image'),
|
message: t('message.file.not_image'),
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
clearFile();
|
clearFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
changeWorldImageDialogLoading.value = true;
|
|
||||||
const r = new FileReader();
|
const r = new FileReader();
|
||||||
r.onload = async function (file) {
|
r.onload = async function () {
|
||||||
try {
|
try {
|
||||||
const base64File = await resizeImageToFitLimits(btoa(r.result.toString()));
|
const base64File = await resizeImageToFitLimits(btoa(r.result.toString()));
|
||||||
// 10MB
|
// 10MB
|
||||||
const fileMd5 = await genMd5(base64File);
|
await initiateUpload(base64File);
|
||||||
const fileSizeInBytes = parseInt(file.total.toString(), 10);
|
|
||||||
const base64SignatureFile = await genSig(base64File);
|
|
||||||
const signatureMd5 = await genMd5(base64SignatureFile);
|
|
||||||
const signatureSizeInBytes = parseInt(await genLength(base64SignatureFile), 10);
|
|
||||||
const worldId = worldDialog.value.id;
|
|
||||||
const { imageUrl } = worldDialog.value.ref;
|
|
||||||
const fileId = extractFileId(imageUrl);
|
|
||||||
if (!fileId) {
|
|
||||||
$message({
|
|
||||||
message: t('message.world.image_invalid'),
|
|
||||||
type: 'error'
|
|
||||||
});
|
|
||||||
clearFile();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
worldImage.value = {
|
|
||||||
base64File,
|
|
||||||
fileMd5,
|
|
||||||
base64SignatureFile,
|
|
||||||
signatureMd5,
|
|
||||||
fileId,
|
|
||||||
worldId,
|
|
||||||
...worldImage.value
|
|
||||||
};
|
|
||||||
const params = {
|
|
||||||
fileMd5,
|
|
||||||
fileSizeInBytes,
|
|
||||||
signatureMd5,
|
|
||||||
signatureSizeInBytes
|
|
||||||
};
|
|
||||||
|
|
||||||
// Upload chaining
|
|
||||||
await initiateUpload(params, fileId);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('World image upload process failed:', error);
|
console.error('World image upload process failed:', error);
|
||||||
} finally {
|
} finally {
|
||||||
changeWorldImageDialogLoading.value = false;
|
|
||||||
clearFile();
|
clearFile();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
changeWorldImageDialogLoading.value = true;
|
||||||
r.readAsBinaryString(files[0]);
|
r.readAsBinaryString(files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------ Upload Process Start ------------
|
async function initiateUpload(base64File) {
|
||||||
|
const args = await worldRequest.uploadWorldImage(base64File);
|
||||||
async function initiateUpload(params, fileId) {
|
const fileUrl = args.json.versions[args.json.versions.length - 1].file.url;
|
||||||
const res = await imageRequest.uploadWorldImage(params, fileId);
|
const worldArgs = await worldRequest.saveWorld({
|
||||||
return worldImageInit(res);
|
id: worldDialog.value.id,
|
||||||
}
|
imageUrl: fileUrl
|
||||||
|
|
||||||
async function worldImageInit(args) {
|
|
||||||
const fileId = args.json.id;
|
|
||||||
const fileVersion = args.json.versions[args.json.versions.length - 1].version;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadWorldImageFileStart(params);
|
|
||||||
return worldImageFileStart(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function worldImageFileStart(args) {
|
|
||||||
const { url } = args.json;
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
url,
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
return uploadWorldImageFileAWS(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadWorldImageFileAWS(params) {
|
|
||||||
const json = await webApiService.execute({
|
|
||||||
url: params.url,
|
|
||||||
uploadFilePUT: true,
|
|
||||||
fileData: worldImage.value.base64File,
|
|
||||||
fileMIME: 'image/png',
|
|
||||||
headers: {
|
|
||||||
'Content-MD5': worldImage.value.fileMd5
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
const ref = applyWorld(worldArgs.json);
|
||||||
if (json.status !== 200) {
|
|
||||||
changeWorldImageDialogLoading.value = false;
|
changeWorldImageDialogLoading.value = false;
|
||||||
$throw(json.status, 'World image upload failed', params.url);
|
emit('update:previousImageUrl', ref.imageUrl);
|
||||||
}
|
ElMessage({
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return worldImageFileAWS(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function worldImageFileAWS(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadWorldImageFileFinish(params);
|
|
||||||
return worldImageFileFinish(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function worldImageFileFinish(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadWorldImageSigStart(params);
|
|
||||||
return worldImageSigStart(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function worldImageSigStart(args) {
|
|
||||||
const { url } = args.json;
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
url,
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
return uploadWorldImageSigAWS(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadWorldImageSigAWS(params) {
|
|
||||||
const json = await webApiService.execute({
|
|
||||||
url: params.url,
|
|
||||||
uploadFilePUT: true,
|
|
||||||
fileData: worldImage.value.base64SignatureFile,
|
|
||||||
fileMIME: 'application/x-rsync-signature',
|
|
||||||
headers: {
|
|
||||||
'Content-MD5': worldImage.value.signatureMd5
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (json.status !== 200) {
|
|
||||||
changeWorldImageDialogLoading.value = false;
|
|
||||||
$throw(json.status, 'World image upload failed', params.url);
|
|
||||||
}
|
|
||||||
const args = {
|
|
||||||
json,
|
|
||||||
params
|
|
||||||
};
|
|
||||||
return worldImageSigAWS(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function worldImageSigAWS(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const params = {
|
|
||||||
fileId,
|
|
||||||
fileVersion
|
|
||||||
};
|
|
||||||
const res = await imageRequest.uploadWorldImageSigFinish(params);
|
|
||||||
return worldImageSigFinish(res);
|
|
||||||
}
|
|
||||||
async function worldImageSigFinish(args) {
|
|
||||||
const { fileId, fileVersion } = args.params;
|
|
||||||
const parmas = {
|
|
||||||
id: worldImage.value.worldId,
|
|
||||||
imageUrl: `${AppGlobal.endpointDomain}/file/${fileId}/${fileVersion}/file`
|
|
||||||
};
|
|
||||||
const res = await imageRequest.setWorldImage(parmas);
|
|
||||||
return worldImageSet(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
function worldImageSet(args) {
|
|
||||||
changeWorldImageDialogLoading.value = false;
|
|
||||||
if (args.json.imageUrl === args.params.imageUrl) {
|
|
||||||
$message({
|
|
||||||
message: t('message.world.image_changed'),
|
message: t('message.world.image_changed'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
refresh();
|
|
||||||
} else {
|
// closeDialog();
|
||||||
$throw(0, 'World image change failed', args.params.imageUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------ Upload Process End ------------
|
function uploadWorldImage() {
|
||||||
|
document.getElementById('WorldImageUploadButton').click();
|
||||||
function setWorldImage(image) {
|
|
||||||
changeWorldImageDialogLoading.value = true;
|
|
||||||
const parmas = {
|
|
||||||
id: worldDialog.value.id,
|
|
||||||
imageUrl: `${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file`
|
|
||||||
};
|
|
||||||
imageRequest
|
|
||||||
.setWorldImage(parmas)
|
|
||||||
.then((args) => worldImageSet(args))
|
|
||||||
.finally(() => {
|
|
||||||
changeWorldImageDialogLoading.value = false;
|
|
||||||
closeDialog();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareCurrentImage(image) {
|
|
||||||
if (
|
|
||||||
`${AppGlobal.endpointDomain}/file/${props.previousImagesFileId}/${image.version}/file` ===
|
|
||||||
worldDialog.value.ref.imageUrl
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.img-size {
|
||||||
|
width: 500px;
|
||||||
|
height: 375px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
:visible.sync="isVisible"
|
v-model="isVisible"
|
||||||
:title="t('dialog.set_world_tags.header')"
|
:title="t('dialog.set_world_tags.header')"
|
||||||
width="400px"
|
width="400px"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<el-input
|
<el-input
|
||||||
v-model="setWorldTagsDialog.authorTags"
|
v-model="setWorldTagsDialog.authorTags"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
size="mini"
|
size="small"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
@@ -81,12 +81,13 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, getCurrentInstance } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { worldRequest } from '../../../api';
|
import { worldRequest } from '../../../api';
|
||||||
import { useWorldStore } from '../../../stores';
|
import { useWorldStore } from '../../../stores';
|
||||||
|
|
||||||
@@ -115,8 +116,6 @@
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const setWorldTagsDialog = ref({
|
const setWorldTagsDialog = ref({
|
||||||
authorTags: '',
|
authorTags: '',
|
||||||
contentTags: '',
|
contentTags: '',
|
||||||
@@ -296,7 +295,7 @@
|
|||||||
tags
|
tags
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Tags updated',
|
message: 'Tags updated',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<safe-dialog
|
<el-dialog
|
||||||
:visible.sync="isVisible"
|
v-model="isVisible"
|
||||||
:title="t('dialog.allowed_video_player_domains.header')"
|
:title="t('dialog.allowed_video_player_domains.header')"
|
||||||
width="600px"
|
width="600px"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
v-model="urlList[index]"
|
v-model="urlList[index]"
|
||||||
size="small"
|
size="small"
|
||||||
style="margin-top: 5px">
|
style="margin-top: 5px">
|
||||||
<el-button slot="append" icon="el-icon-delete" @click="urlList.splice(index, 1)"></el-button>
|
<el-button :icon="Delete" @click="urlList.splice(index, 1)"></el-button>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-button size="mini" style="margin-top: 5px" @click="urlList.push('')">
|
<el-button size="small" style="margin-top: 5px" @click="urlList.push('')">
|
||||||
{{ t('dialog.allowed_video_player_domains.add_domain') }}
|
{{ t('dialog.allowed_video_player_domains.add_domain') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,12 +27,16 @@
|
|||||||
{{ t('dialog.allowed_video_player_domains.save') }}
|
{{ t('dialog.allowed_video_player_domains.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</safe-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, getCurrentInstance } from 'vue';
|
import { Delete } from '@element-plus/icons-vue';
|
||||||
import { useI18n } from 'vue-i18n-bridge';
|
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
import { ref, computed, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { worldRequest } from '../../../api';
|
import { worldRequest } from '../../../api';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -44,8 +48,6 @@
|
|||||||
|
|
||||||
const emit = defineEmits(['update:worldAllowedDomainsDialog']);
|
const emit = defineEmits(['update:worldAllowedDomainsDialog']);
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const urlList = ref([]);
|
const urlList = ref([]);
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
urlList: urlList.value
|
urlList: urlList.value
|
||||||
})
|
})
|
||||||
.then((args) => {
|
.then((args) => {
|
||||||
proxy.$message({
|
ElMessage({
|
||||||
message: 'Allowed Video Player Domains updated',
|
message: 'Allowed Video Player Domains updated',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||||
|
/>
|
||||||
|
<meta name="referrer" content="no-referrer" />
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache" />
|
||||||
|
|
||||||
|
<title>VRCX</title>
|
||||||
|
|
||||||
|
<!-- <link rel="stylesheet" href="app.css" /> -->
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://api.vrchat.cloud" />
|
||||||
|
<link rel="preconnect" href="https://files.vrchat.cloud" />
|
||||||
|
<link rel="preconnect" href="https://d348imysud55la.cloudfront.net" />
|
||||||
|
<link rel="dns-prefetch" href="https://assets.vrchat.com" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"language": "Čeština (CZ)",
|
"language": "Čeština (cs)",
|
||||||
"translator": "-",
|
"translator": "-",
|
||||||
"nav_tooltip": {
|
"nav_tooltip": {
|
||||||
"feed": "Zdroj",
|
"feed": "Zdroj",
|
||||||
@@ -617,7 +617,7 @@
|
|||||||
"launch_commands": {
|
"launch_commands": {
|
||||||
"header": "Launch Commands (Deep Links)",
|
"header": "Launch Commands (Deep Links)",
|
||||||
"docs": "Launch Command Docs",
|
"docs": "Launch Command Docs",
|
||||||
"show_confirmation_on_switch_avatar_enable": "Show confirmation diaglog before switching avatars",
|
"show_confirmation_on_switch_avatar_enable": "Show confirmation dialog before switching avatars",
|
||||||
"show_confirmation_on_switch_avatar_tooltip": "When disabled VRCX will not come to the front when switching avatars and ask for confirmation",
|
"show_confirmation_on_switch_avatar_tooltip": "When disabled VRCX will not come to the front when switching avatars and ask for confirmation",
|
||||||
"website_userscript": "VRC Website Userscript"
|
"website_userscript": "VRC Website Userscript"
|
||||||
},
|
},
|
||||||
@@ -653,6 +653,7 @@
|
|||||||
"avatar_name_cache": "Avatar Name cache:",
|
"avatar_name_cache": "Avatar Name cache:",
|
||||||
"instance_cache": "Instance cache:",
|
"instance_cache": "Instance cache:",
|
||||||
"clear_cache": "Clear Cache",
|
"clear_cache": "Clear Cache",
|
||||||
|
"refresh_cache": "Refresh Cache",
|
||||||
"auto_clear_cache": "Auto Clear Cache",
|
"auto_clear_cache": "Auto Clear Cache",
|
||||||
"show_console": "Show Console"
|
"show_console": "Show Console"
|
||||||
},
|
},
|
||||||
@@ -766,7 +767,6 @@
|
|||||||
"show_avatar_author": "Show Avatar Author",
|
"show_avatar_author": "Show Avatar Author",
|
||||||
"show_fallback_avatar": "Show Fallback Avatar Details",
|
"show_fallback_avatar": "Show Fallback Avatar Details",
|
||||||
"show_previous_instances": "Show Previous Instances",
|
"show_previous_instances": "Show Previous Instances",
|
||||||
"show_previous_images": "Show Previous Images",
|
|
||||||
"moderation_block": "Block",
|
"moderation_block": "Block",
|
||||||
"moderation_unblock": "Unblock",
|
"moderation_unblock": "Unblock",
|
||||||
"moderation_mute": "Mute",
|
"moderation_mute": "Mute",
|
||||||
@@ -915,7 +915,6 @@
|
|||||||
"make_home": "Make Home",
|
"make_home": "Make Home",
|
||||||
"reset_home": "Reset Home",
|
"reset_home": "Reset Home",
|
||||||
"show_previous_instances": "Show Previous Instances",
|
"show_previous_instances": "Show Previous Instances",
|
||||||
"show_previous_images": "Show Previous Images",
|
|
||||||
"rename": "Rename",
|
"rename": "Rename",
|
||||||
"change_description": "Change Description",
|
"change_description": "Change Description",
|
||||||
"change_capacity": "Change Capacity",
|
"change_capacity": "Change Capacity",
|
||||||
@@ -1002,7 +1001,6 @@
|
|||||||
"select_fallback": "Select Fallback Avatar",
|
"select_fallback": "Select Fallback Avatar",
|
||||||
"block": "Block Avatar",
|
"block": "Block Avatar",
|
||||||
"unblock": "Unblock Avatar",
|
"unblock": "Unblock Avatar",
|
||||||
"show_previous_images": "Show Previous Images",
|
|
||||||
"make_public": "Make Public",
|
"make_public": "Make Public",
|
||||||
"make_private": "Make Private",
|
"make_private": "Make Private",
|
||||||
"rename": "Rename",
|
"rename": "Rename",
|
||||||
@@ -1118,7 +1116,6 @@
|
|||||||
"load_more": "Load more...",
|
"load_more": "Load more...",
|
||||||
"sort_by": "Sort By:",
|
"sort_by": "Sort By:",
|
||||||
"sorting": {
|
"sorting": {
|
||||||
"user_id": "User ID (Ascending)",
|
|
||||||
"joined_at_asc": "Joined At (Ascending)",
|
"joined_at_asc": "Joined At (Ascending)",
|
||||||
"joined_at_desc": "Joined At (Descending)"
|
"joined_at_desc": "Joined At (Descending)"
|
||||||
},
|
},
|
||||||
@@ -1227,7 +1224,8 @@
|
|||||||
"group": "Group",
|
"group": "Group",
|
||||||
"legacy": "Legacy",
|
"legacy": "Legacy",
|
||||||
"roles": "Roles",
|
"roles": "Roles",
|
||||||
"open_ingame": "Open in-game"
|
"open_ingame": "Open in-game",
|
||||||
|
"display_name": "Display Name (VRC+)"
|
||||||
},
|
},
|
||||||
"launch_options": {
|
"launch_options": {
|
||||||
"header": "VRChat Launch Options",
|
"header": "VRChat Launch Options",
|
||||||
@@ -1518,12 +1516,8 @@
|
|||||||
"avatar": "Change Avatar Image",
|
"avatar": "Change Avatar Image",
|
||||||
"world": "Change World Image",
|
"world": "Change World Image",
|
||||||
"description": "Recommended image size: 1200x900px (4:3)",
|
"description": "Recommended image size: 1200x900px (4:3)",
|
||||||
"refresh": "Refresh",
|
|
||||||
"upload": "Upload Image"
|
"upload": "Upload Image"
|
||||||
},
|
},
|
||||||
"previous_images": {
|
|
||||||
"header": "Previous Images"
|
|
||||||
},
|
|
||||||
"previous_instances": {
|
"previous_instances": {
|
||||||
"header": "Previous Instances",
|
"header": "Previous Instances",
|
||||||
"info": "Previous Instance Info",
|
"info": "Previous Instance Info",
|
||||||
@@ -1663,6 +1657,10 @@
|
|||||||
"choose_group_placeholder": "Choose Group",
|
"choose_group_placeholder": "Choose Group",
|
||||||
"groups_with_moderation_permission": "Groups with Moderation Permission",
|
"groups_with_moderation_permission": "Groups with Moderation Permission",
|
||||||
"moderation_tools": "Moderation Tools"
|
"moderation_tools": "Moderation Tools"
|
||||||
|
},
|
||||||
|
"fullscreen_image": {
|
||||||
|
"download_and_save_image": "Download and save image",
|
||||||
|
"copy_image_to_clipboard": "Copy image to clipboard"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
|
|||||||
@@ -1,73 +1,44 @@
|
|||||||
import en from './en/en.json' assert { type: 'JSON' };
|
import en from './en/en.json' assert { type: 'JSON' };
|
||||||
import elements_en from 'element-ui/lib/locale/lang/en';
|
|
||||||
|
|
||||||
import es from './es/en.json' assert { type: 'JSON' };
|
import es from './es/en.json' assert { type: 'JSON' };
|
||||||
import elements_es from 'element-ui/lib/locale/lang/es';
|
|
||||||
|
|
||||||
import fr from './fr/en.json' assert { type: 'JSON' };
|
import fr from './fr/en.json' assert { type: 'JSON' };
|
||||||
import elements_fr from 'element-ui/lib/locale/lang/fr';
|
|
||||||
|
|
||||||
// import hu from './hu/en.json' assert { type: 'JSON' };
|
import hu from './hu/en.json' assert { type: 'JSON' };
|
||||||
// import elements_hu from 'element-ui/lib/locale/lang/hu';
|
|
||||||
|
|
||||||
import ja from './ja/en.json' assert { type: 'JSON' };
|
import ja from './ja/en.json' assert { type: 'JSON' };
|
||||||
import elements_ja from 'element-ui/lib/locale/lang/ja';
|
|
||||||
|
|
||||||
import ko from './ko/en.json' assert { type: 'JSON' };
|
import ko from './ko/en.json' assert { type: 'JSON' };
|
||||||
import elements_ko from 'element-ui/lib/locale/lang/ko';
|
|
||||||
|
|
||||||
import pl from './pl/en.json' assert { type: 'JSON' };
|
import pl from './pl/en.json' assert { type: 'JSON' };
|
||||||
import elements_pl from 'element-ui/lib/locale/lang/pl';
|
|
||||||
|
|
||||||
import pt from './pt/en.json' assert { type: 'JSON' };
|
import pt from './pt/en.json' assert { type: 'JSON' };
|
||||||
import elements_pt from 'element-ui/lib/locale/lang/pt';
|
|
||||||
|
|
||||||
import cz from './cz/en.json' assert { type: 'JSON' };
|
import cs from './cs/en.json' assert { type: 'JSON' };
|
||||||
import elements_cz from 'element-ui/lib/locale/lang/cs-CZ';
|
|
||||||
|
|
||||||
import ru_RU from './ru/en.json' assert { type: 'JSON' };
|
import ru from './ru/en.json' assert { type: 'JSON' };
|
||||||
import elements_ru from 'element-ui/lib/locale/lang/ru-RU';
|
|
||||||
|
|
||||||
import vi from './vi/en.json' assert { type: 'JSON' };
|
import vi from './vi/en.json' assert { type: 'JSON' };
|
||||||
import elements_vi from 'element-ui/lib/locale/lang/vi';
|
|
||||||
|
|
||||||
import zh_CN from './zh-CN/en.json' assert { type: 'JSON' };
|
import zh_CN from './zh-CN/en.json' assert { type: 'JSON' };
|
||||||
import elements_zh_CN from 'element-ui/lib/locale/lang/zh-CN';
|
|
||||||
|
|
||||||
import zh_TW from './zh-TW/en.json' assert { type: 'JSON' };
|
import zh_TW from './zh-TW/en.json' assert { type: 'JSON' };
|
||||||
import elements_zh_TW from 'element-ui/lib/locale/lang/zh-TW';
|
|
||||||
|
|
||||||
import th from './th/en.json' assert { type: 'JSON' };
|
import th from './th/en.json' assert { type: 'JSON' };
|
||||||
import elements_th from 'element-ui/lib/locale/lang/th';
|
|
||||||
|
|
||||||
const localized_en = { ...en, ...elements_en };
|
|
||||||
const localized_es = { ...es, ...elements_es };
|
|
||||||
const localized_fr = { ...fr, ...elements_fr };
|
|
||||||
// const localized_hu = { ...hu, ...elements_hu };
|
|
||||||
const localized_ja = { ...ja, ...elements_ja };
|
|
||||||
const localized_ko = { ...ko, ...elements_ko };
|
|
||||||
const localized_pl = { ...pl, ...elements_pl };
|
|
||||||
const localized_pt = { ...pt, ...elements_pt };
|
|
||||||
const localized_cz = { ...cz, ...elements_cz };
|
|
||||||
const localized_ru = { ...ru_RU, ...elements_ru };
|
|
||||||
const localized_vi = { ...vi, ...elements_vi };
|
|
||||||
const localized_zh_CN = { ...zh_CN, ...elements_zh_CN };
|
|
||||||
const localized_zh_TW = { ...zh_TW, ...elements_zh_TW };
|
|
||||||
const localized_th = { ...th, ...elements_th };
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
localized_en as en,
|
en,
|
||||||
localized_es as es,
|
es,
|
||||||
localized_fr as fr,
|
fr,
|
||||||
// localized_hu as hu,
|
hu,
|
||||||
localized_ja as ja_JP,
|
ja,
|
||||||
localized_ko as ko,
|
ko,
|
||||||
localized_pl as pl,
|
pl,
|
||||||
localized_pt as pt,
|
pt,
|
||||||
localized_cz as cz,
|
cs,
|
||||||
localized_ru as ru_RU,
|
ru,
|
||||||
localized_vi as vi,
|
vi,
|
||||||
localized_zh_CN as zh_CN,
|
zh_CN,
|
||||||
localized_zh_TW as zh_TW,
|
zh_TW,
|
||||||
localized_th as th
|
th
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user